mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 05:27:50 +00:00
Published flag hardening. Better error handling path for when publish fails (unlikely)
This commit is contained in:
@@ -678,6 +678,11 @@ bool bbs_is_valid_object_type(const std::string& type)
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
bool is_published_3mf_flag(const std::string &value)
|
||||||
|
{
|
||||||
|
return value == "1";
|
||||||
|
}
|
||||||
|
|
||||||
void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||||
{
|
{
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
@@ -1224,7 +1229,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
// Reads the parse-time metadata: the model XML carries it before its resources, while
|
// Reads the parse-time metadata: the model XML carries it before its resources, while
|
||||||
// m_model->model_info is only filled in after the whole XML has been parsed.
|
// m_model->model_info is only filled in after the whole XML has been parsed.
|
||||||
bool _is_published_3mf() const {
|
bool _is_published_3mf() const {
|
||||||
return this->model_info.metadata_items.find(ORCA_PUBLISHED_TAG) != this->model_info.metadata_items.end();
|
const auto it = this->model_info.metadata_items.find(ORCA_PUBLISHED_TAG);
|
||||||
|
return it != this->model_info.metadata_items.end() && is_published_3mf_flag(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _is_svg_shape_file(const std::string &filename) const;
|
bool _is_svg_shape_file(const std::string &filename) const;
|
||||||
|
|||||||
@@ -163,6 +163,10 @@ inline constexpr const char *ORCA_PUBLISHED_KEYS_TAG = "orca_published_keys"
|
|||||||
inline constexpr const char *ORCA_PUBLISHED_MATERIAL_TAG = "orca_published_material_keys";
|
inline constexpr const char *ORCA_PUBLISHED_MATERIAL_TAG = "orca_published_material_keys";
|
||||||
inline constexpr const char *ORCA_PUBLISHED_CONFIG_TAG = "orca_published_config";
|
inline constexpr const char *ORCA_PUBLISHED_CONFIG_TAG = "orca_published_config";
|
||||||
|
|
||||||
|
// Published files are produced with "1". The importer and the GUI loader both gate on this
|
||||||
|
// exact value, so a "0"/"false"/unknown value is rejected consistently.
|
||||||
|
bool is_published_3mf_flag(const std::string &value);
|
||||||
|
|
||||||
inline SaveStrategy operator | (SaveStrategy lhs, SaveStrategy rhs)
|
inline SaveStrategy operator | (SaveStrategy lhs, SaveStrategy rhs)
|
||||||
{
|
{
|
||||||
using T = std::underlying_type_t <SaveStrategy>;
|
using T = std::underlying_type_t <SaveStrategy>;
|
||||||
|
|||||||
@@ -6921,8 +6921,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
// whether to import geometry only.
|
// whether to import geometry only.
|
||||||
if (model.model_info != nullptr) {
|
if (model.model_info != nullptr) {
|
||||||
auto published_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_TAG);
|
auto published_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_TAG);
|
||||||
if (published_it != model.model_info->metadata_items.end() &&
|
if (published_it != model.model_info->metadata_items.end() && is_published_3mf_flag(published_it->second)) {
|
||||||
(published_it->second == "true" || published_it->second == "1")) {
|
|
||||||
published_config.published = true;
|
published_config.published = true;
|
||||||
auto keys_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_KEYS_TAG);
|
auto keys_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_KEYS_TAG);
|
||||||
if (keys_it != model.model_info->metadata_items.end()) {
|
if (keys_it != model.model_info->metadata_items.end()) {
|
||||||
@@ -6943,7 +6942,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
auto jm = nlohmann::json::parse(material_keys_it->second);
|
auto jm = nlohmann::json::parse(material_keys_it->second);
|
||||||
if (jm.is_array())
|
if (jm.is_array())
|
||||||
for (const auto &m : jm) {
|
for (const auto &m : jm) {
|
||||||
// Malformed entries are skipped individually.
|
try {
|
||||||
|
// Malformed entries are isolated so one bad item
|
||||||
|
// cannot discard valid entries that follow it.
|
||||||
if (!m.is_object())
|
if (!m.is_object())
|
||||||
continue;
|
continue;
|
||||||
PublishedMaterialEntry entry;
|
PublishedMaterialEntry entry;
|
||||||
@@ -6985,8 +6986,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
if (m.contains("color") && m["color"].is_string())
|
if (m.contains("color") && m["color"].is_string())
|
||||||
entry.color = m["color"].get<std::string>();
|
entry.color = m["color"].get<std::string>();
|
||||||
published_config.material_keys.emplace_back(std::move(entry));
|
published_config.material_keys.emplace_back(std::move(entry));
|
||||||
|
} catch (const nlohmann::json::exception &) {
|
||||||
|
// Ignore only this malformed material entry.
|
||||||
}
|
}
|
||||||
} catch (...) {
|
}
|
||||||
|
} catch (const nlohmann::json::exception &) {
|
||||||
// Ignore malformed published_material_keys; the project still loads normally.
|
// Ignore malformed published_material_keys; the project still loads normally.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16297,37 +16301,21 @@ int Plater::export_published_3mf(const std::vector<std::string>& published_keys,
|
|||||||
const std::string prev_published_keys = had_published_keys ? model.model_info->metadata_items.at(ORCA_PUBLISHED_KEYS_TAG) : std::string();
|
const std::string prev_published_keys = had_published_keys ? model.model_info->metadata_items.at(ORCA_PUBLISHED_KEYS_TAG) : std::string();
|
||||||
const std::string prev_material_keys = had_material_keys ? model.model_info->metadata_items.at(ORCA_PUBLISHED_MATERIAL_TAG) : std::string();
|
const std::string prev_material_keys = had_material_keys ? model.model_info->metadata_items.at(ORCA_PUBLISHED_MATERIAL_TAG) : std::string();
|
||||||
const std::string prev_payload = had_payload ? model.model_info->metadata_items.at(ORCA_PUBLISHED_CONFIG_TAG) : std::string();
|
const std::string prev_payload = had_payload ? model.model_info->metadata_items.at(ORCA_PUBLISHED_CONFIG_TAG) : std::string();
|
||||||
if (model.model_info == nullptr)
|
|
||||||
model.model_info = std::make_shared<ModelInfo>();
|
|
||||||
model.model_info->metadata_items[ORCA_PUBLISHED_TAG] = "1";
|
|
||||||
model.model_info->metadata_items[ORCA_PUBLISHED_KEYS_TAG] = j.dump();
|
|
||||||
model.model_info->metadata_items[ORCA_PUBLISHED_MATERIAL_TAG] = jm.dump();
|
|
||||||
|
|
||||||
// Minimal published export: filter full_config to the published keys, material keys,
|
// export_3mf() assigns archive paths to previously unsaved SVGs. Preserve those fields too,
|
||||||
// identity fields and plate geometry keys, and omit the project config file, the
|
// otherwise a publish changes what a later normal project save writes.
|
||||||
// project-embedded preset dumps and the OrcaSlicer version tag from the archive. The
|
std::vector<std::pair<std::string *, std::string>> previous_svg_paths;
|
||||||
// filtered values are serialized into the published_config metadata payload instead, so
|
for (ModelObject *object : model.objects)
|
||||||
// OrcaSlicer versions without the publish feature fall back to importing the geometry only
|
for (ModelVolume *volume : object->volumes)
|
||||||
// (keeping the receiver's presets) while new versions rebuild the config from the payload.
|
if (volume != nullptr && volume->emboss_shape.has_value() && volume->emboss_shape->svg_file.has_value()) {
|
||||||
DynamicPrintConfig full_cfg = wxGetApp().preset_bundle->full_config_secure();
|
std::string *path_in_3mf = &volume->emboss_shape->svg_file->path_in_3mf;
|
||||||
DynamicPrintConfig filtered_cfg = filter_published_config(full_cfg, published_keys, material_keys);
|
previous_svg_paths.emplace_back(path_in_3mf, *path_in_3mf);
|
||||||
std::string payload;
|
}
|
||||||
for (const std::string &key : filtered_cfg.keys())
|
|
||||||
payload += key + " = " + filtered_cfg.opt_serialize(key) + "\n";
|
|
||||||
model.model_info->metadata_items[ORCA_PUBLISHED_CONFIG_TAG] = std::move(payload);
|
|
||||||
|
|
||||||
// Same file layout as save_project(), plus Silence (so export_3mf does not set the project
|
auto restore_temporary_state = [&]() {
|
||||||
// filename on success, keeping this a pure export like export_core_3mf()) and MinimalPublished.
|
for (const auto &[path_in_3mf, previous_path] : previous_svg_paths)
|
||||||
auto save_strategy = SaveStrategy::SplitModel | SaveStrategy::ShareMesh | SaveStrategy::Silence | SaveStrategy::MinimalPublished;
|
*path_in_3mf = previous_path;
|
||||||
bool full_pathnames = wxGetApp().app_config->get_bool("export_sources_full_pathnames");
|
|
||||||
if (full_pathnames)
|
|
||||||
save_strategy = save_strategy | SaveStrategy::FullPathSources;
|
|
||||||
|
|
||||||
// Restore the previous metadata state (both on success and on failure): a thrown export
|
|
||||||
// must not leave the published metadata on the in-memory project, or a later Save Project
|
|
||||||
// would write a hybrid file (full config + slicer tags + published metadata) that receivers
|
|
||||||
// silently load in published mode, skipping the project's own presets.
|
|
||||||
auto restore_metadata = [&]() {
|
|
||||||
if (!had_model_info) {
|
if (!had_model_info) {
|
||||||
model.model_info = nullptr;
|
model.model_info = nullptr;
|
||||||
} else {
|
} else {
|
||||||
@@ -16349,23 +16337,57 @@ int Plater::export_published_3mf(const std::vector<std::string>& published_keys,
|
|||||||
model.model_info->metadata_items.erase(ORCA_PUBLISHED_CONFIG_TAG);
|
model.model_info->metadata_items.erase(ORCA_PUBLISHED_CONFIG_TAG);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
bool state_restored = false;
|
||||||
|
auto restore_now = [&]() {
|
||||||
|
if (state_restored)
|
||||||
|
return;
|
||||||
|
restore_temporary_state();
|
||||||
|
state_restored = true;
|
||||||
|
};
|
||||||
|
ScopeGuard restore_guard(restore_now);
|
||||||
|
|
||||||
int ret;
|
if (model.model_info == nullptr)
|
||||||
|
model.model_info = std::make_shared<ModelInfo>();
|
||||||
|
model.model_info->metadata_items[ORCA_PUBLISHED_TAG] = "1";
|
||||||
|
model.model_info->metadata_items[ORCA_PUBLISHED_KEYS_TAG] = j.dump();
|
||||||
|
model.model_info->metadata_items[ORCA_PUBLISHED_MATERIAL_TAG] = jm.dump();
|
||||||
|
|
||||||
|
int ret = -1;
|
||||||
try {
|
try {
|
||||||
|
// Minimal published export: filter full_config to the published keys, material keys,
|
||||||
|
// identity fields and plate geometry keys, and omit the project config file, the
|
||||||
|
// project-embedded preset dumps and the OrcaSlicer version tag from the archive. The
|
||||||
|
// filtered values are serialized into the published_config metadata payload instead, so
|
||||||
|
// OrcaSlicer versions without the publish feature fall back to importing the geometry only
|
||||||
|
// (keeping the receiver's presets) while new versions rebuild the config from the payload.
|
||||||
|
DynamicPrintConfig full_cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||||
|
DynamicPrintConfig filtered_cfg = filter_published_config(full_cfg, published_keys, material_keys);
|
||||||
|
std::string payload;
|
||||||
|
for (const std::string &key : filtered_cfg.keys())
|
||||||
|
payload += key + " = " + filtered_cfg.opt_serialize(key) + "\n";
|
||||||
|
model.model_info->metadata_items[ORCA_PUBLISHED_CONFIG_TAG] = std::move(payload);
|
||||||
|
|
||||||
|
// Same file layout as save_project(), plus Silence (so export_3mf does not set the project
|
||||||
|
// filename on success, keeping this a pure export like export_core_3mf()) and MinimalPublished.
|
||||||
|
auto save_strategy = SaveStrategy::SplitModel | SaveStrategy::ShareMesh | SaveStrategy::Silence | SaveStrategy::MinimalPublished;
|
||||||
|
bool full_pathnames = wxGetApp().app_config->get_bool("export_sources_full_pathnames");
|
||||||
|
if (full_pathnames)
|
||||||
|
save_strategy = save_strategy | SaveStrategy::FullPathSources;
|
||||||
ret = export_3mf(into_path(path), save_strategy, -1, nullptr);
|
ret = export_3mf(into_path(path), save_strategy, -1, nullptr);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
restore_metadata();
|
restore_now();
|
||||||
MessageDialog(this, _L("Failed to export the published 3MF file.\nPlease check whether the folder exists online or if other programs have the file open."),
|
MessageDialog(this, _L("Failed to export the published 3MF file.\nPlease check whether the folder exists online or if other programs have the file open."),
|
||||||
_L("Publish"), wxOK | wxICON_WARNING).ShowModal();
|
_L("Publish"), wxOK | wxICON_WARNING).ShowModal();
|
||||||
return wxID_CANCEL;
|
return wxID_CANCEL;
|
||||||
}
|
}
|
||||||
restore_metadata();
|
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
restore_now();
|
||||||
MessageDialog(this, _L("Failed to export the published 3MF file.\nPlease check whether the folder exists online or if other programs have the file open."),
|
MessageDialog(this, _L("Failed to export the published 3MF file.\nPlease check whether the folder exists online or if other programs have the file open."),
|
||||||
_L("Publish"), wxOK | wxICON_WARNING).ShowModal();
|
_L("Publish"), wxOK | wxICON_WARNING).ShowModal();
|
||||||
return wxID_CANCEL;
|
return wxID_CANCEL;
|
||||||
}
|
}
|
||||||
|
restore_now();
|
||||||
return wxID_YES;
|
return wxID_YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user