Repository navigation
Upload Handlers
In edit mode, editors upload images (image elements, backgrounds, gallery slides, layers) and videos (video backgrounds). The Page Builder does not hard-code any upload endpoint. Instead, you give it two functions that return an upload URL for the item being edited. This page explains the contract and shows the setup used with Selldone.
// Builder.types.ts
type ModelType = "page" | "popup" | "menu";
type IModel = Page | Popup | ShopMenu; // from @selldone/core-js
type IUploadUrlFunction = (type: ModelType, model: IModel) => string | null;
interface IServer {
uploadImageUrl: IUploadUrlFunction;
uploadVideoUrl: IUploadUrlFunction;
}Pass server to SetupPageBuilder or PageHyper in edit mode. Both functions receive:
-
type— what is being edited:"page"(landing page),"popup"or"menu"(a custom page used for menus or notifications), -
model— the model linked with$builder.setModel(type, model).LPageEditordoes this for you fromv-model, usingisPopup/isMenuto choose the type.
Return the full URL to upload to, or null when uploads are not supported for that type.
With the Backoffice SDK installed (BackofficeSDK.Setup() creates
window.API), use the Selldone dashboard endpoints:
import { SetupPageBuilder } from "@selldone/page-builder";
SetupPageBuilder(app, {
mode: "edit",
server: {
uploadImageUrl: (type, model) => {
if (type === "page") {
// Shop landing pages:
return window.API.POST_UPLOAD_PAGE_IMAGE(model.shop_id, model.id);
} else if (type === "menu") {
// Shop custom page for menus:
return window.API.POST_UPLOAD_PAGE_IMAGE(model.shop_id, "default"); // Save in default (For menus / notifications custom page design)
} else if (type === "popup") {
// Shop popups:
return window.API.POST_UPLOAD_POPUP_IMAGE(model.shop_id, model.id);
}
return null;
},
uploadVideoUrl: (type, model) => {
if (type === "page") {
// Shop landing pages:
return window.API.POST_UPLOAD_PAGE_VIDEO(model.shop_id, model.id);
} else if (type === "menu") {
// Shop custom page for menus:
return window.API.POST_UPLOAD_PAGE_VIDEO(model.shop_id, "default"); // Save in default (For menus / notifications custom page design)
} else if (type === "popup") {
// Shop popups:
return null;
}
return null;
},
},
});The same server object works with PageHyper(app, { mode: "edit", server }).
SetupPageBuilder calls uploadImageUrl("page", { id: 0, shop_id: 0 }) once during setup to check your function.
If it is not a function, or it returns an empty value, an error is logged to the console. Make sure your function
returns a string for this dummy model and does not throw.
At upload time, $builder.getImageUploadUrl() and $builder.getVideoUploadUrl() log an error and return nothing when
the function is missing or when no model was linked with setModel().
The built-in image element (XUploader) sends the file as multipart/form-data in a field named photo with a POST
request to the URL you return. It expects a JSON response like:
{
"files": {
"path": "https://cdn.example.com/images/abc.jpg",
"item": { "id": 12, "path": "abc.jpg", "size": 48213 }
}
}-
files.pathbecomes the imagesrc. -
files.item(optional) is added to the editor asset library set with$builder.setAssets(images, videos). - On failure, return
{ "error": true, "error_msg": "Reason" }; the message is shown to the editor.
Other upload fields in the editor (backgrounds, gallery slides, image layers, typography and dynamic settings) also
get their URL from getImageUploadUrl() / getVideoUploadUrl(). Some of them use different upload inputs (for
example SImageUploader from Components), so when you build a custom
backend, test each of these flows.
Show previously uploaded files in the editor by passing them to the builder:
$builder.setAssets(
[{ id: 1, path: "hero.jpg", size: 120034 }], // images
[{ id: 7, path: "intro.mp4", size: 5023411 }], // videos (optional)
);