bool addons_client::install_addon(config& archive_cfg, const addon_info& info) { const cursor::setter cursor_setter(cursor::WAIT); utils::string_map i18n_symbols; i18n_symbols["addon_title"] = font::escape_text(info.title); if(!check_names_legal(archive_cfg)) { gui2::show_error_message( VGETTEXT("The add-on <i>$addon_title</i> has an invalid file or directory " "name and cannot be installed.", i18n_symbols)); return false; } if(!check_case_insensitive_duplicates(archive_cfg)){ gui2::show_error_message( VGETTEXT("The add-on <i>$addon_title</i> has file or directory names " "with case conflicts. This may cause problems.", i18n_symbols)); } // Add local version information before unpacking config* maindir = &archive_cfg.find_child("dir", "name", info.id); if(!*maindir) { LOG_ADDONS << "downloaded add-on '" << info.id << "' is missing its directory in the archive; creating it\n"; maindir = &archive_cfg.add_child("dir"); (*maindir)["name"] = info.id; } LOG_ADDONS << "generating version info for add-on '" << info.id << "'\n"; std::ostringstream info_contents; config wml; info_contents << "#\n" "# File automatically generated by Wesnoth to keep track\n" "# of version information on installed add-ons. DO NOT EDIT!\n" "#\n"; info.write_minimal(wml.add_child("info")); write(info_contents, wml); config file; file["name"] = "_info.cfg"; file["contents"] = info_contents.str(); maindir->add_child("file", file); LOG_ADDONS << "unpacking " << info.id << '\n'; // Remove any previously installed versions if(!remove_local_addon(info.id)) { WRN_ADDONS << "failed to uninstall previous version of " << info.id << "; the add-on may not work properly!" << std::endl; } unarchive_addon(archive_cfg); LOG_ADDONS << "unpacking finished\n"; return true; }
/** Warns the user about unresolved dependencies and installs them if they choose to do so. * Returns: outcome: ABORT in case the user chose to abort because of an issue * SUCCESS otherwise * wml_change: indicates if new wml content was installed */ addon_op_result do_resolve_addon_dependencies(display& disp, addons_client& client, const addons_list& addons, const addon_info& addon) { addon_op_result result; result.outcome = SUCCESS; result.wml_changed = false; boost::scoped_ptr<cursor::setter> cursor_setter(new cursor::setter(cursor::WAIT)); // TODO: We don't currently check for the need to upgrade. I'll probably // work on that when implementing dependency tiers later. const std::set<std::string>& deps = addon.resolve_dependencies(addons); std::vector<std::string> missing_deps; std::vector<std::string> broken_deps; BOOST_FOREACH(const std::string& dep, deps) { if(!is_addon_installed(dep)) { if(addons.find(dep) != addons.end()) { missing_deps.push_back(dep); } else { broken_deps.push_back(dep); } } } cursor_setter.reset(); if(!broken_deps.empty()) { std::string broken_deps_report; broken_deps_report = _n( "The selected add-on has the following dependency, which is not currently installed or available from the server. Do you wish to continue?", "The selected add-on has the following dependencies, which are not currently installed or available from the server. Do you wish to continue?", broken_deps.size()); broken_deps_report += "\n"; BOOST_FOREACH(const std::string& broken_dep_id, broken_deps) { broken_deps_report += "\n " + utils::unicode_bullet + " " + make_addon_title(broken_dep_id); }
addons_client::install_result addons_client::do_resolve_addon_dependencies(const addons_list& addons, const addon_info& addon) { install_result result; result.outcome = install_outcome::success; result.wml_changed = false; auto cursor_setter = std::make_unique<cursor::setter>(cursor::WAIT); // TODO: We don't currently check for the need to upgrade. I'll probably // work on that when implementing dependency tiers later. const std::set<std::string>& deps = addon.resolve_dependencies(addons); std::vector<std::string> missing_deps; std::vector<std::string> broken_deps; for(const std::string& dep : deps) { try { addon_tracking_info info = get_addon_tracking_info(addons.at(dep)); // ADDON_NONE means not installed. if(info.state == ADDON_NONE) { missing_deps.push_back(dep); } else if(info.state == ADDON_INSTALLED_UPGRADABLE) { // Tight now, we don't need to distinguish the lists of missing // and outdated addons, so just add them to missing. missing_deps.push_back(dep); } } catch(const std::out_of_range&) { // Dependency wasn't found on server, check locally directly. if(!is_addon_installed(dep)) { broken_deps.push_back(dep); } } } cursor_setter.reset(); if(!broken_deps.empty()) { std::string broken_deps_report; broken_deps_report = _n( "The selected add-on has the following dependency, which is not currently installed or available from the server. Do you wish to continue?", "The selected add-on has the following dependencies, which are not currently installed or available from the server. Do you wish to continue?", broken_deps.size()); broken_deps_report += "\n"; for(const std::string& broken_dep_id : broken_deps) { broken_deps_report += "\n " + font::unicode_bullet + " " + make_addon_title(broken_dep_id); } if(gui2::show_message(_("Broken Dependencies"), broken_deps_report, gui2::dialogs::message::yes_no_buttons) != gui2::retval::OK) { result.outcome = install_outcome::abort; return result; // canceled by user } } if(missing_deps.empty()) { // No dependencies to install, carry on. return result; } { addons_list options; for(const std::string& dep : missing_deps) { options[dep] = addons.at(dep); } gui2::dialogs::install_dependencies dlg(options); bool cont = dlg.show(); if(!cont) { return result; // the user has chosen to continue without installing anything. } } // // Install dependencies now. // std::vector<std::string> failed_titles; for(const std::string& dep : missing_deps) { const addon_info& missing_addon = addons.at(dep); if(!try_fetch_addon(missing_addon)) { failed_titles.push_back(missing_addon.title); } else { result.wml_changed = true; } } if(!failed_titles.empty()) { const std::string& failed_deps_report = _n( "The following dependency could not be installed. Do you still wish to continue?", "The following dependencies could not be installed. Do you still wish to continue?", failed_titles.size()) + std::string("\n\n") + utils::bullet_list(failed_titles); result.outcome = gui2::show_message(_("Dependencies Installation Failed"), failed_deps_report, gui2::dialogs::message::yes_no_buttons) == gui2::retval::OK ? install_outcome::success : install_outcome::abort; // If the user cancels, return abort. Otherwise, return success, since the user chose to ignore the failure. return result; } return result; }