diff --git a/products.d/agama-products-opensuse.changes b/products.d/agama-products-opensuse.changes index f638e5adeb..0aa654f7ca 100644 --- a/products.d/agama-products-opensuse.changes +++ b/products.d/agama-products-opensuse.changes @@ -4,6 +4,11 @@ Wed Jun 26 08:25:00 UTC 2024 - Ladislav Slezák - Remove the "32bit" pattern from openSUSE MicroOS (gh#openSUSE/agama#1381) +------------------------------------------------------------------- +Mon Jun 24 20:35:00 UTC 2024 - Lubos Kocman + +- Add Leap 16 (gh#openSUSE/agama#1371) + ------------------------------------------------------------------- Tue May 21 11:35:51 UTC 2024 - Richard Brown diff --git a/rust/agama-lib/src/proxies.rs b/rust/agama-lib/src/proxies.rs index d7e5944bbd..240327ab59 100644 --- a/rust/agama-lib/src/proxies.rs +++ b/rust/agama-lib/src/proxies.rs @@ -182,3 +182,13 @@ trait Issues { #[dbus_proxy(property)] fn all(&self) -> zbus::Result>; } + +#[dbus_proxy( + interface = "org.opensuse.Agama1.Locale", + default_service = "org.opensuse.Agama.Manager1", + default_path = "/org/opensuse/Agama/Manager1" +)] +trait Locale { + /// SetLocale method + fn set_locale(&self, locale: &str) -> zbus::Result<()>; +} diff --git a/rust/agama-lib/src/software/proxies.rs b/rust/agama-lib/src/software/proxies.rs index cdb79b4829..f7762c1d43 100644 --- a/rust/agama-lib/src/software/proxies.rs +++ b/rust/agama-lib/src/software/proxies.rs @@ -80,8 +80,7 @@ trait SoftwareProduct { /// SelectProduct method fn select_product(&self, id: &str) -> zbus::Result<(u32, String)>; - /// AvailableProducts property - #[dbus_proxy(property)] + /// AvailableProducts method fn available_products(&self) -> zbus::Result>; /// SelectedProduct property diff --git a/rust/agama-server/src/l10n/web.rs b/rust/agama-server/src/l10n/web.rs index 1647a50a1d..dfdab552de 100644 --- a/rust/agama-server/src/l10n/web.rs +++ b/rust/agama-server/src/l10n/web.rs @@ -7,7 +7,9 @@ use crate::{ error::Error, web::{Event, EventsSender}, }; -use agama_lib::{error::ServiceError, localization::LocaleProxy}; +use agama_lib::{ + error::ServiceError, localization::LocaleProxy, proxies::LocaleProxy as ManagerLocaleProxy, +}; use agama_locale_data::LocaleId; use axum::{ extract::State, @@ -24,6 +26,7 @@ use tokio::sync::RwLock; struct LocaleState<'a> { locale: Arc>, proxy: LocaleProxy<'a>, + manager_proxy: ManagerLocaleProxy<'a>, events: EventsSender, } @@ -37,9 +40,11 @@ pub async fn l10n_service( let id = LocaleId::default(); let locale = L10n::new_with_locale(&id).unwrap(); let proxy = LocaleProxy::new(&dbus).await?; + let manager_proxy = ManagerLocaleProxy::new(&dbus).await?; let state = LocaleState { locale: Arc::new(RwLock::new(locale)), proxy, + manager_proxy, events, }; @@ -144,7 +149,9 @@ async fn set_config( .try_into() .map_err(|_e| LocaleError::UnknownLocale(ui_locale.to_string()))?; data.translate(&locale)?; - changes.ui_locale = Some(locale.to_string()); + let locale_string = locale.to_string(); + state.manager_proxy.set_locale(&locale_string).await?; + changes.ui_locale = Some(locale_string); _ = state.events.send(Event::LocaleChanged { locale: locale.to_string(), diff --git a/rust/agama-server/src/network/model.rs b/rust/agama-server/src/network/model.rs index c6d6f3ba56..739a7b6c47 100644 --- a/rust/agama-server/src/network/model.rs +++ b/rust/agama-server/src/network/model.rs @@ -1314,7 +1314,6 @@ impl fmt::Display for InfinibandTransportMode { } } - #[derive(Default, Debug, PartialEq, Clone, Serialize)] pub enum TunMode { #[default] @@ -1329,7 +1328,6 @@ pub struct TunConfig { pub owner: Option, } - /// Represents a network change. #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] diff --git a/rust/agama-server/src/network/nm/dbus.rs b/rust/agama-server/src/network/nm/dbus.rs index d5eba9a5b2..b0e66c0b77 100644 --- a/rust/agama-server/src/network/nm/dbus.rs +++ b/rust/agama-server/src/network/nm/dbus.rs @@ -543,9 +543,8 @@ fn infiniband_config_from_dbus(conn: &OwnedNestedHash) -> Option HashMap<&str, zvariant::Value> { - let mut tun_config: HashMap<&str, zvariant::Value> = HashMap::from([ - ("mode", Value::new(config.mode.clone() as u32)), - ]); + let mut tun_config: HashMap<&str, zvariant::Value> = + HashMap::from([("mode", Value::new(config.mode.clone() as u32))]); if let Some(group) = &config.group { tun_config.insert("group", group.into()); @@ -563,7 +562,6 @@ fn tun_config_from_dbus(conn: &OwnedNestedHash) -> Option { let mut tun_config = TunConfig::default(); - if let Some(mode) = tun.get("mode") { tun_config.mode = match mode.downcast_ref::()? { 2 => TunMode::Tap, diff --git a/rust/package/agama.changes b/rust/package/agama.changes index 6087cd1599..1e106d521d 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -1,12 +1,13 @@ ------------------------------------------------------------------- -Tue Jun 25 15:04:20 UTC 2024 - David Diaz +Tue Jun 25 15:16:33 UTC 2024 - Imobach Gonzalez Sosa -- Add resize actions to storage model (gh#openSUSE/agama#1354). +- Use the new SetLocale D-Bus method to change the language and the + keyboard layout (gh#openSUSE/agama#1375). ------------------------------------------------------------------- -Mon Jun 24 20:35:00 UTC 2024 - Lubos Kocman +Tue Jun 25 15:04:20 UTC 2024 - David Diaz -- Add Leap 16 (gh#openSUSE/agama#1371) +- Add resize actions to storage model (gh#openSUSE/agama#1354). ------------------------------------------------------------------- Thu Jun 21 15:00:00 UTC 2024 - Clemens Famulla-Conrad diff --git a/service/lib/agama/dbus/clients/software.rb b/service/lib/agama/dbus/clients/software.rb index aba2860f11..4e89a1c92a 100644 --- a/service/lib/agama/dbus/clients/software.rb +++ b/service/lib/agama/dbus/clients/software.rb @@ -21,6 +21,7 @@ require "agama/dbus/clients/base" require "agama/dbus/clients/with_issues" +require "agama/dbus/clients/with_locale" require "agama/dbus/clients/with_progress" require "agama/dbus/clients/with_service_status" @@ -29,6 +30,7 @@ module DBus module Clients # D-Bus client for software configuration class Software < Base + include WithLocale include WithIssues include WithProgress include WithServiceStatus diff --git a/service/lib/agama/dbus/clients/storage.rb b/service/lib/agama/dbus/clients/storage.rb index 03e3406f6e..f6201d1dc0 100644 --- a/service/lib/agama/dbus/clients/storage.rb +++ b/service/lib/agama/dbus/clients/storage.rb @@ -21,6 +21,7 @@ require "agama/dbus/clients/base" require "agama/dbus/clients/with_service_status" +require "agama/dbus/clients/with_locale" require "agama/dbus/clients/with_progress" require "agama/dbus/clients/with_issues" @@ -29,6 +30,7 @@ module DBus module Clients # D-Bus client for storage configuration class Storage < Base + include WithLocale include WithServiceStatus include WithProgress include WithIssues diff --git a/service/lib/agama/dbus/clients/with_locale.rb b/service/lib/agama/dbus/clients/with_locale.rb new file mode 100644 index 0000000000..08d9fd208a --- /dev/null +++ b/service/lib/agama/dbus/clients/with_locale.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +module Agama + module DBus + module Clients + # Mixin for clients of services that define the Locale D-Bus interface + # + # Provides a method to interact with the API of the Locale interface. + # + # @note This mixin is expected to be included in a class inherited from {Clients::Base} and + # it requires a #dbus_object method that returns a {::DBus::Object} implementing the + # Locale interface. + module WithLocale + # Changes the service locale + # + # @param locale [String] new locale + def locale=(locale) + dbus_object.SetLocale(locale) + end + end + end + end +end diff --git a/service/lib/agama/dbus/interfaces/locale.rb b/service/lib/agama/dbus/interfaces/locale.rb new file mode 100644 index 0000000000..506cbc4c8c --- /dev/null +++ b/service/lib/agama/dbus/interfaces/locale.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "dbus" + +Yast.import "WFM" + +module Agama + module DBus + module Interfaces + # Mixin to define the Locale interface. + # + # @note This mixin is expected to be included in a class that inherits from {DBus::BaseObject} + # and it requires a #locale= method that sets the service's locale. + module Locale + include Yast::I18n + include Yast::Logger + + LOCALE_INTERFACE = "org.opensuse.Agama1.Locale" + + def self.included(base) + base.class_eval do + dbus_interface LOCALE_INTERFACE do + # It expects a locale (en_US.UTF-8) as argument. + dbus_method :SetLocale, "in locale:s" do |locale| + self.locale = locale + end + end + end + end + end + end + end +end diff --git a/service/lib/agama/dbus/manager.rb b/service/lib/agama/dbus/manager.rb index 61b4df984f..19a69c359c 100644 --- a/service/lib/agama/dbus/manager.rb +++ b/service/lib/agama/dbus/manager.rb @@ -24,6 +24,7 @@ require "agama/dbus/base_object" require "agama/dbus/with_service_status" require "agama/dbus/interfaces/progress" +require "agama/dbus/interfaces/locale" require "agama/dbus/interfaces/service_status" require "agama/autoyast/converter" @@ -34,6 +35,7 @@ class Manager < BaseObject include WithServiceStatus include Interfaces::Progress include Interfaces::ServiceStatus + include Interfaces::Locale PATH = "/org/opensuse/Agama/Manager1" private_constant :PATH @@ -141,6 +143,12 @@ def service_status backend.service_status end + def locale=(locale) + safe_run do + busy_while { backend.locale = locale } + end + end + private # @return [Agama::Manager] diff --git a/service/lib/agama/dbus/manager_service.rb b/service/lib/agama/dbus/manager_service.rb index 7a9c9bf432..0f16e08fbd 100644 --- a/service/lib/agama/dbus/manager_service.rb +++ b/service/lib/agama/dbus/manager_service.rb @@ -28,7 +28,6 @@ require "agama/dbus/manager" require "agama/dbus/users" require "agama/dbus/storage/proposal" -require "agama/ui_locale" module Agama module DBus @@ -74,8 +73,6 @@ def start export # We need locale for data from users locale_client = Clients::Locale.instance - # TODO: test if we need to pass block with additional actions - @ui_locale = UILocale.new(locale_client) manager.on_progress_change { dispatch } # make single thread more responsive manager.startup_phase end diff --git a/service/lib/agama/dbus/software/manager.rb b/service/lib/agama/dbus/software/manager.rb index 21faf5e536..1ebd0b0088 100644 --- a/service/lib/agama/dbus/software/manager.rb +++ b/service/lib/agama/dbus/software/manager.rb @@ -24,6 +24,7 @@ require "agama/dbus/clients/locale" require "agama/dbus/clients/network" require "agama/dbus/interfaces/issues" +require "agama/dbus/interfaces/locale" require "agama/dbus/interfaces/progress" require "agama/dbus/interfaces/service_status" require "agama/dbus/with_service_status" @@ -37,6 +38,7 @@ class Manager < BaseObject include Interfaces::Progress include Interfaces::ServiceStatus include Interfaces::Issues + include Interfaces::Locale PATH = "/org/opensuse/Agama/Software1" private_constant :PATH @@ -134,6 +136,12 @@ def finish busy_while { backend.finish } end + def locale=(locale) + busy_while do + backend.locale = locale + end + end + private # @return [Agama::Software] diff --git a/service/lib/agama/dbus/software/product.rb b/service/lib/agama/dbus/software/product.rb index ba983232ec..1c85087b8c 100644 --- a/service/lib/agama/dbus/software/product.rb +++ b/service/lib/agama/dbus/software/product.rb @@ -23,6 +23,7 @@ require "suse/connect" require "agama/dbus/base_object" require "agama/dbus/interfaces/issues" +require "agama/dbus/clients/locale" require "agama/registration" module Agama @@ -90,7 +91,9 @@ def select_product(id) private_constant :PRODUCT_INTERFACE dbus_interface PRODUCT_INTERFACE do - dbus_reader :available_products, "a(ssa{sv})" + dbus_method :AvailableProducts, "out result:a(ssa{sv})" do + [available_products] + end dbus_reader :selected_product, "s" @@ -248,6 +251,11 @@ def registration_properties_changed interfaces_and_properties[REGISTRATION_INTERFACE], []) end + def product_properties_changed + dbus_properties_changed(PRODUCT_INTERFACE, + interfaces_and_properties[PRODUCT_INTERFACE], []) + end + # Result from calling to SUSE connect. # # @raise [Exception] if an unexpected error is found. diff --git a/service/lib/agama/dbus/software_service.rb b/service/lib/agama/dbus/software_service.rb index cc0404fdf1..636bbf300b 100644 --- a/service/lib/agama/dbus/software_service.rb +++ b/service/lib/agama/dbus/software_service.rb @@ -24,7 +24,6 @@ require "agama/dbus/clients/locale" require "agama/dbus/software" require "agama/software" -require "agama/ui_locale" require "yast" Yast.import "Pkg" @@ -58,10 +57,6 @@ def start # for some reason the the "export" method must be called before # registering the language change callback to work properly export - @ui_locale = UILocale.new(Clients::Locale.instance) do |locale| - # call the language change handler - locale_handler(locale) - end end # Exports the software object through the D-Bus service @@ -94,41 +89,6 @@ def dbus_objects Agama::DBus::Software::Proposal.new(logger) ] end - - # Language change callback handler, activate new locale in the libzypp backend - # @param locale [String] the new locale - def locale_handler(locale) - language, = locale.split(".") - - # set the locale in the Language module, when changing the repository - # (product) it calls Pkg.SetTextLocale(Language.language) internally - Yast::Language.Set(language) - - # set libzypp locale (for communication only, Pkg.SetPackageLocale - # call can be used for *installing* the language packages) - Yast::Pkg.SetTextLocale(language) - - # refresh all enabled repositories to download the missing translation files - Yast::Pkg.SourceGetCurrent(true).each do |src| - Yast::Pkg.SourceForceRefreshNow(src) - end - - # remember the currently selected packages and patterns by YaST - # (ignore the automatic selections done by the solver) - # - # NOTE: we will need to handle also the tabooed and soft-locked objects - # when we allow to set them via UI or CLI - selected = Y2Packager::Resolvable.find(status: :selected, transact_by: :appl_high) - - # save and reload all repositories to activate the new translations - Yast::Pkg.SourceSaveAll - Yast::Pkg.SourceFinishAll - Yast::Pkg.SourceRestore - Yast::Pkg.SourceLoad - - # restore back the selected objects - selected.each { |s| Yast::Pkg.ResolvableInstall(s.name, s.kind) } - end end end end diff --git a/service/lib/agama/dbus/storage/manager.rb b/service/lib/agama/dbus/storage/manager.rb index a28ed1227c..382899695b 100644 --- a/service/lib/agama/dbus/storage/manager.rb +++ b/service/lib/agama/dbus/storage/manager.rb @@ -25,6 +25,7 @@ require "y2storage/storage_manager" require "agama/dbus/base_object" require "agama/dbus/interfaces/issues" +require "agama/dbus/interfaces/locale" require "agama/dbus/interfaces/progress" require "agama/dbus/interfaces/service_status" require "agama/dbus/storage/devices_tree" @@ -48,6 +49,7 @@ class Manager < BaseObject # rubocop:disable Metrics/ClassLength include WithServiceStatus include ::DBus::ObjectManager include DBus::Interfaces::Issues + include DBus::Interfaces::Locale include DBus::Interfaces::Progress include DBus::Interfaces::ServiceStatus @@ -356,6 +358,10 @@ def iscsi_delete(path) dbus_method(:Delete, "in node:o, out result:u") { |n| iscsi_delete(n) } end + def locale=(locale) + backend.locale = locale + end + private # @return [Agama::Storage::Manager] diff --git a/service/lib/agama/dbus/storage_service.rb b/service/lib/agama/dbus/storage_service.rb index 37da268335..f4ee03f1ba 100644 --- a/service/lib/agama/dbus/storage_service.rb +++ b/service/lib/agama/dbus/storage_service.rb @@ -24,7 +24,6 @@ require "agama/dbus/clients/locale" require "agama/dbus/storage" require "agama/storage" -require "agama/ui_locale" module Agama module DBus @@ -53,8 +52,6 @@ def bus # Starts storage service. It does more then just #export method. def start export - # TODO: test if we need to pass block with additional actions - @ui_locale = UILocale.new(Clients::Locale.instance) end # Exports the storage proposal object through the D-Bus service diff --git a/service/lib/agama/manager.rb b/service/lib/agama/manager.rb index 956f46c5b0..4a48e64a16 100644 --- a/service/lib/agama/manager.rb +++ b/service/lib/agama/manager.rb @@ -23,6 +23,7 @@ require "agama/config" require "agama/network" require "agama/proxy_setup" +require "agama/with_locale" require "agama/with_progress" require "agama/installation_phase" require "agama/service_status_recorder" @@ -43,6 +44,7 @@ module Agama # other services via D-Bus (e.g., `org.opensuse.Agama.Software1`). class Manager include WithProgress + include WithLocale include Helpers include Yast::I18n @@ -79,6 +81,20 @@ def startup_phase service_status.idle end + def locale=(locale) + service_status.busy + change_process_locale(locale) + start_progress_with_descriptions( + _("Load software translations"), + _("Load storage translations") + ) + progress.step { software.locale = locale } + progress.step { storage.locale = locale } + ensure + service_status.idle + finish_progress + end + # Runs the config phase def config_phase service_status.busy diff --git a/service/lib/agama/software/manager.rb b/service/lib/agama/software/manager.rb index c7c56f5b7c..63f4675af5 100644 --- a/service/lib/agama/software/manager.rb +++ b/service/lib/agama/software/manager.rb @@ -33,9 +33,11 @@ require "agama/software/product_builder" require "agama/software/proposal" require "agama/software/repositories_manager" +require "agama/with_locale" require "agama/with_progress" require "agama/with_issues" +Yast.import "Language" Yast.import "Package" Yast.import "Packages" Yast.import "PackageCallbacks" @@ -54,6 +56,7 @@ module Software # It shoud be splitted in separate and smaller classes. class Manager # rubocop:disable Metrics/ClassLength include Helpers + include WithLocale include WithIssues include WithProgress include Yast::I18n @@ -391,6 +394,43 @@ def product_issues issues end + # Change the locale and activate new locale in the libzypp backend + # + # @param locale [String] the new locale + def locale=(locale) + change_process_locale(locale) + language, = locale.split(".") + + # set the locale in the Language module, when changing the repository + # (product) it calls Pkg.SetTextLocale(Language.language) internally + Yast::Language.Set(language) + + # set libzypp locale (for communication only, Pkg.SetPackageLocale + # call can be used for *installing* the language packages) + Yast::Pkg.SetTextLocale(language) + + # refresh all enabled repositories to download the missing translation files + Yast::Pkg.SourceGetCurrent(true).each do |src| + Yast::Pkg.SourceForceRefreshNow(src) + end + + # remember the currently selected packages and patterns by YaST + # (ignore the automatic selections done by the solver) + # + # NOTE: we will need to handle also the tabooed and soft-locked objects + # when we allow to set them via UI or CLI + selected = Y2Packager::Resolvable.find(status: :selected, transact_by: :appl_high) + + # save and reload all repositories to activate the new translations + Yast::Pkg.SourceSaveAll + Yast::Pkg.SourceFinishAll + Yast::Pkg.SourceRestore + Yast::Pkg.SourceLoad + + # restore back the selected objects + selected.each { |s| Yast::Pkg.ResolvableInstall(s.name, s.kind) } + end + private # @return [Agama::Config] diff --git a/service/lib/agama/storage/manager.rb b/service/lib/agama/storage/manager.rb index a12ceb79ac..a880adf3cc 100644 --- a/service/lib/agama/storage/manager.rb +++ b/service/lib/agama/storage/manager.rb @@ -31,6 +31,7 @@ require "agama/storage/finisher" require "agama/storage/proposal_settings_reader" require "agama/issue" +require "agama/with_locale" require "agama/with_issues" require "agama/with_progress" require "agama/security" @@ -43,6 +44,7 @@ module Agama module Storage # Manager to handle storage configuration class Manager + include WithLocale include WithIssues include WithProgress include Yast::I18n @@ -172,6 +174,13 @@ def actions ActionsGenerator.new(probed, staging).generate end + # Changes the service's locale + # + # @param locale [String] new locale + def locale=(locale) + change_process_locale(locale) + end + private PROPOSAL_ID = "storage_proposal" diff --git a/service/lib/agama/ui_locale.rb b/service/lib/agama/with_locale.rb similarity index 62% rename from service/lib/agama/ui_locale.rb rename to service/lib/agama/with_locale.rb index 4f21dd046e..6ec4cd7773 100644 --- a/service/lib/agama/ui_locale.rb +++ b/service/lib/agama/with_locale.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) [2022] SUSE LLC +# Copyright (c) [2024] SUSE LLC # # All Rights Reserved. # @@ -24,27 +24,12 @@ Yast.import "WFM" module Agama - # Object responsible for managing changes of localization produced by D-Bus backend. - class UILocale + # Mixin that implements methods for handling with the localization + module WithLocale include Yast::I18n include Yast::Logger - # creates new UILocale object that will handle change of UI locale. - # - # @param [Agama::DBus::Clients::Locale] locale_client to communicate with dbus service - # @yield block is callback that is called when ui locale is changed - # to allow user to call methods that needs retranslation - def initialize(locale_client, &block) - @client = locale_client - change_locale(@client.ui_locale) - @client.on_ui_locale_change do |locale| - change_locale(locale) - block.call(locale) if block_given? - end - end - - private - def change_locale(locale) + def change_process_locale(locale) language, encoding = locale.split(".") Yast::WFM.SetLanguage(language, encoding) # explicitly set ENV to get localization also from libraries like libstorage diff --git a/service/package/rubygem-agama-yast.changes b/service/package/rubygem-agama-yast.changes index 2cfb4553c3..3a3d2e3201 100644 --- a/service/package/rubygem-agama-yast.changes +++ b/service/package/rubygem-agama-yast.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Jun 26 09:53:23 UTC 2024 - Imobach Gonzalez Sosa + +- Use a D-Bus method instead of a signal to change the language and + the keyboard layout (gh#openSUSE/agama#1375). + ------------------------------------------------------------------- Wed Jun 26 09:12:33 UTC 2024 - José Iván López González diff --git a/service/test/agama/dbus/software/product_test.rb b/service/test/agama/dbus/software/product_test.rb index 72e1ee7438..8ca3d45787 100644 --- a/service/test/agama/dbus/software/product_test.rb +++ b/service/test/agama/dbus/software/product_test.rb @@ -24,6 +24,7 @@ require "agama/config" require "agama/registration" require "agama/software/manager" +require "agama/dbus/clients/locale" require "suse/connect" describe Agama::DBus::Software::Product do @@ -37,8 +38,16 @@ let(:target_dir) { Dir.mktmpdir } + let(:locale_client) do + instance_double( + Agama::DBus::Clients::Locale, + ui_locale: "en_US.UTF-8", on_ui_locale_change: nil + ) + end + before do stub_const("Agama::Software::Manager::TARGET_DIR", target_dir) + allow(Agama::DBus::Clients::Locale).to receive(:instance).and_return(locale_client) allow(config).to receive(:products).and_return(products) allow(subject).to receive(:dbus_properties_changed) allow(Agama::ProductReader).to receive(:new).and_call_original @@ -49,7 +58,7 @@ end let(:products) do - { "Tumbleweed" => {}, "ALP-Dolomite" => {} } + { "Tumbleweed" => {}, "MicroOS" => {} } end it "defines Product D-Bus interface" do @@ -83,7 +92,7 @@ context "if the current product is registered" do before do - subject.select_product("Leap16") + subject.select_product("MicroOS") allow(backend.registration).to receive(:reg_code).and_return("123XX432") end diff --git a/web/package/agama-web-ui.changes b/web/package/agama-web-ui.changes index 7b68884b59..3fa653e7ff 100644 --- a/web/package/agama-web-ui.changes +++ b/web/package/agama-web-ui.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Wed Jun 26 08:31:31 UTC 2024 - Imobach Gonzalez Sosa + +- Use the new SetLocale D-Bus method to change the language and the + keyboard layout (gh#openSUSE/agama#1375). +- Move the "Download logs" to the sidebar (gh#openSUSE/agama#1375). +- Improve the behavior when changing the installer language + (gh#openSUSE/agama#1375, gh#openSUSE/agama#1235). + ------------------------------------------------------------------- Wed Jun 26 07:52:22 UTC 2024 - David Diaz diff --git a/web/src/MainLayout.jsx b/web/src/MainLayout.jsx index 19c7ffc44d..bc9eac5b34 100644 --- a/web/src/MainLayout.jsx +++ b/web/src/MainLayout.jsx @@ -29,7 +29,7 @@ import { Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from "@patternfly/react-core"; import { Icon } from "~/components/layout"; -import { About, InstallerOptions } from "~/components/core"; +import { About, InstallerOptions, LogsButton } from "~/components/core"; import { _ } from "~/i18n"; import { rootRoutes } from "~/router"; import { useProduct } from "~/context/product"; @@ -81,6 +81,9 @@ const ChangeProductButton = () => { + + + ); }; diff --git a/web/src/SimpleLayout.jsx b/web/src/SimpleLayout.jsx index 342a4786dd..828721ed15 100644 --- a/web/src/SimpleLayout.jsx +++ b/web/src/SimpleLayout.jsx @@ -21,34 +21,16 @@ import React from "react"; import { Outlet } from "react-router-dom"; -import { - Masthead, MastheadContent, - Page, - Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem -} from "@patternfly/react-core"; -import { InstallerOptions } from "~/components/core"; +import { Page } from "@patternfly/react-core"; import { _ } from "~/i18n"; /** * Simple layout for displaying content that comes before product configuration * TODO: improve documentation */ -export default function SimpleLayout({ showOutlet = true, showInstallerOptions = true, children }) { +export default function SimpleLayout({ showOutlet = true, children }) { return ( - - - - - - - {showInstallerOptions && } - - - - - - {showOutlet ? : children} ); diff --git a/web/src/components/core/InstallationFinished.test.jsx b/web/src/components/core/InstallationFinished.test.jsx index eef9d63afc..c6901c4981 100644 --- a/web/src/components/core/InstallationFinished.test.jsx +++ b/web/src/components/core/InstallationFinished.test.jsx @@ -29,6 +29,7 @@ import { EncryptionMethods } from "~/client/storage"; import InstallationFinished from "./InstallationFinished"; jest.mock("~/client"); +jest.mock("~/components/core/InstallerOptions", () => () =>
Installer Options
); const finishInstallationFn = jest.fn(); let encryptionPassword; diff --git a/web/src/components/core/InstallationProgress.jsx b/web/src/components/core/InstallationProgress.jsx index 5e7c083293..bf5f3f08dc 100644 --- a/web/src/components/core/InstallationProgress.jsx +++ b/web/src/components/core/InstallationProgress.jsx @@ -36,7 +36,7 @@ function InstallationProgress() { // TRANSLATORS: %s is replaced by a product name (e.g., openSUSE Tumbleweed) const title = sprintf(_("Installing %s, please wait ..."), selectedProduct.name); return ( - + ); diff --git a/web/src/components/core/InstallerOptions.jsx b/web/src/components/core/InstallerOptions.jsx index 6150fb50c1..8b270e8c99 100644 --- a/web/src/components/core/InstallerOptions.jsx +++ b/web/src/components/core/InstallerOptions.jsx @@ -23,11 +23,14 @@ import React, { useState } from "react"; import { useLocation } from "react-router-dom"; -import { Button, Card, CardBody, Flex } from "@patternfly/react-core"; +import { Button, Flex, Form, FormGroup, FormSelect, FormSelectOption } from "@patternfly/react-core"; import { Icon } from "~/components/layout"; -import { LogsButton, Popup } from "~/components/core"; -import { InstallerLocaleSwitcher, InstallerKeymapSwitcher } from "~/components/l10n"; +import { Popup } from "~/components/core"; import { _ } from "~/i18n"; +import { localConnection } from "~/utils"; +import { useInstallerL10n } from "~/context/installerL10n"; +import supportedLanguages from "~/languages.json"; +import { useL10n } from "~/context/l10n"; /** * @typedef {import("@patternfly/react-core").ButtonProps} ButtonProps @@ -39,8 +42,18 @@ import { _ } from "~/i18n"; * @todo Write documentation */ export default function InstallerOptions() { + const { + language: initialLanguage, + keymap: initialKeymap, + changeLanguage, + changeKeymap + } = useInstallerL10n(); + const [language, setLanguage] = useState(initialLanguage); + const [keymap, setKeymap] = useState(initialKeymap); + const { keymaps } = useL10n(); const location = useLocation(); const [isOpen, setIsOpen] = useState(false); + const [inProgress, setInProgress] = useState(false); // FIXME: Installer options should be available in the login too. if (location.pathname.includes("login")) return; @@ -48,6 +61,15 @@ export default function InstallerOptions() { const open = () => setIsOpen(true); const close = () => setIsOpen(false); + const onSubmit = async (e) => { + e.preventDefault(); + setInProgress(true); + changeKeymap(keymap); + changeLanguage(language) + .then(close) + .catch(() => setInProgress(false)); + }; + return ( <> - {isCollecting && - } + + {isCollecting && + } - {error && - } + {error && + } + + {_("Close")} + + ); }; diff --git a/web/src/components/l10n/InstallerKeymapSwitcher.jsx b/web/src/components/l10n/InstallerKeymapSwitcher.jsx deleted file mode 100644 index f208afecdf..0000000000 --- a/web/src/components/l10n/InstallerKeymapSwitcher.jsx +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) [2023] SUSE LLC - * - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, contact SUSE LLC. - * - * To contact SUSE LLC about this file by physical or electronic mail, you may - * find current contact information at www.suse.com. - */ - -import React, { useState } from "react"; -import { - Flex, - Select, SelectList, SelectOption, - MenuToggle -} from "@patternfly/react-core"; -import { Icon } from "~/components/layout"; -import agama from "~/agama"; -import { _ } from "~/i18n"; -import { useInstallerL10n } from "~/context/installerL10n"; -import { useL10n } from "~/context/l10n"; -import { localConnection } from "~/utils"; - -const sort = (keymaps) => { - // sort the keymap names using the current locale - const lang = agama.language || "en"; - return keymaps.sort((k1, k2) => k1.name.localeCompare(k2.name, lang)); -}; - -export default function InstallerKeymapSwitcher() { - const { keymap: keymapId, changeKeymap } = useInstallerL10n(); - const { keymaps } = useL10n(); - const [isOpen, setIsOpen] = useState(false); - const selectedKeymap = keymaps.find(k => k.id === keymapId); - - const onSelect = (_, id) => { - setIsOpen(false); - changeKeymap(id); - }; - - const toggle = toggleRef => ( - setIsOpen(!isOpen)} isExpanded={isOpen}> - {selectedKeymap?.name} - - ); - - const options = sort(keymaps) - .map((keymap, index) => ( - {keymap.name} - )); - - return ( - -
- {_("Keyboard")} -
- { - localConnection() - ? ( - - ) - : _("Cannot be changed in remote installation") - - } -
- ); -} diff --git a/web/src/components/l10n/InstallerKeymapSwitcher.test.jsx b/web/src/components/l10n/InstallerKeymapSwitcher.test.jsx deleted file mode 100644 index 2a97f1940a..0000000000 --- a/web/src/components/l10n/InstallerKeymapSwitcher.test.jsx +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) [2023] SUSE LLC - * - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, contact SUSE LLC. - * - * To contact SUSE LLC about this file by physical or electronic mail, you may - * find current contact information at www.suse.com. - */ - -import React from "react"; -import { screen } from "@testing-library/react"; -import { plainRender } from "~/test-utils"; - -import InstallerKeymapSwitcher from "./InstallerKeymapSwitcher"; - -let mockChangeKeyboardFn; - -jest.mock("~/lib/cockpit", () => ({ - gettext: term => term, -})); - -jest.mock("~/context/installerL10n", () => ({ - ...jest.requireActual("~/context/installerL10n"), - useInstallerL10n: () => ({ - changeKeymap: mockChangeKeyboardFn, - keymap: "us" - }) -})); - -jest.mock("~/context/l10n", () => ({ - ...jest.requireActual("~/context/l10n"), - useL10n: () => ({ - keymaps: [ - { id: "cz", name: "Czech" }, - { id: "cz(qwerty)", name: "Czech (QWERTY)" }, - { id: "de", name: "German" }, - { id: "us", name: "English (US)" }, - { id: "us(dvorak)", name: "English (Dvorak)" } - ] - }) -})); - -beforeEach(() => { - mockChangeKeyboardFn = jest.fn(); -}); - -it("InstallerKeymapSwitcher", async () => { - const { user } = plainRender(); - const button = screen.getByRole("button", { name: "English (US)" }); - await user.click(button); - const option = screen.getByRole("option", { name: "Czech" }); - await user.click(option); - expect(mockChangeKeyboardFn).toHaveBeenCalledWith("cz"); -}); diff --git a/web/src/components/l10n/InstallerLocaleSwitcher.jsx b/web/src/components/l10n/InstallerLocaleSwitcher.jsx deleted file mode 100644 index 0e24561d5d..0000000000 --- a/web/src/components/l10n/InstallerLocaleSwitcher.jsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) [2023] SUSE LLC - * - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, contact SUSE LLC. - * - * To contact SUSE LLC about this file by physical or electronic mail, you may - * find current contact information at www.suse.com. - */ - -import React, { useState } from "react"; -import { - Flex, - Select, SelectList, SelectOption, - MenuToggle -} from "@patternfly/react-core"; -import { Icon } from "../layout"; -import { _ } from "~/i18n"; -import { useInstallerL10n } from "~/context/installerL10n"; -import supportedLanguages from "~/languages.json"; - -export default function InstallerLocaleSwitcher() { - const { language, changeLanguage } = useInstallerL10n(); - const [isOpen, setIsOpen] = useState(false); - const [selected, setSelected] = useState(language); - - const onSelect = (_event, value) => { - setIsOpen(false); - setSelected(value); - changeLanguage(value); - }; - - const toggle = toggleRef => ( - setIsOpen(!isOpen)} isExpanded={isOpen}> - {supportedLanguages[selected]} - - ); - - // sort by the language code to maintain consistent order - const options = Object.keys(supportedLanguages).sort() - .map(id => {supportedLanguages[id]}); - - return ( - -
- {_("Language")} -
- -
- ); -} diff --git a/web/src/components/l10n/InstallerLocaleSwitcher.test.jsx b/web/src/components/l10n/InstallerLocaleSwitcher.test.jsx deleted file mode 100644 index 5cbdd522e5..0000000000 --- a/web/src/components/l10n/InstallerLocaleSwitcher.test.jsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) [2023] SUSE LLC - * - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, contact SUSE LLC. - * - * To contact SUSE LLC about this file by physical or electronic mail, you may - * find current contact information at www.suse.com. - */ - -import React from "react"; -import { screen } from "@testing-library/react"; -import { plainRender } from "~/test-utils"; -import InstallerLocaleSwitcher from "./InstallerLocaleSwitcher"; - -const mockLanguage = "es-es"; -let mockChangeLanguageFn; - -jest.mock("~/languages.json", () => ({ - "de-de": "Deutsch", - "en-us": "English (US)", - "es-es": "Español" -})); - -jest.mock("~/context/installerL10n", () => ({ - ...jest.requireActual("~/context/installerL10n"), - useInstallerL10n: () => ({ - language: mockLanguage, - changeLanguage: mockChangeLanguageFn - }) -})); - -beforeEach(() => { - mockChangeLanguageFn = jest.fn(); -}); - -it("InstallerLocaleSwitcher", async () => { - const { user } = plainRender(); - const button = screen.getByRole("button", { name: "Español" }); - await user.click(button); - const option = screen.getByRole("option", { name: "English (US)" }); - await user.click(option); - expect(mockChangeLanguageFn).toHaveBeenCalledWith("en-us"); -}); diff --git a/web/src/components/l10n/index.js b/web/src/components/l10n/index.js index 349ada74b1..fb1b98b3bc 100644 --- a/web/src/components/l10n/index.js +++ b/web/src/components/l10n/index.js @@ -19,8 +19,6 @@ * find current contact information at www.suse.com. */ -export { default as InstallerKeymapSwitcher } from "./InstallerKeymapSwitcher"; -export { default as InstallerLocaleSwitcher } from "./InstallerLocaleSwitcher"; export { default as L10nPage } from "./L10nPage"; export { default as LocaleSelection } from "./LocaleSelection"; export { default as KeymapSelection } from "./KeyboardSelection"; diff --git a/web/src/components/layout/Loading.jsx b/web/src/components/layout/Loading.jsx index 63036d1282..9c40f97021 100644 --- a/web/src/components/layout/Loading.jsx +++ b/web/src/components/layout/Loading.jsx @@ -30,7 +30,7 @@ const LoadingIcon = () => ; function Loading({ text = _("Loading installation environment, please wait.") }) { return ( - +
void} changeLanguage - Function to change the current language. + * @property {string|undefined} keymap - Current keymap. + * @property {(language: string) => Promise} changeLanguage - Function to change the current language. + * @property {(keymap: string) => Promise} changeKeymap - Function to change the current keymap. * * @return {L10nContext} */