The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
manager_ui.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
3  2008 - 2015 by Ignacio Riquelme Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "addon/manager_ui.hpp"
17 
18 #include "addon/client.hpp"
19 #include "addon/info.hpp"
20 #include "addon/manager.hpp"
21 #include "filesystem.hpp"
22 #include "formula/string_utils.hpp"
23 #include "preferences/game.hpp"
24 #include "gettext.hpp"
28 #include "gui/dialogs/message.hpp"
30 #include "gui/widgets/window.hpp"
31 #include "log.hpp"
32 #include "wml_exception.hpp"
33 
34 static lg::log_domain log_config("config");
35 static lg::log_domain log_network("network");
36 static lg::log_domain log_filesystem("filesystem");
37 static lg::log_domain log_addons_client("addons-client");
38 
39 #define ERR_CFG LOG_STREAM(err, log_config)
40 
41 #define ERR_NET LOG_STREAM(err, log_network)
42 
43 #define ERR_FS LOG_STREAM(err, log_filesystem)
44 
45 #define LOG_AC LOG_STREAM(info, log_addons_client)
46 
47 
48 namespace {
49 
50 bool get_addons_list(addons_client& client, addons_list& list)
51 {
52  list.clear();
53 
54  config cfg;
55  client.request_addons_list(cfg);
56 
57  if(!cfg) {
58  return false;
59  }
60 
61  read_addons_list(cfg, list);
62 
63  return true;
64 }
65 
66 bool addons_manager_ui(CVideo& v, const std::string& remote_address)
67 {
68  bool need_wml_cache_refresh = false;
69 
70  preferences::set_campaign_server(remote_address);
71 
72  try {
73  addons_client client(v, remote_address);
74  client.connect();
75 
76  gui2::dialogs::addon_manager dlg(client);
77  dlg.show(v);
78 
79  need_wml_cache_refresh = dlg.get_need_wml_cache_refresh();
80  } catch(const config::error& e) {
81  ERR_CFG << "config::error thrown during transaction with add-on server; \""<< e.message << "\"" << std::endl;
82  gui2::show_error_message(v, _("Network communication error."));
83  } catch(const network_asio::error& e) {
84  ERR_NET << "network_asio::error thrown during transaction with add-on server; \""<< e.what() << "\"" << std::endl;
85  gui2::show_error_message(v, _("Remote host disconnected."));
86  } catch(const filesystem::io_exception& e) {
87  ERR_FS << "filesystem::io_exception thrown while installing an addon; \"" << e.what() << "\"" << std::endl;
88  gui2::show_error_message(v, _("A problem occurred when trying to create the files necessary to install this add-on."));
89  } catch(const invalid_pbl_exception& e) {
90  ERR_CFG << "could not read .pbl file " << e.path << ": " << e.message << std::endl;
91 
92  utils::string_map symbols;
93  symbols["path"] = e.path;
94  symbols["msg"] = e.message;
95 
97  vgettext("A local file with add-on publishing information could not be read.\n\nFile: $path\nError message: $msg", symbols));
98  } catch(wml_exception& e) {
99  e.show(v);
100  } catch(const addons_client::user_exit&) {
101  LOG_AC << "initial connection canceled by user\n";
102  } catch(const addons_client::invalid_server_address&) {
103  gui2::show_error_message(v, _("The add-ons server address specified is not valid."));
104  }
105 
106  return need_wml_cache_refresh;
107 }
108 
109 bool uninstall_local_addons(CVideo& v)
110 {
111  const std::string list_lead = "\n\n";
112 
113  const std::vector<std::string>& addons = installed_addons();
114 
115  if(addons.empty()) {
117  _("You have no add-ons installed."));
118  return false;
119  }
120 
121  std::map<std::string, std::string> addon_titles_map;
122 
123  for(const std::string& id : addons) {
124  std::string title;
125 
126  if(have_addon_install_info(id)) {
127  // _info.cfg may have the add-on's title starting with 1.11.7,
128  // if the add-on was downloading using the revised _info.cfg writer.
129  config cfg;
130  get_addon_install_info(id, cfg);
131 
132  const config& info_cfg = cfg.child("info");
133 
134  if(info_cfg) {
135  title = info_cfg["title"].str();
136  }
137  }
138 
139  if(title.empty()) {
140  // Transform the id into a title as a last resort.
141  title = make_addon_title(id);
142  }
143 
144  addon_titles_map[id] = title;
145  }
146 
147  int res;
148 
149  std::vector<std::string> remove_ids;
150  std::set<std::string> remove_names;
151 
152  do {
153  gui2::dialogs::addon_uninstall_list dlg(addon_titles_map);
154  dlg.show(v);
155 
156  remove_ids = dlg.selected_addons();
157  if(remove_ids.empty()) {
158  return false;
159  }
160 
161  remove_names.clear();
162 
163  for(const std::string& id : remove_ids) {
164  remove_names.insert(addon_titles_map[id]);
165  }
166 
167  const std::string confirm_message = _n(
168  "Are you sure you want to remove the following installed add-on?",
169  "Are you sure you want to remove the following installed add-ons?",
170  remove_ids.size()) + list_lead + utils::bullet_list(remove_names);
171 
172  res = gui2::show_message(v
173  , _("Confirm")
174  , confirm_message
176  } while (res != gui2::window::OK);
177 
178  std::set<std::string> failed_names, skipped_names, succeeded_names;
179 
180  for(const std::string& id : remove_ids) {
181  const std::string& name = addon_titles_map[id];
182 
184  skipped_names.insert(name);
185  } else if(remove_local_addon(id)) {
186  succeeded_names.insert(name);
187  } else {
188  failed_names.insert(name);
189  }
190  }
191 
192  if(!skipped_names.empty()) {
193  const std::string dlg_msg = _n(
194  "The following add-on appears to have publishing or version control information stored locally, and will not be removed:",
195  "The following add-ons appear to have publishing or version control information stored locally, and will not be removed:",
196  skipped_names.size());
197 
199  v, dlg_msg + list_lead + utils::bullet_list(skipped_names));
200  }
201 
202  if(!failed_names.empty()) {
204  "The following add-on could not be deleted properly:",
205  "The following add-ons could not be deleted properly:",
206  failed_names.size()) + list_lead + utils::bullet_list(failed_names));
207  }
208 
209  if(!succeeded_names.empty()) {
210  const std::string dlg_title =
211  _n("Add-on Deleted", "Add-ons Deleted", succeeded_names.size());
212  const std::string dlg_msg = _n(
213  "The following add-on was successfully deleted:",
214  "The following add-ons were successfully deleted:",
215  succeeded_names.size());
216 
218  v, dlg_title,
219  dlg_msg + list_lead + utils::bullet_list(succeeded_names), "", false, false, true);
220 
221  return true;
222  }
223 
224  return false;
225 }
226 
227 } // end anonymous namespace
228 
230 {
231  static const int addon_download = 0;
232  // NOTE: the following two values are also known by WML, so don't change them.
233  static const int addon_uninstall = 2;
234 
236  const bool have_addons = !installed_addons().empty();
237 
238  gui2::dialogs::addon_connect addon_dlg(host_name, have_addons);
239  addon_dlg.show(v);
240  int res = addon_dlg.get_retval();
241 
242  if(res == gui2::window::OK) {
243  res = addon_download;
244  }
245 
246  switch(res) {
247  case addon_download:
248  return addons_manager_ui(v, host_name);
249  case addon_uninstall:
250  return uninstall_local_addons(v);
251  default:
252  return false;
253  }
254 }
255 
256 bool ad_hoc_addon_fetch_session(CVideo& v, const std::vector<std::string>& addon_ids)
257 {
258  std::string remote_address = preferences::campaign_server();
259 
260  // These exception handlers copied from addon_manager_ui fcn above.
261  try {
262 
263  addons_client client(v, remote_address);
264  client.connect();
265 
266  addons_list addons;
267 
268  if(!get_addons_list(client, addons)) {
269  gui2::show_error_message(v, _("An error occurred while downloading the add-ons list from the server."));
270  return false;
271  }
272 
273  bool return_value = true;
274  for(const std::string & addon_id : addon_ids) {
275  addons_list::const_iterator it = addons.find(addon_id);
276  if(it != addons.end()) {
277  const addon_info& addon = it->second;
278  addons_client::install_result res = client.install_addon_with_checks(addons, addon);
279  return_value = return_value && (res.outcome == addons_client::install_outcome::success);
280  } else {
281  utils::string_map symbols;
282  symbols["addon_id"] = addon_id;
283  gui2::show_error_message(v, vgettext("Could not find an add-on matching id $addon_id on the add-on server.", symbols));
284  return_value = false;
285  }
286  }
287 
288  return return_value;
289 
290  } catch(const config::error& e) {
291  ERR_CFG << "config::error thrown during transaction with add-on server; \""<< e.message << "\"" << std::endl;
292  gui2::show_error_message(v, _("Network communication error."));
293  } catch(const network_asio::error& e) {
294  ERR_NET << "network_asio::error thrown during transaction with add-on server; \""<< e.what() << "\"" << std::endl;
295  gui2::show_error_message(v, _("Remote host disconnected."));
296  } catch(const filesystem::io_exception& e) {
297  ERR_FS << "io_exception thrown while installing an addon; \"" << e.what() << "\"" << std::endl;
298  gui2::show_error_message(v, _("A problem occurred when trying to create the files necessary to install this add-on."));
299  } catch(const invalid_pbl_exception& e) {
300  ERR_CFG << "could not read .pbl file " << e.path << ": " << e.message << std::endl;
301 
302  utils::string_map symbols;
303  symbols["path"] = e.path;
304  symbols["msg"] = e.message;
305 
307  vgettext("A local file with add-on publishing information could not be read.\n\nFile: $path\nError message: $msg", symbols));
308  } catch(wml_exception& e) {
309  e.show(v);
310  } catch(const addons_client::user_exit&) {
311  LOG_AC << "initial connection canceled by user\n";
312  } catch(const addons_client::invalid_server_address&) {
313  gui2::show_error_message(v, _("The add-ons server address specified is not valid."));
314  }
315 
316  return false;
317 }
Shows the list of addons on the server.
Definition: manager.hpp:39
void read_addons_list(const config &cfg, addons_list &dest)
Definition: info.cpp:194
void show_error_message(CVideo &video, const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:207
Dialog is closed with ok button.
Definition: window.hpp:111
void set_campaign_server(const std::string &host)
Definition: game.cpp:415
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:352
const char * what() const NOEXCEPT
Definition: exceptions.hpp:37
std::vector< char_t > string
std::map< std::string, t_string > string_map
const std::string path
Path to the faulty .pbl file.
Definition: manager.hpp:43
#define ERR_FS
Definition: manager_ui.cpp:43
#define ERR_CFG
Definition: manager_ui.cpp:39
std::string campaign_server()
Definition: game.cpp:406
Addon connect dialog.
Definition: connect.hpp:27
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
Exception thrown when the WML parser fails to read a .pbl file.
Definition: manager.hpp:30
#define LOG_AC
Definition: manager_ui.cpp:45
Definition: video.hpp:29
This file contains the window object, this object is a top level container which has the event manage...
void get_addon_install_info(const std::string &addon_name, config &cfg)
Gets the installation info (_info.cfg) for an add-on.
Definition: manager.cpp:105
bool get_need_wml_cache_refresh() const
Definition: manager.hpp:44
bool manage_addons(CVideo &v)
Shows the add-ons server connection dialog, for access to the various management front-ends.
Definition: manager_ui.cpp:229
void show_transient_message(CVideo &video, const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
bool have_addon_in_vcs_tree(const std::string &addon_name)
Returns true if the specified add-ons appear to be managed by a 'supported' VCS.
Definition: manager.cpp:66
#define ERR_NET
Definition: manager_ui.cpp:41
void show(CVideo &video)
Shows the error in a dialog.
static lg::log_domain log_network("network")
bool have_addon_install_info(const std::string &addon_name)
Returns true if there is a local installation info (_info.cfg) file for the add-on.
Definition: manager.cpp:100
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
bool ad_hoc_addon_fetch_session(CVideo &v, const std::vector< std::string > &addon_ids)
Conducts an ad-hoc add-ons server connection to download an add-on with a particular id and all it's ...
Definition: manager_ui.cpp:256
const std::string message
Error message to display.
Definition: manager.hpp:46
static UNUSEDNOWARN std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:93
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
Definition: manager.cpp:148
Shows a yes and no button.
Definition: message.hpp:79
bool remove_local_addon(const std::string &addon)
Definition: manager.cpp:118
void connect()
Try to establish a connection to the add-ons server.
Definition: client.cpp:63
Add-ons (campaignd) client class.
Definition: client.hpp:29
Helper class, don't construct this directly.
static lg::log_domain log_addons_client("addons-client")
void show_message(CVideo &video, const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:152
bool have_addon_pbl_info(const std::string &addon_name)
Returns true if there's a local .pbl file stored for the specified add-on.
Definition: manager.cpp:75
An exception object used when an IO error occurs.
Definition: filesystem.hpp:46
std::string bullet_list(const T &v, size_t indent=4, const std::string &bullet=font::unicode_bullet)
Generates a new string containing a bullet list.
bool show(CVideo &video, const unsigned auto_close_time=0)
Shows the window.
Declarations for File-IO.
install_outcome outcome
Definition: client.hpp:36
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:223
std::string vgettext(const char *msgid, const utils::string_map &symbols)
static lg::log_domain log_filesystem("filesystem")
Standard logging facilities (interface).
std::vector< std::string > selected_addons() const
std::string message
Definition: exceptions.hpp:31
static const char * name(const std::vector< SDL_Joystick * > &joysticks, const size_t index)
Definition: joystick.cpp:48
#define e
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
std::map< std::string, addon_info > addons_list
Definition: info.hpp:26
install_result install_addon_with_checks(const addons_list &addons, const addon_info &addon)
Do a 'smart' fetch of an add-on, checking to avoid overwrites for devs and resolving dependencies...
Definition: client.cpp:454
bool request_addons_list(config &cfg)
Request the add-ons list from the server.
Definition: client.cpp:76
static lg::log_domain log_config("config")