library/packages/src/modules/PackageCallbacks.rb
# ***************************************************************************
#
# Copyright (c) 2002 - 2012 Novell, Inc.
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, contact Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail,
# you may find current contact information at www.novell.com
#
# ***************************************************************************
require "yast"
require "uri"
require "packages/dummy_callbacks"
require "packages/file_conflict_callbacks"
module Yast
# Provides the default Callbacks for Pkg::
class PackageCallbacksClass < Module
include Yast::Logger
# text to clean progress bar in command line
CLEAR_PROGRESS_TEXT = ("\b" * 10) + (" " * 10) + ("\b" * 10)
# max. length of the text in the repository popup window
MAX_POPUP_TEXT_SIZE = 60
# base in seconds for automatic retry after a timeout,
# it will be logarithmic increased upto {RETRY_MAX_TIMEOUT}
RETRY_TIMEOUT = 30
# number of automatic retries
RETRY_ATTEMPTS = 100
# max. retry timeout (15 minutes)
RETRY_MAX_TIMEOUT = 15 * 60
# symbols for ticking in cmd line
TICK_LABELS = ["/", "-", "\\", "|"].freeze
# Debugging: log the called callbacks when Y2DEBUG_CALLBACKS is set to 1
#
# This uses some Ruby meta programming, the "method_added" is called whenever
# a new method is added into this class, i.e. when each of the following "def"
# is processed.
#
# @param name [Symbol] name of the added method
def self.method_added(name)
super
# log the callbacks only when requested, it's quite verbose
return if ENV["Y2DEBUG_CALLBACKS"] != "1"
name_str = name.to_s
# do not add a hook for a hook itself otherwise it would result
# in an endless recursive loop adding a hook for a hook for a hook for...
if name_str.end_with?("_hook") ||
# ignore dynamically added helper methods for the published variables
name_str.start_with?("_") ||
# ignore lowercase methods, they are just some helper methods
name_str.match(/^[[:lower:]]/) ||
# already present
method_defined?("#{name}_hook")
return
end
# add a new *_hook method as a wrapper for the original method,
# log the name of the called method
hook = <<-HOOK
def #{name}_hook(*params)
log.info("Starting callback #{self}::#{name}")
result = #{name}_without_hook(*params)
log.info("Callback #{self}::#{name} returned: \#{result.inspect}")
result
end
HOOK
# __FILE__ and __LINE__ are used in a backtrace
class_eval(hook, __FILE__, __LINE__)
# rename the original method
class_eval("alias #{name}_without_hook #{name}", __FILE__, __LINE__) # alias m_without_hook m
# replace the original method with the hook
class_eval("alias #{name} #{name}_hook", __FILE__, __LINE__) # alias m m_hook
end
def main
Yast.import "Pkg"
Yast.import "UI"
textdomain "base"
Yast.import "Directory"
Yast.import "Label"
Yast.import "Mode"
Yast.import "Stage"
Yast.import "Popup"
Yast.import "URL"
Yast.import "CommandLine"
Yast.import "String"
Yast.import "Icon"
Yast.import "Report"
Yast.import "Wizard"
Yast.import "Progress"
Yast.import "FileUtils"
Yast.import "SignatureCheckCallbacks"
Yast.import "Linuxrc"
@_provide_popup = false
@_package_popup = false
@_script_popup = false
@_scan_popup = false
@_package_name = ""
@_package_size = 0
@_deleting_package = false
@_current_source = 1
# make showLongInfo module-global so it gets remembered (cf. #14018)
@showLongInfo = false
# used to en-/disable StartPackage, ProgressPackage and DonePackage
@enable_asterix_package = true
@provide_aborted = false
@source_aborted = false
@back_string = "\b\b\b\b\b\b\b\b\b\b"
@clear_string = Ops.add(Ops.add(@back_string, " "), @back_string)
# max. length of the text in the repository popup window
@max_size = 60
@autorefreshing = false
@autorefreshing_aborted = false
# Location of the persistent storage
@conf_file = File.join(Directory.vardir, "/package_callbacks.conf")
@config = nil
# auto ejecting is in progress
@doing_eject = false
# current values for retry functionality
@retry_url = ""
@current_retry_timeout = RETRY_TIMEOUT
@current_retry_attempt = 0
#=============================================================================
# MEDIA CHANGE
#=============================================================================
@detected_cd_devices = []
# reference counter to the open popup window
@_source_open = 0
@download_file = ""
# TODO: use the ID in the prgress popup callbacks,
# then callbacks may be nested...
@tick_progress = false
@val_progress = false
@current_tick = 0
# ProgressStart/End events may be nested, remember the types of progresses
@progress_stack = []
@last_stage = 0
@opened_wizard = []
Builtins.y2milestone("PackageCallbacks constructor")
InitPackageCallbacks()
end
#--------------------------------------------------------------------------
# defaults
# at start of file providal
#
def StartProvide(name, archivesize, remote)
Builtins.y2milestone("StartProvide: name: %1, remote: %2", name, remote)
if remote
sz = String.FormatSizeWithPrecision(archivesize, 2, false)
if Mode.commandline
CommandLine.PrintVerbose(
Builtins.sformat(_("Downloading package %1 (%2)..."), name, sz)
)
else
UI.CloseDialog if @_provide_popup
if full_screen
Progress.SubprogressType(:progress, 100)
Progress.SubprogressTitle(
Builtins.sformat(_("Downloading package %1 (%2)..."), name, sz)
)
else
# popup heading
providebox = progress_box(_("Downloading Package"), name, sz)
UI.OpenDialog(providebox)
@_provide_popup = true
end
end
end
nil
end
# during file providal
#
def ProgressProvide(percent)
Builtins.y2milestone("ProgressProvide: %1", percent)
if @_provide_popup
UI.ChangeWidget(Id(:progress), :Value, percent)
@provide_aborted = UI.PollInput == :abort
return !@provide_aborted
elsif Mode.commandline
# there is no popup window, but command line mode is set
CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{percent}%")
end
true
end
# redirect ProgressDeltaApply callback (a different signature is required)
def ProgressDeltaApply(percent)
ProgressProvide(percent)
nil
end
# during file providal
# *
# // return "I" for ignore
# // return "R" for retry
# // return "C" for abort
def DoneProvide(error, reason, name)
Builtins.y2milestone("DoneProvide: %1, %2, %3", error, reason, name)
if @_provide_popup
UI.CloseDialog
@_provide_popup = false
end
if Mode.commandline
# remove the progress
CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT)
end
if @provide_aborted
@provide_aborted = false
return "C"
end
# https://github.com/openSUSE/libzypp/blob/8dda46306f06440e1acaefb36fb60f6ce909fd42/zypp/ZYppCallbacks.h#L106
message =
case error
when 1
# NOT_FOUND (error = 1) is handled via MediaChange callback.
nil
when 2
Builtins.sformat(_("Package %1 could not be downloaded (input/output error)."), name)
when 3
Builtins.sformat(_("Package %1 is broken, integrity check has failed."), name)
else
log.warn "DoneProvide: unknown error '#{error}'"
end
# IO/INVALID
if message
# error message, %1 is a package name
if Mode.commandline
CommandLine.Print(message)
# ask user in the interactive mode
if CommandLine.Interactive
CommandLine.Print("")
# command line mode - ask user whether installation of the failed package should be retried
CommandLine.Print(_("Retry installation of the package?"))
if CommandLine.YesNo
# return Retry
return "R"
end
# command line mode - ask user whether the installation should be aborted
CommandLine.Print(_("Abort the installation?"))
if CommandLine.YesNo
# return Abort
return "C"
end
# otherwise return Ignore (default)
return "I"
end
return "I"
end
button_box = ButtonBox(
PushButton(Id(:abort), Opt(:cancelButton, :key_F9), Label.AbortButton),
PushButton(Id(:retry), Opt(:customButton), Label.RetryButton),
PushButton(Id(:ignore), Opt(:okButton), Label.IgnoreButton)
)
if @showLongInfo
UI.OpenDialog(
Opt(:decorated),
layout_popup(message, button_box, true)
)
UI.ReplaceWidget(
Id(:info),
RichText(
Opt(:plainText),
Ops.add(Builtins.sformat(_("Error: %1:"), error), reason)
)
)
else
UI.OpenDialog(
Opt(:decorated),
layout_popup(message, button_box, false)
)
UI.ReplaceWidget(Id(:info), Empty())
end
r = nil
loop do
r = UI.UserInput
if r == :show
@showLongInfo = show_log_info(message, button_box)
if @showLongInfo
error_symbol = "ERROR"
# https://github.com/openSUSE/libzypp/blob/8dda46306f06440e1acaefb36fb60f6ce909fd42/zypp/ZYppCallbacks.h#L106
case error
when 2
error_symbol = "IO"
when 3
error_symbol = "INVALID"
end
UI.ReplaceWidget(
Id(:info),
RichText(
Opt(:plainText),
Ops.add(
# error message, %1 is code of the error,
# detail string is appended to the end
Builtins.sformat(_("Error: %1:"), error_symbol),
reason
)
)
)
else
UI.ReplaceWidget(Id(:info), Empty())
end
end
break if [:abort, :retry, :ignore].include?(r)
end
Builtins.y2milestone("DoneProvide %1", r)
UI.CloseDialog
return "C" if r == :abort
return "R" if r == :retry
if r == :ignore
# don't show the warning when a refresh fails or for signature errors (error 3)
if !@autorefreshing && error != 3
# TODO: add "Don't show again" checkbox
# a warning popup displayed after pressing [Ignore] after a download error
Popup.Warning(
_(
"Ignoring a download failure may result in a broken system.\nVerify the system later by running the Software Management module.\n"
)
)
end
return "I"
end
Builtins.y2error("Unknown user input: %1", r)
end
"I"
end
# Enable or disable StartPackage, ProgressPackage and DonePackage
# callbacks, but only the progress bar and not the final error
# message. Returns old value.
# @note nasty hack for inst_do_net_test client. Remove it when client disappear
def EnableAsterixPackage(value)
ret = @enable_asterix_package
@enable_asterix_package = value
ret
end
# At start of package install.
def StartPackage(name, _location, _summary, installsize, is_delete)
return if !@enable_asterix_package
@_package_name = name
@_package_size = installsize
@_deleting_package = is_delete
sz = String.FormatSizeWithPrecision(installsize, 2, false)
if Mode.commandline
CommandLine.PrintVerbose(
Builtins.sformat(
if is_delete
_("Uninstalling package %1 (%2)...")
else
_("Installing package %1 (%2)...")
end,
@_package_name,
sz
)
)
else
packagebox = progress_box(
is_delete ? _("Uninstalling Package") : _("Installing Package"),
@_package_name,
sz
)
UI.OpenDialog(Opt(:decorated), packagebox)
@_package_popup = true
end
nil
end
# During package install.
def ProgressPackage(percent)
if @_package_popup
UI.ChangeWidget(Id(:progress), :Value, percent)
return UI.PollInput != :abort
elsif Mode.commandline
CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{percent}%")
if percent == 100
# sleep for a wile
Builtins.sleep(200)
# remove the progress
CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT)
end
end
true
end
# Handle GPG check result (pkgGpgCheck)
#
# If insecure mode is set to '1', the check result is ignored. Otherwise, no decision is made.
# When running on an installed system, it always return "".
#
# @param data [Hash] Output from `pkgGpgCheck` callback.
# @option data [Integer] "CheckPackageResult" Check result code according to libzypp.
# @option data [String] "Package" Package's name.
# @option data [String] "Localpath" Path to RPM file.
# @option data [String] "RepoMediaUrl" Media URL.
# @return [String] "I" if the package should be accepted; otherwise
# a blank string is returned (so no decision is made).
def pkg_gpg_check(data)
log.debug("pkgGpgCheck data: #{data}")
log.warn("Signature check failed: #{data}") if data["CheckPackageResult"] && data["CheckPackageResult"] != 0
insecure = Stage.initial && Linuxrc.InstallInf("Insecure") == "1"
insecure ? "I" : ""
end
# After package install.
#
# return "I" for ignore
# return "R" for retry
# return "C" for abort (not implemented !)
def DonePackage(error, reason)
# remove invalid characters (bnc#876459)
if !reason.valid_encoding?
reason.encode!("UTF-16", undef: :replace, invalid: :replace, replace: "?")
reason.encode!("UTF-8")
log.warn "Invalid byte sequence found, fixed text: #{reason}"
end
UI.CloseDialog if @_package_popup
@_package_popup = false
if error == 0
# no error, there is additional info (rpm output), see bnc#456446
Builtins.y2milestone("Additional RPM otput: %1", reason)
CommandLine.Print(reason) if Mode.commandline
else
Builtins.y2milestone(
"DonePackage(error: %1, reason: '%2')",
error,
reason
)
message = Builtins.sformat(
if @_deleting_package
# error popup during package installation, %1 is the name of the package
_("Removal of package %1 failed.")
else
# error popup during package installation, %1 is the name of the package
_("Installation of package %1 failed.")
end,
@_package_name
)
if Mode.commandline
CommandLine.Print(message)
CommandLine.Print(reason)
# ask user in the interactive mode
if CommandLine.Interactive
CommandLine.Print("")
# command line mode - ask user whether installation of the failed package should be retried
CommandLine.Print(_("Retry installation of the package?"))
if CommandLine.YesNo
# return Retry
return "R"
end
# command line mode - ask user whether the installation should be aborted
CommandLine.Print(_("Abort the installation?"))
if CommandLine.YesNo
# return Abort
return "C"
end
# otherwise return Ignore (default)
return "I"
end
else
button_box = ButtonBox(
PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton),
PushButton(Id(:retry), Opt(:customButton), Label.RetryButton),
PushButton(Id(:ignore), Opt(:okButton), Label.IgnoreButton)
)
if @showLongInfo
UI.OpenDialog(
Opt(:decorated),
layout_popup(message, button_box, true)
)
UI.ReplaceWidget(Id(:info), RichText(Opt(:plainText), reason))
else
UI.OpenDialog(
Opt(:decorated),
layout_popup(message, button_box, false)
)
UI.ReplaceWidget(Id(:info), Empty())
end
r = nil
loop do
r = UI.UserInput
if r == :show
@showLongInfo = show_log_info(message, button_box)
if @showLongInfo
UI.ReplaceWidget(Id(:info), RichText(Opt(:plainText), reason))
else
UI.ReplaceWidget(Id(:info), Empty())
end
end
break if [:abort, :retry, :ignore].include?(r)
end
Builtins.y2milestone("DonePackage %1", r)
UI.CloseDialog
if r == :ignore
# TODO: add "Don't show again" checkbox
# a warning popup displayed after pressing [Ignore] after a package installation error
Popup.Warning(
_(
"Ignoring a package failure may result in a broken system.\nThe system should be later verified by running the Software Management module."
)
)
end
return "C" if r == :abort
return "R" if r == :retry
end
# default: ignore
end
"I"
end
#-------------------------------------------------------------------------
#
# media change callback
#
# if current == -1, show "Ignore"
#
# return "" for ok, retry
# return "E" for eject media
# return "I" for ignore bad media
# return "S" for skip this media
# return "C" for cancel (not implemented !)
# return url to change media URL
def MediaChange(error_code, error, url, product, current, current_label, wanted, wanted_label, double_sided, devices, current_device)
devices = deep_copy(devices)
if @autorefreshing && @autorefreshing_aborted
Builtins.y2milestone("Refresh aborted")
return "C"
end
Builtins.y2milestone(
"MediaChange error: err'%1', url'%2', prd'%3', cur'%4'/'%5', wan'%6'/'%7', devs: %8, curr_dev: %9",
Ops.add(Ops.add(error_code, ":"), error),
URL.HidePassword(url),
product,
current,
current_label,
wanted,
wanted_label,
devices,
current_device
)
if full_screen
# make sure the old subprogress is cleared when displaying a popup (bsc#1175926)
Progress.SubprogressValue(0)
Progress.SubprogressTitle("")
end
url_scheme = Ops.get_string(URL.Parse(url), "scheme", "").downcase
# true if it makes sense to offer an eject button (for cd/dvd only ...)
is_disc = ["cd", "dvd"].include?(url_scheme)
# do automatic eject
if is_disc && autoeject && !@doing_eject
Builtins.y2milestone("Automatically ejecting the medium...")
@doing_eject = true
return "E"
end
if Builtins.issubstring(error, "ERROR(InstSrc:E_bad_id)")
error =
# error report
_(
"<p>The repository at the specified URL now provides a different media ID.\n" \
"If the URL is correct, this indicates that the repository content has changed. To \n" \
"continue using this repository, start <b>Installation Repositories</b> from \n" \
"the YaST control center and refresh the repository.</p>\n"
)
end
if wanted_label == ""
# use only product name for network repository
# there is no medium 1, 2, ...
if double_sided
# media is double sided, we want the user to insert the 'Side A' of the media
# the complete string will be "<product> <media> <number>, <side>"
# e.g. "'SuSE Linux 9.0' DVD 1, Side A"
side = _("Side A")
if Ops.bitwise_and(wanted, 1) == 0
# media is double sided, we want the user to insert the 'Side B' of the media
side = _("Side B")
end
wanted = Ops.shift_right(Ops.add(wanted, 1), 1)
wanted_label = if is_disc
# label for a repository - %1 product name (e.g. "openSUSE 10.2"), %2 medium number (e.g. 2)
# %3 side (e.g. "Side A")
Builtins.sformat("%1 (Disc %2, %3)", product, wanted, side)
else
# label for a repository - %1 product name (e.g. "openSUSE 10.2"), %2 medium number (e.g. 2)
# %3 side (e.g. "Side A")
Builtins.sformat("%1 (Medium %2, %3)", product, wanted, side)
end
else
wanted_label = if is_disc
# label for a repository - %1 product name (e.g. openSUSE 10.2), %2 medium number (e.g. 2)
Builtins.sformat(_("%1 (Disc %2)"), product, wanted)
else
# label for a repository - %1 product name (e.g. openSUSE 10.2), %2 medium number (e.g. 2)
Builtins.sformat(_("%1 (Medium %2)"), product, wanted)
end
end
end
# prompt to insert product (%1 == "SuSE Linux version 9.2 CD 2")
message = Builtins.sformat(_("Insert\n'%1'"), wanted_label)
# with network repository it doesn't make sense to ask for disk
if url_scheme == "dir"
# report error while accessing local directory with product (%1 = URL, %2 = "SuSE Linux ...")
message = Builtins.sformat(
_(
"Cannot access installation media\n" \
"%1\n" \
"%2.\n" \
"Check whether the directory is accessible."
),
URL.HidePassword(url),
wanted_label
)
elsif !is_disc
# report error while accessing network media of product (%1 = URL, %2 = "SuSE Linux ...")
message = Builtins.sformat(
_(
"Cannot access installation media \n" \
"%1\n" \
"%2.\n" \
"Check whether the server is accessible."
),
URL.HidePassword(url),
wanted_label
)
end
# --------------------------------------
# build up button box
button_box = ButtonBox(
PushButton(Id(:retry), Opt(:default, :okButton), Label.RetryButton)
)
button_box.params << PushButton(Id(:ignore), Opt(:customButton), Label.IgnoreButton) if current == -1 # wrong media id, offer "Ignore"
button_box.params << PushButton(
Id(:cancel),
Opt(:cancelButton),
@autorefreshing ? _("Skip Autorefresh") : Label.AbortButton
)
# push button label during media change popup, user can skip
# this media (CD) so no packages from this media will be installed
button_box.params << PushButton(Id(:skip), Opt(:customButton), _("&Skip"))
if is_disc
@detected_cd_devices = cd_devices(Ops.get(devices, current_device, "")) if !@doing_eject
# detect the CD/DVD devices if the ejecting is not in progress,
# the CD detection closes the ejected tray!
cds = deep_copy(@detected_cd_devices)
# display a menu button if there are more CD devices
if Ops.greater_than(Builtins.size(cds), 1)
# menu button label - used for more then one device
button_box = HBox(button_box, MenuButton(_("&Eject"), cds))
else
# push button label - in the media change popup, user can eject the CD/DVD
button_box.params << PushButton(Id(:eject), Opt(:customButton), _("&Eject"))
end
button_box = VBox(
Left(
CheckBox(
Id(:auto_eject),
_("A&utomatically Eject CD or DVD Medium"),
autoeject
)
),
button_box
)
end
@doing_eject = false
# Autoretry code
doing_auto_retry = false
if error_code == "IO_SOFT" ||
Builtins.contains(
["ftp", "sftp", "http", "https", "nfs", "smb"],
url_scheme
)
# this a different file, reset the retry counter
if @retry_url != url
@retry_url = url
@current_retry_attempt = 0
end
# is the maximum retry count reached?
if Ops.less_than(@current_retry_attempt, RETRY_ATTEMPTS)
# reset the counter, use logarithmic back-off with maximum limit
@current_retry_timeout = if @current_retry_attempt < 10
RETRY_TIMEOUT * (1 << @current_retry_attempt)
else
RETRY_MAX_TIMEOUT
end
@current_retry_timeout = RETRY_MAX_TIMEOUT if Ops.greater_than(@current_retry_timeout, RETRY_MAX_TIMEOUT)
button_box = VBox(
# failed download will be automatically retried after the timeout, %1 = formatted time (MM:SS format)
Left(Label(Id(:auto_retry), retry_label(@current_retry_timeout))),
button_box
)
doing_auto_retry = true
else
Builtins.y2warning(
"Max. autoretry count (%1) reached, giving up...",
RETRY_ATTEMPTS
)
end
end
Builtins.y2milestone("Autoretry: %1", doing_auto_retry)
Builtins.y2milestone("Autoretry attempt: %1", @current_retry_attempt) if doing_auto_retry
if Mode.commandline
CommandLine.Print(message)
CommandLine.Print(error)
# ask user in the interactive mode
if CommandLine.Interactive
CommandLine.Print("")
# command line mode - ask user whether installation of the failed package should be retried
CommandLine.Print(_("Retry the installation?"))
if CommandLine.YesNo
# return Retry
return ""
end
# command line mode - ask user whether the installation should be aborted
CommandLine.Print(_("Skip the medium?"))
if CommandLine.YesNo
# return Skip
return "S"
end
# otherwise ignore the medium
CommandLine.Print(_("Ignoring the bad medium..."))
return "I"
end
return "S"
end
Builtins.y2debug(
"Opening Dialog: %1",
layout_popup(message, button_box, true)
)
if @showLongInfo
UI.OpenDialog(
Opt(:decorated),
layout_popup(message, button_box, true)
)
# TextEntry label
UI.ReplaceWidget(
Id(:info),
VBox(
InputField(Id(:url), Opt(:hstretch), _("&URL")),
RichText(Opt(:plainText), error)
)
)
UI.ChangeWidget(Id(:url), :Value, url)
else
UI.OpenDialog(
Opt(:decorated),
layout_popup(message, button_box, false)
)
UI.ReplaceWidget(Id(:info), Empty())
end
# notification
UI.Beep
r = nil
eject_device = ""
loop do
r = doing_auto_retry ? UI.TimeoutUserInput(1000) : UI.UserInput
# timout in autoretry mode?
if doing_auto_retry
if r == :timeout
# decrease timeout counter
@current_retry_timeout -= 1
if @current_retry_timeout == 0
Builtins.y2milestone("The time is out, doing automatic retry...")
# do the retry
r = :retry
@current_retry_attempt += 1
else
# popup string - refresh the displayed counter
UI.ChangeWidget(
Id(:auto_retry),
:Label,
retry_label(@current_retry_timeout)
)
end
else
# user has pressed a button, reset the retry counter in the next timeout
Builtins.y2milestone("User input: %1, resetting autoretry url", r)
@retry_url = ""
end
end
if r == :show
@showLongInfo = show_log_info(message, button_box)
if @showLongInfo
# TextEntry label
UI.ReplaceWidget(
Id(:info),
VBox(
TextEntry(Id(:url), _("&URL")),
RichText(Opt(:plainText), error)
)
)
UI.ChangeWidget(Id(:url), :Value, url)
else
UI.ReplaceWidget(Id(:info), Empty())
end
elsif [:retry, :url].include?(r)
if @showLongInfo # id(`url) must exist
newurl = Convert.to_string(UI.QueryWidget(Id(:url), :Value))
if newurl != url
url = newurl
r = :url
end
end
elsif r.is_a?(::String) && r.start_with?("/dev/")
Builtins.y2milestone("Eject request for %1", r)
eject_device = r
r = :eject
end
break if [:cancel, :retry, :eject, :skip, :ignore, :url].include?(r)
end
# check and save the autoeject configuration if needed
remember_autoeject if is_disc
Builtins.y2milestone("MediaChange %1", r)
UI.CloseDialog
if @_provide_popup
UI.CloseDialog
@_provide_popup = false
end
case r
when :ignore then "I"
when :skip then "S"
when :cancel
# abort during autorefresh should abort complete autorefresh, not only the failed repo
if @autorefreshing
@autorefreshing_aborted = true
Pkg.SkipRefresh
else
@provide_aborted = true
end
"C"
when :eject
@doing_eject = true
return "E" if eject_device == ""
# get the index in the list
dindex = -1
found = Builtins.find(devices) do |d|
dindex = Ops.add(dindex, 1)
d == eject_device
end
if found
Builtins.y2milestone("Device %1 has index %2", eject_device, dindex)
"E#{dindex}"
else
Builtins.y2warning(
"Device %1 not found in the list, using default",
eject_device
)
"E"
end
when :url
Builtins.y2milestone("Redirecting to: %1", URL.HidePassword(url))
url
else
""
end
end
# dummy repository change callback, see SlideShowCallbacks for the real one
def SourceChange(source, medianr)
Builtins.y2milestone("SourceChange (%1, %2)", source, medianr)
@_current_source = source
nil
end
def OpenSourcePopup
if @_source_open == 0
UI.OpenDialog(
VBox(
HSpacing(MAX_POPUP_TEXT_SIZE),
Heading(Id(:label_source_popup), Opt(:hstretch), " "),
ProgressBar(Id(:progress), " ", 100, 0)
)
)
end
@_source_open = Ops.add(@_source_open, 1)
Builtins.y2milestone("OpenSourcePopup: _source_open: %1", @_source_open)
nil
end
def SetHeaderSourcePopup(text)
# Qt UI uses bold font, the string must be shortened even more
ui_adjustment = textmode ? 0 : 5
if Ops.greater_than(
Builtins.size(text),
Ops.subtract(MAX_POPUP_TEXT_SIZE, ui_adjustment)
)
text = process_message(text, Ops.subtract(MAX_POPUP_TEXT_SIZE, ui_adjustment))
end
UI.ChangeWidget(:label_source_popup, :Value, text)
Builtins.y2milestone("SourcePopup: new header: %1", text)
nil
end
def SetLabelSourcePopup(text)
# Qt uses proportional font, the string might be longer
ui_adjustment = textmode ? 0 : 6
if Ops.greater_than(
Builtins.size(text),
Ops.add(MAX_POPUP_TEXT_SIZE, ui_adjustment)
)
text = process_message(text, Ops.add(MAX_POPUP_TEXT_SIZE, ui_adjustment))
end
# refresh the label in the popup
UI.ChangeWidget(:progress, :Label, text)
Builtins.y2milestone("SourcePopup: new label: %1", text)
nil
end
# is the top level window source popup?
def IsSourcePopup
UI.WidgetExists(Id(:progress)) && UI.WidgetExists(Id(:label_source_popup))
end
def SourcePopupSetProgress(value)
if Ops.greater_than(@_source_open, 0) && IsSourcePopup()
UI.ChangeWidget(Id(:progress), :Value, value)
input = UI.PollInput
return false if input == :abort
end
true
end
def CloseSourcePopup
if !IsSourcePopup()
Builtins.y2error(
"The toplevel dialog is not a repository popup dialog!"
)
return
end
@_source_open = Ops.subtract(@_source_open, 1)
if @_source_open == 0
Builtins.y2milestone("Closing repository progress popup")
UI.CloseDialog
end
Builtins.y2milestone("CloseSourcePopup: _source_open: %1", @_source_open)
nil
end
def SourceCreateInit
Builtins.y2milestone("SourceCreateInit")
OpenSourcePopup()
nil
end
def SourceCreateDestroy
Builtins.y2milestone("SourceCreateDestroy")
CloseSourcePopup()
nil
end
def SourceCreateStart(url)
Builtins.y2milestone("SourceCreateStart: %1", url)
# popup label (%1 is repository URL)
msg = Builtins.sformat(_("Creating Repository %1"), url)
if Mode.commandline
CommandLine.Print(msg)
else
Builtins.y2milestone("_source_open: %1", @_source_open)
if @_source_open == 1
SetHeaderSourcePopup(msg)
else
SetLabelSourcePopup(msg)
end
end
nil
end
def SourceCreateProgress(percent)
ret = SourcePopupSetProgress(percent)
Builtins.y2milestone("SourceCreateProgress(%1) = %2", percent, ret)
ret
end
def SourceCreateError(url, error, description)
Builtins.y2milestone(
"Source create: error: url: %1, error: %2, description: %3",
URL.HidePassword(url),
error,
description
)
# error message - a label followed by a richtext with details
message = _("An error occurred while creating the repository.")
case error
when :NOT_FOUND
# error message - a label followed by a richtext with details
message = _("Unable to retrieve the remote repository description.")
when :IO
# error message - a label followed by a richtext with details
message = _("An error occurred while retrieving the new metadata.")
when :INVALID
# error message - a label followed by a richtext with details
message = _("The repository is not valid.")
when :REJECTED
# error message - a label followed by a richtext with details
message = _("The repository metadata is invalid.")
end
if Mode.commandline
CommandLine.Print(message)
CommandLine.Print(URL.HidePassword(url))
CommandLine.Print(description)
# ask user in the interactive mode
if CommandLine.Interactive
CommandLine.Print("")
# command line mode - ask user whether the repository refreshment should be retried
CommandLine.Print(_("Retry?"))
if CommandLine.YesNo
# return Retry
return :RETRY
end
end
return :ABORT
end
detail = Builtins.sformat("%1<br>%2", url, description)
UI.OpenDialog(
VBox(
Label(message),
RichText(detail),
ButtonBox(
PushButton(Id(:RETRY), Opt(:okButton), Label.RetryButton),
PushButton(Id(:ABORT), Opt(:cancelButton), Label.AbortButton)
)
)
)
ret = Convert.to_symbol(UI.UserInput)
UI.CloseDialog
Builtins.y2milestone("Source create error: Returning %1", ret)
ret
end
def SourceCreateEnd(url, error, description)
# set 100% progress
SourcePopupSetProgress(100)
Builtins.y2milestone(
"Source create end: error: url: %1, error: %2, description: %3",
URL.HidePassword(url),
error,
description
)
nil
end
def SourceProbeStart(url)
Builtins.y2milestone("SourceProbeStart: %1", URL.HidePassword(url))
# popup label (%1 is repository URL)
msg = Builtins.sformat(_("Probing Repository %1"), URL.HidePassword(url))
if Mode.commandline
CommandLine.Print(msg)
else
OpenSourcePopup()
msg2 = Builtins.sformat(
_("Probing Repository %1"),
URL.HidePassword(url)
)
if @_source_open == 1
SetHeaderSourcePopup(msg2)
else
SetLabelSourcePopup(msg2)
end
end
nil
end
def SourceProbeFailed(url, type)
Builtins.y2milestone(
"Repository %1 is not %2 repository",
URL.HidePassword(url),
type
)
nil
end
def SourceProbeSucceeded(url, type)
Builtins.y2milestone(
"Repository %1 is type %2",
URL.HidePassword(url),
type
)
nil
end
def SourceProbeProgress(_url, value)
SourcePopupSetProgress(value)
end
def SourceProbeError(url, error, description)
Builtins.y2milestone(
"Source probe: error: url: %1, error: %2, description: %3",
URL.HidePassword(url),
error,
description
)
# error message - a label followed by a richtext with details
message = _("Error occurred while probing the repository.")
case error
when :NOT_FOUND
# error message - a label followed by a richtext with details
message = _("Unable to retrieve the remote repository description.")
when :IO
# error message - a label followed by a richtext with details
message = _("An error occurred while retrieving the new metadata.")
when :INVALID
# error message - a label followed by a richtext with details
message = _("The repository is not valid.")
when :NO_ERROR
# error message - a label followed by a richtext with details
message = _("Repository probing details.")
when :REJECTED
# error message - a label followed by a richtext with details
message = _("Repository metadata is invalid.")
end
if Mode.commandline
CommandLine.Print(message)
CommandLine.Print(URL.HidePassword(url))
CommandLine.Print(description)
# ask user in the interactive mode
if CommandLine.Interactive
CommandLine.Print("")
# command line mode - ask user whether the repository refreshment should be retried
CommandLine.Print(_("Retry?"))
if CommandLine.YesNo
# return Retry
return :RETRY
end
end
return :ABORT
end
detail = Builtins.sformat("%1<br>%2", url, description)
UI.OpenDialog(
VBox(
Label(message),
RichText(detail),
ButtonBox(
PushButton(Id(:RETRY), Opt(:okButton), Label.RetryButton),
PushButton(Id(:ABORT), Opt(:cancelButton), Label.AbortButton)
)
)
)
ret = Convert.to_symbol(UI.UserInput)
UI.CloseDialog
Builtins.y2milestone("Source probe error: Returning %1", ret)
ret
end
def SourceProbeEnd(url, error, description)
CloseSourcePopup()
CloseSourcePopup()
Builtins.y2milestone(
"Source probe end: error: url: %1, error: %2, description: %3",
URL.HidePassword(url),
error,
description
)
nil
end
def SourceReportStart(source_id, url, task)
Builtins.y2milestone(
"Source report start: src: %1, URL: %2, task: %3",
source_id,
URL.HidePassword(url),
task
)
if Mode.commandline
CommandLine.Print(task)
else
Builtins.y2milestone("_source_open: %1", @_source_open)
if @_source_open == 1
SetHeaderSourcePopup(task)
else
SetLabelSourcePopup(task)
end
end
nil
end
def SourceReportProgress(value)
ret = SourcePopupSetProgress(value)
Builtins.y2debug("SourceReportProgress(%1) = %2", value, ret)
ret
end
def SourceReportError(source_id, url, error, description)
Builtins.y2milestone(
"Source report: error: id: %1, url: %2, error: %3, description: %4",
source_id,
URL.HidePassword(url),
error,
description
)
# error message - a label followed by a richtext with details
message = Builtins.sformat(_("Repository %1"), url)
case error
when :NOT_FOUND
# error message - a label followed by a richtext with details
message = _("Unable to retrieve the remote repository description.")
when :IO
# error message - a label followed by a richtext with details
message = _("An error occurred while retrieving the new metadata.")
when :INVALID
# error message - a label followed by a richtext with details
message = _("The repository is not valid.")
end
if Mode.commandline
CommandLine.Print(message)
CommandLine.Print(url)
CommandLine.Print(description)
# ask user in the interactive mode
if CommandLine.Interactive
CommandLine.Print("")
# command line mode - ask user whether the repository refreshment should be retried
CommandLine.Print(_("Retry?"))
if CommandLine.YesNo
# return Retry
return :RETRY
end
end
return :ABORT
end
detail = Builtins.sformat("%1<br>%2", url, description)
UI.OpenDialog(
VBox(
Label(message),
RichText(detail),
HBox(
PushButton(Id(:RETRY), Opt(:okButton), Label.RetryButton),
PushButton(Id(:ABORT), Opt(:cancelButton), Label.AbortButton)
)
)
)
ret = Convert.to_symbol(UI.UserInput)
UI.CloseDialog
Builtins.y2milestone("Source report error: Returning %1", ret)
ret
end
def SourceReportEnd(src_id, url, task, error, description)
Builtins.y2milestone(
"Source report end: src: %1, url: %2, task: %3, error: %4, description: %5",
src_id,
URL.HidePassword(url),
task,
error,
description
)
# set 100% progress
SourcePopupSetProgress(100)
nil
end
def SourceReportInit
Builtins.y2milestone("Source report init")
OpenSourcePopup()
nil
end
def SourceReportDestroy
Builtins.y2milestone("Source report destroy")
CloseSourcePopup()
nil
end
# at start of delta providal
#
def StartDeltaProvide(name, archivesize)
sz = String.FormatSizeWithPrecision(archivesize, 2, false)
if Mode.commandline
CommandLine.PrintVerbose(
Builtins.sformat(
_("Downloading delta RPM package %1 (%2)..."),
name,
sz
)
)
else
UI.CloseDialog if @_provide_popup
# popup heading
providebox = progress_box(_("Downloading Delta RPM package"), name, sz)
UI.OpenDialog(providebox)
@_provide_popup = true
end
nil
end
# at start of delta application
#
def StartDeltaApply(name)
if Mode.commandline
CommandLine.PrintVerbose(
Builtins.sformat(_("Applying delta RPM package %1..."), name)
)
else
# popup heading
progressbox = VBox(
HSpacing(40),
# popup heading
Heading(_("Applying delta RPM package")),
Left(
HBox(Left(Label(Opt(:boldFont), _("Package: "))), Left(Label(name)))
),
ProgressBar(Id(:progress), "", 100, 0)
)
UI.CloseDialog if @_provide_popup
UI.OpenDialog(progressbox)
@_provide_popup = true
end
nil
end
def FinishDeltaProvide
if @_provide_popup
UI.CloseDialog
@_provide_popup = false
end
nil
end
def ProblemDeltaDownload(descr)
FinishDeltaProvide() # close popup
Builtins.y2milestone("Failed to download delta RPM: %1", descr)
nil
end
def ProblemDeltaApply(descr)
FinishDeltaProvide() # close popup
Builtins.y2milestone("Failed to apply delta RPM: %1", descr)
nil
end
def FormatPatchName(patch_name, patch_version, patch_arch)
patch_full_name = (!patch_name.nil? && patch_name != "") ? patch_name : ""
if patch_full_name != ""
if !patch_version.nil? && patch_version != ""
patch_full_name = Ops.add(
Ops.add(patch_full_name, "-"),
patch_version
)
end
patch_full_name = Ops.add(Ops.add(patch_full_name, "."), patch_arch) if !patch_arch.nil? && patch_arch != ""
end
patch_full_name
end
def ScriptStart(patch_name, patch_version, patch_arch, script_path)
patch_full_name = FormatPatchName(patch_name, patch_version, patch_arch)
Builtins.y2milestone(
"ScriptStart callback: patch: %1, script: %2",
patch_full_name,
script_path
)
if Mode.commandline
CommandLine.PrintVerbose(
Builtins.sformat(
_("Starting script %1 (patch %2)..."),
script_path,
patch_full_name
)
)
else
progressbox = VBox(
HSpacing(60),
# popup heading
Heading(_("Running Script")),
VBox(
if patch_full_name == ""
Empty()
else
HBox(
# label, patch name follows
Label(Opt(:boldFont), _("Patch: ")),
Label(patch_full_name),
HStretch()
)
end,
HBox(
# label, script name follows
Label(Opt(:boldFont), _("Script: ")),
Label(script_path),
HStretch()
)
),
# label
LogView(Id(:log), _("Output of the Script"), 10, 0),
ButtonBox(
PushButton(
Id(:abort),
Opt(:default, :key_F9, :cancelButton),
Label.AbortButton
)
)
)
UI.CloseDialog if @_script_popup
UI.OpenDialog(progressbox)
UI.SetFocus(Id(:abort))
@_script_popup = true
end
nil
end
def ScriptProgress(ping, output)
Builtins.y2milestone("ScriptProgress: ping:%1, output: %2", ping, output)
if @_script_popup
if ping
# TODO: refresh progress indicator
Builtins.y2debug("-ping-")
end
if !output.nil? && output != ""
# add the output to the log widget
UI.ChangeWidget(Id(:log), :Value, output)
end
input = UI.PollInput
return false if [:abort, :close].include?(input)
end
true
end
def ScriptProblem(description)
Builtins.y2warning("ScriptProblem: %1", description)
ui = Popup.AnyQuestion3(
"", # symbol focus
description,
Label.RetryButton, # yes_button_message
Label.AbortButton, # no_button_message
Label.IgnoreButton, # retry_button_message
:retry
)
Builtins.y2milestone("Problem result: %1", ui)
# Abort is the default
ret = "A"
case ui
when :retry
# ignore
ret = "I"
when :yes
# retry
ret = "R"
when :no
# abort
ret = "A"
else
Builtins.y2warning("Unknown result: %1, aborting", ui)
end
ret
end
def ScriptFinish
Builtins.y2milestone("ScriptFinish")
UI.CloseDialog if @_script_popup
nil
end
def Message(patch_name, patch_version, patch_arch, message)
patch_full_name = FormatPatchName(patch_name, patch_version, patch_arch)
Builtins.y2milestone("Message (%1): %2", patch_full_name, message)
if patch_full_name != ""
# label, %1 is patch name with version and architecture
patch_full_name = Builtins.sformat(_("Patch: %1\n\n"), patch_full_name)
end
ret = Popup.ContinueCancel(Ops.add(patch_full_name, message))
Builtins.y2milestone("User input: %1", ret)
ret
end
def AskAbortRefresh
UI.OpenDialog(
MarginBox(
1,
0.5,
VBox(
# a popup question with "Continue", "Skip" and "Abort" buttons
Label(
_(
"The repositories are being refreshed.\n" \
"Continue with refreshing?\n" \
"\n" \
"Note: If the refresh is skipped some packages\n" \
"might be missing or out of date."
)
),
ButtonBox(
PushButton(
Id(:continue),
Opt(:default, :okButton),
Label.ContinueButton
),
# push button label
PushButton(Id(:skip), Opt(:cancelButton), _("&Skip Refresh"))
)
)
)
)
UI.SetFocus(Id(:continue))
ui = Convert.to_symbol(UI.UserInput)
UI.CloseDialog
ui = :continue if ui == :close
Builtins.y2milestone("User request: %1", ui)
ui
end
def IsDownloadProgressPopup
!Mode.commandline && UI.WidgetExists(Id(:download_progress_popup_window)) &&
UI.WidgetExists(Id(:progress))
end
def CloseDownloadProgressPopup
UI.CloseDialog if IsDownloadProgressPopup()
nil
end
def InitDownload(task)
if !Mode.commandline && (!full_screen && !IsDownloadProgressPopup())
# heading of popup
heading = _("Downloading")
UI.OpenDialog(
Opt(:decorated),
VBox(
Heading(Id(:download_progress_popup_window), heading),
VBox(
HSpacing(60),
HBox(
HSpacing(1),
ProgressBar(Id(:progress), task, 100),
HSpacing(1)
),
VSpacing(0.5),
ButtonBox(
PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton)
),
VSpacing(0.5)
)
)
)
UI.ChangeWidget(Id(:progress), :Value, 0)
end
nil
end
def DestDownload
CloseDownloadProgressPopup() if !full_screen
nil
end
def StartDownload(url, localfile)
Builtins.y2milestone(
"Downloading %1 to %2",
URL.HidePassword(url),
localfile
)
# reformat the URL
url_report = URL.FormatURL(URL.Parse(URL.HidePassword(url)), MAX_POPUP_TEXT_SIZE)
# remember the URL
@download_file = url_report
# message in a progress popup
message = Builtins.sformat(_("Downloading: %1"), url_report)
if Mode.commandline
CommandLine.PrintVerbose(message)
elsif IsDownloadProgressPopup()
# change the label
UI.ChangeWidget(Id(:progress), :Label, message)
UI.ChangeWidget(Id(:progress), :Value, 0)
elsif full_screen
Progress.SubprogressType(:progress, 100)
Progress.SubprogressTitle(message)
end
nil
end
def ProgressDownload(percent, bps_avg, bps_current)
if @autorefreshing && @autorefreshing_aborted
Builtins.y2milestone("Refresh aborted")
return false
end
if Mode.commandline
CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{percent}%")
if percent == 100
# sleep for a wile
Builtins.sleep(200)
# remove the progress
CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT)
# print newline when reached 100%
end
else
msg_rate = ""
if Ops.greater_than(bps_current, 0)
# do not show the average download rate if the space is limited
bps_avg = -1 if textmode && Ops.less_than(display_width, 100)
format = if textmode
Ops.add("%1 - ", @download_file)
else
Ops.add(@download_file, " - %1")
end
# progress bar label, %1 is URL with optional download rate
msg_rate = Builtins.sformat(
_("Downloading: %1"),
String.FormatRateMessage(format, bps_avg, bps_current)
)
end
if full_screen
Progress.SubprogressValue(percent)
Progress.SubprogressTitle(msg_rate) if Ops.greater_than(Builtins.size(msg_rate), 0)
else
UI.ChangeWidget(Id(:progress), :Value, percent)
UI.ChangeWidget(Id(:progress), :Label, msg_rate) if Ops.greater_than(Builtins.size(msg_rate), 0)
end
download_aborted = UI.PollInput == :abort
if download_aborted && @autorefreshing
# display "Continue", "Skip Refresh" dialog
answer = AskAbortRefresh()
case answer
when :continue
download_aborted = false
when :skip
download_aborted = true
@autorefreshing_aborted = true
Pkg.SkipRefresh
else
Builtins.y2error("Unknown input value: %1", answer)
end
end
return !download_aborted
end
true
end
# just log the status, errors are handled in MediaChange callback
def DoneDownload(error_value, error_text)
if error_value == 0
Builtins.y2milestone("Download finished")
elsif @autorefreshing && @autorefreshing_aborted
Builtins.y2milestone("Refresh aborted")
else
Builtins.y2warning(
"Download failed: error %1: %2",
error_value,
error_text
)
end
nil
end
def RefreshStarted
Builtins.y2milestone("Autorefreshing repositories...")
if !Mode.commandline && UI.WidgetExists(Id(:abort))
# push button label
UI.ChangeWidget(Id(:abort), :Label, _("Skip Autorefresh"))
UI.RecalcLayout
end
@autorefreshing = true
@autorefreshing_aborted = false
nil
end
def RefreshDone
if !Mode.commandline && UI.WidgetExists(Id(:abort))
UI.ChangeWidget(Id(:abort), :Label, Label.AbortButton)
UI.RecalcLayout
end
Builtins.y2milestone("Autorefresh done")
@autorefreshing = false
@autorefreshing_aborted = false
nil
end
def ClearDownloadCallbacks
Pkg.CallbackInitDownload(nil)
Pkg.CallbackStartDownload(nil)
Pkg.CallbackProgressDownload(nil)
Pkg.CallbackDoneDownload(nil)
Pkg.CallbackDestDownload(nil)
Pkg.CallbackStartRefresh(nil)
Pkg.CallbackDoneRefresh(nil)
nil
end
def StartRebuildDB
# heading of popup
heading = _("Checking Package Database")
# message in a progress popup
message = _(
"Rebuilding package database. This process can take some time."
)
UI.OpenDialog(
Opt(:decorated),
VBox(
Heading(heading),
VBox(
Label(message),
HSpacing(60),
HBox(HSpacing(2), ProgressBar(Id(:progress), "", 100), HSpacing(2)),
VSpacing(1)
)
)
)
UI.ChangeWidget(Id(:progress), :Value, 0)
nil
end
def ProgressRebuildDB(percent)
UI.ChangeWidget(Id(:progress), :Value, percent)
nil
end
def StopRebuildDB(error_value, error_text)
if error_value != 0
# error message, %1 is the cause for the error
Popup.Error(
Builtins.sformat(
_("Rebuilding of package database failed:\n%1"),
error_text
)
)
end
UI.CloseDialog
nil
end
def NotifyRebuildDB
nil
end
def SetRebuildDBCallbacks
Pkg.CallbackStartRebuildDb(fun_ref(method(:StartRebuildDB), "void ()"))
Pkg.CallbackProgressRebuildDb(
fun_ref(method(:ProgressRebuildDB), "void (integer)")
)
Pkg.CallbackStopRebuildDb(
fun_ref(method(:StopRebuildDB), "void (integer, string)")
)
Pkg.CallbackNotifyRebuildDb(fun_ref(method(:NotifyRebuildDB), "void ()"))
nil
end
def StartConvertDB(_unused1)
# heading of popup
heading = _("Checking Package Database")
# message in a progress popup
message = _(
"Converting package database. This process can take some time."
)
UI.OpenDialog(
Opt(:decorated),
VBox(
Heading(heading),
VBox(
Label(message),
HSpacing(60),
HBox(
HSpacing(2),
ProgressBar(Id(:progress), _("Status"), 100),
HSpacing(2)
),
VSpacing(1)
)
)
)
UI.ChangeWidget(Id(:progress), :Value, 0)
nil
end
def ProgressConvertDB(percent, _file)
UI.ChangeWidget(Id(:progress), :Value, percent)
nil
end
def StopConvertDB(error_value, error_text)
if error_value != 0
# error message, %1 is the cause for the error
Popup.Error(
Builtins.sformat(
_("Conversion of package database failed:\n%1"),
error_text
)
)
end
UI.CloseDialog
nil
end
def NotifyConvertDB
nil
end
def SetConvertDBCallbacks
Pkg.CallbackStartConvertDb(
fun_ref(method(:StartConvertDB), "void (string)")
)
Pkg.CallbackProgressConvertDb(
fun_ref(method(:ProgressConvertDB), "void (integer, string)")
)
Pkg.CallbackStopConvertDb(
fun_ref(method(:StopConvertDB), "void (integer, string)")
)
Pkg.CallbackNotifyConvertDb(fun_ref(method(:NotifyConvertDB), "void ()"))
nil
end
# Callback for start RPM DB scan event
def StartScanDb
Builtins.y2milestone("Scanning RPM DB...")
if Mode.commandline
# progress message (command line mode)
CommandLine.PrintVerbose(_("Reading RPM database..."))
elsif !full_screen
UI.OpenDialog(
VBox(
HSpacing(60),
# popup heading
Heading(
Id(:label_scanDB_popup),
Opt(:hstretch),
_("Reading Installed Packages")
),
HBox(
# progress bar label
ProgressBar(
Id(:progress),
_("Scanning RPM database..."),
100,
0
), # TODO: allow Abort
# ,
# `VBox(
# `Label(""),
# `PushButton(`id(`abort), Label::AbortButton())
# )
HSpacing(1)
)
)
)
@_scan_popup = true
else
Progress.Title(_("Scanning RPM database..."))
end
nil
end
# Callback for RPM DB scan progress
def ProgressScanDb(value)
if Mode.commandline
CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{value}%")
elsif @_scan_popup && UI.WidgetExists(Id(:label_scanDB_popup))
UI.ChangeWidget(Id(:progress), :Value, value)
cont = UI.PollInput != :abort
Builtins.y2warning("Scan DB aborted") if !cont
return cont
elsif full_screen
Progress.Step(value)
end
# continue
true
end
# Callback for error handling during RPM DB scan
def ErrorScanDb(error, description)
Builtins.y2error(
"ErrorScanDb callback: error: %1, description: %2",
error,
description
)
# error message, could not read RPM database
message = _("Initialization of the target failed.")
if Mode.commandline
CommandLine.Print(message)
CommandLine.Print(description)
# ask user in the interactive mode
if CommandLine.Interactive
CommandLine.Print("")
# command line mode - ask user whether target initializatin can be restarted
CommandLine.Print(_("Retry?"))
if CommandLine.YesNo
# return Retry
return "R"
end
end
# return Cancel
return "C"
end
show_details = false
button_box = ButtonBox(
PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton),
PushButton(Id(:retry), Opt(:customButton), Label.RetryButton),
PushButton(Id(:ignore), Opt(:okButton), Label.IgnoreButton)
)
UI.OpenDialog(
Opt(:decorated),
layout_popup(message, button_box, false)
)
r = nil
loop do
r = UI.UserInput
if r == :show
show_details = show_log_info(message, button_box)
if show_details
error_symbol = "UNKNOWN"
case error
when 0
error_symbol = "NO_ERROR"
when 1
error_symbol = "FAILED"
end
UI.ReplaceWidget(
Id(:info),
RichText(
Opt(:plainText),
Ops.add(
# error message, %1 is code of the error,
# detail string is appended to the end
Builtins.sformat(_("Error: %1:"), error_symbol),
description
)
)
)
else
UI.ReplaceWidget(Id(:info), Empty())
end
end
break if [:abort, :retry, :ignore].include?(r)
end
Builtins.y2milestone("ErrorScanDb: user input: %1", r)
UI.CloseDialog
return "C" if r == :abort
return "R" if r == :retry
return "I" if r == :ignore
Builtins.y2error("Unknown user input: %1", r)
"C"
end
# Callback for finish RPM DB scan event
def DoneScanDb(error, description)
Builtins.y2milestone(
"RPM DB scan finished: error: %1, reason: '%2'",
error,
description
)
if Mode.commandline
# status message (command line mode)
CommandLine.PrintVerbose(_("RPM database read"))
elsif @_scan_popup && UI.WidgetExists(Id(:label_scanDB_popup))
UI.CloseDialog
@_scan_popup = false
elsif !full_screen
Builtins.y2error("The toplevel dialog is not a scan DB popup!")
end
nil
end
def Authentication(url, msg, username, password)
# FIXME: after SLE12 release
# The following 'if' block is a workaround for bnc#895719 that should be
# extracted to a proper private method (not sure if it will work as
# expected being a callback) and adapted to use normal _() instead of
# dgettext()
url_query = URI(url).query
if url_query
url_params = URI.decode_www_form(url_query).to_h
if url_params.key?("credentials")
# Seems to be the url of a registration server, so add the tip to msg
tip = Builtins.dgettext("registration",
"Check that this system is known to the registration server.")
msg = "#{tip}\n#{msg}"
end
end
popup = VBox(
HSpacing(50), # enforce width
VSpacing(0.1),
# heading in a popup window
Heading(_("User Authentication")),
VSpacing(0.1),
HBox(
HSpacing(0.1),
RichText(
Opt(:plainText),
Builtins.sformat(_("URL: %1\n\n%2"), url, msg)
),
HSpacing(0.1)
),
VSpacing(0.1),
HBox(
HSpacing(1),
VBox(
# textentry label
InputField(Id(:username), Opt(:hstretch), _("&User Name"), username),
VSpacing(0.1),
# textentry label
Password(Id(:password), Opt(:hstretch), _("&Password"), password)
),
HSpacing(1)
),
VSpacing(0.5),
ButtonBox(
PushButton(Id(:cont), Opt(:default, :okButton), Label.ContinueButton),
PushButton(Id(:cancel), Opt(:cancelButton), Label.CancelButton)
),
VSpacing(0.5)
)
UI.OpenDialog(Opt(:decorated), popup)
ui = Convert.to_symbol(UI.UserInput)
username = Convert.to_string(UI.QueryWidget(Id(:username), :Value))
password = Convert.to_string(UI.QueryWidget(Id(:password), :Value))
UI.CloseDialog
{
"username" => username,
"password" => password,
"continue" => ui == :cont
}
end
def NextTick
@current_tick = (@current_tick + 1) % TICK_LABELS
nil
end
# is the top level progress popup?
def IsProgressPopup
UI.WidgetExists(Id(:progress_widget)) &&
UI.WidgetExists(Id(:callback_progress_popup))
end
def ProgressStart(id, task, in_percent, is_alive, _min, _max, _val_raw, val_percent)
Builtins.y2milestone("ProgressStart: %1", id)
@tick_progress = is_alive
@val_progress = !in_percent && !is_alive
@current_tick = 0
if Mode.commandline
CommandLine.Print(task)
else
subprogress_type = @tick_progress ? :tick : :progress
@progress_stack = Builtins.add(
@progress_stack,
"type" => subprogress_type, "task" => task
)
if IsProgressPopup() &&
Ops.less_or_equal(Builtins.size(@progress_stack), 1)
# huh, the popup is already there?
Builtins.y2warning("Progress popup already opened...")
UI.CloseDialog
end
if full_screen
Progress.SubprogressType(subprogress_type, 100)
Progress.SubprogressTitle(task)
else
UI.OpenDialog(
HBox(
HSpacing(1),
VBox(
VSpacing(0.5),
HSpacing(Id(:callback_progress_popup), MAX_POPUP_TEXT_SIZE),
if in_percent
ProgressBar(Id(:progress_widget), task, 100, val_percent)
else
BusyIndicator(Id(:progress_widget), task, 3000)
end,
VSpacing(0.2),
ButtonBox(
PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton)
),
VSpacing(0.5)
),
HSpacing(1)
)
)
end
end
nil
end
def ProgressEnd(id)
Builtins.y2milestone("ProgressFinish: %1", id)
# remove the last element from the progress stack
@progress_stack = Builtins.remove(
@progress_stack,
Ops.subtract(Builtins.size(@progress_stack), 1)
)
if !Mode.commandline && IsProgressPopup()
UI.CloseDialog if Builtins.size(@progress_stack) == 0
elsif full_screen
if Ops.greater_than(Builtins.size(@progress_stack), 0)
progress_type = Ops.get_symbol(
@progress_stack,
[Ops.subtract(Builtins.size(@progress_stack), 1), "type"],
:none
)
task = Ops.get_string(
@progress_stack,
[Ops.subtract(Builtins.size(@progress_stack), 1), "task"],
""
)
Progress.SubprogressType(progress_type, 100)
Progress.SubprogressTitle(task)
end
end
nil
end
def ProgressProgress(id, val_raw, val_percent)
Builtins.y2debug("ProgressProgress: %1, %2%% ", id, val_percent)
if Mode.commandline
if @tick_progress
tick_label = TICK_LABELS[@current_tick]
CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + tick_label)
NextTick()
else
CommandLine.PrintVerboseNoCR(CLEAR_PROGRESS_TEXT + "#{val_percent}%")
end
elsif IsProgressPopup()
if @tick_progress || @val_progress
UI.ChangeWidget(Id(:progress_widget), :Alive, true)
else
UI.ChangeWidget(Id(:progress_widget), :Value, val_percent)
end
# aborted ?
input = UI.PollInput
if input == :abort
Builtins.y2warning(
"Callback %1 has been aborted at %2%% (raw: %3)",
id,
val_percent,
val_raw
)
return false
end
elsif full_screen
# fullscreen callbacks
Progress.SubprogressValue(val_percent)
end
true
end
# Hanler for ProcessStart callback - handle start of a package manager process
# @param [String] task Decription of the task
# @param [Array<String>] stages Descriptions of the stages
# @param [String] help Help text describing the process
def ProcessStart(task, stages, help)
stages = deep_copy(stages)
Builtins.y2milestone(
"Process: Start: task: %1, stages: %2, help: %3",
task,
stages,
help
)
Builtins.y2milestone(
"Progress: status: %1, isrunning: %2",
Progress.status,
Progress.IsRunning
)
return if Mode.commandline
opened = false
if Progress.status
if !Progress.IsRunning
Builtins.y2milestone("Opening Wizard window...")
Wizard.CreateDialog
opened = true
end
# set 100 + number of stages as the max value,
# Progress module counts stages as extra steps
Progress.New(task, "", 100 + stages.size, stages, [], help)
Progress.Title(task)
@last_stage = 0
end
@opened_wizard = Builtins.add(@opened_wizard, opened)
Builtins.y2milestone("Wizard stack: %1", @opened_wizard)
nil
end
# Hander for ProcessProgress callback - report total progress
# @param [Fixnum] percent Total progress in percent
def ProcessProgress(percent)
Builtins.y2debug("Process: %1%%", percent)
return true if Mode.commandline
Progress.Step(percent)
true
end
# Hander for ProcessNextStage callback - the current stage has been finished
def ProcessNextStage
Builtins.y2milestone("Setting stage: %1", @last_stage)
return if Mode.commandline
Progress.Stage(@last_stage, "", -1)
@last_stage = Ops.add(@last_stage, 1)
nil
end
# Hander for ProcessDone callback - the process has been finished
def ProcessDone
Builtins.y2milestone("Process: Finished")
return if Mode.commandline
idx = Ops.subtract(Builtins.size(@opened_wizard), 1)
close = Ops.get(@opened_wizard, idx, false)
@opened_wizard = Builtins.remove(@opened_wizard, idx)
Builtins.y2milestone(
"Close Wizard window: %1, new stack: %2",
close,
@opened_wizard
)
# set 100%
Progress.Finish
if close
Builtins.y2milestone("Closing Wizard window...")
Wizard.CloseDialog
end
nil
end
# Register callbacks for media change
def SetMediaCallbacks
Pkg.CallbackMediaChange(
fun_ref(
method(:MediaChange),
"string (string, string, string, string, integer, string, integer, string, boolean, list <string>, integer)"
)
)
Pkg.CallbackSourceChange(
fun_ref(method(:SourceChange), "void (integer, integer)")
)
nil
end
def ClearScriptCallbacks
Pkg.CallbackScriptStart(nil)
Pkg.CallbackScriptProgress(nil)
Pkg.CallbackScriptProblem(nil)
Pkg.CallbackScriptFinish(nil)
Pkg.CallbackMessage(nil)
nil
end
def SetScriptCallbacks
Pkg.CallbackScriptStart(
fun_ref(method(:ScriptStart), "void (string, string, string, string)")
)
Pkg.CallbackScriptProgress(
fun_ref(method(:ScriptProgress), "boolean (boolean, string)")
)
Pkg.CallbackScriptProblem(
fun_ref(method(:ScriptProblem), "string (string)")
)
Pkg.CallbackScriptFinish(fun_ref(method(:ScriptFinish), "void ()"))
Pkg.CallbackMessage(
fun_ref(method(:Message), "boolean (string, string, string, string)")
)
nil
end
def SetScanDBCallbacks
Pkg.CallbackStartScanDb(fun_ref(method(:StartScanDb), "void ()"))
Pkg.CallbackProgressScanDb(
fun_ref(method(:ProgressScanDb), "boolean (integer)")
)
Pkg.CallbackErrorScanDb(
fun_ref(method(:ErrorScanDb), "string (integer, string)")
)
Pkg.CallbackDoneScanDb(
fun_ref(method(:DoneScanDb), "void (integer, string)")
)
nil
end
def ResetScanDBCallbacks
Pkg.CallbackStartScanDb(nil)
Pkg.CallbackProgressScanDb(nil)
Pkg.CallbackErrorScanDb(nil)
Pkg.CallbackDoneScanDb(nil)
nil
end
def SetDownloadCallbacks
Pkg.CallbackInitDownload(fun_ref(method(:InitDownload), "void (string)"))
Pkg.CallbackStartDownload(
fun_ref(method(:StartDownload), "void (string, string)")
)
Pkg.CallbackProgressDownload(
fun_ref(
method(:ProgressDownload),
"boolean (integer, integer, integer)"
)
)
Pkg.CallbackDoneDownload(
fun_ref(method(:DoneDownload), "void (integer, string)")
)
Pkg.CallbackDestDownload(fun_ref(method(:DestDownload), "void ()"))
Pkg.CallbackStartRefresh(fun_ref(method(:RefreshStarted), "void ()"))
Pkg.CallbackDoneRefresh(fun_ref(method(:RefreshDone), "void ()"))
nil
end
def ResetDownloadCallbacks
Pkg.CallbackInitDownload(nil)
Pkg.CallbackStartDownload(nil)
Pkg.CallbackProgressDownload(nil)
Pkg.CallbackDoneDownload(nil)
Pkg.CallbackDestDownload(nil)
Pkg.CallbackStartRefresh(nil)
Pkg.CallbackDoneRefresh(nil)
nil
end
def SetSourceCreateCallbacks
# source create callbacks
Pkg.CallbackSourceCreateStart(
fun_ref(method(:SourceCreateStart), "void (string)")
)
Pkg.CallbackSourceCreateProgress(
fun_ref(method(:SourceCreateProgress), "boolean (integer)")
)
Pkg.CallbackSourceCreateError(
fun_ref(method(:SourceCreateError), "symbol (string, symbol, string)")
)
Pkg.CallbackSourceCreateEnd(
fun_ref(method(:SourceCreateEnd), "void (string, symbol, string)")
)
Pkg.CallbackSourceCreateInit(
fun_ref(method(:SourceCreateInit), "void ()")
)
Pkg.CallbackSourceCreateDestroy(
fun_ref(method(:SourceCreateDestroy), "void ()")
)
nil
end
def SetSourceProbeCallbacks
# source probing callbacks
Pkg.CallbackSourceProbeStart(
fun_ref(method(:SourceProbeStart), "void (string)")
)
Pkg.CallbackSourceProbeFailed(
fun_ref(method(:SourceProbeFailed), "void (string, string)")
)
Pkg.CallbackSourceProbeSucceeded(
fun_ref(method(:SourceProbeSucceeded), "void (string, string)")
)
Pkg.CallbackSourceProbeProgress(
fun_ref(method(:SourceProbeProgress), "boolean (string, integer)")
)
Pkg.CallbackSourceProbeError(
fun_ref(method(:SourceProbeError), "symbol (string, symbol, string)")
)
Pkg.CallbackSourceProbeEnd(
fun_ref(method(:SourceProbeEnd), "void (string, symbol, string)")
)
nil
end
def SetProcessCallbacks
# register process callbacks (total progress)
Pkg.CallbackProcessStart(
fun_ref(method(:ProcessStart), "void (string, list <string>, string)")
)
Pkg.CallbackProcessProgress(
fun_ref(method(:ProcessProgress), "boolean (integer)")
)
Pkg.CallbackProcessNextStage(
fun_ref(method(:ProcessNextStage), "void ()")
)
Pkg.CallbackProcessDone(fun_ref(method(:ProcessDone), "void ()"))
nil
end
def SetProvideCallbacks
Pkg.CallbackStartProvide(
fun_ref(method(:StartProvide), "void (string, integer, boolean)")
)
Pkg.CallbackProgressProvide(
fun_ref(method(:ProgressProvide), "boolean (integer)")
)
Pkg.CallbackDoneProvide(
fun_ref(method(:DoneProvide), "string (integer, string, string)")
)
Pkg.CallbackStartPackage(
fun_ref(
method(:StartPackage),
"void (string, string, string, integer, boolean)"
)
)
Pkg.CallbackProgressPackage(
fun_ref(method(:ProgressPackage), "boolean (integer)")
)
Pkg.CallbackDonePackage(
fun_ref(method(:DonePackage), "string (integer, string)")
)
Pkg.CallbackPkgGpgCheck(
fun_ref(method(:pkg_gpg_check), "string(map)")
)
nil
end
def SetPatchCallbacks
Pkg.CallbackStartDeltaDownload(
fun_ref(method(:StartDeltaProvide), "void (string, integer)")
)
Pkg.CallbackProgressDeltaDownload(
fun_ref(method(:ProgressProvide), "boolean (integer)")
)
Pkg.CallbackProblemDeltaDownload(
fun_ref(method(:ProblemDeltaDownload), "void (string)")
)
Pkg.CallbackFinishDeltaDownload(
fun_ref(method(:FinishDeltaProvide), "void ()")
)
Pkg.CallbackStartDeltaApply(
fun_ref(method(:StartDeltaApply), "void (string)")
)
Pkg.CallbackProgressDeltaApply(
fun_ref(method(:ProgressDeltaApply), "void (integer)")
)
Pkg.CallbackProblemDeltaApply(
fun_ref(method(:ProblemDeltaApply), "void (string)")
)
Pkg.CallbackFinishDeltaApply(
fun_ref(method(:FinishDeltaProvide), "void ()")
)
nil
end
def SetSourceReportCallbacks
# source report callbacks
Pkg.CallbackSourceReportStart(
fun_ref(method(:SourceReportStart), "void (integer, string, string)")
)
Pkg.CallbackSourceReportProgress(
fun_ref(method(:SourceReportProgress), "boolean (integer)")
)
Pkg.CallbackSourceReportError(
fun_ref(
method(:SourceReportError),
"symbol (integer, string, symbol, string)"
)
)
Pkg.CallbackSourceReportEnd(
fun_ref(
method(:SourceReportEnd),
"void (integer, string, string, symbol, string)"
)
)
Pkg.CallbackSourceReportInit(
fun_ref(method(:SourceReportInit), "void ()")
)
Pkg.CallbackSourceReportDestroy(
fun_ref(method(:SourceReportDestroy), "void ()")
)
nil
end
def SetProgressReportCallbacks
Pkg.CallbackProgressReportStart(
fun_ref(
method(:ProgressStart),
"void (integer, string, boolean, boolean, integer, integer, integer, integer)"
)
)
Pkg.CallbackProgressReportProgress(
fun_ref(
method(:ProgressProgress),
"boolean (integer, integer, integer)"
)
)
Pkg.CallbackProgressReportEnd(
fun_ref(method(:ProgressEnd), "void (integer)")
)
nil
end
def SetFileConflictCallbacks
::Packages::FileConflictCallbacks.register
end
# Register package manager callbacks
def InitPackageCallbacks
SetProcessCallbacks()
SetProvideCallbacks()
SetPatchCallbacks()
SetSourceCreateCallbacks()
SetSourceProbeCallbacks()
SetSourceReportCallbacks()
SetProgressReportCallbacks()
SetFileConflictCallbacks()
# authentication callback
Pkg.CallbackAuthentication(
fun_ref(
method(:Authentication),
"map <string, any> (string, string, string, string)"
)
)
# @see bugzilla #183821
# Do not register these callbacks in case of AutoInstallation
# And for AutoUpgrade neither (bnc#820166)
if !(Mode.autoinst || Mode.autoupgrade)
# Signature-related callbacks
Pkg.CallbackAcceptUnsignedFile(
fun_ref(
SignatureCheckCallbacks.method(:AcceptUnsignedFile),
"boolean (string, integer)"
)
)
Pkg.CallbackAcceptUnknownGpgKey(
fun_ref(
SignatureCheckCallbacks.method(:AcceptUnknownGpgKey),
"boolean (string, string, integer)"
)
)
# During installation untrusted repositories are disabled to avoid
# asking again
gpg_callback = Stage.initial ? :import_gpg_key_or_disable : :ImportGpgKey
Pkg.CallbackImportGpgKey(
fun_ref(
SignatureCheckCallbacks.method(gpg_callback),
"boolean (map <string, any>, integer)"
)
)
Pkg.CallbackAcceptVerificationFailed(
fun_ref(
SignatureCheckCallbacks.method(:AcceptVerificationFailed),
"boolean (string, map <string, any>, integer)"
)
)
Pkg.CallbackTrustedKeyAdded(
fun_ref(
SignatureCheckCallbacks.method(:TrustedKeyAdded),
"void (map <string, any>)"
)
)
Pkg.CallbackTrustedKeyRemoved(
fun_ref(
SignatureCheckCallbacks.method(:TrustedKeyRemoved),
"void (map <string, any>)"
)
)
Pkg.CallbackAcceptFileWithoutChecksum(
fun_ref(
SignatureCheckCallbacks.method(:AcceptFileWithoutChecksum),
"boolean (string)"
)
)
Pkg.CallbackAcceptWrongDigest(
fun_ref(
SignatureCheckCallbacks.method(:AcceptWrongDigest),
"boolean (string, string, string)"
)
)
Pkg.CallbackAcceptUnknownDigest(
fun_ref(
SignatureCheckCallbacks.method(:AcceptUnknownDigest),
"boolean (string, string)"
)
)
end
SetMediaCallbacks()
SetScriptCallbacks()
SetScanDBCallbacks()
SetDownloadCallbacks()
nil
end
#=============================================================================
# constructor and callback init
#=============================================================================
def RegisterEmptyProgressCallbacks
::Packages::DummyCallbacks.register
end
def RestoreProcessCallbacks
Pkg.CallbackProcessStart(nil)
Pkg.CallbackProcessProgress(nil)
Pkg.CallbackProcessNextStage(nil)
Pkg.CallbackProcessDone(nil)
nil
end
def RestoreProvideCallbacks
Pkg.CallbackStartProvide(nil)
Pkg.CallbackProgressProvide(nil)
Pkg.CallbackDoneProvide(nil)
Pkg.CallbackStartPackage(nil)
Pkg.CallbackProgressPackage(nil)
Pkg.CallbackDonePackage(nil)
Pkg.CallbackPkgGpgCheck(nil)
nil
end
def RestorePatchCallbacks
Pkg.CallbackStartDeltaDownload(nil)
Pkg.CallbackProgressDeltaDownload(nil)
Pkg.CallbackProblemDeltaDownload(nil)
Pkg.CallbackFinishDeltaDownload(nil)
Pkg.CallbackStartDeltaApply(nil)
Pkg.CallbackProgressDeltaApply(nil)
Pkg.CallbackProblemDeltaApply(nil)
Pkg.CallbackFinishDeltaApply(nil)
nil
end
def RestoreSourceCreateCallbacks
Pkg.CallbackSourceCreateStart(nil)
Pkg.CallbackSourceCreateProgress(nil)
Pkg.CallbackSourceCreateError(nil)
Pkg.CallbackSourceCreateEnd(nil)
Pkg.CallbackSourceCreateInit(nil)
Pkg.CallbackSourceCreateDestroy(nil)
nil
end
def RestoreSourceReportCallbacks
Pkg.CallbackSourceReportStart(nil)
Pkg.CallbackSourceReportProgress(nil)
Pkg.CallbackSourceReportError(nil)
Pkg.CallbackSourceReportEnd(nil)
Pkg.CallbackSourceReportInit(nil)
Pkg.CallbackSourceReportDestroy(nil)
nil
end
def RestoreProgressReportCallbacks
Pkg.CallbackProgressReportStart(nil)
Pkg.CallbackProgressReportProgress(nil)
Pkg.CallbackProgressReportEnd(nil)
nil
end
def RestorePreviousProgressCallbacks
RestoreProcessCallbacks()
RestoreProvideCallbacks()
RestorePatchCallbacks()
RestoreSourceCreateCallbacks()
RestoreSourceReportCallbacks()
RestoreProgressReportCallbacks()
ClearScriptCallbacks()
ResetScanDBCallbacks()
ResetDownloadCallbacks()
nil
end
# all published variables used only by specific slideshow callbacks module
publish variable: :_package_name, type: "string"
publish variable: :_package_size, type: "integer"
publish variable: :_deleting_package, type: "boolean"
publish variable: :_current_source, type: "integer"
publish function: :StartProvide, type: "void (string, integer, boolean)"
publish function: :ProgressProvide, type: "boolean (integer)"
publish function: :DoneProvide, type: "string (integer, string, string)"
publish function: :EnableAsterixPackage, type: "boolean (boolean)"
publish function: :StartPackage, type: "void (string, string, string, integer, boolean)"
publish function: :ProgressPackage, type: "boolean (integer)"
publish function: :DonePackage, type: "string (integer, string)"
publish function: :MediaChange, type: "string (string, string, string, string, integer, string, integer, string, boolean, list <string>, integer)"
publish function: :SourceChange, type: "void (integer, integer)"
publish function: :FormatPatchName, type: "string (string, string, string)"
publish function: :ScriptProblem, type: "string (string)"
publish function: :StartDownload, type: "void (string, string)"
publish function: :ProgressDownload, type: "boolean (integer, integer, integer)"
publish function: :DoneDownload, type: "void (integer, string)"
publish function: :StartRebuildDB, type: "void ()"
publish function: :ProgressRebuildDB, type: "void (integer)"
publish function: :StopRebuildDB, type: "void (integer, string)"
publish function: :SetRebuildDBCallbacks, type: "void ()"
publish function: :StartConvertDB, type: "void (string)"
publish function: :ProgressConvertDB, type: "void (integer, string)"
publish function: :StopConvertDB, type: "void (integer, string)"
publish function: :SetConvertDBCallbacks, type: "void ()"
publish function: :SetMediaCallbacks, type: "void ()"
publish function: :SetScriptCallbacks, type: "void ()"
publish function: :InitPackageCallbacks, type: "void ()"
publish function: :RegisterEmptyProgressCallbacks, type: "void ()"
publish function: :RestorePreviousProgressCallbacks, type: "void ()"
private
# creates layout for ChangeMediumPopup
def layout_popup(message, button_box, info_on)
vertical_size = info_on ? 10 : 1
VBox(
HSpacing(50), # enforce width
VSpacing(0.1),
Left(Label(message)),
VSpacing(0.1),
HBox(
HSpacing(0.6),
Left(
CheckBox(
Id(:show),
Opt(:notify),
# check box
_("Show &details"),
info_on
)
)
),
VSpacing(0.4),
HBox(
VSpacing(vertical_size),
HSpacing(0.1),
ReplacePoint(Id(:info), Empty()),
HSpacing(0.1)
),
HBox(HSpacing(0.1), button_box, HSpacing(0.1)),
VSpacing(0.2)
)
end
# TODO: looks like generic enough to not be here
def textmode
Mode.commandline || UI.GetDisplayInfo["TextMode"]
end
# TODO: looks like generic enough to not be here
def display_width
Mode.commandline ? 0 : Ops.get_integer(UI.GetDisplayInfo, "Width", 0)
end
# functions related to the persistent storage
def load_config
@config = {}
if FileUtils.IsFile(@conf_file)
log.info "Reading config file #{@conf_file}"
read_conf = SCR.Read(path(".target.ycp"), @conf_file)
@config = read_conf if read_conf.is_a?(::Hash)
log.info "Current config: #{@config}"
else
log.info "No configuration found (file #{@conf_file} is missing)"
end
end
def autoeject
load_config unless @config
@config.fetch("automatic_eject", false)
end
def store_autoeject(value)
load_config unless @config
log.info "Config: store automatic_eject to #{value}"
@config["automatic_eject"] = value
SCR.Write(path(".target.ycp"), @conf_file, @config)
end
def progress_box(heading, name, size)
VBox(
HSpacing(40),
# popup heading
Heading(heading),
Left(
HBox(
VBox(
Left(Label(Opt(:boldFont), _("Package: "))),
Left(Label(Opt(:boldFont), _("Size: ")))
),
VBox(Left(Label(name)), Left(Label(size)))
)
),
ProgressBar(Id(:progress), " ", 100, 0),
ButtonBox(
PushButton(Id(:abort), Opt(:key_F9, :cancelButton), Label.AbortButton)
)
)
end
def full_screen
return false if Mode.commandline
ret = UI.WidgetExists(:progress_replace_point)
log.debug "Running in fullscreen mode: #{ret}"
ret
end
def retry_label(timeout)
Builtins.sformat(
_("Remaining time to automatic retry: %1"),
String.FormatTime(timeout)
)
end
def show_log_info(message, buttonbox)
show_value = UI.QueryWidget(Id(:show), :Value)
UI.CloseDialog
if show_value
UI.OpenDialog(
Opt(:decorated),
layout_popup(message, buttonbox, true)
)
true
else
UI.OpenDialog(
Opt(:decorated),
layout_popup(message, buttonbox, false)
)
UI.ReplaceWidget(Id(:info), Empty())
false
end
end
def cd_devices(preferred)
cds = Convert.convert(
SCR.Read(path(".probe.cdrom")),
from: "any",
to: "list <map>"
)
ret = []
if !cds.nil?
Builtins.foreach(cds) do |cd|
dev = Ops.get_string(cd, "dev_name", "")
model = Ops.get_string(cd, "model", "")
deflt = preferred == dev
if !dev.nil? && dev != "" && !model.nil?
ret = Builtins.add(
ret,
Item(
Id(dev),
Ops.add(
Ops.add(deflt ? "\u27A4 " : "", model),
Builtins.sformat(" (%1)", dev)
)
)
)
end
end
end
log.info "Detected CD devices: #{ret}"
deep_copy(ret)
end
# check and save the autoeject configuration if needed
def remember_autoeject
new_value = UI.QueryWidget(Id(:auto_eject), :Value)
store_autoeject(new_value) if new_value != autoeject
end
def process_message(msg, max_len)
words = msg.split
log.info "words: %{words}"
words = words.map do |w|
parsed = URL.Parse(w)
req_size = max_len - (msg.size - w.size)
# is it a valid URL? TODO: move to URL this check
if ["ftp", "http", "nfs", "file", "dir", "iso", "smb", "disk"].include?(parsed["scheme"])
# reformat the URL
w = URL.FormatURL(parsed, max_len)
elsif w.start_with?("/")
parts = w.split("/")
w = String.FormatFilename(w, req_size) if parts.size > 2 # why this number?
end
w
end
ret = words.join(" ")
log.info "URL conversion: '#{URL.HidePassword(msg)}' converted to '#{URL.HidePassword(ret)}%2'" if ret != msg
ret
end
end
PackageCallbacks = PackageCallbacksClass.new
PackageCallbacks.main
end