CMMS embedding: support creating a brand-new file, not just editing
cad-edit.php?new=1 launches the editor with no attachment_id/load_url yet - CmmsConfig's load_url/attachment_id/original_name become Optional (only save_url/job_id/csrf_token are always required). load_from_cmms_web() and boot_web()'s auto-load task both skip cleanly when load_url is absent (no error, just the normal blank Start tab). post_bytes_to_host() now takes the filename and picks cad_save (update, has attachment_id) vs cad_save_new (create); on a successful create it writes the new id/loadUrl/originalName back into window.CAD_EDITOR_CONFIG in place, so the tab's *next* save updates that row instead of creating a duplicate each time.
This commit is contained in:
parent
494ddfece9
commit
64d0230952
4 changed files with 86 additions and 25 deletions
|
|
@ -3673,13 +3673,16 @@ impl OpenCADStudio {
|
|||
);
|
||||
let thumbs_fetch = s.refresh_recent_thumbs();
|
||||
|
||||
// CMMS embedding (not upstream): if launched from cad-edit.php,
|
||||
// window.CAD_EDITOR_CONFIG is set and the host has already validated
|
||||
// the attachment belongs to the job the editor was opened from - load
|
||||
// it immediately instead of waiting for File > Open. Standalone use
|
||||
// (opencadstudio.com, no config present) is unaffected; the Start tab
|
||||
// just shows as normal. Mirrors on_open_file's wasm branch.
|
||||
let cmms_load = if crate::sys::cmms_config().is_some() {
|
||||
// CMMS embedding (not upstream): if launched from cad-edit.php to
|
||||
// *edit* an existing attachment, window.CAD_EDITOR_CONFIG carries a
|
||||
// load_url the host has already validated - load it immediately
|
||||
// instead of waiting for File > Open, mirrors on_open_file's wasm
|
||||
// branch. Gated on load_url specifically, not just cmms_config()'s
|
||||
// presence: a "new file" launch (cad-edit.php?new=1) sets the config
|
||||
// but omits load_url on purpose (nothing exists yet to load), and
|
||||
// should boot to the normal blank Start tab, not show a load error.
|
||||
// Standalone use (opencadstudio.com, no config at all) is unaffected.
|
||||
let cmms_load = if crate::sys::cmms_config().is_some_and(|c| c.load_url.is_some()) {
|
||||
let state = std::sync::Arc::new(crate::io::OpenProgressState::new(
|
||||
crate::app::OPEN_PHASE_READING,
|
||||
));
|
||||
|
|
|
|||
|
|
@ -2217,8 +2217,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
Ok(bytes) => {
|
||||
if crate::sys::cmms_config().is_some() {
|
||||
let post_bytes = bytes.clone();
|
||||
let post_filename = filename.clone();
|
||||
cmms_save_task = Task::perform(
|
||||
async move { crate::sys::post_bytes_to_host(&post_bytes).await },
|
||||
async move { crate::sys::post_bytes_to_host(&post_bytes, &post_filename).await },
|
||||
Message::CmmsSaveResult,
|
||||
);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -525,21 +525,24 @@ pub async fn pick_and_load_web(
|
|||
}
|
||||
|
||||
/// CMMS embedding (not upstream): load the one file the host page already
|
||||
/// validated, instead of showing the browser picker. `crate::sys::cmms_config()`
|
||||
/// returning `None` means this build is running standalone (opencadstudio.com
|
||||
/// or a plain local file), so callers should fall back to `pick_and_load_web`
|
||||
/// in that case — this never shows a picker itself.
|
||||
/// validated, instead of showing the browser picker. Returns `None` both
|
||||
/// when this build is running standalone (no `cmms_config()` at all) and
|
||||
/// when it's embedded but launched to create a brand-new file (`cad-edit.php
|
||||
/// ?new=1` - config present, but no `load_url` since there's nothing to
|
||||
/// load yet) - callers treat both the same: don't auto-load, let the app
|
||||
/// boot to its normal blank Start tab.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub async fn load_from_cmms_web(progress: Arc<OpenProgressState>) -> Option<WebOpenOutcome> {
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
|
||||
let cfg = crate::sys::cmms_config()?;
|
||||
let load_url = cfg.load_url?;
|
||||
progress.set(crate::app::OPEN_PHASE_READING, 500, 1, 2);
|
||||
let name = cfg.original_name.clone();
|
||||
let name = cfg.original_name.clone().unwrap_or_else(|| "CMMS attachment".to_string());
|
||||
|
||||
let window = web_sys::window()?;
|
||||
let response: web_sys::Response = JsFuture::from(window.fetch_with_str(&cfg.load_url))
|
||||
let response: web_sys::Response = JsFuture::from(window.fetch_with_str(&load_url))
|
||||
.await
|
||||
.ok()?
|
||||
.dyn_into()
|
||||
|
|
|
|||
76
src/sys.rs
76
src/sys.rs
|
|
@ -311,12 +311,15 @@ pub fn download_bytes(name: &str, bytes: &[u8]) {
|
|||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub struct CmmsConfig {
|
||||
pub load_url: String,
|
||||
pub save_url: String,
|
||||
pub attachment_id: String,
|
||||
pub job_id: String,
|
||||
pub csrf_token: String,
|
||||
pub original_name: String,
|
||||
// Absent when the editor was launched to create a brand-new file
|
||||
// (public/cad-edit.php?job_id=&new=1) rather than to edit an existing
|
||||
// attachment - there is nothing to auto-load and nothing to update yet.
|
||||
pub load_url: Option<String>,
|
||||
pub attachment_id: Option<String>,
|
||||
pub original_name: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
|
@ -334,30 +337,46 @@ pub fn cmms_config() -> Option<CmmsConfig> {
|
|||
.as_string()
|
||||
};
|
||||
Some(CmmsConfig {
|
||||
load_url: get_str("loadUrl")?,
|
||||
save_url: get_str("saveUrl")?,
|
||||
attachment_id: get_str("attachmentId")?,
|
||||
job_id: get_str("jobId")?,
|
||||
csrf_token: get_str("csrfToken")?,
|
||||
original_name: get_str("originalName")?,
|
||||
load_url: get_str("loadUrl"),
|
||||
attachment_id: get_str("attachmentId"),
|
||||
original_name: get_str("originalName"),
|
||||
})
|
||||
}
|
||||
|
||||
/// POST the current document's bytes back to the CMMS in place of a browser
|
||||
/// download. Mirrors `download_bytes`'s Blob construction but sends it as a
|
||||
/// multipart file field instead of triggering an `<a download>` click.
|
||||
///
|
||||
/// `cfg.attachment_id` decides create vs. update: present -> `cad_save`
|
||||
/// (overwrite that exact attachment); absent -> `cad_save_new` (this is the
|
||||
/// first save of a brand-new, editor-created file) using `filename` as the
|
||||
/// new attachment's name. On a successful create, the new id is written back
|
||||
/// into `window.CAD_EDITOR_CONFIG` so the *next* save on this same tab
|
||||
/// updates that row instead of creating a duplicate each time - there is no
|
||||
/// other state to thread through the app's own Message/Task plumbing for
|
||||
/// this, the host page's own JS global is the simplest shared place to park it.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub async fn post_bytes_to_host(bytes: &[u8]) -> Result<(), String> {
|
||||
use wasm_bindgen::JsCast;
|
||||
pub async fn post_bytes_to_host(bytes: &[u8], filename: &str) -> Result<(), String> {
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
|
||||
let cfg = cmms_config().ok_or("not running inside the CMMS")?;
|
||||
let is_new = cfg.attachment_id.is_none();
|
||||
|
||||
let form = web_sys::FormData::new().map_err(|_| "could not build form data")?;
|
||||
form.append_with_str("action", "cad_save")
|
||||
.map_err(|_| "form append failed")?;
|
||||
form.append_with_str("id", &cfg.attachment_id)
|
||||
form.append_with_str("action", if is_new { "cad_save_new" } else { "cad_save" })
|
||||
.map_err(|_| "form append failed")?;
|
||||
if let Some(id) = &cfg.attachment_id {
|
||||
form.append_with_str("id", id)
|
||||
.map_err(|_| "form append failed")?;
|
||||
}
|
||||
if is_new {
|
||||
form.append_with_str("original_name", filename)
|
||||
.map_err(|_| "form append failed")?;
|
||||
}
|
||||
form.append_with_str("job_id", &cfg.job_id)
|
||||
.map_err(|_| "form append failed")?;
|
||||
form.append_with_str("csrf_token", &cfg.csrf_token)
|
||||
|
|
@ -385,6 +404,41 @@ pub async fn post_bytes_to_host(bytes: &[u8]) -> Result<(), String> {
|
|||
if !response.ok() {
|
||||
return Err(format!("save failed: HTTP {}", response.status()));
|
||||
}
|
||||
|
||||
if is_new {
|
||||
let text = JsFuture::from(response.text().map_err(|_| "text() unavailable")?)
|
||||
.await
|
||||
.map_err(|_| "save response body read failed")?;
|
||||
let body = text.as_string().ok_or("save response body is not a string")?;
|
||||
let parsed: serde_json::Value =
|
||||
serde_json::from_str(&body).map_err(|_| "save response is not valid JSON")?;
|
||||
let new_id = parsed
|
||||
.get("id")
|
||||
.and_then(|v| v.as_i64())
|
||||
.ok_or("save response missing id")?;
|
||||
|
||||
let config_obj = js_sys::Reflect::get(&window, &JsValue::from_str("CAD_EDITOR_CONFIG"))
|
||||
.map_err(|_| "could not re-read CAD_EDITOR_CONFIG")?;
|
||||
js_sys::Reflect::set(
|
||||
&config_obj,
|
||||
&JsValue::from_str("attachmentId"),
|
||||
&JsValue::from_str(&new_id.to_string()),
|
||||
)
|
||||
.map_err(|_| "could not update attachmentId")?;
|
||||
js_sys::Reflect::set(
|
||||
&config_obj,
|
||||
&JsValue::from_str("loadUrl"),
|
||||
&JsValue::from_str(&format!("/attachment.php?id={new_id}")),
|
||||
)
|
||||
.map_err(|_| "could not update loadUrl")?;
|
||||
js_sys::Reflect::set(
|
||||
&config_obj,
|
||||
&JsValue::from_str("originalName"),
|
||||
&JsValue::from_str(filename),
|
||||
)
|
||||
.map_err(|_| "could not update originalName")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue