• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yast / yast-yast2 / 13439774818

20 Feb 2025 04:17PM UTC coverage: 41.869% (-0.02%) from 41.889%
13439774818

Pull #1316

github

web-flow
Merge aa675c3b3 into 037b94b1f
Pull Request #1316: Respect Agama kernel parameters

2 of 4 new or added lines in 1 file covered. (50.0%)

265 existing lines in 40 files now uncovered.

12605 of 30106 relevant lines covered (41.87%)

10.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.04
/library/packages/src/modules/PackageCallbacks.rb
1
# ***************************************************************************
2
#
3
# Copyright (c) 2002 - 2012 Novell, Inc.
4
# All Rights Reserved.
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of version 2 of the GNU General Public License as
8
# published by the Free Software Foundation.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, contact Novell, Inc.
17
#
18
# To contact Novell about this file by physical or electronic mail,
19
# you may find current contact information at www.novell.com
20
#
21
# ***************************************************************************
22

23
require "yast"
1✔
24
require "uri"
1✔
25
require "packages/dummy_callbacks"
1✔
26
require "packages/file_conflict_callbacks"
1✔
27

28
module Yast
1✔
29
  # Provides the default Callbacks for Pkg::
30
  class PackageCallbacksClass < Module
1✔
31
    include Yast::Logger
1✔
32

33
    # text to clean progress bar in command line
34
    CLEAR_PROGRESS_TEXT = ("\b" * 10) + (" " * 10) + ("\b" * 10)
1✔
35
    # max. length of the text in the repository popup window
36
    MAX_POPUP_TEXT_SIZE = 60
1✔
37
    # base in seconds for automatic retry after a timeout,
38
    # it will be logarithmic increased upto {RETRY_MAX_TIMEOUT}
39
    RETRY_TIMEOUT = 30
1✔
40
    # number of automatic retries
41
    RETRY_ATTEMPTS = 100
1✔
42
    # max. retry timeout (15 minutes)
43
    RETRY_MAX_TIMEOUT = 15 * 60
1✔
44
    # symbols for ticking in cmd line
45
    TICK_LABELS = ["/", "-", "\\", "|"].freeze
1✔
46

47
    # Debugging: log the called callbacks when Y2DEBUG_CALLBACKS is set to 1
48
    #
49
    # This uses some Ruby meta programming, the "method_added" is called whenever
50
    # a new method is added into this class, i.e. when each of the following "def"
51
    # is processed.
52
    #
53
    # @param name [Symbol] name of the added method
54
    def self.method_added(name)
1✔
55
      super
127✔
56

57
      # log the callbacks only when requested, it's quite verbose
58
      return if ENV["Y2DEBUG_CALLBACKS"] != "1"
127✔
59

60
      name_str = name.to_s
×
61

62
      # do not add a hook for a hook itself otherwise it would result
63
      # in an endless recursive loop adding a hook for a hook for a hook for...
64
      if name_str.end_with?("_hook") ||
×
65
          # ignore dynamically added helper methods for the published variables
66
          name_str.start_with?("_") ||
67
          # ignore lowercase methods, they are just some helper methods
68
          name_str.match(/^[[:lower:]]/) ||
69
          # already present
70
          method_defined?("#{name}_hook")
71

72
        return
×
73
      end
74

75
      # add a new *_hook method as a wrapper for the original method,
76
      # log the name of the called method
77
      hook = <<-HOOK
×
78
      def #{name}_hook(*params)
79
        log.info("Starting callback #{self}::#{name}")
80
        result = #{name}_without_hook(*params)
81
        log.info("Callback #{self}::#{name} returned: \#{result.inspect}")
82
        result
83
      end
84
      HOOK
85
      # __FILE__ and __LINE__ are used in a backtrace
86
      class_eval(hook, __FILE__, __LINE__)
×
87

88
      # rename the original method
89
      class_eval("alias #{name}_without_hook #{name}", __FILE__, __LINE__) # alias m_without_hook m
×
90

91
      # replace the original method with the hook
92
      class_eval("alias #{name} #{name}_hook", __FILE__, __LINE__) # alias m m_hook
×
93
    end
94

95
    def main
1✔
96
      Yast.import "Pkg"
1✔
97
      Yast.import "UI"
1✔
98

99
      textdomain "base"
1✔
100

101
      Yast.import "Directory"
1✔
102
      Yast.import "Label"
1✔
103
      Yast.import "Mode"
1✔
104
      Yast.import "Stage"
1✔
105
      Yast.import "Popup"
1✔
106
      Yast.import "URL"
1✔
107
      Yast.import "CommandLine"
1✔
108
      Yast.import "String"
1✔
109
      Yast.import "Icon"
1✔
110
      Yast.import "Report"
1✔
111
      Yast.import "Wizard"
1✔
112
      Yast.import "Progress"
1✔
113
      Yast.import "FileUtils"
1✔
114
      Yast.import "SignatureCheckCallbacks"
1✔
115
      Yast.import "Linuxrc"
1✔
116

117
      @_provide_popup = false
1✔
118
      @_package_popup = false
1✔
119
      @_script_popup = false
1✔
120
      @_scan_popup = false
1✔
121
      @_package_name = ""
1✔
122
      @_package_size = 0
1✔
123
      @_deleting_package = false
1✔
124

125
      @_current_source = 1
1✔
126

127
      # make showLongInfo module-global so it gets remembered (cf. #14018)
128
      @showLongInfo = false
1✔
129

130
      # used to en-/disable StartPackage, ProgressPackage and DonePackage
131
      @enable_asterix_package = true
1✔
132

133
      @provide_aborted = false
1✔
134
      @source_aborted = false
1✔
135

136
      @back_string = "\b\b\b\b\b\b\b\b\b\b"
1✔
137
      @clear_string = Ops.add(Ops.add(@back_string, "          "), @back_string)
1✔
138

139
      # max. length of the text in the repository popup window
140
      @max_size = 60
1✔
141

142
      @autorefreshing = false
1✔
143
      @autorefreshing_aborted = false
1✔
144

145
      # Location of the persistent storage
146
      @conf_file = File.join(Directory.vardir, "/package_callbacks.conf")
1✔
147
      @config = nil
1✔
148

149
      # auto ejecting is in progress
150
      @doing_eject = false
1✔
151

152
      # current values for retry functionality
153
      @retry_url = ""
1✔
154
      @current_retry_timeout = RETRY_TIMEOUT
1✔
155
      @current_retry_attempt = 0
1✔
156

157
      #=============================================================================
158
      #  MEDIA CHANGE
159
      #=============================================================================
160

161
      @detected_cd_devices = []
1✔
162

163
      # reference counter to the open popup window
164
      @_source_open = 0
1✔
165

166
      @download_file = ""
1✔
167

168
      # TODO: use the ID in the prgress popup callbacks,
169
      # then callbacks may be nested...
170

171
      @tick_progress = false
1✔
172
      @val_progress = false
1✔
173
      @current_tick = 0
1✔
174

175
      # ProgressStart/End events may be nested, remember the types of progresses
176
      @progress_stack = []
1✔
177

178
      @last_stage = 0
1✔
179

180
      @opened_wizard = []
1✔
181

182
      Builtins.y2milestone("PackageCallbacks constructor")
1✔
183
      InitPackageCallbacks()
1✔
184
    end
185

186
    #--------------------------------------------------------------------------
187
    # defaults
188

189
    # at start of file providal
190
    #
191
    def StartProvide(name, archivesize, remote)
1✔
192
      Builtins.y2milestone("StartProvide: name: %1, remote: %2", name, remote)
×
193
      if remote
×
194
        sz = String.FormatSizeWithPrecision(archivesize, 2, false)
×
195
        if Mode.commandline
×
196
          CommandLine.PrintVerbose(
×
197
            Builtins.sformat(_("Downloading package %1 (%2)..."), name, sz)
198
          )
199
        else
200
          UI.CloseDialog if @_provide_popup
×
201

202
          if full_screen
×
203
            Progress.SubprogressType(:progress, 100)
×
204
            Progress.SubprogressTitle(
×
205
              Builtins.sformat(_("Downloading package %1 (%2)..."), name, sz)
206
            )
207
          else
208
            # popup heading
209
            providebox = progress_box(_("Downloading Package"), name, sz)
×
210
            UI.OpenDialog(providebox)
×
211
            @_provide_popup = true
×
212
          end
213
        end
214
      end
215

UNCOV
216
      nil
×
217
    end
218

219
    # during file providal
220
    #
221
    def ProgressProvide(percent)
1✔
222
      Builtins.y2milestone("ProgressProvide: %1", percent)
×
223
      if @_provide_popup
×
224
        UI.ChangeWidget(Id(:progress), :Value, percent)
×
225
        @provide_aborted = UI.PollInput == :abort
×
226
        return !@provide_aborted
×
227
      elsif Mode.commandline
×
228
        # there is no popup window, but command line mode is set
229
        CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{percent}%")
×
230
      end
231
      true
×
232
    end
233

234
    # redirect ProgressDeltaApply callback (a different signature is required)
235
    def ProgressDeltaApply(percent)
1✔
236
      ProgressProvide(percent)
×
237

UNCOV
238
      nil
×
239
    end
240

241
    # during file providal
242
    #  *
243
    #  // return "I" for ignore
244
    #  // return "R" for retry
245
    #  // return "C" for abort
246
    def DoneProvide(error, reason, name)
1✔
247
      Builtins.y2milestone("DoneProvide: %1, %2, %3", error, reason, name)
8✔
248

249
      if @_provide_popup
8✔
250
        UI.CloseDialog
×
251
        @_provide_popup = false
×
252
      end
253

254
      if Mode.commandline
8✔
255
        # remove the progress
256
        CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT)
×
257
      end
258

259
      if @provide_aborted
8✔
260
        @provide_aborted = false
×
261
        return "C"
×
262
      end
263

264
      # https://github.com/openSUSE/libzypp/blob/8dda46306f06440e1acaefb36fb60f6ce909fd42/zypp/ZYppCallbacks.h#L106
265
      message =
266
        case error
8✔
267
        when 1
268
          # NOT_FOUND (error = 1) is handled via MediaChange callback.
269
          nil
×
270
        when 2
271
          Builtins.sformat(_("Package %1 could not be downloaded (input/output error)."), name)
5✔
272
        when 3
273
          Builtins.sformat(_("Package %1 is broken, integrity check has failed."), name)
1✔
274
        else
275
          log.warn "DoneProvide: unknown error '#{error}'"
2✔
276
        end
277

278
      # IO/INVALID
279
      if message
8✔
280
        # error message, %1 is a package name
281

282
        if Mode.commandline
6✔
283
          CommandLine.Print(message)
×
284

285
          # ask user in the interactive mode
286
          if CommandLine.Interactive
×
287
            CommandLine.Print("")
×
288

289
            # command line mode - ask user whether installation of the failed package should be retried
290
            CommandLine.Print(_("Retry installation of the package?"))
×
291

292
            if CommandLine.YesNo
×
293
              # return Retry
294
              return "R"
×
295
            end
296

297
            # command line mode - ask user whether the installation should be aborted
298
            CommandLine.Print(_("Abort the installation?"))
×
299
            if CommandLine.YesNo
×
300
              # return Abort
301
              return "C"
×
302
            end
303

304
            # otherwise return Ignore (default)
305
            return "I"
×
306
          end
307

308
          return "I"
×
309
        end
310

311
        button_box = ButtonBox(
6✔
312
          PushButton(Id(:abort), Opt(:cancelButton, :key_F9), Label.AbortButton),
313
          PushButton(Id(:retry), Opt(:customButton), Label.RetryButton),
314
          PushButton(Id(:ignore), Opt(:okButton), Label.IgnoreButton)
315
        )
316

317
        if @showLongInfo
6✔
318
          UI.OpenDialog(
×
319
            Opt(:decorated),
320
            layout_popup(message, button_box, true)
321
          )
322
          UI.ReplaceWidget(
×
323
            Id(:info),
324
            RichText(
325
              Opt(:plainText),
326
              Ops.add(Builtins.sformat(_("Error: %1:"), error), reason)
327
            )
328
          )
329
        else
330
          UI.OpenDialog(
6✔
331
            Opt(:decorated),
332
            layout_popup(message, button_box, false)
333
          )
334
          UI.ReplaceWidget(Id(:info), Empty())
6✔
335
        end
336

337
        r = nil
6✔
338
        loop do
6✔
339
          r = UI.UserInput
7✔
340
          if r == :show
7✔
341
            @showLongInfo = show_log_info(message, button_box)
1✔
342
            if @showLongInfo
1✔
343
              error_symbol = "ERROR"
1✔
344

345
              # https://github.com/openSUSE/libzypp/blob/8dda46306f06440e1acaefb36fb60f6ce909fd42/zypp/ZYppCallbacks.h#L106
346
              case error
1✔
347
              when 2
348
                error_symbol = "IO"
1✔
349
              when 3
350
                error_symbol = "INVALID"
×
351
              end
352

353
              UI.ReplaceWidget(
1✔
354
                Id(:info),
355
                RichText(
356
                  Opt(:plainText),
357
                  Ops.add(
358
                    # error message, %1 is code of the error,
359
                    # detail string is appended to the end
360
                    Builtins.sformat(_("Error: %1:"), error_symbol),
361
                    reason
362
                  )
363
                )
364
              )
365
            else
366
              UI.ReplaceWidget(Id(:info), Empty())
×
367
            end
368
          end
369
          break if [:abort, :retry, :ignore].include?(r)
7✔
370
        end
371

372
        Builtins.y2milestone("DoneProvide %1", r)
6✔
373

374
        UI.CloseDialog
6✔
375

376
        return "C" if r == :abort
6✔
377
        return "R" if r == :retry
3✔
378

379
        if r == :ignore
2✔
380
          # don't show the warning when a refresh fails or for signature errors (error 3)
381
          if !@autorefreshing && error != 3
2✔
382
            # TODO: add "Don't show again" checkbox
383
            # a warning popup displayed after pressing [Ignore] after a download error
384
            Popup.Warning(
2✔
385
              _(
386
                "Ignoring a download failure may result in a broken system.\nVerify the system later by running the Software Management module.\n"
387
              )
388
            )
389
          end
390

391
          return "I"
2✔
392
        end
393

394
        Builtins.y2error("Unknown user input: %1", r)
×
395
      end
396

397
      "I"
2✔
398
    end
399

400
    # Enable or disable StartPackage, ProgressPackage and DonePackage
401
    # callbacks, but only the progress bar and not the final error
402
    # message.  Returns old value.
403
    # @note nasty hack for inst_do_net_test client. Remove it when client disappear
404
    def EnableAsterixPackage(value)
1✔
405
      ret = @enable_asterix_package
×
406
      @enable_asterix_package = value
×
407
      ret
×
408
    end
409

410
    #  At start of package install.
411
    def StartPackage(name, _location, _summary, installsize, is_delete)
1✔
412
      return if !@enable_asterix_package
×
413

414
      @_package_name = name
×
415
      @_package_size = installsize
×
416
      @_deleting_package = is_delete
×
417
      sz = String.FormatSizeWithPrecision(installsize, 2, false)
×
418

419
      if Mode.commandline
×
420
        CommandLine.PrintVerbose(
×
421
          Builtins.sformat(
422
            if is_delete
×
423
              _("Uninstalling package %1 (%2)...")
×
424
            else
425
              _("Installing package %1 (%2)...")
×
426
            end,
427
            @_package_name,
428
            sz
429
          )
430
        )
431
      else
432
        packagebox = progress_box(
×
433
          is_delete ? _("Uninstalling Package") : _("Installing Package"),
×
434
          @_package_name,
435
          sz
436
        )
437

438
        UI.OpenDialog(Opt(:decorated), packagebox)
×
439
        @_package_popup = true
×
440
      end
441

UNCOV
442
      nil
×
443
    end
444

445
    #  During package install.
446
    def ProgressPackage(percent)
1✔
447
      if @_package_popup
×
448
        UI.ChangeWidget(Id(:progress), :Value, percent)
×
449
        return UI.PollInput != :abort
×
450
      elsif Mode.commandline
×
451
        CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{percent}%")
×
452
        if percent == 100
×
453
          # sleep for a wile
454
          Builtins.sleep(200)
×
455
          # remove the progress
456
          CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT)
×
457
        end
458
      end
459

460
      true
×
461
    end
462

463
    # Handle GPG check result (pkgGpgCheck)
464
    #
465
    # If insecure mode is set to '1', the check result is ignored. Otherwise, no decision is made.
466
    # When running on an installed system, it always return "".
467
    #
468
    # @param data [Hash] Output from `pkgGpgCheck` callback.
469
    # @option data [Integer] "CheckPackageResult" Check result code according to libzypp.
470
    # @option data [String]  "Package" Package's name.
471
    # @option data [String]  "Localpath" Path to RPM file.
472
    # @option data [String]  "RepoMediaUrl" Media URL.
473
    # @return [String] "I" if the package should be accepted; otherwise
474
    #   a blank string is returned (so no decision is made).
475
    def pkg_gpg_check(data)
1✔
476
      log.debug("pkgGpgCheck data: #{data}")
4✔
477

478
      log.warn("Signature check failed: #{data}") if data["CheckPackageResult"] && data["CheckPackageResult"] != 0
4✔
479

480
      insecure = Stage.initial && Linuxrc.InstallInf("Insecure") == "1"
4✔
481
      insecure ? "I" : ""
4✔
482
    end
483

484
    #  After package install.
485
    #
486
    #  return "I" for ignore
487
    #  return "R" for retry
488
    #  return "C" for abort (not implemented !)
489
    def DonePackage(error, reason)
1✔
490
      # remove invalid characters (bnc#876459)
491
      if !reason.valid_encoding?
×
492
        reason.encode!("UTF-16", undef: :replace, invalid: :replace, replace: "?")
×
493
        reason.encode!("UTF-8")
×
494
        log.warn "Invalid byte sequence found, fixed text: #{reason}"
×
495
      end
496

497
      UI.CloseDialog if @_package_popup
×
498
      @_package_popup = false
×
499

500
      if error == 0
×
501
        # no error, there is additional info (rpm output), see bnc#456446
502
        Builtins.y2milestone("Additional RPM otput: %1", reason)
×
503

504
        CommandLine.Print(reason) if Mode.commandline
×
505
      else
506
        Builtins.y2milestone(
×
507
          "DonePackage(error: %1, reason: '%2')",
508
          error,
509
          reason
510
        )
511

512
        message = Builtins.sformat(
×
513
          if @_deleting_package
×
514
            # error popup during package installation, %1 is the name of the package
515
            _("Removal of package %1 failed.")
×
516
          else
517
            # error popup during package installation, %1 is the name of the package
518
            _("Installation of package %1 failed.")
×
519
          end,
520
          @_package_name
521
        )
522

523
        if Mode.commandline
×
524
          CommandLine.Print(message)
×
525
          CommandLine.Print(reason)
×
526

527
          # ask user in the interactive mode
528
          if CommandLine.Interactive
×
529
            CommandLine.Print("")
×
530

531
            # command line mode - ask user whether installation of the failed package should be retried
532
            CommandLine.Print(_("Retry installation of the package?"))
×
533

534
            if CommandLine.YesNo
×
535
              # return Retry
536
              return "R"
×
537
            end
538

539
            # command line mode - ask user whether the installation should be aborted
540
            CommandLine.Print(_("Abort the installation?"))
×
541
            if CommandLine.YesNo
×
542
              # return Abort
543
              return "C"
×
544
            end
545

546
            # otherwise return Ignore (default)
547
            return "I"
×
548
          end
549
        else
550
          button_box = ButtonBox(
×
551
            PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton),
552
            PushButton(Id(:retry), Opt(:customButton), Label.RetryButton),
553
            PushButton(Id(:ignore), Opt(:okButton), Label.IgnoreButton)
554
          )
555

556
          if @showLongInfo
×
557
            UI.OpenDialog(
×
558
              Opt(:decorated),
559
              layout_popup(message, button_box, true)
560
            )
561
            UI.ReplaceWidget(Id(:info), RichText(Opt(:plainText), reason))
×
562
          else
563
            UI.OpenDialog(
×
564
              Opt(:decorated),
565
              layout_popup(message, button_box, false)
566
            )
567
            UI.ReplaceWidget(Id(:info), Empty())
×
568
          end
569

570
          r = nil
×
571
          loop do
×
572
            r = UI.UserInput
×
573
            if r == :show
×
574
              @showLongInfo = show_log_info(message, button_box)
×
575
              if @showLongInfo
×
576
                UI.ReplaceWidget(Id(:info), RichText(Opt(:plainText), reason))
×
577
              else
578
                UI.ReplaceWidget(Id(:info), Empty())
×
579
              end
580
            end
581
            break if [:abort, :retry, :ignore].include?(r)
×
582
          end
583
          Builtins.y2milestone("DonePackage %1", r)
×
584

585
          UI.CloseDialog
×
586

587
          if r == :ignore
×
588
            # TODO: add "Don't show again" checkbox
589
            # a warning popup displayed after pressing [Ignore] after a package installation error
590
            Popup.Warning(
×
591
              _(
592
                "Ignoring a package failure may result in a broken system.\nThe system should be later verified by running the Software Management module."
593
              )
594
            )
595
          end
596

597
          return "C" if r == :abort
×
598
          return "R" if r == :retry
×
599
        end
600

601
        # default: ignore
602
      end
603

604
      "I"
×
605
    end
606

607
    #-------------------------------------------------------------------------
608
    #
609
    # media change callback
610
    #
611
    # if current == -1, show "Ignore"
612
    #
613
    # return "" for ok, retry
614
    # return "E" for eject media
615
    # return "I" for ignore bad media
616
    # return "S" for skip this media
617
    # return "C" for cancel (not implemented !)
618
    # return url to change media URL
619

620
    def MediaChange(error_code, error, url, product, current, current_label, wanted, wanted_label, double_sided, devices, current_device)
1✔
621
      devices = deep_copy(devices)
×
622
      if @autorefreshing && @autorefreshing_aborted
×
623
        Builtins.y2milestone("Refresh aborted")
×
624
        return "C"
×
625
      end
626

627
      Builtins.y2milestone(
×
628
        "MediaChange error: err'%1', url'%2', prd'%3', cur'%4'/'%5', wan'%6'/'%7', devs: %8, curr_dev: %9",
629
        Ops.add(Ops.add(error_code, ":"), error),
630
        URL.HidePassword(url),
631
        product,
632
        current,
633
        current_label,
634
        wanted,
635
        wanted_label,
636
        devices,
637
        current_device
638
      )
639

640
      if full_screen
×
641
        # make sure the old subprogress is cleared when displaying a popup (bsc#1175926)
642
        Progress.SubprogressValue(0)
×
643
        Progress.SubprogressTitle("")
×
644
      end
645

646
      url_scheme = Ops.get_string(URL.Parse(url), "scheme", "").downcase
×
647

648
      # true if it makes sense to offer an eject button (for cd/dvd only ...)
649
      is_disc = ["cd", "dvd"].include?(url_scheme)
×
650

651
      # do automatic eject
652
      if is_disc && autoeject && !@doing_eject
×
653
        Builtins.y2milestone("Automatically ejecting the medium...")
×
654
        @doing_eject = true
×
655
        return "E"
×
656
      end
657

658
      if Builtins.issubstring(error, "ERROR(InstSrc:E_bad_id)")
×
659
        error =
660
          # error report
661
          _(
×
662
            "<p>The repository at the specified URL now provides a different media ID.\n" \
663
            "If the URL is correct, this indicates that the repository content has changed. To \n" \
664
            "continue using this repository, start <b>Installation Repositories</b> from \n" \
665
            "the YaST control center and refresh the repository.</p>\n"
666
          )
667
      end
668

669
      if wanted_label == ""
×
670
        # use only product name for network repository
671
        # there is no medium 1, 2, ...
672
        if double_sided
×
673
          # media is double sided, we want the user to insert the 'Side A' of the media
674
          # the complete string will be "<product> <media> <number>, <side>"
675
          # e.g. "'SuSE Linux 9.0' DVD 1, Side A"
676
          side = _("Side A")
×
677
          if Ops.bitwise_and(wanted, 1) == 0
×
678
            # media is double sided, we want the user to insert the 'Side B' of the media
679
            side = _("Side B")
×
680
          end
681
          wanted = Ops.shift_right(Ops.add(wanted, 1), 1)
×
682
          wanted_label = if is_disc
×
683
            # label for a repository - %1 product name (e.g. "openSUSE 10.2"), %2 medium number (e.g. 2)
684
            # %3 side (e.g. "Side A")
685
            Builtins.sformat("%1 (Disc %2, %3)", product, wanted, side)
×
686
          else
687
            # label for a repository - %1 product name (e.g. "openSUSE 10.2"), %2 medium number (e.g. 2)
688
            # %3 side (e.g. "Side A")
689
            Builtins.sformat("%1 (Medium %2, %3)", product, wanted, side)
×
690
          end
691
        else
692
          wanted_label = if is_disc
×
693
            # label for a repository - %1 product name (e.g. openSUSE 10.2), %2 medium number (e.g. 2)
694
            Builtins.sformat(_("%1 (Disc %2)"), product, wanted)
×
695
          else
696
            # label for a repository - %1 product name (e.g. openSUSE 10.2), %2 medium number (e.g. 2)
697
            Builtins.sformat(_("%1 (Medium %2)"), product, wanted)
×
698
          end
699
        end
700
      end
701

702
      # prompt to insert product (%1 == "SuSE Linux version 9.2 CD 2")
703
      message = Builtins.sformat(_("Insert\n'%1'"), wanted_label)
×
704
      # with network repository it doesn't make sense to ask for disk
705
      if url_scheme == "dir"
×
706
        # report error while accessing local directory with product (%1 = URL, %2 = "SuSE Linux ...")
707
        message = Builtins.sformat(
×
708
          _(
709
            "Cannot access installation media\n" \
710
            "%1\n" \
711
            "%2.\n" \
712
            "Check whether the directory is accessible."
713
          ),
714
          URL.HidePassword(url),
715
          wanted_label
716
        )
717
      elsif !is_disc
×
718
        # report error while accessing network media of product (%1 = URL, %2 = "SuSE Linux ...")
719
        message = Builtins.sformat(
×
720
          _(
721
            "Cannot access installation media \n" \
722
            "%1\n" \
723
            "%2.\n" \
724
            "Check whether the server is accessible."
725
          ),
726
          URL.HidePassword(url),
727
          wanted_label
728
        )
729
      end
730

731
      # --------------------------------------
732
      # build up button box
733

734
      button_box = ButtonBox(
×
735
        PushButton(Id(:retry), Opt(:default, :okButton), Label.RetryButton)
736
      )
737

738
      button_box.params << PushButton(Id(:ignore), Opt(:customButton), Label.IgnoreButton) if current == -1 # wrong media id, offer "Ignore"
×
739

740
      button_box.params << PushButton(
×
741
        Id(:cancel),
742
        Opt(:cancelButton),
743
        @autorefreshing ? _("Skip Autorefresh") : Label.AbortButton
×
744
      )
745

746
      # push button label during media change popup, user can skip
747
      # this media (CD) so no packages from this media will be installed
748
      button_box.params << PushButton(Id(:skip), Opt(:customButton), _("&Skip"))
×
749

750
      if is_disc
×
751
        @detected_cd_devices = cd_devices(Ops.get(devices, current_device, "")) if !@doing_eject
×
752

753
        # detect the CD/DVD devices if the ejecting is not in progress,
754
        # the CD detection closes the ejected tray!
755
        cds = deep_copy(@detected_cd_devices)
×
756

757
        # display a menu button if there are more CD devices
758
        if Ops.greater_than(Builtins.size(cds), 1)
×
759
          # menu button label - used for more then one device
760
          button_box = HBox(button_box, MenuButton(_("&Eject"), cds))
×
761
        else
762
          # push button label - in the media change popup, user can eject the CD/DVD
763
          button_box.params << PushButton(Id(:eject), Opt(:customButton), _("&Eject"))
×
764
        end
765

766
        button_box = VBox(
×
767
          Left(
768
            CheckBox(
769
              Id(:auto_eject),
770
              _("A&utomatically Eject CD or DVD Medium"),
771
              autoeject
772
            )
773
          ),
774
          button_box
775
        )
776
      end
777

778
      @doing_eject = false
×
779

780
      # Autoretry code
781
      doing_auto_retry = false
×
782

783
      if error_code == "IO_SOFT" ||
×
784
          Builtins.contains(
785
            ["ftp", "sftp", "http", "https", "nfs", "smb"],
786
            url_scheme
787
          )
788
        # this a different file, reset the retry counter
789
        if @retry_url != url
×
790
          @retry_url = url
×
791
          @current_retry_attempt = 0
×
792
        end
793

794
        # is the maximum retry count reached?
795
        if Ops.less_than(@current_retry_attempt, RETRY_ATTEMPTS)
×
796
          # reset the counter, use logarithmic back-off with maximum limit
797
          @current_retry_timeout = if @current_retry_attempt < 10
×
798
            RETRY_TIMEOUT * (1 << @current_retry_attempt)
×
799
          else
800
            RETRY_MAX_TIMEOUT
×
801
          end
802

803
          @current_retry_timeout = RETRY_MAX_TIMEOUT if Ops.greater_than(@current_retry_timeout, RETRY_MAX_TIMEOUT)
×
804

805
          button_box = VBox(
×
806
            # failed download will be automatically retried after the timeout, %1 = formatted time (MM:SS format)
807
            Left(Label(Id(:auto_retry), retry_label(@current_retry_timeout))),
808
            button_box
809
          )
810

811
          doing_auto_retry = true
×
812
        else
813
          Builtins.y2warning(
×
814
            "Max. autoretry count (%1) reached, giving up...",
815
            RETRY_ATTEMPTS
816
          )
817
        end
818
      end
819

820
      Builtins.y2milestone("Autoretry: %1", doing_auto_retry)
×
821

822
      Builtins.y2milestone("Autoretry attempt: %1", @current_retry_attempt) if doing_auto_retry
×
823

824
      if Mode.commandline
×
825
        CommandLine.Print(message)
×
826
        CommandLine.Print(error)
×
827

828
        # ask user in the interactive mode
829
        if CommandLine.Interactive
×
830
          CommandLine.Print("")
×
831

832
          # command line mode - ask user whether installation of the failed package should be retried
833
          CommandLine.Print(_("Retry the installation?"))
×
834

835
          if CommandLine.YesNo
×
836
            # return Retry
837
            return ""
×
838
          end
839

840
          # command line mode - ask user whether the installation should be aborted
841
          CommandLine.Print(_("Skip the medium?"))
×
842
          if CommandLine.YesNo
×
843
            # return Skip
844
            return "S"
×
845
          end
846

847
          # otherwise ignore the medium
848
          CommandLine.Print(_("Ignoring the bad medium..."))
×
849
          return "I"
×
850
        end
851

852
        return "S"
×
853
      end
854

855
      Builtins.y2debug(
×
856
        "Opening Dialog: %1",
857
        layout_popup(message, button_box, true)
858
      )
859

860
      if @showLongInfo
×
861
        UI.OpenDialog(
×
862
          Opt(:decorated),
863
          layout_popup(message, button_box, true)
864
        )
865
        # TextEntry label
866
        UI.ReplaceWidget(
×
867
          Id(:info),
868
          VBox(
869
            InputField(Id(:url), Opt(:hstretch), _("&URL")),
870
            RichText(Opt(:plainText), error)
871
          )
872
        )
873
        UI.ChangeWidget(Id(:url), :Value, url)
×
874
      else
875
        UI.OpenDialog(
×
876
          Opt(:decorated),
877
          layout_popup(message, button_box, false)
878
        )
879
        UI.ReplaceWidget(Id(:info), Empty())
×
880
      end
881

882
      # notification
883
      UI.Beep
×
884

885
      r = nil
×
886

887
      eject_device = ""
×
888
      loop do
×
889
        r = doing_auto_retry ? UI.TimeoutUserInput(1000) : UI.UserInput
×
890

891
        # timout in autoretry mode?
892
        if doing_auto_retry
×
893
          if r == :timeout
×
894
            # decrease timeout counter
895
            @current_retry_timeout -= 1
×
896

897
            if @current_retry_timeout == 0
×
898
              Builtins.y2milestone("The time is out, doing automatic retry...")
×
899
              # do the retry
900
              r = :retry
×
901

902
              @current_retry_attempt += 1
×
903
            else
904
              # popup string - refresh the displayed counter
905
              UI.ChangeWidget(
×
906
                Id(:auto_retry),
907
                :Label,
908
                retry_label(@current_retry_timeout)
909
              )
910
            end
911
          else
912
            # user has pressed a button, reset the retry counter in the next timeout
913
            Builtins.y2milestone("User input: %1, resetting autoretry url", r)
×
914
            @retry_url = ""
×
915
          end
916
        end
917

918
        if r == :show
×
919
          @showLongInfo = show_log_info(message, button_box)
×
920
          if @showLongInfo
×
921
            # TextEntry label
922
            UI.ReplaceWidget(
×
923
              Id(:info),
924
              VBox(
925
                TextEntry(Id(:url), _("&URL")),
926
                RichText(Opt(:plainText), error)
927
              )
928
            )
929
            UI.ChangeWidget(Id(:url), :Value, url)
×
930
          else
931
            UI.ReplaceWidget(Id(:info), Empty())
×
932
          end
933
        elsif [:retry, :url].include?(r)
×
934
          if @showLongInfo # id(`url) must exist
×
935
            newurl = Convert.to_string(UI.QueryWidget(Id(:url), :Value))
×
936
            if newurl != url
×
937
              url = newurl
×
938
              r = :url
×
939
            end
940
          end
941
        elsif r.is_a?(::String) && r.start_with?("/dev/")
×
942
          Builtins.y2milestone("Eject request for %1", r)
×
943
          eject_device = r
×
944
          r = :eject
×
945
        end
946
        break if [:cancel, :retry, :eject, :skip, :ignore, :url].include?(r)
×
947
      end
948

949
      # check and save the autoeject configuration if needed
950
      remember_autoeject if is_disc
×
951

952
      Builtins.y2milestone("MediaChange %1", r)
×
953

954
      UI.CloseDialog
×
955

956
      if @_provide_popup
×
957
        UI.CloseDialog
×
958
        @_provide_popup = false
×
959
      end
960

961
      case r
×
962
      when :ignore then "I"
×
963
      when :skip then "S"
×
964
      when :cancel
965
        # abort during autorefresh should abort complete autorefresh, not only the failed repo
966
        if @autorefreshing
×
967
          @autorefreshing_aborted = true
×
968
          Pkg.SkipRefresh
×
969
        else
970
          @provide_aborted = true
×
971
        end
972

973
        "C"
×
974
      when :eject
975
        @doing_eject = true
×
976

977
        return "E" if eject_device == ""
×
978

979
        # get the index in the list
980
        dindex = -1
×
981

982
        found = Builtins.find(devices) do |d|
×
983
          dindex = Ops.add(dindex, 1)
×
984
          d == eject_device
×
985
        end
986

987
        if found
×
988
          Builtins.y2milestone("Device %1 has index %2", eject_device, dindex)
×
989
          "E#{dindex}"
×
990
        else
991
          Builtins.y2warning(
×
992
            "Device %1 not found in the list, using default",
993
            eject_device
994
          )
995
          "E"
×
996
        end
997
      when :url
998
        Builtins.y2milestone("Redirecting to: %1", URL.HidePassword(url))
×
999
        url
×
1000
      else
1001
        ""
×
1002
      end
1003
    end
1004

1005
    # dummy repository change callback, see SlideShowCallbacks for the real one
1006
    def SourceChange(source, medianr)
1✔
1007
      Builtins.y2milestone("SourceChange (%1, %2)", source, medianr)
×
1008
      @_current_source = source
×
1009

UNCOV
1010
      nil
×
1011
    end
1012

1013
    def OpenSourcePopup
1✔
1014
      if @_source_open == 0
×
1015
        UI.OpenDialog(
×
1016
          VBox(
1017
            HSpacing(MAX_POPUP_TEXT_SIZE),
1018
            Heading(Id(:label_source_popup), Opt(:hstretch), " "),
1019
            ProgressBar(Id(:progress), " ", 100, 0)
1020
          )
1021
        )
1022
      end
1023

1024
      @_source_open = Ops.add(@_source_open, 1)
×
1025
      Builtins.y2milestone("OpenSourcePopup: _source_open: %1", @_source_open)
×
1026

UNCOV
1027
      nil
×
1028
    end
1029

1030
    def SetHeaderSourcePopup(text)
1✔
1031
      # Qt UI uses bold font, the string must be shortened even more
1032
      ui_adjustment = textmode ? 0 : 5
×
1033

1034
      if Ops.greater_than(
×
1035
        Builtins.size(text),
1036
        Ops.subtract(MAX_POPUP_TEXT_SIZE, ui_adjustment)
1037
      )
1038
        text = process_message(text, Ops.subtract(MAX_POPUP_TEXT_SIZE, ui_adjustment))
×
1039
      end
1040

1041
      UI.ChangeWidget(:label_source_popup, :Value, text)
×
1042
      Builtins.y2milestone("SourcePopup: new header: %1", text)
×
1043

UNCOV
1044
      nil
×
1045
    end
1046

1047
    def SetLabelSourcePopup(text)
1✔
1048
      # Qt uses proportional font, the string might be longer
1049
      ui_adjustment = textmode ? 0 : 6
×
1050

1051
      if Ops.greater_than(
×
1052
        Builtins.size(text),
1053
        Ops.add(MAX_POPUP_TEXT_SIZE, ui_adjustment)
1054
      )
1055
        text = process_message(text, Ops.add(MAX_POPUP_TEXT_SIZE, ui_adjustment))
×
1056
      end
1057

1058
      # refresh the label in the popup
1059
      UI.ChangeWidget(:progress, :Label, text)
×
1060
      Builtins.y2milestone("SourcePopup: new label: %1", text)
×
1061

UNCOV
1062
      nil
×
1063
    end
1064

1065
    # is the top level window source popup?
1066
    def IsSourcePopup
1✔
1067
      UI.WidgetExists(Id(:progress)) && UI.WidgetExists(Id(:label_source_popup))
×
1068
    end
1069

1070
    def SourcePopupSetProgress(value)
1✔
1071
      if Ops.greater_than(@_source_open, 0) && IsSourcePopup()
×
1072
        UI.ChangeWidget(Id(:progress), :Value, value)
×
1073
        input = UI.PollInput
×
1074
        return false if input == :abort
×
1075
      end
1076
      true
×
1077
    end
1078

1079
    def CloseSourcePopup
1✔
1080
      if !IsSourcePopup()
×
1081
        Builtins.y2error(
×
1082
          "The toplevel dialog is not a repository popup dialog!"
1083
        )
1084
        return
×
1085
      end
1086

1087
      @_source_open = Ops.subtract(@_source_open, 1)
×
1088

1089
      if @_source_open == 0
×
1090
        Builtins.y2milestone("Closing repository progress popup")
×
1091
        UI.CloseDialog
×
1092
      end
1093
      Builtins.y2milestone("CloseSourcePopup: _source_open: %1", @_source_open)
×
1094

UNCOV
1095
      nil
×
1096
    end
1097

1098
    def SourceCreateInit
1✔
1099
      Builtins.y2milestone("SourceCreateInit")
×
1100

1101
      OpenSourcePopup()
×
1102

UNCOV
1103
      nil
×
1104
    end
1105

1106
    def SourceCreateDestroy
1✔
1107
      Builtins.y2milestone("SourceCreateDestroy")
×
1108

1109
      CloseSourcePopup()
×
1110

UNCOV
1111
      nil
×
1112
    end
1113

1114
    def SourceCreateStart(url)
1✔
1115
      Builtins.y2milestone("SourceCreateStart: %1", url)
×
1116

1117
      # popup label (%1 is repository URL)
1118
      msg = Builtins.sformat(_("Creating Repository %1"), url)
×
1119

1120
      if Mode.commandline
×
1121
        CommandLine.Print(msg)
×
1122
      else
1123
        Builtins.y2milestone("_source_open: %1", @_source_open)
×
1124

1125
        if @_source_open == 1
×
1126
          SetHeaderSourcePopup(msg)
×
1127
        else
1128
          SetLabelSourcePopup(msg)
×
1129
        end
1130
      end
1131

UNCOV
1132
      nil
×
1133
    end
1134

1135
    def SourceCreateProgress(percent)
1✔
1136
      ret = SourcePopupSetProgress(percent)
×
1137
      Builtins.y2milestone("SourceCreateProgress(%1) = %2", percent, ret)
×
1138

1139
      ret
×
1140
    end
1141

1142
    def SourceCreateError(url, error, description)
1✔
1143
      Builtins.y2milestone(
×
1144
        "Source create: error: url: %1, error: %2, description: %3",
1145
        URL.HidePassword(url),
1146
        error,
1147
        description
1148
      )
1149

1150
      # error message - a label followed by a richtext with details
1151
      message = _("An error occurred while creating the repository.")
×
1152

1153
      case error
×
1154
      when :NOT_FOUND
1155
        # error message - a label followed by a richtext with details
1156
        message = _("Unable to retrieve the remote repository description.")
×
1157
      when :IO
1158
        # error message - a label followed by a richtext with details
1159
        message = _("An error occurred while retrieving the new metadata.")
×
1160
      when :INVALID
1161
        # error message - a label followed by a richtext with details
1162
        message = _("The repository is not valid.")
×
1163
      when :REJECTED
1164
        # error message - a label followed by a richtext with details
1165
        message = _("The repository metadata is invalid.")
×
1166
      end
1167

1168
      if Mode.commandline
×
1169
        CommandLine.Print(message)
×
1170
        CommandLine.Print(URL.HidePassword(url))
×
1171
        CommandLine.Print(description)
×
1172

1173
        # ask user in the interactive mode
1174
        if CommandLine.Interactive
×
1175
          CommandLine.Print("")
×
1176

1177
          # command line mode - ask user whether the repository refreshment should be retried
1178
          CommandLine.Print(_("Retry?"))
×
1179

1180
          if CommandLine.YesNo
×
1181
            # return Retry
1182
            return :RETRY
×
1183
          end
1184
        end
1185

1186
        return :ABORT
×
1187
      end
1188
      detail = Builtins.sformat("%1<br>%2", url, description)
×
1189
      UI.OpenDialog(
×
1190
        VBox(
1191
          Label(message),
1192
          RichText(detail),
1193
          ButtonBox(
1194
            PushButton(Id(:RETRY), Opt(:okButton), Label.RetryButton),
1195
            PushButton(Id(:ABORT), Opt(:cancelButton), Label.AbortButton)
1196
          )
1197
        )
1198
      )
1199
      ret = Convert.to_symbol(UI.UserInput)
×
1200
      UI.CloseDialog
×
1201
      Builtins.y2milestone("Source create error: Returning %1", ret)
×
1202

1203
      ret
×
1204
    end
1205

1206
    def SourceCreateEnd(url, error, description)
1✔
1207
      # set 100% progress
1208
      SourcePopupSetProgress(100)
×
1209

1210
      Builtins.y2milestone(
×
1211
        "Source create end: error: url: %1, error: %2, description: %3",
1212
        URL.HidePassword(url),
1213
        error,
1214
        description
1215
      )
1216

UNCOV
1217
      nil
×
1218
    end
1219

1220
    def SourceProbeStart(url)
1✔
1221
      Builtins.y2milestone("SourceProbeStart: %1", URL.HidePassword(url))
×
1222

1223
      # popup label (%1 is repository URL)
1224
      msg = Builtins.sformat(_("Probing Repository %1"), URL.HidePassword(url))
×
1225

1226
      if Mode.commandline
×
1227
        CommandLine.Print(msg)
×
1228
      else
1229
        OpenSourcePopup()
×
1230

1231
        msg2 = Builtins.sformat(
×
1232
          _("Probing Repository %1"),
1233
          URL.HidePassword(url)
1234
        )
1235

1236
        if @_source_open == 1
×
1237
          SetHeaderSourcePopup(msg2)
×
1238
        else
1239
          SetLabelSourcePopup(msg2)
×
1240
        end
1241
      end
1242

UNCOV
1243
      nil
×
1244
    end
1245

1246
    def SourceProbeFailed(url, type)
1✔
1247
      Builtins.y2milestone(
×
1248
        "Repository %1 is not %2 repository",
1249
        URL.HidePassword(url),
1250
        type
1251
      )
1252

UNCOV
1253
      nil
×
1254
    end
1255

1256
    def SourceProbeSucceeded(url, type)
1✔
1257
      Builtins.y2milestone(
×
1258
        "Repository %1 is type %2",
1259
        URL.HidePassword(url),
1260
        type
1261
      )
1262

UNCOV
1263
      nil
×
1264
    end
1265

1266
    def SourceProbeProgress(_url, value)
1✔
1267
      SourcePopupSetProgress(value)
×
1268
    end
1269

1270
    def SourceProbeError(url, error, description)
1✔
1271
      Builtins.y2milestone(
×
1272
        "Source probe: error: url: %1, error: %2, description: %3",
1273
        URL.HidePassword(url),
1274
        error,
1275
        description
1276
      )
1277

1278
      # error message - a label followed by a richtext with details
1279
      message = _("Error occurred while probing the repository.")
×
1280

1281
      case error
×
1282
      when :NOT_FOUND
1283
        # error message - a label followed by a richtext with details
1284
        message = _("Unable to retrieve the remote repository description.")
×
1285
      when :IO
1286
        # error message - a label followed by a richtext with details
1287
        message = _("An error occurred while retrieving the new metadata.")
×
1288
      when :INVALID
1289
        # error message - a label followed by a richtext with details
1290
        message = _("The repository is not valid.")
×
1291
      when :NO_ERROR
1292
        # error message - a label followed by a richtext with details
1293
        message = _("Repository probing details.")
×
1294
      when :REJECTED
1295
        # error message - a label followed by a richtext with details
1296
        message = _("Repository metadata is invalid.")
×
1297
      end
1298

1299
      if Mode.commandline
×
1300
        CommandLine.Print(message)
×
1301
        CommandLine.Print(URL.HidePassword(url))
×
1302
        CommandLine.Print(description)
×
1303

1304
        # ask user in the interactive mode
1305
        if CommandLine.Interactive
×
1306
          CommandLine.Print("")
×
1307

1308
          # command line mode - ask user whether the repository refreshment should be retried
1309
          CommandLine.Print(_("Retry?"))
×
1310

1311
          if CommandLine.YesNo
×
1312
            # return Retry
1313
            return :RETRY
×
1314
          end
1315
        end
1316

1317
        return :ABORT
×
1318
      end
1319
      detail = Builtins.sformat("%1<br>%2", url, description)
×
1320
      UI.OpenDialog(
×
1321
        VBox(
1322
          Label(message),
1323
          RichText(detail),
1324
          ButtonBox(
1325
            PushButton(Id(:RETRY), Opt(:okButton), Label.RetryButton),
1326
            PushButton(Id(:ABORT), Opt(:cancelButton), Label.AbortButton)
1327
          )
1328
        )
1329
      )
1330
      ret = Convert.to_symbol(UI.UserInput)
×
1331
      UI.CloseDialog
×
1332
      Builtins.y2milestone("Source probe error: Returning %1", ret)
×
1333
      ret
×
1334
    end
1335

1336
    def SourceProbeEnd(url, error, description)
1✔
1337
      CloseSourcePopup()
×
1338
      CloseSourcePopup()
×
1339

1340
      Builtins.y2milestone(
×
1341
        "Source probe end: error: url: %1, error: %2, description: %3",
1342
        URL.HidePassword(url),
1343
        error,
1344
        description
1345
      )
1346

UNCOV
1347
      nil
×
1348
    end
1349

1350
    def SourceReportStart(source_id, url, task)
1✔
1351
      Builtins.y2milestone(
×
1352
        "Source report start: src: %1, URL: %2, task: %3",
1353
        source_id,
1354
        URL.HidePassword(url),
1355
        task
1356
      )
1357

1358
      if Mode.commandline
×
1359
        CommandLine.Print(task)
×
1360
      else
1361
        Builtins.y2milestone("_source_open: %1", @_source_open)
×
1362

1363
        if @_source_open == 1
×
1364
          SetHeaderSourcePopup(task)
×
1365
        else
1366
          SetLabelSourcePopup(task)
×
1367
        end
1368
      end
1369

UNCOV
1370
      nil
×
1371
    end
1372

1373
    def SourceReportProgress(value)
1✔
1374
      ret = SourcePopupSetProgress(value)
×
1375
      Builtins.y2debug("SourceReportProgress(%1) = %2", value, ret)
×
1376

1377
      ret
×
1378
    end
1379

1380
    def SourceReportError(source_id, url, error, description)
1✔
1381
      Builtins.y2milestone(
×
1382
        "Source report: error: id: %1, url: %2, error: %3, description: %4",
1383
        source_id,
1384
        URL.HidePassword(url),
1385
        error,
1386
        description
1387
      )
1388

1389
      # error message - a label followed by a richtext with details
1390
      message = Builtins.sformat(_("Repository %1"), url)
×
1391

1392
      case error
×
1393
      when :NOT_FOUND
1394
        # error message - a label followed by a richtext with details
1395
        message = _("Unable to retrieve the remote repository description.")
×
1396
      when :IO
1397
        # error message - a label followed by a richtext with details
1398
        message = _("An error occurred while retrieving the new metadata.")
×
1399
      when :INVALID
1400
        # error message - a label followed by a richtext with details
1401
        message = _("The repository is not valid.")
×
1402
      end
1403

1404
      if Mode.commandline
×
1405
        CommandLine.Print(message)
×
1406
        CommandLine.Print(url)
×
1407
        CommandLine.Print(description)
×
1408

1409
        # ask user in the interactive mode
1410
        if CommandLine.Interactive
×
1411
          CommandLine.Print("")
×
1412

1413
          # command line mode - ask user whether the repository refreshment should be retried
1414
          CommandLine.Print(_("Retry?"))
×
1415

1416
          if CommandLine.YesNo
×
1417
            # return Retry
1418
            return :RETRY
×
1419
          end
1420
        end
1421

1422
        return :ABORT
×
1423
      end
1424
      detail = Builtins.sformat("%1<br>%2", url, description)
×
1425
      UI.OpenDialog(
×
1426
        VBox(
1427
          Label(message),
1428
          RichText(detail),
1429
          HBox(
1430
            PushButton(Id(:RETRY), Opt(:okButton), Label.RetryButton),
1431
            PushButton(Id(:ABORT), Opt(:cancelButton), Label.AbortButton)
1432
          )
1433
        )
1434
      )
1435
      ret = Convert.to_symbol(UI.UserInput)
×
1436
      UI.CloseDialog
×
1437
      Builtins.y2milestone("Source report error: Returning %1", ret)
×
1438

1439
      ret
×
1440
    end
1441

1442
    def SourceReportEnd(src_id, url, task, error, description)
1✔
1443
      Builtins.y2milestone(
×
1444
        "Source report end: src: %1, url: %2, task: %3, error: %4, description: %5",
1445
        src_id,
1446
        URL.HidePassword(url),
1447
        task,
1448
        error,
1449
        description
1450
      )
1451

1452
      # set 100% progress
1453
      SourcePopupSetProgress(100)
×
1454

UNCOV
1455
      nil
×
1456
    end
1457

1458
    def SourceReportInit
1✔
1459
      Builtins.y2milestone("Source report init")
×
1460
      OpenSourcePopup()
×
1461

UNCOV
1462
      nil
×
1463
    end
1464

1465
    def SourceReportDestroy
1✔
1466
      Builtins.y2milestone("Source report destroy")
×
1467
      CloseSourcePopup()
×
1468

UNCOV
1469
      nil
×
1470
    end
1471

1472
    # at start of delta providal
1473
    #
1474
    def StartDeltaProvide(name, archivesize)
1✔
1475
      sz = String.FormatSizeWithPrecision(archivesize, 2, false)
×
1476
      if Mode.commandline
×
1477
        CommandLine.PrintVerbose(
×
1478
          Builtins.sformat(
1479
            _("Downloading delta RPM package %1 (%2)..."),
1480
            name,
1481
            sz
1482
          )
1483
        )
1484
      else
1485
        UI.CloseDialog if @_provide_popup
×
1486
        # popup heading
1487
        providebox = progress_box(_("Downloading Delta RPM package"), name, sz)
×
1488
        UI.OpenDialog(providebox)
×
1489
        @_provide_popup = true
×
1490
      end
1491

UNCOV
1492
      nil
×
1493
    end
1494

1495
    # at start of delta application
1496
    #
1497
    def StartDeltaApply(name)
1✔
1498
      if Mode.commandline
×
1499
        CommandLine.PrintVerbose(
×
1500
          Builtins.sformat(_("Applying delta RPM package %1..."), name)
1501
        )
1502
      else
1503
        # popup heading
1504
        progressbox = VBox(
×
1505
          HSpacing(40),
1506
          # popup heading
1507
          Heading(_("Applying delta RPM package")),
1508
          Left(
1509
            HBox(Left(Label(Opt(:boldFont), _("Package: "))), Left(Label(name)))
1510
          ),
1511
          ProgressBar(Id(:progress), "", 100, 0)
1512
        )
1513
        UI.CloseDialog if @_provide_popup
×
1514
        UI.OpenDialog(progressbox)
×
1515
        @_provide_popup = true
×
1516
      end
1517

UNCOV
1518
      nil
×
1519
    end
1520

1521
    def FinishDeltaProvide
1✔
1522
      if @_provide_popup
×
1523
        UI.CloseDialog
×
1524
        @_provide_popup = false
×
1525
      end
1526

UNCOV
1527
      nil
×
1528
    end
1529

1530
    def ProblemDeltaDownload(descr)
1✔
1531
      FinishDeltaProvide() # close popup
×
1532
      Builtins.y2milestone("Failed to download delta RPM: %1", descr)
×
1533

UNCOV
1534
      nil
×
1535
    end
1536

1537
    def ProblemDeltaApply(descr)
1✔
1538
      FinishDeltaProvide() # close popup
×
1539
      Builtins.y2milestone("Failed to apply delta RPM: %1", descr)
×
1540

UNCOV
1541
      nil
×
1542
    end
1543

1544
    def FormatPatchName(patch_name, patch_version, patch_arch)
1✔
1545
      patch_full_name = (!patch_name.nil? && patch_name != "") ? patch_name : ""
×
1546

1547
      if patch_full_name != ""
×
1548
        if !patch_version.nil? && patch_version != ""
×
1549
          patch_full_name = Ops.add(
×
1550
            Ops.add(patch_full_name, "-"),
1551
            patch_version
1552
          )
1553
        end
1554

1555
        patch_full_name = Ops.add(Ops.add(patch_full_name, "."), patch_arch) if !patch_arch.nil? && patch_arch != ""
×
1556
      end
1557

1558
      patch_full_name
×
1559
    end
1560

1561
    def ScriptStart(patch_name, patch_version, patch_arch, script_path)
1✔
1562
      patch_full_name = FormatPatchName(patch_name, patch_version, patch_arch)
×
1563

1564
      Builtins.y2milestone(
×
1565
        "ScriptStart callback: patch: %1, script: %2",
1566
        patch_full_name,
1567
        script_path
1568
      )
1569

1570
      if Mode.commandline
×
1571
        CommandLine.PrintVerbose(
×
1572
          Builtins.sformat(
1573
            _("Starting script %1 (patch %2)..."),
1574
            script_path,
1575
            patch_full_name
1576
          )
1577
        )
1578
      else
1579
        progressbox = VBox(
×
1580
          HSpacing(60),
1581
          # popup heading
1582
          Heading(_("Running Script")),
1583
          VBox(
1584
            if patch_full_name == ""
×
1585
              Empty()
×
1586
            else
1587
              HBox(
×
1588
                # label, patch name follows
1589
                Label(Opt(:boldFont), _("Patch: ")),
1590
                Label(patch_full_name),
1591
                HStretch()
1592
              )
1593
            end,
1594
            HBox(
1595
              # label, script name follows
1596
              Label(Opt(:boldFont), _("Script: ")),
1597
              Label(script_path),
1598
              HStretch()
1599
            )
1600
          ),
1601
          # label
1602
          LogView(Id(:log), _("Output of the Script"), 10, 0),
1603
          ButtonBox(
1604
            PushButton(
1605
              Id(:abort),
1606
              Opt(:default, :key_F9, :cancelButton),
1607
              Label.AbortButton
1608
            )
1609
          )
1610
        )
1611

1612
        UI.CloseDialog if @_script_popup
×
1613

1614
        UI.OpenDialog(progressbox)
×
1615
        UI.SetFocus(Id(:abort))
×
1616

1617
        @_script_popup = true
×
1618
      end
1619

UNCOV
1620
      nil
×
1621
    end
1622

1623
    def ScriptProgress(ping, output)
1✔
1624
      Builtins.y2milestone("ScriptProgress: ping:%1, output: %2", ping, output)
×
1625

1626
      if @_script_popup
×
1627
        if ping
×
1628
          # TODO: refresh progress indicator
1629
          Builtins.y2debug("-ping-")
×
1630
        end
1631

1632
        if !output.nil? && output != ""
×
1633
          # add the output to the log widget
1634
          UI.ChangeWidget(Id(:log), :Value, output)
×
1635
        end
1636

1637
        input = UI.PollInput
×
1638
        return false if [:abort, :close].include?(input)
×
1639
      end
1640
      true
×
1641
    end
1642

1643
    def ScriptProblem(description)
1✔
1644
      Builtins.y2warning("ScriptProblem: %1", description)
×
1645

1646
      ui = Popup.AnyQuestion3(
×
1647
        "", # symbol focus
1648
        description,
1649
        Label.RetryButton, # yes_button_message
1650
        Label.AbortButton, # no_button_message
1651
        Label.IgnoreButton, # retry_button_message
1652
        :retry
1653
      )
1654

1655
      Builtins.y2milestone("Problem result: %1", ui)
×
1656

1657
      # Abort is the default
1658
      ret = "A"
×
1659

1660
      case ui
×
1661
      when :retry
1662
        # ignore
1663
        ret = "I"
×
1664
      when :yes
1665
        # retry
1666
        ret = "R"
×
1667
      when :no
1668
        # abort
1669
        ret = "A"
×
1670
      else
1671
        Builtins.y2warning("Unknown result: %1, aborting", ui)
×
1672
      end
1673

1674
      ret
×
1675
    end
1676

1677
    def ScriptFinish
1✔
1678
      Builtins.y2milestone("ScriptFinish")
×
1679

1680
      UI.CloseDialog if @_script_popup
×
1681

UNCOV
1682
      nil
×
1683
    end
1684

1685
    def Message(patch_name, patch_version, patch_arch, message)
1✔
1686
      patch_full_name = FormatPatchName(patch_name, patch_version, patch_arch)
×
1687
      Builtins.y2milestone("Message (%1): %2", patch_full_name, message)
×
1688

1689
      if patch_full_name != ""
×
1690
        # label, %1 is patch name with version and architecture
1691
        patch_full_name = Builtins.sformat(_("Patch: %1\n\n"), patch_full_name)
×
1692
      end
1693

1694
      ret = Popup.ContinueCancel(Ops.add(patch_full_name, message))
×
1695
      Builtins.y2milestone("User input: %1", ret)
×
1696

1697
      ret
×
1698
    end
1699

1700
    def AskAbortRefresh
1✔
1701
      UI.OpenDialog(
×
1702
        MarginBox(
1703
          1,
1704
          0.5,
1705
          VBox(
1706
            # a popup question with "Continue", "Skip" and "Abort" buttons
1707
            Label(
1708
              _(
1709
                "The repositories are being refreshed.\n" \
1710
                "Continue with refreshing?\n" \
1711
                "\n" \
1712
                "Note: If the refresh is skipped some packages\n" \
1713
                "might be missing or out of date."
1714
              )
1715
            ),
1716
            ButtonBox(
1717
              PushButton(
1718
                Id(:continue),
1719
                Opt(:default, :okButton),
1720
                Label.ContinueButton
1721
              ),
1722
              # push button label
1723
              PushButton(Id(:skip), Opt(:cancelButton), _("&Skip Refresh"))
1724
            )
1725
          )
1726
        )
1727
      )
1728

1729
      UI.SetFocus(Id(:continue))
×
1730

1731
      ui = Convert.to_symbol(UI.UserInput)
×
1732

1733
      UI.CloseDialog
×
1734

1735
      ui = :continue if ui == :close
×
1736

1737
      Builtins.y2milestone("User request: %1", ui)
×
1738

1739
      ui
×
1740
    end
1741

1742
    def IsDownloadProgressPopup
1✔
1743
      !Mode.commandline && UI.WidgetExists(Id(:download_progress_popup_window)) &&
12✔
1744
        UI.WidgetExists(Id(:progress))
1745
    end
1746

1747
    def CloseDownloadProgressPopup
1✔
1748
      UI.CloseDialog if IsDownloadProgressPopup()
6✔
1749

1750
      nil
6✔
1751
    end
1752

1753
    def InitDownload(task)
1✔
1754
      if !Mode.commandline && (!full_screen && !IsDownloadProgressPopup())
6✔
1755
        # heading of popup
1756
        heading = _("Downloading")
6✔
1757

1758
        UI.OpenDialog(
6✔
1759
          Opt(:decorated),
1760
          VBox(
1761
            Heading(Id(:download_progress_popup_window), heading),
1762
            VBox(
1763
              HSpacing(60),
1764
              HBox(
1765
                HSpacing(1),
1766
                ProgressBar(Id(:progress), task, 100),
1767
                HSpacing(1)
1768
              ),
1769
              VSpacing(0.5),
1770
              ButtonBox(
1771
                PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton)
1772
              ),
1773
              VSpacing(0.5)
1774
            )
1775
          )
1776
        )
1777
        UI.ChangeWidget(Id(:progress), :Value, 0)
6✔
1778
      end
1779

1780
      nil
6✔
1781
    end
1782

1783
    def DestDownload
1✔
1784
      CloseDownloadProgressPopup() if !full_screen
6✔
1785

1786
      nil
6✔
1787
    end
1788

1789
    def StartDownload(url, localfile)
1✔
1790
      Builtins.y2milestone(
×
1791
        "Downloading %1 to %2",
1792
        URL.HidePassword(url),
1793
        localfile
1794
      )
1795

1796
      # reformat the URL
1797
      url_report = URL.FormatURL(URL.Parse(URL.HidePassword(url)), MAX_POPUP_TEXT_SIZE)
×
1798
      # remember the URL
1799
      @download_file = url_report
×
1800

1801
      # message in a progress popup
1802
      message = Builtins.sformat(_("Downloading: %1"), url_report)
×
1803

1804
      if Mode.commandline
×
1805
        CommandLine.PrintVerbose(message)
×
1806
      elsif IsDownloadProgressPopup()
×
1807
        # change the label
1808
        UI.ChangeWidget(Id(:progress), :Label, message)
×
1809
        UI.ChangeWidget(Id(:progress), :Value, 0)
×
1810
      elsif full_screen
×
1811
        Progress.SubprogressType(:progress, 100)
×
1812
        Progress.SubprogressTitle(message)
×
1813
      end
1814

UNCOV
1815
      nil
×
1816
    end
1817

1818
    def ProgressDownload(percent, bps_avg, bps_current)
1✔
1819
      if @autorefreshing && @autorefreshing_aborted
×
1820
        Builtins.y2milestone("Refresh aborted")
×
1821
        return false
×
1822
      end
1823

1824
      if Mode.commandline
×
1825
        CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{percent}%")
×
1826
        if percent == 100
×
1827
          # sleep for a wile
1828
          Builtins.sleep(200)
×
1829
          # remove the progress
1830
          CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT)
×
1831
          # print newline when reached 100%
1832
        end
1833
      else
1834
        msg_rate = ""
×
1835

1836
        if Ops.greater_than(bps_current, 0)
×
1837
          # do not show the average download rate if the space is limited
1838
          bps_avg = -1 if textmode && Ops.less_than(display_width, 100)
×
1839

1840
          format = if textmode
×
1841
            Ops.add("%1 - ", @download_file)
×
1842
          else
1843
            Ops.add(@download_file, " - %1")
×
1844
          end
1845

1846
          # progress bar label, %1 is URL with optional download rate
1847
          msg_rate = Builtins.sformat(
×
1848
            _("Downloading: %1"),
1849
            String.FormatRateMessage(format, bps_avg, bps_current)
1850
          )
1851
        end
1852

1853
        if full_screen
×
1854
          Progress.SubprogressValue(percent)
×
1855

1856
          Progress.SubprogressTitle(msg_rate) if Ops.greater_than(Builtins.size(msg_rate), 0)
×
1857
        else
1858
          UI.ChangeWidget(Id(:progress), :Value, percent)
×
1859

1860
          UI.ChangeWidget(Id(:progress), :Label, msg_rate) if Ops.greater_than(Builtins.size(msg_rate), 0)
×
1861
        end
1862

1863
        download_aborted = UI.PollInput == :abort
×
1864

1865
        if download_aborted && @autorefreshing
×
1866
          # display "Continue", "Skip Refresh" dialog
1867
          answer = AskAbortRefresh()
×
1868

1869
          case answer
×
1870
          when :continue
1871
            download_aborted = false
×
1872
          when :skip
1873
            download_aborted = true
×
1874
            @autorefreshing_aborted = true
×
1875

1876
            Pkg.SkipRefresh
×
1877
          else
1878
            Builtins.y2error("Unknown input value: %1", answer)
×
1879
          end
1880
        end
1881

1882
        return !download_aborted
×
1883
      end
1884

1885
      true
×
1886
    end
1887

1888
    # just log the status, errors are handled in MediaChange callback
1889
    def DoneDownload(error_value, error_text)
1✔
1890
      if error_value == 0
×
1891
        Builtins.y2milestone("Download finished")
×
1892
      elsif @autorefreshing && @autorefreshing_aborted
×
1893
        Builtins.y2milestone("Refresh aborted")
×
1894
      else
1895
        Builtins.y2warning(
×
1896
          "Download failed: error %1: %2",
1897
          error_value,
1898
          error_text
1899
        )
1900
      end
1901

UNCOV
1902
      nil
×
1903
    end
1904

1905
    def RefreshStarted
1✔
1906
      Builtins.y2milestone("Autorefreshing repositories...")
×
1907

1908
      if !Mode.commandline && UI.WidgetExists(Id(:abort))
×
1909
        # push button label
1910
        UI.ChangeWidget(Id(:abort), :Label, _("Skip Autorefresh"))
×
1911
        UI.RecalcLayout
×
1912
      end
1913

1914
      @autorefreshing = true
×
1915
      @autorefreshing_aborted = false
×
1916

UNCOV
1917
      nil
×
1918
    end
1919

1920
    def RefreshDone
1✔
1921
      if !Mode.commandline && UI.WidgetExists(Id(:abort))
×
1922
        UI.ChangeWidget(Id(:abort), :Label, Label.AbortButton)
×
1923
        UI.RecalcLayout
×
1924
      end
1925

1926
      Builtins.y2milestone("Autorefresh done")
×
1927
      @autorefreshing = false
×
1928
      @autorefreshing_aborted = false
×
1929

UNCOV
1930
      nil
×
1931
    end
1932

1933
    def ClearDownloadCallbacks
1✔
1934
      Pkg.CallbackInitDownload(nil)
×
1935
      Pkg.CallbackStartDownload(nil)
×
1936
      Pkg.CallbackProgressDownload(nil)
×
1937
      Pkg.CallbackDoneDownload(nil)
×
1938
      Pkg.CallbackDestDownload(nil)
×
1939
      Pkg.CallbackStartRefresh(nil)
×
1940
      Pkg.CallbackDoneRefresh(nil)
×
1941

UNCOV
1942
      nil
×
1943
    end
1944

1945
    def StartRebuildDB
1✔
1946
      # heading of popup
1947
      heading = _("Checking Package Database")
×
1948

1949
      # message in a progress popup
1950
      message = _(
×
1951
        "Rebuilding package database. This process can take some time."
1952
      )
1953

1954
      UI.OpenDialog(
×
1955
        Opt(:decorated),
1956
        VBox(
1957
          Heading(heading),
1958
          VBox(
1959
            Label(message),
1960
            HSpacing(60),
1961
            HBox(HSpacing(2), ProgressBar(Id(:progress), "", 100), HSpacing(2)),
1962
            VSpacing(1)
1963
          )
1964
        )
1965
      )
1966

1967
      UI.ChangeWidget(Id(:progress), :Value, 0)
×
1968

UNCOV
1969
      nil
×
1970
    end
1971

1972
    def ProgressRebuildDB(percent)
1✔
1973
      UI.ChangeWidget(Id(:progress), :Value, percent)
×
1974

UNCOV
1975
      nil
×
1976
    end
1977

1978
    def StopRebuildDB(error_value, error_text)
1✔
1979
      if error_value != 0
×
1980
        # error message, %1 is the cause for the error
1981
        Popup.Error(
×
1982
          Builtins.sformat(
1983
            _("Rebuilding of package database failed:\n%1"),
1984
            error_text
1985
          )
1986
        )
1987
      end
1988

1989
      UI.CloseDialog
×
1990

UNCOV
1991
      nil
×
1992
    end
1993

1994
    def NotifyRebuildDB
1✔
UNCOV
1995
      nil
×
1996
    end
1997

1998
    def SetRebuildDBCallbacks
1✔
1999
      Pkg.CallbackStartRebuildDb(fun_ref(method(:StartRebuildDB), "void ()"))
×
2000
      Pkg.CallbackProgressRebuildDb(
×
2001
        fun_ref(method(:ProgressRebuildDB), "void (integer)")
2002
      )
2003
      Pkg.CallbackStopRebuildDb(
×
2004
        fun_ref(method(:StopRebuildDB), "void (integer, string)")
2005
      )
2006
      Pkg.CallbackNotifyRebuildDb(fun_ref(method(:NotifyRebuildDB), "void ()"))
×
2007

UNCOV
2008
      nil
×
2009
    end
2010

2011
    def StartConvertDB(_unused1)
1✔
2012
      # heading of popup
2013
      heading = _("Checking Package Database")
×
2014

2015
      # message in a progress popup
2016
      message = _(
×
2017
        "Converting package database. This process can take some time."
2018
      )
2019

2020
      UI.OpenDialog(
×
2021
        Opt(:decorated),
2022
        VBox(
2023
          Heading(heading),
2024
          VBox(
2025
            Label(message),
2026
            HSpacing(60),
2027
            HBox(
2028
              HSpacing(2),
2029
              ProgressBar(Id(:progress), _("Status"), 100),
2030
              HSpacing(2)
2031
            ),
2032
            VSpacing(1)
2033
          )
2034
        )
2035
      )
2036

2037
      UI.ChangeWidget(Id(:progress), :Value, 0)
×
2038

UNCOV
2039
      nil
×
2040
    end
2041

2042
    def ProgressConvertDB(percent, _file)
1✔
2043
      UI.ChangeWidget(Id(:progress), :Value, percent)
×
2044

UNCOV
2045
      nil
×
2046
    end
2047

2048
    def StopConvertDB(error_value, error_text)
1✔
2049
      if error_value != 0
×
2050
        # error message, %1 is the cause for the error
2051
        Popup.Error(
×
2052
          Builtins.sformat(
2053
            _("Conversion of package database failed:\n%1"),
2054
            error_text
2055
          )
2056
        )
2057
      end
2058

2059
      UI.CloseDialog
×
2060

UNCOV
2061
      nil
×
2062
    end
2063

2064
    def NotifyConvertDB
1✔
UNCOV
2065
      nil
×
2066
    end
2067

2068
    def SetConvertDBCallbacks
1✔
2069
      Pkg.CallbackStartConvertDb(
×
2070
        fun_ref(method(:StartConvertDB), "void (string)")
2071
      )
2072
      Pkg.CallbackProgressConvertDb(
×
2073
        fun_ref(method(:ProgressConvertDB), "void (integer, string)")
2074
      )
2075
      Pkg.CallbackStopConvertDb(
×
2076
        fun_ref(method(:StopConvertDB), "void (integer, string)")
2077
      )
2078
      Pkg.CallbackNotifyConvertDb(fun_ref(method(:NotifyConvertDB), "void ()"))
×
2079

UNCOV
2080
      nil
×
2081
    end
2082

2083
    # Callback for start RPM DB scan event
2084
    def StartScanDb
1✔
2085
      Builtins.y2milestone("Scanning RPM DB...")
×
2086

2087
      if Mode.commandline
×
2088
        # progress message (command line mode)
2089
        CommandLine.PrintVerbose(_("Reading RPM database..."))
×
2090
      elsif !full_screen
×
2091
        UI.OpenDialog(
×
2092
          VBox(
2093
            HSpacing(60),
2094
            # popup heading
2095
            Heading(
2096
              Id(:label_scanDB_popup),
2097
              Opt(:hstretch),
2098
              _("Reading Installed Packages")
2099
            ),
2100
            HBox(
2101
              # progress bar label
2102
              ProgressBar(
2103
                Id(:progress),
2104
                _("Scanning RPM database..."),
2105
                100,
2106
                0
2107
              ), # TODO: allow Abort
2108
              #       ,
2109
              #       `VBox(
2110
              #           `Label(""),
2111
              #           `PushButton(`id(`abort), Label::AbortButton())
2112
              #       )
2113
              HSpacing(1)
2114
            )
2115
          )
2116
        )
2117

2118
        @_scan_popup = true
×
2119
      else
2120
        Progress.Title(_("Scanning RPM database..."))
×
2121
      end
2122

UNCOV
2123
      nil
×
2124
    end
2125

2126
    # Callback for RPM DB scan progress
2127
    def ProgressScanDb(value)
1✔
2128
      if Mode.commandline
×
2129
        CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{value}%")
×
2130
      elsif @_scan_popup && UI.WidgetExists(Id(:label_scanDB_popup))
×
2131
        UI.ChangeWidget(Id(:progress), :Value, value)
×
2132
        cont = UI.PollInput != :abort
×
2133

2134
        Builtins.y2warning("Scan DB aborted") if !cont
×
2135

2136
        return cont
×
2137
      elsif full_screen
×
2138
        Progress.Step(value)
×
2139
      end
2140

2141
      # continue
2142
      true
×
2143
    end
2144

2145
    # Callback for error handling during RPM DB scan
2146
    def ErrorScanDb(error, description)
1✔
2147
      Builtins.y2error(
×
2148
        "ErrorScanDb callback: error: %1, description: %2",
2149
        error,
2150
        description
2151
      )
2152

2153
      # error message, could not read RPM database
2154
      message = _("Initialization of the target failed.")
×
2155

2156
      if Mode.commandline
×
2157
        CommandLine.Print(message)
×
2158
        CommandLine.Print(description)
×
2159

2160
        # ask user in the interactive mode
2161
        if CommandLine.Interactive
×
2162
          CommandLine.Print("")
×
2163

2164
          # command line mode - ask user whether target initializatin can be restarted
2165
          CommandLine.Print(_("Retry?"))
×
2166

2167
          if CommandLine.YesNo
×
2168
            # return Retry
2169
            return "R"
×
2170
          end
2171
        end
2172

2173
        # return Cancel
2174
        return "C"
×
2175
      end
2176

2177
      show_details = false
×
2178

2179
      button_box = ButtonBox(
×
2180
        PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton),
2181
        PushButton(Id(:retry), Opt(:customButton), Label.RetryButton),
2182
        PushButton(Id(:ignore), Opt(:okButton), Label.IgnoreButton)
2183
      )
2184

2185
      UI.OpenDialog(
×
2186
        Opt(:decorated),
2187
        layout_popup(message, button_box, false)
2188
      )
2189

2190
      r = nil
×
2191
      loop do
×
2192
        r = UI.UserInput
×
2193
        if r == :show
×
2194
          show_details = show_log_info(message, button_box)
×
2195
          if show_details
×
2196
            error_symbol = "UNKNOWN"
×
2197

2198
            case error
×
2199
            when 0
2200
              error_symbol = "NO_ERROR"
×
2201
            when 1
2202
              error_symbol = "FAILED"
×
2203
            end
2204

2205
            UI.ReplaceWidget(
×
2206
              Id(:info),
2207
              RichText(
2208
                Opt(:plainText),
2209
                Ops.add(
2210
                  # error message, %1 is code of the error,
2211
                  # detail string is appended to the end
2212
                  Builtins.sformat(_("Error: %1:"), error_symbol),
2213
                  description
2214
                )
2215
              )
2216
            )
2217
          else
2218
            UI.ReplaceWidget(Id(:info), Empty())
×
2219
          end
2220
        end
2221
        break if [:abort, :retry, :ignore].include?(r)
×
2222
      end
2223

2224
      Builtins.y2milestone("ErrorScanDb: user input: %1", r)
×
2225

2226
      UI.CloseDialog
×
2227

2228
      return "C" if r == :abort
×
2229
      return "R" if r == :retry
×
2230
      return "I" if r == :ignore
×
2231

2232
      Builtins.y2error("Unknown user input: %1", r)
×
2233

2234
      "C"
×
2235
    end
2236

2237
    # Callback for finish RPM DB scan event
2238
    def DoneScanDb(error, description)
1✔
2239
      Builtins.y2milestone(
×
2240
        "RPM DB scan finished: error: %1, reason: '%2'",
2241
        error,
2242
        description
2243
      )
2244

2245
      if Mode.commandline
×
2246
        # status message (command line mode)
2247
        CommandLine.PrintVerbose(_("RPM database read"))
×
2248
      elsif @_scan_popup && UI.WidgetExists(Id(:label_scanDB_popup))
×
2249
        UI.CloseDialog
×
2250
        @_scan_popup = false
×
2251
      elsif !full_screen
×
2252
        Builtins.y2error("The toplevel dialog is not a scan DB popup!")
×
2253
      end
2254

UNCOV
2255
      nil
×
2256
    end
2257

2258
    def Authentication(url, msg, username, password)
1✔
2259
      # FIXME: after SLE12 release
2260
      # The following 'if' block is a workaround for bnc#895719 that should be
2261
      # extracted to a proper private method (not sure if it will work as
2262
      # expected being a callback) and adapted to use normal _() instead of
2263
      # dgettext()
2264
      url_query = URI(url).query
×
2265
      if url_query
×
2266
        url_params = URI.decode_www_form(url_query).to_h
×
2267
        if url_params.key?("credentials")
×
2268
          # Seems to be the url of a registration server, so add the tip to msg
2269
          tip = Builtins.dgettext("registration",
×
2270
            "Check that this system is known to the registration server.")
2271
          msg = "#{tip}\n#{msg}"
×
2272
        end
2273
      end
2274

2275
      popup = VBox(
×
2276
        HSpacing(50), # enforce width
2277
        VSpacing(0.1),
2278
        # heading in a popup window
2279
        Heading(_("User Authentication")),
2280
        VSpacing(0.1),
2281
        HBox(
2282
          HSpacing(0.1),
2283
          RichText(
2284
            Opt(:plainText),
2285
            Builtins.sformat(_("URL: %1\n\n%2"), url, msg)
2286
          ),
2287
          HSpacing(0.1)
2288
        ),
2289
        VSpacing(0.1),
2290
        HBox(
2291
          HSpacing(1),
2292
          VBox(
2293
            # textentry label
2294
            InputField(Id(:username), Opt(:hstretch), _("&User Name"), username),
2295
            VSpacing(0.1),
2296
            # textentry label
2297
            Password(Id(:password), Opt(:hstretch), _("&Password"), password)
2298
          ),
2299
          HSpacing(1)
2300
        ),
2301
        VSpacing(0.5),
2302
        ButtonBox(
2303
          PushButton(Id(:cont), Opt(:default, :okButton), Label.ContinueButton),
2304
          PushButton(Id(:cancel), Opt(:cancelButton), Label.CancelButton)
2305
        ),
2306
        VSpacing(0.5)
2307
      )
2308

2309
      UI.OpenDialog(Opt(:decorated), popup)
×
2310

2311
      ui = Convert.to_symbol(UI.UserInput)
×
2312

2313
      username = Convert.to_string(UI.QueryWidget(Id(:username), :Value))
×
2314
      password = Convert.to_string(UI.QueryWidget(Id(:password), :Value))
×
2315

2316
      UI.CloseDialog
×
2317

2318
      {
2319
        "username" => username,
×
2320
        "password" => password,
2321
        "continue" => ui == :cont
2322
      }
2323
    end
2324

2325
    def NextTick
1✔
2326
      @current_tick = (@current_tick + 1) % TICK_LABELS
×
2327

UNCOV
2328
      nil
×
2329
    end
2330

2331
    # is the top level progress popup?
2332
    def IsProgressPopup
1✔
2333
      UI.WidgetExists(Id(:progress_widget)) &&
10✔
2334
        UI.WidgetExists(Id(:callback_progress_popup))
2335
    end
2336

2337
    def ProgressStart(id, task, in_percent, is_alive, _min, _max, _val_raw, val_percent)
1✔
2338
      Builtins.y2milestone("ProgressStart: %1", id)
2✔
2339

2340
      @tick_progress = is_alive
2✔
2341
      @val_progress = !in_percent && !is_alive
2✔
2342
      @current_tick = 0
2✔
2343

2344
      if Mode.commandline
2✔
2345
        CommandLine.Print(task)
×
2346
      else
2347
        subprogress_type = @tick_progress ? :tick : :progress
2✔
2348
        @progress_stack = Builtins.add(
2✔
2349
          @progress_stack,
2350
          "type" => subprogress_type, "task" => task
2351
        )
2352

2353
        if IsProgressPopup() &&
2✔
2354
            Ops.less_or_equal(Builtins.size(@progress_stack), 1)
2355
          # huh, the popup is already there?
2356
          Builtins.y2warning("Progress popup already opened...")
×
2357
          UI.CloseDialog
×
2358
        end
2359

2360
        if full_screen
2✔
2361
          Progress.SubprogressType(subprogress_type, 100)
×
2362
          Progress.SubprogressTitle(task)
×
2363
        else
2364
          UI.OpenDialog(
2✔
2365
            HBox(
2366
              HSpacing(1),
2367
              VBox(
2368
                VSpacing(0.5),
2369
                HSpacing(Id(:callback_progress_popup), MAX_POPUP_TEXT_SIZE),
2370
                if in_percent
2✔
2371
                  ProgressBar(Id(:progress_widget), task, 100, val_percent)
2✔
2372
                else
2373
                  BusyIndicator(Id(:progress_widget), task, 3000)
×
2374
                end,
2375
                VSpacing(0.2),
2376
                ButtonBox(
2377
                  PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton)
2378
                ),
2379
                VSpacing(0.5)
2380
              ),
2381
              HSpacing(1)
2382
            )
2383
          )
2384
        end
2385
      end
2386

2387
      nil
2✔
2388
    end
2389

2390
    def ProgressEnd(id)
1✔
2391
      Builtins.y2milestone("ProgressFinish: %1", id)
2✔
2392

2393
      # remove the last element from the progress stack
2394
      @progress_stack = Builtins.remove(
2✔
2395
        @progress_stack,
2396
        Ops.subtract(Builtins.size(@progress_stack), 1)
2397
      )
2398

2399
      if !Mode.commandline && IsProgressPopup()
2✔
2400
        UI.CloseDialog if Builtins.size(@progress_stack) >= 0
×
2401
      elsif full_screen
2✔
2402
        if Ops.greater_than(Builtins.size(@progress_stack), 0)
×
2403
          progress_type = Ops.get_symbol(
×
2404
            @progress_stack,
2405
            [Ops.subtract(Builtins.size(@progress_stack), 1), "type"],
2406
            :none
2407
          )
2408
          task = Ops.get_string(
×
2409
            @progress_stack,
2410
            [Ops.subtract(Builtins.size(@progress_stack), 1), "task"],
2411
            ""
2412
          )
2413

2414
          Progress.SubprogressType(progress_type, 100)
×
2415
          Progress.SubprogressTitle(task)
×
2416
        end
2417
      end
2418

2419
      nil
2✔
2420
    end
2421

2422
    def ProgressProgress(id, val_raw, val_percent)
1✔
2423
      Builtins.y2debug("ProgressProgress: %1, %2%% ", id, val_percent)
6✔
2424

2425
      if Mode.commandline
6✔
2426
        if @tick_progress
×
2427
          tick_label = TICK_LABELS[@current_tick]
×
2428
          CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + tick_label)
×
2429
          NextTick()
×
2430
        else
2431
          CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{val_percent}%")
×
2432
        end
2433
      elsif IsProgressPopup()
6✔
2434
        if @tick_progress || @val_progress
×
2435
          UI.ChangeWidget(Id(:progress_widget), :Alive, true)
×
2436
        else
2437
          UI.ChangeWidget(Id(:progress_widget), :Value, val_percent)
×
2438
        end
2439

2440
        # aborted ?
2441
        input = UI.PollInput
×
2442
        if input == :abort
×
2443
          Builtins.y2warning(
×
2444
            "Callback %1 has been aborted at %2%% (raw: %3)",
2445
            id,
2446
            val_percent,
2447
            val_raw
2448
          )
2449
          return false
×
2450
        end
2451
      elsif full_screen
6✔
2452
        # fullscreen callbacks
2453
        Progress.SubprogressValue(val_percent)
×
2454
      end
2455

2456
      true
6✔
2457
    end
2458

2459
    # Hanler for ProcessStart callback - handle start of a package manager process
2460
    # @param [String] task Decription of the task
2461
    # @param [Array<String>] stages Descriptions of the stages
2462
    # @param [String] help Help text describing the process
2463
    def ProcessStart(task, stages, help)
1✔
2464
      stages = deep_copy(stages)
2✔
2465
      Builtins.y2milestone(
2✔
2466
        "Process: Start: task: %1, stages: %2, help: %3",
2467
        task,
2468
        stages,
2469
        help
2470
      )
2471
      Builtins.y2milestone(
2✔
2472
        "Progress: status: %1, isrunning: %2",
2473
        Progress.status,
2474
        Progress.IsRunning
2475
      )
2476

2477
      return if Mode.commandline
2✔
2478

2479
      opened = false
2✔
2480

2481
      if Progress.status
2✔
2482
        if !Progress.IsRunning
2✔
2483
          Builtins.y2milestone("Opening Wizard window...")
2✔
2484
          Wizard.CreateDialog
2✔
2485

2486
          opened = true
2✔
2487
        end
2488

2489
        # set 100 + number of stages as the max value,
2490
        # Progress module counts stages as extra steps
2491
        Progress.New(task, "", 100 + stages.size, stages, [], help)
2✔
2492
        Progress.Title(task)
2✔
2493
        @last_stage = 0
2✔
2494
      end
2495

2496
      @opened_wizard = Builtins.add(@opened_wizard, opened)
2✔
2497
      Builtins.y2milestone("Wizard stack: %1", @opened_wizard)
2✔
2498

2499
      nil
2✔
2500
    end
2501

2502
    # Hander for ProcessProgress callback - report total progress
2503
    # @param [Fixnum] percent Total progress in percent
2504
    def ProcessProgress(percent)
1✔
2505
      Builtins.y2debug("Process: %1%%", percent)
4✔
2506

2507
      return true if Mode.commandline
4✔
2508

2509
      Progress.Step(percent)
4✔
2510

2511
      true
4✔
2512
    end
2513

2514
    # Hander for ProcessNextStage callback - the current stage has been finished
2515
    def ProcessNextStage
1✔
2516
      Builtins.y2milestone("Setting stage: %1", @last_stage)
10✔
2517

2518
      return if Mode.commandline
10✔
2519

2520
      Progress.Stage(@last_stage, "", -1)
10✔
2521

2522
      @last_stage = Ops.add(@last_stage, 1)
10✔
2523

2524
      nil
10✔
2525
    end
2526

2527
    # Hander for ProcessDone callback - the process has been finished
2528
    def ProcessDone
1✔
2529
      Builtins.y2milestone("Process: Finished")
2✔
2530
      return if Mode.commandline
2✔
2531

2532
      idx = Ops.subtract(Builtins.size(@opened_wizard), 1)
2✔
2533

2534
      close = Ops.get(@opened_wizard, idx, false)
2✔
2535
      @opened_wizard = Builtins.remove(@opened_wizard, idx)
2✔
2536

2537
      Builtins.y2milestone(
2✔
2538
        "Close Wizard window: %1, new stack: %2",
2539
        close,
2540
        @opened_wizard
2541
      )
2542

2543
      # set 100%
2544
      Progress.Finish
2✔
2545

2546
      if close
2✔
2547
        Builtins.y2milestone("Closing Wizard window...")
2✔
2548
        Wizard.CloseDialog
2✔
2549
      end
2550

2551
      nil
2✔
2552
    end
2553

2554
    # Register callbacks for media change
2555
    def SetMediaCallbacks
1✔
2556
      Pkg.CallbackMediaChange(
1✔
2557
        fun_ref(
2558
          method(:MediaChange),
2559
          "string (string, string, string, string, integer, string, integer, string, boolean, list <string>, integer)"
2560
        )
2561
      )
2562
      Pkg.CallbackSourceChange(
1✔
2563
        fun_ref(method(:SourceChange), "void (integer, integer)")
2564
      )
2565

2566
      nil
1✔
2567
    end
2568

2569
    def ClearScriptCallbacks
1✔
2570
      Pkg.CallbackScriptStart(nil)
×
2571
      Pkg.CallbackScriptProgress(nil)
×
2572
      Pkg.CallbackScriptProblem(nil)
×
2573
      Pkg.CallbackScriptFinish(nil)
×
2574

2575
      Pkg.CallbackMessage(nil)
×
2576

UNCOV
2577
      nil
×
2578
    end
2579

2580
    def SetScriptCallbacks
1✔
2581
      Pkg.CallbackScriptStart(
1✔
2582
        fun_ref(method(:ScriptStart), "void (string, string, string, string)")
2583
      )
2584
      Pkg.CallbackScriptProgress(
1✔
2585
        fun_ref(method(:ScriptProgress), "boolean (boolean, string)")
2586
      )
2587
      Pkg.CallbackScriptProblem(
1✔
2588
        fun_ref(method(:ScriptProblem), "string (string)")
2589
      )
2590
      Pkg.CallbackScriptFinish(fun_ref(method(:ScriptFinish), "void ()"))
1✔
2591

2592
      Pkg.CallbackMessage(
1✔
2593
        fun_ref(method(:Message), "boolean (string, string, string, string)")
2594
      )
2595

2596
      nil
1✔
2597
    end
2598

2599
    def SetScanDBCallbacks
1✔
2600
      Pkg.CallbackStartScanDb(fun_ref(method(:StartScanDb), "void ()"))
1✔
2601
      Pkg.CallbackProgressScanDb(
1✔
2602
        fun_ref(method(:ProgressScanDb), "boolean (integer)")
2603
      )
2604
      Pkg.CallbackErrorScanDb(
1✔
2605
        fun_ref(method(:ErrorScanDb), "string (integer, string)")
2606
      )
2607
      Pkg.CallbackDoneScanDb(
1✔
2608
        fun_ref(method(:DoneScanDb), "void (integer, string)")
2609
      )
2610

2611
      nil
1✔
2612
    end
2613

2614
    def ResetScanDBCallbacks
1✔
2615
      Pkg.CallbackStartScanDb(nil)
×
2616
      Pkg.CallbackProgressScanDb(nil)
×
2617
      Pkg.CallbackErrorScanDb(nil)
×
2618
      Pkg.CallbackDoneScanDb(nil)
×
2619

UNCOV
2620
      nil
×
2621
    end
2622

2623
    def SetDownloadCallbacks
1✔
2624
      Pkg.CallbackInitDownload(fun_ref(method(:InitDownload), "void (string)"))
1✔
2625
      Pkg.CallbackStartDownload(
1✔
2626
        fun_ref(method(:StartDownload), "void (string, string)")
2627
      )
2628
      Pkg.CallbackProgressDownload(
1✔
2629
        fun_ref(
2630
          method(:ProgressDownload),
2631
          "boolean (integer, integer, integer)"
2632
        )
2633
      )
2634
      Pkg.CallbackDoneDownload(
1✔
2635
        fun_ref(method(:DoneDownload), "void (integer, string)")
2636
      )
2637
      Pkg.CallbackDestDownload(fun_ref(method(:DestDownload), "void ()"))
1✔
2638
      Pkg.CallbackStartRefresh(fun_ref(method(:RefreshStarted), "void ()"))
1✔
2639
      Pkg.CallbackDoneRefresh(fun_ref(method(:RefreshDone), "void ()"))
1✔
2640

2641
      nil
1✔
2642
    end
2643

2644
    def ResetDownloadCallbacks
1✔
2645
      Pkg.CallbackInitDownload(nil)
×
2646
      Pkg.CallbackStartDownload(nil)
×
2647
      Pkg.CallbackProgressDownload(nil)
×
2648
      Pkg.CallbackDoneDownload(nil)
×
2649
      Pkg.CallbackDestDownload(nil)
×
2650
      Pkg.CallbackStartRefresh(nil)
×
2651
      Pkg.CallbackDoneRefresh(nil)
×
2652

UNCOV
2653
      nil
×
2654
    end
2655

2656
    def SetSourceCreateCallbacks
1✔
2657
      # source create callbacks
2658
      Pkg.CallbackSourceCreateStart(
1✔
2659
        fun_ref(method(:SourceCreateStart), "void (string)")
2660
      )
2661
      Pkg.CallbackSourceCreateProgress(
1✔
2662
        fun_ref(method(:SourceCreateProgress), "boolean (integer)")
2663
      )
2664
      Pkg.CallbackSourceCreateError(
1✔
2665
        fun_ref(method(:SourceCreateError), "symbol (string, symbol, string)")
2666
      )
2667
      Pkg.CallbackSourceCreateEnd(
1✔
2668
        fun_ref(method(:SourceCreateEnd), "void (string, symbol, string)")
2669
      )
2670
      Pkg.CallbackSourceCreateInit(
1✔
2671
        fun_ref(method(:SourceCreateInit), "void ()")
2672
      )
2673
      Pkg.CallbackSourceCreateDestroy(
1✔
2674
        fun_ref(method(:SourceCreateDestroy), "void ()")
2675
      )
2676

2677
      nil
1✔
2678
    end
2679

2680
    def SetSourceProbeCallbacks
1✔
2681
      # source probing callbacks
2682
      Pkg.CallbackSourceProbeStart(
1✔
2683
        fun_ref(method(:SourceProbeStart), "void (string)")
2684
      )
2685
      Pkg.CallbackSourceProbeFailed(
1✔
2686
        fun_ref(method(:SourceProbeFailed), "void (string, string)")
2687
      )
2688
      Pkg.CallbackSourceProbeSucceeded(
1✔
2689
        fun_ref(method(:SourceProbeSucceeded), "void (string, string)")
2690
      )
2691
      Pkg.CallbackSourceProbeProgress(
1✔
2692
        fun_ref(method(:SourceProbeProgress), "boolean (string, integer)")
2693
      )
2694
      Pkg.CallbackSourceProbeError(
1✔
2695
        fun_ref(method(:SourceProbeError), "symbol (string, symbol, string)")
2696
      )
2697
      Pkg.CallbackSourceProbeEnd(
1✔
2698
        fun_ref(method(:SourceProbeEnd), "void (string, symbol, string)")
2699
      )
2700

2701
      nil
1✔
2702
    end
2703

2704
    def SetProcessCallbacks
1✔
2705
      # register process callbacks (total progress)
2706
      Pkg.CallbackProcessStart(
1✔
2707
        fun_ref(method(:ProcessStart), "void (string, list <string>, string)")
2708
      )
2709
      Pkg.CallbackProcessProgress(
1✔
2710
        fun_ref(method(:ProcessProgress), "boolean (integer)")
2711
      )
2712
      Pkg.CallbackProcessNextStage(
1✔
2713
        fun_ref(method(:ProcessNextStage), "void ()")
2714
      )
2715
      Pkg.CallbackProcessDone(fun_ref(method(:ProcessDone), "void ()"))
1✔
2716

2717
      nil
1✔
2718
    end
2719

2720
    def SetProvideCallbacks
1✔
2721
      Pkg.CallbackStartProvide(
1✔
2722
        fun_ref(method(:StartProvide), "void (string, integer, boolean)")
2723
      )
2724
      Pkg.CallbackProgressProvide(
1✔
2725
        fun_ref(method(:ProgressProvide), "boolean (integer)")
2726
      )
2727
      Pkg.CallbackDoneProvide(
1✔
2728
        fun_ref(method(:DoneProvide), "string (integer, string, string)")
2729
      )
2730
      Pkg.CallbackStartPackage(
1✔
2731
        fun_ref(
2732
          method(:StartPackage),
2733
          "void (string, string, string, integer, boolean)"
2734
        )
2735
      )
2736
      Pkg.CallbackProgressPackage(
1✔
2737
        fun_ref(method(:ProgressPackage), "boolean (integer)")
2738
      )
2739
      Pkg.CallbackDonePackage(
1✔
2740
        fun_ref(method(:DonePackage), "string (integer, string)")
2741
      )
2742
      Pkg.CallbackPkgGpgCheck(
1✔
2743
        fun_ref(method(:pkg_gpg_check), "string(map)")
2744
      )
2745

2746
      nil
1✔
2747
    end
2748

2749
    def SetPatchCallbacks
1✔
2750
      Pkg.CallbackStartDeltaDownload(
1✔
2751
        fun_ref(method(:StartDeltaProvide), "void (string, integer)")
2752
      )
2753
      Pkg.CallbackProgressDeltaDownload(
1✔
2754
        fun_ref(method(:ProgressProvide), "boolean (integer)")
2755
      )
2756
      Pkg.CallbackProblemDeltaDownload(
1✔
2757
        fun_ref(method(:ProblemDeltaDownload), "void (string)")
2758
      )
2759
      Pkg.CallbackFinishDeltaDownload(
1✔
2760
        fun_ref(method(:FinishDeltaProvide), "void ()")
2761
      )
2762

2763
      Pkg.CallbackStartDeltaApply(
1✔
2764
        fun_ref(method(:StartDeltaApply), "void (string)")
2765
      )
2766
      Pkg.CallbackProgressDeltaApply(
1✔
2767
        fun_ref(method(:ProgressDeltaApply), "void (integer)")
2768
      )
2769
      Pkg.CallbackProblemDeltaApply(
1✔
2770
        fun_ref(method(:ProblemDeltaApply), "void (string)")
2771
      )
2772
      Pkg.CallbackFinishDeltaApply(
1✔
2773
        fun_ref(method(:FinishDeltaProvide), "void ()")
2774
      )
2775

2776
      nil
1✔
2777
    end
2778

2779
    def SetSourceReportCallbacks
1✔
2780
      # source report callbacks
2781
      Pkg.CallbackSourceReportStart(
1✔
2782
        fun_ref(method(:SourceReportStart), "void (integer, string, string)")
2783
      )
2784
      Pkg.CallbackSourceReportProgress(
1✔
2785
        fun_ref(method(:SourceReportProgress), "boolean (integer)")
2786
      )
2787
      Pkg.CallbackSourceReportError(
1✔
2788
        fun_ref(
2789
          method(:SourceReportError),
2790
          "symbol (integer, string, symbol, string)"
2791
        )
2792
      )
2793
      Pkg.CallbackSourceReportEnd(
1✔
2794
        fun_ref(
2795
          method(:SourceReportEnd),
2796
          "void (integer, string, string, symbol, string)"
2797
        )
2798
      )
2799
      Pkg.CallbackSourceReportInit(
1✔
2800
        fun_ref(method(:SourceReportInit), "void ()")
2801
      )
2802
      Pkg.CallbackSourceReportDestroy(
1✔
2803
        fun_ref(method(:SourceReportDestroy), "void ()")
2804
      )
2805

2806
      nil
1✔
2807
    end
2808

2809
    def SetProgressReportCallbacks
1✔
2810
      Pkg.CallbackProgressReportStart(
1✔
2811
        fun_ref(
2812
          method(:ProgressStart),
2813
          "void (integer, string, boolean, boolean, integer, integer, integer, integer)"
2814
        )
2815
      )
2816
      Pkg.CallbackProgressReportProgress(
1✔
2817
        fun_ref(
2818
          method(:ProgressProgress),
2819
          "boolean (integer, integer, integer)"
2820
        )
2821
      )
2822
      Pkg.CallbackProgressReportEnd(
1✔
2823
        fun_ref(method(:ProgressEnd), "void (integer)")
2824
      )
2825

2826
      nil
1✔
2827
    end
2828

2829
    def SetFileConflictCallbacks
1✔
2830
      ::Packages::FileConflictCallbacks.register
1✔
2831
    end
2832

2833
    # Register package manager callbacks
2834
    def InitPackageCallbacks
1✔
2835
      SetProcessCallbacks()
1✔
2836

2837
      SetProvideCallbacks()
1✔
2838

2839
      SetPatchCallbacks()
1✔
2840

2841
      SetSourceCreateCallbacks()
1✔
2842

2843
      SetSourceProbeCallbacks()
1✔
2844

2845
      SetSourceReportCallbacks()
1✔
2846

2847
      SetProgressReportCallbacks()
1✔
2848

2849
      SetFileConflictCallbacks()
1✔
2850

2851
      # authentication callback
2852
      Pkg.CallbackAuthentication(
1✔
2853
        fun_ref(
2854
          method(:Authentication),
2855
          "map <string, any> (string, string, string, string)"
2856
        )
2857
      )
2858

2859
      # @see bugzilla #183821
2860
      # Do not register these callbacks in case of AutoInstallation
2861
      # And for AutoUpgrade neither (bnc#820166)
2862
      if !(Mode.autoinst || Mode.autoupgrade)
1✔
2863
        # Signature-related callbacks
2864
        Pkg.CallbackAcceptUnsignedFile(
1✔
2865
          fun_ref(
2866
            SignatureCheckCallbacks.method(:AcceptUnsignedFile),
2867
            "boolean (string, integer)"
2868
          )
2869
        )
2870
        Pkg.CallbackAcceptUnknownGpgKey(
1✔
2871
          fun_ref(
2872
            SignatureCheckCallbacks.method(:AcceptUnknownGpgKey),
2873
            "boolean (string, string, integer)"
2874
          )
2875
        )
2876
        # During installation untrusted repositories are disabled to avoid
2877
        # asking again
2878
        gpg_callback = Stage.initial ? :import_gpg_key_or_disable : :ImportGpgKey
1✔
2879
        Pkg.CallbackImportGpgKey(
1✔
2880
          fun_ref(
2881
            SignatureCheckCallbacks.method(gpg_callback),
2882
            "boolean (map <string, any>, integer)"
2883
          )
2884
        )
2885
        Pkg.CallbackAcceptVerificationFailed(
1✔
2886
          fun_ref(
2887
            SignatureCheckCallbacks.method(:AcceptVerificationFailed),
2888
            "boolean (string, map <string, any>, integer)"
2889
          )
2890
        )
2891
        Pkg.CallbackTrustedKeyAdded(
1✔
2892
          fun_ref(
2893
            SignatureCheckCallbacks.method(:TrustedKeyAdded),
2894
            "void (map <string, any>)"
2895
          )
2896
        )
2897
        Pkg.CallbackTrustedKeyRemoved(
1✔
2898
          fun_ref(
2899
            SignatureCheckCallbacks.method(:TrustedKeyRemoved),
2900
            "void (map <string, any>)"
2901
          )
2902
        )
2903
        Pkg.CallbackAcceptFileWithoutChecksum(
1✔
2904
          fun_ref(
2905
            SignatureCheckCallbacks.method(:AcceptFileWithoutChecksum),
2906
            "boolean (string)"
2907
          )
2908
        )
2909
        Pkg.CallbackAcceptWrongDigest(
1✔
2910
          fun_ref(
2911
            SignatureCheckCallbacks.method(:AcceptWrongDigest),
2912
            "boolean (string, string, string)"
2913
          )
2914
        )
2915
        Pkg.CallbackAcceptUnknownDigest(
1✔
2916
          fun_ref(
2917
            SignatureCheckCallbacks.method(:AcceptUnknownDigest),
2918
            "boolean (string, string)"
2919
          )
2920
        )
2921
      end
2922

2923
      SetMediaCallbacks()
1✔
2924

2925
      SetScriptCallbacks()
1✔
2926

2927
      SetScanDBCallbacks()
1✔
2928

2929
      SetDownloadCallbacks()
1✔
2930

2931
      nil
1✔
2932
    end
2933

2934
    #=============================================================================
2935
    #  constructor and callback init
2936
    #=============================================================================
2937

2938
    def RegisterEmptyProgressCallbacks
1✔
2939
      ::Packages::DummyCallbacks.register
×
2940
    end
2941

2942
    def RestoreProcessCallbacks
1✔
2943
      Pkg.CallbackProcessStart(nil)
×
2944
      Pkg.CallbackProcessProgress(nil)
×
2945
      Pkg.CallbackProcessNextStage(nil)
×
2946
      Pkg.CallbackProcessDone(nil)
×
2947

UNCOV
2948
      nil
×
2949
    end
2950

2951
    def RestoreProvideCallbacks
1✔
2952
      Pkg.CallbackStartProvide(nil)
×
2953
      Pkg.CallbackProgressProvide(nil)
×
2954
      Pkg.CallbackDoneProvide(nil)
×
2955
      Pkg.CallbackStartPackage(nil)
×
2956
      Pkg.CallbackProgressPackage(nil)
×
2957
      Pkg.CallbackDonePackage(nil)
×
2958
      Pkg.CallbackPkgGpgCheck(nil)
×
2959

UNCOV
2960
      nil
×
2961
    end
2962

2963
    def RestorePatchCallbacks
1✔
2964
      Pkg.CallbackStartDeltaDownload(nil)
×
2965
      Pkg.CallbackProgressDeltaDownload(nil)
×
2966
      Pkg.CallbackProblemDeltaDownload(nil)
×
2967
      Pkg.CallbackFinishDeltaDownload(nil)
×
2968

2969
      Pkg.CallbackStartDeltaApply(nil)
×
2970
      Pkg.CallbackProgressDeltaApply(nil)
×
2971
      Pkg.CallbackProblemDeltaApply(nil)
×
2972
      Pkg.CallbackFinishDeltaApply(nil)
×
2973

UNCOV
2974
      nil
×
2975
    end
2976

2977
    def RestoreSourceCreateCallbacks
1✔
2978
      Pkg.CallbackSourceCreateStart(nil)
×
2979
      Pkg.CallbackSourceCreateProgress(nil)
×
2980
      Pkg.CallbackSourceCreateError(nil)
×
2981
      Pkg.CallbackSourceCreateEnd(nil)
×
2982
      Pkg.CallbackSourceCreateInit(nil)
×
2983
      Pkg.CallbackSourceCreateDestroy(nil)
×
2984

UNCOV
2985
      nil
×
2986
    end
2987

2988
    def RestoreSourceReportCallbacks
1✔
2989
      Pkg.CallbackSourceReportStart(nil)
×
2990
      Pkg.CallbackSourceReportProgress(nil)
×
2991
      Pkg.CallbackSourceReportError(nil)
×
2992
      Pkg.CallbackSourceReportEnd(nil)
×
2993
      Pkg.CallbackSourceReportInit(nil)
×
2994
      Pkg.CallbackSourceReportDestroy(nil)
×
2995

UNCOV
2996
      nil
×
2997
    end
2998

2999
    def RestoreProgressReportCallbacks
1✔
3000
      Pkg.CallbackProgressReportStart(nil)
×
3001
      Pkg.CallbackProgressReportProgress(nil)
×
3002
      Pkg.CallbackProgressReportEnd(nil)
×
3003

UNCOV
3004
      nil
×
3005
    end
3006

3007
    def RestorePreviousProgressCallbacks
1✔
3008
      RestoreProcessCallbacks()
×
3009

3010
      RestoreProvideCallbacks()
×
3011

3012
      RestorePatchCallbacks()
×
3013

3014
      RestoreSourceCreateCallbacks()
×
3015

3016
      RestoreSourceReportCallbacks()
×
3017

3018
      RestoreProgressReportCallbacks()
×
3019

3020
      ClearScriptCallbacks()
×
3021

3022
      ResetScanDBCallbacks()
×
3023

3024
      ResetDownloadCallbacks()
×
3025

UNCOV
3026
      nil
×
3027
    end
3028

3029
    # all published variables used only by specific slideshow callbacks module
3030
    publish variable: :_package_name, type: "string"
1✔
3031
    publish variable: :_package_size, type: "integer"
1✔
3032
    publish variable: :_deleting_package, type: "boolean"
1✔
3033
    publish variable: :_current_source, type: "integer"
1✔
3034
    publish function: :StartProvide, type: "void (string, integer, boolean)"
1✔
3035
    publish function: :ProgressProvide, type: "boolean (integer)"
1✔
3036
    publish function: :DoneProvide, type: "string (integer, string, string)"
1✔
3037
    publish function: :EnableAsterixPackage, type: "boolean (boolean)"
1✔
3038
    publish function: :StartPackage, type: "void (string, string, string, integer, boolean)"
1✔
3039
    publish function: :ProgressPackage, type: "boolean (integer)"
1✔
3040
    publish function: :DonePackage, type: "string (integer, string)"
1✔
3041
    publish function: :MediaChange, type: "string (string, string, string, string, integer, string, integer, string, boolean, list <string>, integer)"
1✔
3042
    publish function: :SourceChange, type: "void (integer, integer)"
1✔
3043
    publish function: :FormatPatchName, type: "string (string, string, string)"
1✔
3044
    publish function: :ScriptProblem, type: "string (string)"
1✔
3045
    publish function: :StartDownload, type: "void (string, string)"
1✔
3046
    publish function: :ProgressDownload, type: "boolean (integer, integer, integer)"
1✔
3047
    publish function: :DoneDownload, type: "void (integer, string)"
1✔
3048
    publish function: :StartRebuildDB, type: "void ()"
1✔
3049
    publish function: :ProgressRebuildDB, type: "void (integer)"
1✔
3050
    publish function: :StopRebuildDB, type: "void (integer, string)"
1✔
3051
    publish function: :SetRebuildDBCallbacks, type: "void ()"
1✔
3052
    publish function: :StartConvertDB, type: "void (string)"
1✔
3053
    publish function: :ProgressConvertDB, type: "void (integer, string)"
1✔
3054
    publish function: :StopConvertDB, type: "void (integer, string)"
1✔
3055
    publish function: :SetConvertDBCallbacks, type: "void ()"
1✔
3056
    publish function: :SetMediaCallbacks, type: "void ()"
1✔
3057
    publish function: :SetScriptCallbacks, type: "void ()"
1✔
3058
    publish function: :InitPackageCallbacks, type: "void ()"
1✔
3059
    publish function: :RegisterEmptyProgressCallbacks, type: "void ()"
1✔
3060
    publish function: :RestorePreviousProgressCallbacks, type: "void ()"
1✔
3061

3062
  private
1✔
3063

3064
    # creates layout for ChangeMediumPopup
3065
    def layout_popup(message, button_box, info_on)
1✔
3066
      vertical_size = info_on ? 10 : 1
9✔
3067
      VBox(
9✔
3068
        HSpacing(50), # enforce width
3069
        VSpacing(0.1),
3070
        Left(Label(message)),
3071
        VSpacing(0.1),
3072
        HBox(
3073
          HSpacing(0.6),
3074
          Left(
3075
            CheckBox(
3076
              Id(:show),
3077
              Opt(:notify),
3078
              # check box
3079
              _("Show &details"),
3080
              info_on
3081
            )
3082
          )
3083
        ),
3084
        VSpacing(0.4),
3085
        HBox(
3086
          VSpacing(vertical_size),
3087
          HSpacing(0.1),
3088
          ReplacePoint(Id(:info), Empty()),
3089
          HSpacing(0.1)
3090
        ),
3091
        HBox(HSpacing(0.1), button_box, HSpacing(0.1)),
3092
        VSpacing(0.2)
3093
      )
3094
    end
3095

3096
    # TODO: looks like generic enough to not be here
3097
    def textmode
1✔
3098
      Mode.commandline || UI.GetDisplayInfo["TextMode"]
3✔
3099
    end
3100

3101
    # TODO: looks like generic enough to not be here
3102
    def display_width
1✔
3103
      Mode.commandline ? 0 : Ops.get_integer(UI.GetDisplayInfo, "Width", 0)
3✔
3104
    end
3105

3106
    # functions related to the persistent storage
3107
    def load_config
1✔
3108
      @config = {}
×
3109

3110
      if FileUtils.IsFile(@conf_file)
×
3111
        log.info "Reading config file #{@conf_file}"
×
3112
        read_conf = SCR.Read(path(".target.ycp"), @conf_file)
×
3113

3114
        @config = read_conf if read_conf.is_a?(::Hash)
×
3115
        log.info "Current config: #{@config}"
×
3116
      else
3117
        log.info "No configuration found (file #{@conf_file} is missing)"
×
3118
      end
3119
    end
3120

3121
    def autoeject
1✔
3122
      load_config unless @config
×
3123

3124
      @config.fetch("automatic_eject", false)
×
3125
    end
3126

3127
    def store_autoeject(value)
1✔
3128
      load_config unless @config
×
3129

3130
      log.info "Config: store automatic_eject to #{value}"
×
3131
      @config["automatic_eject"] = value
×
3132

3133
      SCR.Write(path(".target.ycp"), @conf_file, @config)
×
3134
    end
3135

3136
    def progress_box(heading, name, size)
1✔
3137
      VBox(
4✔
3138
        HSpacing(40),
3139
        # popup heading
3140
        Heading(heading),
3141
        Left(
3142
          HBox(
3143
            VBox(
3144
              Left(Label(Opt(:boldFont), _("Package: "))),
3145
              Left(Label(Opt(:boldFont), _("Size: ")))
3146
            ),
3147
            VBox(Left(Label(name)), Left(Label(size)))
3148
          )
3149
        ),
3150
        ProgressBar(Id(:progress), " ", 100, 0),
3151
        ButtonBox(
3152
          PushButton(Id(:abort), Opt(:key_F9, :cancelButton), Label.AbortButton)
3153
        )
3154
      )
3155
    end
3156

3157
    def full_screen
1✔
3158
      return false if Mode.commandline
24✔
3159

3160
      ret = UI.WidgetExists(:progress_replace_point)
23✔
3161
      log.debug "Running in fullscreen mode: #{ret}"
23✔
3162

3163
      ret
23✔
3164
    end
3165

3166
    def retry_label(timeout)
1✔
3167
      Builtins.sformat(
1✔
3168
        _("Remaining time to automatic retry: %1"),
3169
        String.FormatTime(timeout)
3170
      )
3171
    end
3172

3173
    def show_log_info(message, buttonbox)
1✔
3174
      show_value = UI.QueryWidget(Id(:show), :Value)
×
3175
      UI.CloseDialog
×
3176
      if show_value
×
3177
        UI.OpenDialog(
×
3178
          Opt(:decorated),
3179
          layout_popup(message, buttonbox, true)
3180
        )
3181
        true
×
3182
      else
3183
        UI.OpenDialog(
×
3184
          Opt(:decorated),
3185
          layout_popup(message, buttonbox, false)
3186
        )
3187
        UI.ReplaceWidget(Id(:info), Empty())
×
3188
        false
×
3189
      end
3190
    end
3191

3192
    def cd_devices(preferred)
1✔
3193
      cds = Convert.convert(
5✔
3194
        SCR.Read(path(".probe.cdrom")),
3195
        from: "any",
3196
        to:   "list <map>"
3197
      )
3198
      ret = []
5✔
3199

3200
      if !cds.nil?
5✔
3201
        Builtins.foreach(cds) do |cd|
4✔
3202
          dev = Ops.get_string(cd, "dev_name", "")
12✔
3203
          model = Ops.get_string(cd, "model", "")
12✔
3204
          deflt = preferred == dev
12✔
3205
          if !dev.nil? && dev != "" && !model.nil?
12✔
3206
            ret = Builtins.add(
12✔
3207
              ret,
3208
              Item(
3209
                Id(dev),
3210
                Ops.add(
3211
                  Ops.add(deflt ? "\u27A4 " : "", model),
12✔
3212
                  Builtins.sformat(" (%1)", dev)
3213
                )
3214
              )
3215
            )
3216
          end
3217
        end
3218
      end
3219

3220
      log.info "Detected CD devices: #{ret}"
5✔
3221

3222
      deep_copy(ret)
5✔
3223
    end
3224

3225
    # check and save the autoeject configuration if needed
3226
    def remember_autoeject
1✔
3227
      new_value = UI.QueryWidget(Id(:auto_eject), :Value)
×
3228

3229
      store_autoeject(new_value) if new_value != autoeject
×
3230
    end
3231

3232
    def process_message(msg, max_len)
1✔
3233
      words = msg.split
×
3234

3235
      log.info "words: %{words}"
×
3236

3237
      words = words.map do |w|
×
3238
        parsed = URL.Parse(w)
×
3239
        req_size = max_len - (msg.size - w.size)
×
3240
        # is it a valid URL? TODO: move to URL this check
3241
        if ["ftp", "http", "nfs", "file", "dir", "iso", "smb", "disk"].include?(parsed["scheme"])
×
3242
          # reformat the URL
3243
          w = URL.FormatURL(parsed, max_len)
×
3244
        elsif w.start_with?("/")
×
3245
          parts = w.split("/")
×
3246

3247
          w = String.FormatFilename(w, req_size) if parts.size > 2 # why this number?
×
3248
        end
3249
        w
×
3250
      end
3251

3252
      ret = words.join(" ")
×
3253

3254
      log.info "URL conversion: '#{URL.HidePassword(msg)}' converted to '#{URL.HidePassword(ret)}%2'" if ret != msg
×
3255

3256
      ret
×
3257
    end
3258
  end
3259

3260
  PackageCallbacks = PackageCallbacksClass.new
1✔
3261
  PackageCallbacks.main
1✔
3262
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc