mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-23 02:42:08 +00:00
Compare commits
6 Commits
nightly-bu
...
fix/plugin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc6ae243ca | ||
|
|
b121051220 | ||
|
|
242e100feb | ||
|
|
2a46bd1384 | ||
|
|
ddbbc7f7f2 | ||
|
|
3a48086f5d |
@@ -48,13 +48,14 @@ std::string plugin_defaults_user_script()
|
|||||||
return WebViewHostDialog::document_start_injector(css, "orca-plugin-defaults", "beforeend");
|
return WebViewHostDialog::document_start_injector(css, "orca-plugin-defaults", "beforeend");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Injected into every page at document start (before the plugin's own scripts).
|
// Injected into the top-level page at document start (before the plugin's own
|
||||||
// Defines window.orca as the only host surface the page may use. It references
|
// scripts). Defines window.orca as the only host surface the page may use. It
|
||||||
// window.wx lazily (at call time) so it never races the backend's deferred
|
// references window.wx lazily (at call time) so it never races the backend's
|
||||||
// registration of the "wx" message handler. Guarded against double-injection so
|
// deferred registration of the "wx" message handler. Guarded against
|
||||||
// it is harmless if also prepended.
|
// double-injection so it is harmless if also prepended.
|
||||||
constexpr char ORCA_BRIDGE_JS[] = R"JS(
|
constexpr char ORCA_BRIDGE_JS[] = R"JS(
|
||||||
(function () {
|
(function () {
|
||||||
|
if (window.top !== window.self) return;
|
||||||
if (window.orca) return;
|
if (window.orca) return;
|
||||||
var handlers = [];
|
var handlers = [];
|
||||||
function send(kind, data) {
|
function send(kind, data) {
|
||||||
|
|||||||
@@ -300,7 +300,12 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
|
|||||||
|
|
||||||
add_action("open_folder", "Show in folder", has_local);
|
add_action("open_folder", "Show in folder", has_local);
|
||||||
|
|
||||||
add_action("reinstall_plugin", "Reinstall");
|
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;
|
return available_actions;
|
||||||
}
|
}
|
||||||
@@ -739,11 +744,13 @@ void PluginsDialog::handle_plugin_menu_action(const std::string& plugin_key, con
|
|||||||
unsubscribe_cloud_plugin(row_data);
|
unsubscribe_cloud_plugin(row_data);
|
||||||
} else if (action == "delete_mine_plugin") {
|
} else if (action == "delete_mine_plugin") {
|
||||||
delete_mine_local_and_cloud_plugin(plugin_key);
|
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") {
|
||||||
|
reload_local_plugin(plugin_key, /*clear_cache=*/true);
|
||||||
} else if (action == "reinstall_plugin") {
|
} else if (action == "reinstall_plugin") {
|
||||||
if (row_data.is_cloud_plugin())
|
if (row_data.is_cloud_plugin())
|
||||||
reinstall_cloud_plugin(row_data);
|
reinstall_cloud_plugin(row_data);
|
||||||
else
|
|
||||||
reinstall_local_plugin(plugin_key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1123,7 +1130,7 @@ void PluginsDialog::unsubscribe_cloud_plugin(const PluginDescriptor& plugin)
|
|||||||
_L("Unsubscribing plugin"), _L("Deleting local files and unsubscribing plugin..."));
|
_L("Unsubscribing plugin"), _L("Deleting local files and unsubscribing plugin..."));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginsDialog::reinstall_local_plugin(const std::string& plugin_key)
|
void PluginsDialog::reload_local_plugin(const std::string& plugin_key, bool clear_cache)
|
||||||
{
|
{
|
||||||
if (plugin_key.empty())
|
if (plugin_key.empty())
|
||||||
return;
|
return;
|
||||||
@@ -1132,11 +1139,34 @@ void PluginsDialog::reinstall_local_plugin(const std::string& plugin_key)
|
|||||||
std::pair<bool, std::string> reload_result{false, ""};
|
std::pair<bool, std::string> reload_result{false, ""};
|
||||||
try {
|
try {
|
||||||
reload_result = run_with_dialog_wait(
|
reload_result = run_with_dialog_wait(
|
||||||
[plugin_key, was_loaded]() -> std::pair<bool, std::string> {
|
[plugin_key, was_loaded, clear_cache]() -> std::pair<bool, std::string> {
|
||||||
PluginManager& manager = PluginManager::instance();
|
PluginManager& manager = PluginManager::instance();
|
||||||
|
|
||||||
|
boost::filesystem::path cache_dir;
|
||||||
|
if (clear_cache) {
|
||||||
|
PluginDescriptor descriptor;
|
||||||
|
if (!manager.try_get_plugin_descriptor(plugin_key, descriptor))
|
||||||
|
return {false, "Plugin not found."};
|
||||||
|
|
||||||
|
boost::filesystem::path resolved_root;
|
||||||
|
std::string resolve_error;
|
||||||
|
if (!resolve_allowed_plugin_root(descriptor, {get_orca_plugins_dir()},
|
||||||
|
"Refusing to clear a plugin cache outside the local plugin directory.",
|
||||||
|
resolved_root, resolve_error))
|
||||||
|
return {false, resolve_error};
|
||||||
|
cache_dir = resolved_root / "__whl_extracted__";
|
||||||
|
}
|
||||||
|
|
||||||
if (!manager.unload_plugin(plugin_key))
|
if (!manager.unload_plugin(plugin_key))
|
||||||
return {false, "Failed to unload plugin."};
|
return {false, "Failed to unload plugin."};
|
||||||
|
|
||||||
|
if (clear_cache) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::filesystem::remove_all(cache_dir, ec);
|
||||||
|
if (ec)
|
||||||
|
return {false, "Failed to clear plugin cache: " + ec.message()};
|
||||||
|
}
|
||||||
|
|
||||||
manager.load_plugin(plugin_key, false);
|
manager.load_plugin(plugin_key, false);
|
||||||
std::string error;
|
std::string error;
|
||||||
if (!manager.wait_for_plugin_load(plugin_key, std::chrono::minutes(5), error) || !manager.is_plugin_loaded(plugin_key))
|
if (!manager.wait_for_plugin_load(plugin_key, std::chrono::minutes(5), error) || !manager.is_plugin_loaded(plugin_key))
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ private:
|
|||||||
void open_plugin_folder(const Slic3r::PluginDescriptor& plugin);
|
void open_plugin_folder(const Slic3r::PluginDescriptor& plugin);
|
||||||
void delete_local_plugin(const Slic3r::PluginDescriptor& plugin);
|
void delete_local_plugin(const Slic3r::PluginDescriptor& plugin);
|
||||||
void unsubscribe_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
|
void unsubscribe_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
|
||||||
void reinstall_local_plugin(const std::string& plugin_key);
|
void reload_local_plugin(const std::string& plugin_key, bool clear_cache);
|
||||||
void reinstall_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
|
void reinstall_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
|
||||||
void delete_mine_local_and_cloud_plugin(const std::string& plugin_key);
|
void delete_mine_local_and_cloud_plugin(const std::string& plugin_key);
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ std::string WebViewHostDialog::document_start_injector(const std::string& markup
|
|||||||
const std::string literal = nlohmann::json(markup).dump();
|
const std::string literal = nlohmann::json(markup).dump();
|
||||||
std::string s;
|
std::string s;
|
||||||
s += "(function(){";
|
s += "(function(){";
|
||||||
|
// wxWebView's AddUserScript runs in child frames too (including cross-origin
|
||||||
|
// frames on WebView2). Host theme state belongs only to the top-level page.
|
||||||
|
s += "if(window.top!==window.self)return;";
|
||||||
s += prelude;
|
s += prelude;
|
||||||
s += "var css=" + literal + ";";
|
s += "var css=" + literal + ";";
|
||||||
s += "function inject(){";
|
s += "function inject(){";
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ struct PluginDescriptor
|
|||||||
std::string description; // Plugin description
|
std::string description; // Plugin description
|
||||||
std::string author; // Plugin author from manifest, if available
|
std::string author; // Plugin author from manifest, if available
|
||||||
std::string version; // Selected plugin version
|
std::string version; // Selected plugin version
|
||||||
std::string latest_version; // Latest available cloud version fallback when changelog is unavailable.
|
std::string latest_version; // Authoritative latest available cloud version.
|
||||||
std::string installed_version; // Locally installed package version. Preserved across cloud merges, which overwrite `version` with the latest cloud version. Empty when not installed.
|
std::string installed_version; // Locally installed package version. Preserved across cloud merges, which overwrite `version` with the latest cloud version. Empty when not installed.
|
||||||
std::vector<std::string> display_types; // Display-only "compatibility" labels (cloud: raw service labels; local: from real capabilities). Never used for dispatch.
|
std::vector<std::string> display_types; // Display-only "compatibility" labels (cloud: raw service labels; local: from real capabilities). Never used for dispatch.
|
||||||
std::string plugin_root; // Installed plugin directory, even when entry_path is invalid or ambiguous.
|
std::string plugin_root; // Installed plugin directory, even when entry_path is invalid or ambiguous.
|
||||||
@@ -113,10 +113,6 @@ struct PluginDescriptor
|
|||||||
bool is_unauthorized() const { return get_update_status() == PluginUpdateStatus::Unauthorized; }
|
bool is_unauthorized() const { return get_update_status() == PluginUpdateStatus::Unauthorized; }
|
||||||
std::string latest_available_version() const
|
std::string latest_available_version() const
|
||||||
{
|
{
|
||||||
for (const PluginChangelog& entry : changelog) {
|
|
||||||
if (!entry.version.empty())
|
|
||||||
return entry.version;
|
|
||||||
}
|
|
||||||
if (!latest_version.empty())
|
if (!latest_version.empty())
|
||||||
return latest_version;
|
return latest_version;
|
||||||
return version;
|
return version;
|
||||||
|
|||||||
@@ -52,6 +52,29 @@ print('ok')
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("plugin latest version uses the authoritative catalog field", "[PluginDescriptor]")
|
||||||
|
{
|
||||||
|
PluginDescriptor descriptor;
|
||||||
|
descriptor.version = "1.3.0";
|
||||||
|
descriptor.latest_version = "1.3.0";
|
||||||
|
PluginChangelog changelog;
|
||||||
|
changelog.version = "1.2.0";
|
||||||
|
descriptor.changelog.push_back(changelog);
|
||||||
|
|
||||||
|
CHECK(descriptor.latest_available_version() == "1.3.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("plugin latest version falls back to the descriptor version", "[PluginDescriptor]")
|
||||||
|
{
|
||||||
|
PluginDescriptor descriptor;
|
||||||
|
descriptor.version = "1.1.0";
|
||||||
|
PluginChangelog changelog;
|
||||||
|
changelog.version = "1.0.0";
|
||||||
|
descriptor.changelog.push_back(changelog);
|
||||||
|
|
||||||
|
CHECK(descriptor.latest_available_version() == "1.1.0");
|
||||||
|
}
|
||||||
|
|
||||||
// Regression: update_cloud_metadata() replaces a matched entry's descriptor wholesale with the
|
// Regression: update_cloud_metadata() replaces a matched entry's descriptor wholesale with the
|
||||||
// cloud catalog record (`entry = cloud_entry`). Configuration used to ride on the descriptor, so
|
// cloud catalog record (`entry = cloud_entry`). Configuration used to ride on the descriptor, so
|
||||||
// that overwrite silently wiped it and plugins fell back to their built-in defaults (found via
|
// that overwrite silently wiped it and plugins fell back to their built-in defaults (found via
|
||||||
|
|||||||
Reference in New Issue
Block a user