mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-14 04:27:36 +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 {
|
||||
|
||||
bool is_published_3mf_flag(const std::string &value)
|
||||
{
|
||||
return value == "1";
|
||||
}
|
||||
|
||||
void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
{
|
||||
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
|
||||
// m_model->model_info is only filled in after the whole XML has been parsed.
|
||||
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;
|
||||
|
||||
@@ -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_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)
|
||||
{
|
||||
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.
|
||||
if (model.model_info != nullptr) {
|
||||
auto published_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_TAG);
|
||||
if (published_it != model.model_info->metadata_items.end() &&
|
||||
(published_it->second == "true" || published_it->second == "1")) {
|
||||
if (published_it != model.model_info->metadata_items.end() && is_published_3mf_flag(published_it->second)) {
|
||||
published_config.published = true;
|
||||
auto keys_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_KEYS_TAG);
|
||||
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);
|
||||
if (jm.is_array())
|
||||
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())
|
||||
continue;
|
||||
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())
|
||||
entry.color = m["color"].get<std::string>();
|
||||
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.
|
||||
}
|
||||
}
|
||||
@@ -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_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();
|
||||
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,
|
||||
// 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);
|
||||
// export_3mf() assigns archive paths to previously unsaved SVGs. Preserve those fields too,
|
||||
// otherwise a publish changes what a later normal project save writes.
|
||||
std::vector<std::pair<std::string *, std::string>> previous_svg_paths;
|
||||
for (ModelObject *object : model.objects)
|
||||
for (ModelVolume *volume : object->volumes)
|
||||
if (volume != nullptr && volume->emboss_shape.has_value() && volume->emboss_shape->svg_file.has_value()) {
|
||||
std::string *path_in_3mf = &volume->emboss_shape->svg_file->path_in_3mf;
|
||||
previous_svg_paths.emplace_back(path_in_3mf, *path_in_3mf);
|
||||
}
|
||||
|
||||
// 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;
|
||||
auto restore_temporary_state = [&]() {
|
||||
for (const auto &[path_in_3mf, previous_path] : previous_svg_paths)
|
||||
*path_in_3mf = previous_path;
|
||||
|
||||
// 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) {
|
||||
model.model_info = nullptr;
|
||||
} 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);
|
||||
}
|
||||
};
|
||||
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 {
|
||||
// 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);
|
||||
} 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."),
|
||||
_L("Publish"), wxOK | wxICON_WARNING).ShowModal();
|
||||
return wxID_CANCEL;
|
||||
}
|
||||
restore_metadata();
|
||||
|
||||
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."),
|
||||
_L("Publish"), wxOK | wxICON_WARNING).ShowModal();
|
||||
return wxID_CANCEL;
|
||||
}
|
||||
restore_now();
|
||||
return wxID_YES;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user