mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-09 22:14:29 +09:00
Adds permission protection around page templates so content editors can create pages from templates without being able to open, edit, or patch template pages directly.
119 lines
3.4 KiB
TypeScript
119 lines
3.4 KiB
TypeScript
import { atom, computed } from "nanostores";
|
|
import {
|
|
findPageByIdOrPath,
|
|
getPagePath,
|
|
isPage,
|
|
type Page,
|
|
type System,
|
|
} from "@webstudio-is/sdk";
|
|
import {
|
|
compilePathnamePattern,
|
|
matchPathnamePattern,
|
|
tokenizePathnamePattern,
|
|
} from "~/builder/shared/url-pattern";
|
|
import { $selectedPage } from "./nano-states/pages";
|
|
import { $pages } from "./sync/data-stores";
|
|
import { $publishedOrigin } from "./nano-states/misc";
|
|
import { serverSyncStore } from "./sync/sync-stores";
|
|
|
|
export const $systemDataByPage = atom(
|
|
new Map<Page["id"], Pick<System, "search" | "params">>()
|
|
);
|
|
|
|
const extractParams = (
|
|
pattern: string,
|
|
path?: string,
|
|
fallbackPattern?: string
|
|
) => {
|
|
const params: System["params"] = {};
|
|
const tokens = tokenizePathnamePattern(pattern);
|
|
// try to match the first item in history to let user
|
|
// see the page without manually entering params
|
|
// or selecting them in address bar
|
|
const matchedParams = path
|
|
? (matchPathnamePattern(pattern, path) ??
|
|
(fallbackPattern
|
|
? matchPathnamePattern(fallbackPattern, path)
|
|
: undefined))
|
|
: undefined;
|
|
for (const token of tokens) {
|
|
if (token.type === "param") {
|
|
params[token.name] = matchedParams?.[token.name] ?? undefined;
|
|
}
|
|
}
|
|
return params;
|
|
};
|
|
|
|
export const $currentSystem = computed(
|
|
[$publishedOrigin, $selectedPage, $pages, $systemDataByPage],
|
|
(origin, page, pages, systemByPage) => {
|
|
const system: System = {
|
|
search: {},
|
|
params: {},
|
|
pathname: "/",
|
|
origin,
|
|
};
|
|
if (page === undefined || pages === undefined || !isPage(page)) {
|
|
return system;
|
|
}
|
|
const systemData = systemByPage.get(page.id);
|
|
const pagePath = getPagePath(page.id, pages);
|
|
const extractedParams = extractParams(
|
|
pagePath,
|
|
page.history?.[0],
|
|
page.path
|
|
);
|
|
const params = { ...extractedParams, ...systemData?.params };
|
|
const pathname = compilePath(pagePath, params) || "/";
|
|
return {
|
|
search: { ...system.search, ...systemData?.search },
|
|
params,
|
|
pathname,
|
|
origin,
|
|
};
|
|
}
|
|
);
|
|
|
|
const compilePath = (pattern: string, params: System["params"]) => {
|
|
const tokens = tokenizePathnamePattern(pattern);
|
|
return compilePathnamePattern(tokens, params);
|
|
};
|
|
|
|
/**
|
|
* put new path into the beginning of history
|
|
* and drop paths in the end when exceeded 20
|
|
*/
|
|
const savePathInHistory = (pageId: string, path: string) => {
|
|
serverSyncStore.createTransaction([$pages], (pages) => {
|
|
if (pages === undefined) {
|
|
return;
|
|
}
|
|
const page = findPageByIdOrPath(pageId, pages);
|
|
if (page === undefined) {
|
|
return;
|
|
}
|
|
const pagePath = getPagePath(page.id, pages);
|
|
const history = Array.from(page.history ?? []);
|
|
history.unshift(path || pagePath);
|
|
page.history = Array.from(new Set(history)).slice(0, 20);
|
|
});
|
|
};
|
|
|
|
export const updateCurrentSystem = (
|
|
update: Partial<Pick<System, "search" | "params">>
|
|
) => {
|
|
const page = $selectedPage.get();
|
|
if (!isPage(page)) {
|
|
return;
|
|
}
|
|
const systemDataByPage = new Map($systemDataByPage.get());
|
|
const systemData = systemDataByPage.get(page.id);
|
|
const search = update.search ?? systemData?.search ?? {};
|
|
const params = update.params ?? systemData?.params ?? {};
|
|
systemDataByPage.set(page.id, { search, params });
|
|
$systemDataByPage.set(systemDataByPage);
|
|
const pages = $pages.get();
|
|
const pagePath = pages ? getPagePath(page.id, pages) : page.path;
|
|
savePathInHistory(page.id, compilePath(pagePath, params));
|
|
};
|