44 #include <boost/scoped_ptr.hpp>
53 #define ERR_CFG LOG_STREAM(err, log_config)
55 #define ERR_NET LOG_STREAM(err, log_network)
57 #define ERR_FS LOG_STREAM(err, log_filesystem)
59 #define ERR_AC LOG_STREAM(err , log_addons_client)
60 #define WRN_AC LOG_STREAM(warn, log_addons_client)
61 #define LOG_AC LOG_STREAM(info, log_addons_client)
62 #define DBG_AC LOG_STREAM(debug, log_addons_client)
68 addons_list::const_iterator it = addons.find(
id);
69 assert(it != addons.end());
96 return info.
can_publish ?
_(
"addon_state^Published, not installed") :
_(
"addon_state^Not installed");
103 info.
can_publish ?
_(
"addon_state^Published") :
_(
"addon_state^Installed"));
106 info.
can_publish ?
_(
"addon_state^Published, upgradable") :
_(
"addon_state^Installed, upgradable"));
109 info.
can_publish ?
_(
"addon_state^Published, outdated on server") :
_(
"addon_state^Installed, outdated on server"));
112 info.
can_publish ?
_(
"addon_state^Published, broken") :
_(
"addon_state^Installed, broken"));
128 if(!server_error.empty()) {
130 std::string(
_(
"The server responded with an error:")) +
"\n" + server_error);
141 struct addon_op_result
155 result.outcome = SUCCESS;
156 result.wml_changed =
false;
165 std::vector<std::string> missing_deps;
166 std::vector<std::string> broken_deps;
170 if(addons.find(dep) != addons.end()) {
171 missing_deps.push_back(dep);
173 broken_deps.push_back(dep);
178 cursor_setter.reset();
180 if(!broken_deps.empty()) {
183 broken_deps_report =
_n(
184 "The selected add-on has the following dependency, which is not currently installed or available from the server. Do you wish to continue?",
185 "The selected add-on has the following dependencies, which are not currently installed or available from the server. Do you wish to continue?",
187 broken_deps_report +=
"\n";
189 for(
const std::string& broken_dep_id : broken_deps) {
194 result.outcome = ABORT;
199 if(missing_deps.empty()) {
210 _(
"Name") << sep <<
_(
"Version") << sep <<
_(
"Author") << sep <<
211 _(
"Size") << sep <<
_(
"Type")).str();
213 std::vector<std::string>
options(1, header);
214 std::vector<int> sort_sizes;
219 const addon_info& addon = addon_at(dep, addons);
231 sort_sizes.push_back(-addon.
size);
236 display_author + sep + display_size + sep +
242 _n(
"The selected add-on has the following dependency, which is not currently installed. Do you wish to install it before continuing?",
243 "The selected add-on has the following dependencies, which are not currently installed. Do you wish to install them before continuing?",
244 missing_deps.size()),
251 dlg.set_menu(addon_menu);
253 cursor_setter.reset();
264 std::vector<std::string> failed_titles;
267 const addon_info& addon = addon_at(dep, addons);
269 if(!try_fetch_addon(v, client, addon)) {
270 failed_titles.push_back(addon.
title);
272 result.wml_changed =
true;
276 if(!failed_titles.empty()) {
278 "The following dependency could not be installed. Do you still wish to continue?",
279 "The following dependencies could not be installed. Do you still wish to continue?",
290 bool do_check_before_overwriting_addon(
CVideo& video,
const addon_info& addon)
302 symbols[
"addon"] = addon.
title;
304 std::vector<std::string> extra_items;
306 text =
vgettext(
"The add-on '$addon|' is already installed and contains additional information that will be permanently lost if you continue:", symbols);
310 extra_items.push_back(
_(
"Publishing information file (.pbl)"));
314 extra_items.push_back(
_(
"Version control system (VCS) information"));
318 text +=
_(
"Do you really wish to continue?");
331 if(!(do_check_before_overwriting_addon(v, addon))) {
334 result.outcome = ABORT;
335 result.wml_changed =
false;
341 addon_op_result
res = do_resolve_addon_dependencies(v, client, addons, addon);
342 if (res.outcome != SUCCESS) {
346 if(!try_fetch_addon(v, client, addon)) {
347 res.outcome = FAILURE;
350 res.wml_changed =
true;
360 const std::string& text =
vgettext(
"Deleting '$addon|' will permanently erase its download and upload counts on the add-ons server. Do you really wish to continue?", symbols);
372 std::string(
_(
"The server responded with an error:")) +
"\n" +
388 const version_info& version_to_publish = cfg[
"version"].str();
390 if(version_to_publish <= remote_version) {
392 _(
"The remote version of this add-on is greater or equal to the version being uploaded. Do you really wish to continue?"),
404 std::string(
_(
"The server responded with an error:")) +
"\n" +
409 std::string(
_(
"The server responded with an error:")) +
"\n" +
421 std::vector<std::string> display_ids_;
428 : v_(v) , display_ids_(display_ids), addons_(addons), tracking_(tracking), filter_(filter)
433 assert(filter_ !=
nullptr);
435 const int menu_selection = filter_->
get_index(filter_choice);
438 const size_t choice =
static_cast<size_t>(menu_selection);
439 if(choice < display_ids_.size()) {
441 assert(tracking_.find(
id) != tracking_.end());
450 struct addons_filter_state
453 std::vector<bool>
types;
462 addons_filter_state()
476 addons_filter_state&
f_;
479 filter_options_action(
CVideo& video, addons_filter_state&
filter)
500 assert(f_.types.size() == new_types.size());
502 if(std::equal(f_.types.begin(), f_.types.end(), new_types.begin()) && f_.status == new_status &&
503 f_.sort == new_sort && f_.direction == new_direction) {
508 f_.types = new_types;
509 f_.status = new_status;
511 f_.direction = new_direction;
521 struct addon_pointer_list_sorter
524 : sort_(sort), dir_(direction)
527 inline bool operator()(
const addons_list::value_type*
a,
const addons_list::value_type*
b) {
528 assert(a !=
nullptr && b !=
nullptr);
531 const addons_list::value_type*
c =
a;
542 return a->second.updated < b->second.updated;
546 return a->second.order < b->second.order;
556 typedef std::vector<const addons_list::value_type*> sorted_addon_pointer_list;
598 sorted_addon_pointer_list
res;
599 addon_pointer_list_sorter sorter(sort, direction);
601 for(
const addons_list::value_type& entry : addons) {
602 res.push_back(&entry);
605 std::stable_sort(res.begin(), res.end(), sorter);
615 filter.changed =
false;
620 const bool updates_only =
623 const bool show_publish_delete = !updates_only;
634 std::vector<std::string> can_delete_ids;
643 std::vector<std::string>
options, filter_options;
647 std::vector<std::string> option_ids;
650 std::vector<int> sort_sizes;
654 header +=
_(
"Old Version") + sep +
_(
"New Version") + sep;
656 header +=
_(
"Version") + sep;
658 header +=
_(
"Author") + sep +
_(
"Size");
667 header += sep +
_(
"Downloads") + sep +
_(
"Type");
671 options.push_back(header);
672 filter_options.push_back(header);
679 const sorted_addon_pointer_list& sorted_addons = sort_addons_list(addons, filter.sort, filter.direction);
681 bool have_upgradable_addons =
false;
683 for(
const sorted_addon_pointer_list::value_type& sorted_entry : sorted_addons) {
684 const addons_list::value_type& entry = *sorted_entry;
693 (!filter.types[addon.
type])
698 have_upgradable_addons =
true;
701 option_ids.push_back(addon.
id);
703 if(tracking[addon.
id].can_publish) {
704 can_delete_ids.push_back(addon.
id);
712 const std::string& display_status = describe_addon_status(tracking[addon.
id]);
715 std::string display_old_version = tracking[addon.
id].installed_version;
720 sort_sizes.push_back(-addon.
size);
728 row = display_title + sep;
730 row += display_old_version + sep;
732 row += display_version + sep + display_author + sep +
733 display_size + sep + display_down + sep +
736 filter_options.push_back(row);
756 color_upgradable + display_old_version +
757 "\n" + color_upgradable + display_version;
760 color_outdated + display_old_version +
761 "\n" + color_outdated + display_version;
769 row += display_old_version + display_sep;
771 row += display_version + display_sep + display_author + display_sep + display_size;
773 row += display_sep + display_down + display_sep + display_type;
776 options.push_back(row);
779 if(show_publish_delete) {
786 static const std::string publish_icon =
"icons/icon-game.png~BLIT(icons/icon-addon-publish.png)";
790 filter_options.push_back(text);
795 static const std::string delete_icon =
"icons/icon-game.png~BLIT(icons/icon-addon-delete.png)";
799 filter_options.push_back(text);
808 const bool dummy_addons_list = options.size() == 1;
812 static const int update_all_value = -255;
821 if(dummy_addons_list) {
822 dlg_message = addons.empty()
823 ?
_(
"There are no add-ons available for download from this server.")
824 :
_(
"There are no add-ons matching the specified criteria on this server.");
842 dlg.set_menu(addons_list_menu);
845 if(!dummy_addons_list) {
846 filter_label =
_(
"Filter: ");
850 filter_label, options, filter_options, 1, dlg, 300);
851 filter_box->
set_text(filter.keywords);
852 dlg.set_textbox(filter_box);
854 description_display_action description_helper(v, option_ids, addons, tracking, filter_box);
861 update_all_button->
enable(have_upgradable_addons);
864 filter_options_action filter_opts_helper(v, filter);
873 if(dummy_addons_list) {
874 filter_box->
hide(
true);
875 description_button->
enable(
false);
876 update_all_button->
enable(
false);
877 addons_list_menu->
hide(
true);
882 std::find(option_ids.begin(), option_ids.end(), last_addon_id) :
885 if(it != option_ids.end()) {
886 addons_list_menu->
move_selection(std::distance(option_ids.begin(), it));
889 cursor_setter.reset();
896 filter.keywords = filter_box->
text();
899 const bool update_everything = result == update_all_value;
901 if(result < 0 && !(update_everything || filter.changed)) {
913 if(show_publish_delete) {
914 if(result >=
int(option_ids.size() + can_publish_ids.size())) {
916 const std::string&
id = can_delete_ids[result -
int(option_ids.size() + can_publish_ids.size())];
917 do_remote_addon_delete(v, client,
id);
919 }
else if(result >=
int(option_ids.size())) {
921 const std::string&
id = can_publish_ids[result -
int(option_ids.size())];
922 do_remote_addon_publish(v, client,
id, tracking[
id].remote_version);
927 std::vector<std::string> ids_to_install;
928 std::vector<std::string> failed_titles;
930 if(update_everything) {
933 ids_to_install.push_back(
id);
937 assert(result >= 0 &&
size_t(result) < option_ids.size());
938 last_addon_id = option_ids[
result];
939 ids_to_install.push_back(option_ids[result]);
943 const addon_info& addon = addon_at(
id, addons);
945 addon_op_result res = try_fetch_addon_with_checks(v, client, addons, addon);
946 wml_changed |= res.wml_changed;
947 if (res.outcome == ABORT) {
949 }
else if (res.outcome == FAILURE) {
950 failed_titles.push_back(addon.
title);
961 const bool updating = update_everything || updates_only;
963 if(ids_to_install.size() == 1 && failed_titles.empty()) {
965 syms[
"addon_title"] = addons[ids_to_install[0]].title;
967 msg_title = !updating ?
_(
"Add-on Installed") : _(
"Add-on Updated");
968 msg_text = !updating ?
_(
"The add-on '$addon_title|' has been successfully installed.") : _(
"The add-on '$addon_title|' has been successfully updated.");
972 }
else if(failed_titles.empty()) {
973 msg_title = !updating ?
_(
"Add-ons Installed") : _(
"Add-ons Updated");
974 msg_text = !updating ?
_(
"All add-ons installed successfully.") : _(
"All add-ons updated successfully.");
978 msg_title = !updating ?
_(
"Installation Failed") : _(
"Update Failed");
980 "The following add-on could not be downloaded or installed successfully:",
981 "The following add-ons could not be downloaded or installed successfully:",
982 failed_titles.size());
990 bool stay_in_manager_ui =
false;
991 bool need_wml_cache_refresh =
false;
993 addons_filter_state
filter;
999 if(need_wml_cache_refresh) {
1023 ,
_(
"An error occurred while downloading the "
1024 "add-ons list from the server."));
1025 return need_wml_cache_refresh;
1029 return need_wml_cache_refresh;
1032 if(!get_addons_list(client, addons)) {
1034 return need_wml_cache_refresh;
1040 show_addons_manager_dialog(v, client, addons, last_addon_id, stay_in_manager_ui, need_wml_cache_refresh, filter);
1041 }
while(filter.changed);
1046 LOG_AC <<
"operation canceled by user; returning to add-ons manager\n";
1048 }
while(stay_in_manager_ui);
1050 ERR_CFG <<
"config::error thrown during transaction with add-on server; \""<< e.
message <<
"\"" << std::endl;
1053 ERR_NET <<
"network_asio::error thrown during transaction with add-on server; \""<< e.
what() <<
"\"" << std::endl;
1056 ERR_FS <<
"filesystem::io_exception thrown while installing an addon; \"" << e.
what() <<
"\"" << std::endl;
1062 symbols[
"path"] = e.
path;
1066 vgettext(
"A local file with add-on publishing information could not be read.\n\nFile: $path\nError message: $msg", symbols));
1070 LOG_AC <<
"initial connection canceled by user\n";
1075 return need_wml_cache_refresh;
1078 bool uninstall_local_addons(
CVideo& v)
1084 if(addons.empty()) {
1086 _(
"You have no add-ons installed."));
1090 std::map<std::string, std::string> addon_titles_map;
1104 title = info_cfg[
"title"].str();
1113 addon_titles_map[
id] = title;
1118 std::vector<std::string> remove_ids;
1119 std::set<std::string> remove_names;
1125 remove_ids = dlg.selected_addons();
1126 if(remove_ids.empty()) {
1130 remove_names.clear();
1133 remove_names.insert(addon_titles_map[
id]);
1137 "Are you sure you want to remove the following installed add-on?",
1138 "Are you sure you want to remove the following installed add-ons?",
1147 std::set<std::string> failed_names, skipped_names, succeeded_names;
1153 skipped_names.insert(name);
1155 succeeded_names.insert(name);
1157 failed_names.insert(name);
1161 if(!skipped_names.empty()) {
1163 "The following add-on appears to have publishing or version control information stored locally, and will not be removed:",
1164 "The following add-ons appear to have publishing or version control information stored locally, and will not be removed:",
1165 skipped_names.size());
1171 if(!failed_names.empty()) {
1173 "The following add-on could not be deleted properly:",
1174 "The following add-ons could not be deleted properly:",
1178 if(!succeeded_names.empty()) {
1180 _n(
"Add-on Deleted",
"Add-ons Deleted", succeeded_names.size());
1182 "The following add-on was successfully deleted:",
1183 "The following add-ons were successfully deleted:",
1184 succeeded_names.size());
1200 static const int addon_download = 0;
1202 static const int addon_uninstall = 2;
1212 res = addon_download;
1216 case addon_download:
1217 return addons_manager_ui(v, host_name);
1218 case addon_uninstall:
1219 return uninstall_local_addons(v);
1237 if(!get_addons_list(client, addons)) {
1242 bool return_value =
true;
1244 addons_list::const_iterator it = addons.find(addon_id);
1245 if(it != addons.end()) {
1247 addon_op_result res = try_fetch_addon_with_checks(v, client, addons, addon);
1248 return_value = return_value && (res.outcome == SUCCESS);
1251 symbols[
"addon_id"] = addon_id;
1253 return_value =
false;
1257 return return_value;
1260 ERR_CFG <<
"config::error thrown during transaction with add-on server; \""<< e.
message <<
"\"" << std::endl;
1263 ERR_NET <<
"network_asio::error thrown during transaction with add-on server; \""<< e.
what() <<
"\"" << std::endl;
1266 ERR_FS <<
"io_exception thrown while installing an addon; \"" << e.
what() <<
"\"" << std::endl;
1272 symbols[
"path"] = e.
path;
1276 vgettext(
"A local file with add-on publishing information could not be read.\n\nFile: $path\nError message: $msg", symbols));
1280 LOG_AC <<
"initial connection canceled by user\n";
bool new_widgets
Do we wish to use the new library or not.
void show_error_message(CVideo &video, const std::string &message, bool message_use_markup)
Shows an error message to the user.
void set_campaign_server(const std::string &host)
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
void read_addons_list(const config &cfg, addons_list &dest)
std::string bullet_list(const T &v, size_t indent=4, const std::string &bullet=unicode_bullet)
Generates a new string containing a bullet list.
const SDL_Color TITLE_COLOR
const std::string path
Path to the faulty .pbl file.
int get_index(int selection) const
ADDON_SORT
Add-on fallback/default sorting criteria for the user interface.
const char * what() const
std::string campaign_server()
bool download_addon(config &archive_cfg, const std::string &id, const std::string &title, bool increase_downloads=true)
Downloads the specified add-on from the server.
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
bool show(CVideo &video, const unsigned auto_close_time=0)
Shows the window.
static void display(const std::string &addon_id, const addons_list &addons_list, const addons_tracking_list &addon_states, CVideo &video)
The display function.
Exception thrown when the WML parser fails to read a .pbl file.
std::string size_display_string(double size)
Get a human-readable representation of the specified byte count.
void truncate_as_ucs4(utf8::string &str, const size_t size)
Truncates a UTF-8 string to the specified number of characters.
int relative_size(int size)
bool is_addon_installed(const std::string &addon_name)
Check whether the specified add-on is currently installed.
ADDON_STATUS
Defines various add-on installation statuses.
void show(CVideo &video)
Shows the error in a dialog.
utf8::string lowercase(const utf8::string &s)
Returns a lowercased version of the string.
void get_addon_install_info(const std::string &addon_name, config &cfg)
Gets the installation info (_info.cfg) for an add-on.
std::string word_wrap_text(const std::string &unwrapped_text, int font_size, int max_width, int max_height, int max_lines, bool partial_line)
Wrap text.
void set_text(const std::string &text, const SDL_Color &color=font::NORMAL_COLOR)
bool manage_addons(CVideo &v)
Shows the add-ons server connection dialog, for access to the various management front-ends.
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 install_addon(config &archive_cfg, const addon_info &info)
Installs the specified add-on using an archive received from the server.
const std::string text() const
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.
GLsizei GLenum GLenum * types
No tracking information available.
void set_displayed_status(ADDON_STATUS_FILTER status)
static lg::log_domain log_network("network")
std::vector< std::string > available_addons()
Returns a list of local add-ons that can be published.
GLdouble GLdouble GLdouble b
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.
gui::filter_textbox & filter_
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
ADDON_SORT_DIRECTION
Add-on fallback/default sorting direction.
void ellipsis_truncate(std::string &str, const size_t size)
Truncates a string to a given utf-8 character count and then appends an ellipsis. ...
const std::string & get_last_server_error() const
Returns the last error message sent by the server, or an empty string.
static UNUSEDNOWARN std::string _(const char *str)
Version in the server is older than local installation.
Dialog is closed with ok button.
std::map< std::string, t_string > string_map
bool is_installed_addon_status(ADDON_STATUS s)
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 ...
ADDON_STATUS_FILTER
Add-on installation status filters for the user interface.
std::string display_title() const
Get a title or automatic title for display.
const std::string message
Error message to display.
std::map< std::string, addon_info > addons_list
void show_message(CVideo &video, const std::string &title, const std::string &message, const std::string &button_caption, const bool auto_close, const bool message_use_markup)
Shows a message to the user.
Version in the server is newer than local installation.
GLboolean GLboolean GLboolean GLboolean a
static UNUSEDNOWARN std::string _n(const char *str1, const char *str2, int n)
Shows an ok and cancel button.
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
void refresh_addon_version_info_cache()
Refreshes the per-session cache of add-on's version information structs.
bool remove_local_addon(const std::string &addon)
void connect()
Try to establish a connection to the add-ons server.
Dependencies not satisfied.
ADDON_SORT_DIRECTION direction() const
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
Add-ons (campaignd) client class.
std::string str() const
Serializes the version number into string form.
std::string color2markup(const SDL_Color &color)
Create string of color-markup, such as "<255,255,0>" for yellow.
std::string display_icon() const
Get an icon path fixed for display (e.g.
void set_direction(ADDON_SORT_DIRECTION direction)
static lg::log_domain log_addons_client("addons-client")
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.
std::string display_type() const
Get an add-on type identifier for display in the user's language.
An exception object used when an IO error occurs.
Declarations for File-IO.
static int sort(lua_State *L)
Represents version numbers.
Sort by last upload time.
GLuint const GLchar * name
void set_sort(ADDON_SORT sort)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
static lg::log_domain log_filesystem("filesystem")
GLenum GLenum GLvoid * row
char const HEADING_PREFIX
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
void get_addon_pbl_info(const std::string &addon_name, config &cfg)
Gets the publish information for an add-on.
char const COLUMN_SEPARATOR
addon_tracking_info get_addon_tracking_info(const addon_info &addon)
Get information about an add-on comparing its local state with the add-ons server entry...
bool find(E event, F functor)
Tests whether an event handler is available.
static const int max_menu_width
std::map< std::string, addon_tracking_info > addons_tracking_list
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
const SDL_Color GRAY_COLOR
bool upload_addon(const std::string &id, std::string &response_message, config &cfg)
Requests the specified add-on to be uploaded.
Standard logging facilities (interface).
Shows the list of addons on the server.
std::vector< bool > displayed_types() const
Stores additional status information about add-ons.
const SDL_Color YELLOW_COLOR
A config object defines a single node in a WML file, with access to child nodes.
Helper class, don't construct this directly.
bool delete_remote_addon(const std::string &id, std::string &response_message)
Requests the specified add-on to be removed from the server.
bool request_distribution_terms(std::string &terms)
Request the add-ons server distribution terms message.
GLsizei const GLcharARB ** string
void set_displayed_types(const std::vector< bool > &types)
std::set< std::string > resolve_dependencies(const addons_list &addons) const
Resolve an add-on's dependency tree in a recursive fashion.
ADDON_STATUS_FILTER displayed_status() const
bool request_addons_list(config &cfg)
Request the add-ons list from the server.
Shows a yes and no button.
const std::string unicode_bullet
Version in the server matches local installation.
static lg::log_domain log_config("config")