Remove cloud deletion of owned plugins from the plugin dialog (#14946)

Owned ("Mine") cloud plugins now offer the same local-only Delete as local
plugins: it removes the installed package and leaves the plugin in the cloud,
still reinstallable. Deleting a plugin from the cloud belongs on the plugin hub
and is no longer reachable from OrcaSlicer, so the whole cloud-delete chain is
removed down to the REST binding.

The deleted row is restored locally instead of via a blocking cloud refetch, so
it survives being offline, and it comes back without the deleted package's error
state.
This commit is contained in:
SoftFever
2026-07-25 22:31:40 +08:00
committed by GitHub
parent 9a72dda79a
commit a62fb17e03
8 changed files with 20 additions and 161 deletions

View File

@@ -138,6 +138,9 @@ PluginDescriptor as_cloud_only_descriptor(PluginDescriptor descriptor)
descriptor.plugin_root.clear();
descriptor.entry_path.clear();
descriptor.installed_version.clear();
// No local package is left behind, so the package verdict and any load error it produced go with it.
descriptor.metadata_valid = false;
descriptor.clear_error();
if (descriptor.cloud.has_value()) {
descriptor.cloud->installed = false;
descriptor.cloud->update_available = false;
@@ -289,11 +292,10 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
available_actions.context_actions.push_back(PluginContextAction{id, label, enabled, danger});
};
if (is_cloud) {
if (is_mine)
add_action("delete_mine_plugin", "Delete", true, true);
else
add_action("unsubscribe_plugin", "Unsubscribe", true, true);
// Owned cloud plugins fall through to the local delete: it removes the installed package only.
// Deleting a plugin from the cloud is a plugin hub operation and is never offered here.
if (is_cloud && !is_mine) {
add_action("unsubscribe_plugin", "Unsubscribe", true, true);
} else if (has_local) {
add_action("delete_plugin", "Delete", true, true);
}
@@ -742,8 +744,6 @@ void PluginsDialog::handle_plugin_menu_action(const std::string& plugin_key, con
delete_local_plugin(row_data);
} else if (action == "unsubscribe_plugin") {
unsubscribe_cloud_plugin(row_data);
} else if (action == "delete_mine_plugin") {
delete_mine_local_and_cloud_plugin(plugin_key);
} else if (action == "reload_plugin") {
reload_local_plugin(plugin_key, /*clear_cache=*/false);
} else if (action == "clear_cache_reload_plugin") {
@@ -1071,19 +1071,25 @@ void PluginsDialog::open_plugin_hub()
void PluginsDialog::delete_local_plugin(const PluginDescriptor& plugin)
{
const wxString plugin_name = from_u8(plugin.name);
const int rc = wxMessageBox(wxString::Format(_L("Delete plugin \"%s\"?\n\nThis permanently removes the plugin folder."), plugin_name),
kDeletePluginTitle, wxYES_NO | wxNO_DEFAULT | wxICON_WARNING, this);
const int rc = wxMessageBox(
wxString::Format(plugin.is_cloud_plugin() ?
_L("Delete plugin \"%s\"?\n\nThis removes the local plugin files. The plugin stays in the cloud "
"and can be reinstalled.") :
_L("Delete plugin \"%s\"?\n\nThis permanently removes the plugin folder."),
plugin_name),
kDeletePluginTitle, wxYES_NO | wxNO_DEFAULT | wxICON_WARNING, this);
restore_z_order();
if (rc != wxYES)
return;
auto state = std::make_shared<PluginOperationState>();
run_with_dialog(
[plugin_key = plugin.plugin_key, should_refresh = plugin.is_cloud_plugin(), state]() {
[plugin_key = plugin.plugin_key, cloud_row = as_cloud_only_descriptor(plugin), state]() {
std::string error;
const bool succeeded = PluginManager::instance().delete_plugin(plugin_key, error);
if (succeeded && should_refresh)
refresh_plugin_metadata_blocking(kFetchCloudMeta);
// Keep the cloud row listed so the plugin stays reinstallable without waiting on a cloud fetch.
if (succeeded && cloud_row.is_cloud_plugin())
PluginManager::instance().update_cloud_metadata(std::vector<PluginDescriptor>{cloud_row});
store_plugin_operation_result(state, succeeded, std::move(error));
},
[this, state, plugin_name]() {
@@ -1249,28 +1255,4 @@ void PluginsDialog::reinstall_cloud_plugin(const PluginDescriptor& plugin)
show_status(wxString::Format(_L("Reloaded \"%s\"."), plugin_display_name(plugin_key)), "success");
}
void PluginsDialog::delete_mine_local_and_cloud_plugin(const std::string& plugin_key)
{
PluginDescriptor descriptor;
const std::string display = get_descriptor(plugin_key, descriptor) ? descriptor.name : std::string{};
const wxString plugin_name = from_u8(display.empty() ? plugin_key : display);
const int rc = wxMessageBox(
wxString::Format(_L("Delete plugin \"%s\" from local and cloud?\n\nThis permanently removes the local plugin files and "
"deletes the plugin from the cloud. This action cannot be undone."),
plugin_name),
kDeletePluginTitle, wxYES_NO | wxNO_DEFAULT | wxICON_WARNING, this);
restore_z_order();
if (rc != wxYES)
return;
std::string error;
if (!PluginManager::instance().delete_mine_local_and_cloud_plugin(plugin_key, error)) {
show_status(error.empty() ? _L("Failed to delete plugin from local and cloud.") : from_u8(error), "error");
return;
}
send_plugins();
show_status(wxString::Format(_L("Deleted \"%s\"."), plugin_name), "success");
}
}} // namespace Slic3r::GUI

View File

@@ -98,7 +98,6 @@ private:
void unsubscribe_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
void reload_local_plugin(const std::string& plugin_key, bool clear_cache);
void reinstall_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
void delete_mine_local_and_cloud_plugin(const std::string& plugin_key);
// In the future, we can allow users to choose which plugin version they want to install.
template<typename Run, typename OnFinish>

View File

@@ -3505,25 +3505,6 @@ int OrcaCloudServiceAgent::unsubscribe_plugins(const std::vector<std::string>& p
return 0;
}
int OrcaCloudServiceAgent::delete_my_plugin(const std::string& plugin_uuid)
{
if (plugin_uuid.empty())
return -1;
const std::string path = std::string(ORCA_PLUGINS_BASE) + "?ids=" + Http::url_encode(plugin_uuid);
std::string response_body;
unsigned int http_code = 0;
int result = http_delete(path, &response_body, &http_code);
if (result != 0 || (http_code != 200 && http_code != 204)) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed: http_code=" << http_code << ", response=" << response_body;
return http_code != 0 ? static_cast<int>(http_code) : result;
}
return 0;
}
int OrcaCloudServiceAgent::fetch_plugin_changelogs(const std::vector<std::string>& uuids,
std::unordered_map<std::string, std::vector<PluginChangelog>>& changelog)
{

View File

@@ -311,7 +311,6 @@ public:
std::string get_plugin_url(const std::string& sharing_token) const;
int subscribe_plugin(const std::string& plugin_uuid);
int unsubscribe_plugins(const std::vector<std::string>& plugin_uuids);
int delete_my_plugin(const std::string& plugin_uuid);
int fetch_plugin_changelogs(const std::vector<std::string>& uuids, std::unordered_map<std::string, std::vector<PluginChangelog>>& changelog);
// ========================================================================

View File

@@ -113,39 +113,6 @@ bool CloudPluginService::request_cloud_unsubscribe(const PluginDescriptor& plugi
return true;
}
bool CloudPluginService::request_cloud_delete(const PluginDescriptor& plugin, std::string& error) const
{
if (!m_orca_agent) {
error = "No cloud agent.";
return false;
}
if (!plugin.is_cloud_plugin()) {
error = "Only cloud plugins can be deleted.";
return false;
}
const std::string cloud_uuid = plugin.cloud_uuid();
if (cloud_uuid.empty()) {
error = "Cloud plugin key is missing UUID.";
return false;
}
if (!plugin.cloud.has_value() || !plugin.cloud->is_mine) {
error = "Only your own plugins can be deleted from the cloud.";
return false;
}
int result = m_orca_agent->delete_my_plugin(cloud_uuid);
if (result != 0) {
error = "Failed to delete plugin from cloud, see logs for more info.";
return false;
}
return true;
}
bool CloudPluginService::download_cloud_plugin(PluginDescriptor& entry,
const std::string& requested_version,
CloudPluginDownload& download,

View File

@@ -29,7 +29,6 @@ public:
std::vector<std::string>& unauthorized) const;
bool request_cloud_subscribe(const std::string& plugin_uuid, std::string& error) const;
bool request_cloud_unsubscribe(const PluginDescriptor& plugin, std::string& error) const;
bool request_cloud_delete(const PluginDescriptor& plugin, std::string& error) const;
bool download_cloud_plugin(PluginDescriptor& entry,
const std::string& requested_version,
CloudPluginDownload& download,

View File

@@ -1857,7 +1857,7 @@ bool PluginManager::unsubscribe_cloud_plugin(const std::string& plugin_key, std:
}
if (descriptor.cloud && descriptor.cloud->is_mine) {
error = "Cannot unsubscribe your own plugins. Use Delete from Cloud instead.";
error = "Cannot unsubscribe your own plugins.";
set_plugin_error(plugin_key, error);
return false;
}
@@ -1890,7 +1890,7 @@ bool PluginManager::delete_and_unsubscribe_cloud_plugin(const std::string& plugi
}
if (descriptor.cloud->is_mine) {
error = "Use Delete local and cloud for owned plugins.";
error = "Cannot unsubscribe your own plugins. Use Delete to remove the local files.";
set_plugin_error(plugin_key, error);
return false;
}
@@ -1903,72 +1903,6 @@ bool PluginManager::delete_and_unsubscribe_cloud_plugin(const std::string& plugi
return finalize_cloud_plugin_removal(descriptor, false, error);
}
bool PluginManager::delete_mine_plugin_from_cloud(const std::string& plugin_key, std::string& error)
{
if (!wait_for_discovery(std::chrono::milliseconds::max(), error))
return false;
error.clear();
PluginDescriptor descriptor;
if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
error = "Plugin not found: " + plugin_key;
return false;
}
if (!descriptor.is_cloud_plugin()) {
error = "Only owned cloud plugins can be deleted from the cloud.";
set_plugin_error(plugin_key, error);
return false;
}
if (!descriptor.cloud->is_mine) {
error = "Only your own plugins can be deleted from the cloud.";
set_plugin_error(plugin_key, error);
return false;
}
if (!m_cloud_service.request_cloud_delete(descriptor, error)) {
set_plugin_error(plugin_key, error);
return false;
}
return finalize_cloud_plugin_removal(descriptor, true, error);
}
bool PluginManager::delete_mine_local_and_cloud_plugin(const std::string& plugin_key, std::string& error)
{
if (!wait_for_discovery(std::chrono::milliseconds::max(), error))
return false;
error.clear();
PluginDescriptor descriptor;
if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
error = "Plugin not found: " + plugin_key;
return false;
}
if (!descriptor.is_cloud_plugin()) {
error = "Only owned cloud plugins can be deleted from local and cloud.";
set_plugin_error(plugin_key, error);
return false;
}
if (!descriptor.cloud->is_mine) {
error = "Only your own plugins can be deleted from local and cloud.";
set_plugin_error(plugin_key, error);
return false;
}
if (!m_cloud_service.request_cloud_delete(descriptor, error)) {
set_plugin_error(plugin_key, error);
return false;
}
return finalize_cloud_plugin_removal(descriptor, false, error);
}
ExecutionResult PluginManager::run_script_capability(const std::string& plugin_key, const std::string& capability_name, std::string& error)
{
if (plugin_key.empty() || capability_name.empty()) {

View File

@@ -196,8 +196,6 @@ public:
bool delete_plugin(const std::string& plugin_key, std::string& error);
bool unsubscribe_cloud_plugin(const std::string& plugin_key, std::string& error);
bool delete_and_unsubscribe_cloud_plugin(const std::string& plugin_key, std::string& error);
bool delete_mine_plugin_from_cloud(const std::string& plugin_key, std::string& error);
bool delete_mine_local_and_cloud_plugin(const std::string& plugin_key, std::string& error);
ExecutionResult run_script_capability(const std::string& plugin_key, const std::string& capability_name, std::string& error);