mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-03 16:22:08 +00:00
fix: keep orphaned cloud plugins runnable (#14859)
* fix: keep orphaned cloud plugins runnable * fix: tests
This commit is contained in:
@@ -11,6 +11,7 @@ namespace Slic3r
|
||||
// IMPORTANT: ordinal order is the Plugins dialog Source sort priority.
|
||||
Mine,
|
||||
Subscribed,
|
||||
Orphaned,
|
||||
Local
|
||||
};
|
||||
|
||||
@@ -20,6 +21,7 @@ namespace Slic3r
|
||||
{
|
||||
case PluginSource::Mine: return "mine";
|
||||
case PluginSource::Subscribed: return "subscribed";
|
||||
case PluginSource::Orphaned: return "orphaned";
|
||||
case PluginSource::Local: return "local";
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ struct PluginDialogItem
|
||||
bool loading = false;
|
||||
|
||||
bool is_cloud_plugin = false;
|
||||
bool orphaned = false;
|
||||
bool has_local_package = false;
|
||||
bool unauthorized = false;
|
||||
bool has_script_capability = false;
|
||||
@@ -246,6 +247,7 @@ nlohmann::json build_plugin_payload_item(const PluginDialogItem& dialog_item)
|
||||
payload_item["sharing_token"] = dialog_item.sharing_token;
|
||||
payload_item["thumbnail_url"] = dialog_item.thumbnail_url;
|
||||
payload_item["installed"] = dialog_item.has_local_package;
|
||||
payload_item["orphaned"] = dialog_item.orphaned;
|
||||
payload_item["installed_version"] = dialog_item.installed_version;
|
||||
payload_item["latest_version"] = dialog_item.latest_version;
|
||||
return payload_item;
|
||||
@@ -268,7 +270,8 @@ PluginSource derive_plugin_source(const PluginDescriptor& descriptor)
|
||||
const bool is_cloud = descriptor.is_cloud_plugin();
|
||||
const bool is_mine = is_cloud && has_cloud_meta && descriptor.cloud->is_mine;
|
||||
|
||||
// Source is ownership/locality only; issue states never replace this badge.
|
||||
if (is_cloud && has_cloud_meta && descriptor.cloud->orphaned)
|
||||
return PluginSource::Orphaned;
|
||||
if (is_mine)
|
||||
return PluginSource::Mine;
|
||||
if (is_cloud)
|
||||
@@ -281,11 +284,12 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
|
||||
PluginAvailableActions available_actions;
|
||||
const bool is_loading = item.status == PluginStatus::Loading;
|
||||
const bool is_cloud = item.is_cloud_plugin;
|
||||
const bool is_orphaned = item.orphaned;
|
||||
const bool is_mine = item.source == PluginSource::Mine;
|
||||
const bool has_local = item.has_local_package;
|
||||
const bool authorized_for_install = !item.unauthorized;
|
||||
|
||||
available_actions.toggle_installs_cloud_plugin = is_cloud && !has_local && authorized_for_install;
|
||||
available_actions.toggle_installs_cloud_plugin = is_cloud && !is_orphaned && !has_local && authorized_for_install;
|
||||
available_actions.can_toggle = !is_loading && (has_local || available_actions.toggle_installs_cloud_plugin);
|
||||
|
||||
auto add_action = [&available_actions](const char* id, const char* label, bool enabled = true, bool danger = false) {
|
||||
@@ -294,7 +298,7 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
|
||||
|
||||
// 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) {
|
||||
if (is_cloud && !is_orphaned && !is_mine) {
|
||||
add_action("unsubscribe_plugin", "Unsubscribe", true, true);
|
||||
} else if (has_local) {
|
||||
add_action("delete_plugin", "Delete", true, true);
|
||||
@@ -302,11 +306,13 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
|
||||
|
||||
add_action("open_folder", "Show in folder", has_local);
|
||||
|
||||
if (is_cloud) {
|
||||
add_action("reinstall_plugin", "Reinstall");
|
||||
} else {
|
||||
add_action("reload_plugin", "Reload");
|
||||
add_action("clear_cache_reload_plugin", "Delete cache and reload");
|
||||
if (!is_orphaned) {
|
||||
if (is_cloud) {
|
||||
add_action("reinstall_plugin", "Reinstall");
|
||||
} else {
|
||||
add_action("reload_plugin", "Reload");
|
||||
add_action("clear_cache_reload_plugin", "Delete cache and reload");
|
||||
}
|
||||
}
|
||||
|
||||
return available_actions;
|
||||
@@ -360,6 +366,7 @@ PluginDialogItem build_plugin_dialog_item(const PluginDescriptor& descriptor)
|
||||
item.error_text = descriptor.normalized_error();
|
||||
item.has_error = descriptor.has_error();
|
||||
item.is_cloud_plugin = descriptor.is_cloud_plugin();
|
||||
item.orphaned = descriptor.cloud.has_value() && descriptor.cloud->orphaned;
|
||||
item.has_local_package = descriptor.has_local_package();
|
||||
item.unauthorized = descriptor.is_unauthorized();
|
||||
item.is_loaded = manager.is_plugin_loaded(descriptor.plugin_key);
|
||||
|
||||
@@ -20,6 +20,7 @@ struct CloudPluginState
|
||||
bool update_available = false; // Cloud version > the local package version.
|
||||
bool unauthorized = false; // Cloud plugin is valid locally, but cannot receive cloud updates.
|
||||
bool is_mine = false; // Plugin was created (and uploaded) by the current user.
|
||||
bool orphaned = false; // Cloud identity remains locally, but the plugin is no longer subscribed/available.
|
||||
};
|
||||
|
||||
enum class PluginUpdateStatus
|
||||
@@ -106,6 +107,8 @@ struct PluginDescriptor
|
||||
{
|
||||
if (!cloud.has_value())
|
||||
return PluginUpdateStatus::Normal;
|
||||
if (cloud->orphaned)
|
||||
return PluginUpdateStatus::Normal;
|
||||
if (cloud->unauthorized)
|
||||
return PluginUpdateStatus::Unauthorized;
|
||||
if (cloud->update_available)
|
||||
|
||||
@@ -1422,7 +1422,8 @@ void PluginManager::fetch_plugins_from_cloud(std::vector<std::string>* out_not_f
|
||||
|
||||
std::vector<PluginDescriptor> cloud_list{};
|
||||
std::vector<std::string> not_found{}, unauthorized{};
|
||||
if (!m_cloud_service.fetch_manifests_into_descriptors(cloud_list, not_found, unauthorized)) {
|
||||
const bool cloud_fetch_succeeded = m_cloud_service.fetch_manifests_into_descriptors(cloud_list, not_found, unauthorized);
|
||||
if (!cloud_fetch_succeeded) {
|
||||
if (wxTheApp != nullptr) {
|
||||
GUI::wxGetApp().CallAfter([] {
|
||||
if (GUI::wxGetApp().is_closing())
|
||||
@@ -1437,9 +1438,10 @@ void PluginManager::fetch_plugins_from_cloud(std::vector<std::string>* out_not_f
|
||||
}
|
||||
}
|
||||
|
||||
update_cloud_metadata(cloud_list);
|
||||
if (cloud_fetch_succeeded)
|
||||
update_cloud_metadata(cloud_list);
|
||||
|
||||
{
|
||||
if (cloud_fetch_succeeded) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
// Clear the previous cloud verdicts before re-applying the fresh ones.
|
||||
@@ -1448,19 +1450,28 @@ void PluginManager::fetch_plugins_from_cloud(std::vector<std::string>* out_not_f
|
||||
if (!entry.is_cloud_plugin())
|
||||
continue;
|
||||
entry.set_unauthorized(false);
|
||||
if (entry.cloud.has_value())
|
||||
entry.cloud->orphaned = false;
|
||||
if (entry.normalized_error() == CLOUD_PLUGIN_NOT_FOUND_ERROR)
|
||||
entry.clear_error();
|
||||
}
|
||||
|
||||
for (const std::string& uuid : not_found) {
|
||||
for (Plugin& plugin : m_plugins) {
|
||||
PluginDescriptor& entry = plugin.descriptor;
|
||||
if (!entry.is_cloud_plugin() || entry.cloud_uuid() != uuid)
|
||||
continue;
|
||||
if (!entry.has_local_package())
|
||||
entry.set_error(CLOUD_PLUGIN_NOT_FOUND_ERROR);
|
||||
break;
|
||||
}
|
||||
// A successful subscriptions response may report missing UUIDs explicitly, or it may
|
||||
// simply omit an unsubscribed plugin from `data`. Both cases leave a locally retained
|
||||
// cloud package orphaned. Owned plugins are returned by the separate mine endpoint and
|
||||
// must not be orphaned merely because they are not subscribed.
|
||||
for (Plugin& plugin : m_plugins) {
|
||||
PluginDescriptor& entry = plugin.descriptor;
|
||||
if (!entry.is_cloud_plugin() || entry.cloud->is_mine)
|
||||
continue;
|
||||
|
||||
const bool explicitly_not_found = std::find(not_found.begin(), not_found.end(), entry.cloud_uuid()) != not_found.end();
|
||||
const bool returned_by_cloud = std::any_of(cloud_list.begin(), cloud_list.end(), [&entry](const PluginDescriptor& cloud_entry) {
|
||||
return cloud_entry.cloud_uuid() == entry.cloud_uuid();
|
||||
});
|
||||
entry.cloud->orphaned = explicitly_not_found || !returned_by_cloud;
|
||||
if (entry.cloud->orphaned)
|
||||
entry.cloud->update_available = false;
|
||||
}
|
||||
|
||||
for (const std::string& uuid : unauthorized) {
|
||||
|
||||
Reference in New Issue
Block a user