@acms-subir/客户端
ACMS 公共 API v1. 支持 Expo/ React 的无依赖性ESM TypeScript客户端
本地、浏览器和节点 18+ 使用全局 fetch, (中文(简体) ), URL, (中文(简体) ), Headers 财务报告和审定财务报表
AbortController供应 fetch 当环境需要适配器时.
npm install @acms-subir/client
import { createClient, isAcmsError } from '@acms-subir/client';
const client = createClient({ baseUrl: 'https://example.com', locale: 'es' });
const site = await client.site();
const home = await client.home();
const page = await client.page('about/team');
for await (const post of client.paginate.posts({ collection: 'blog', limit: 50 })) {
console.log(post.title);
}
const english = client.withLocale('en');
baseUrl 接受一个站点来源或完整 /api/v1 URL,带有可选选项
主机前缀 。 返回所有方法 未包装 data· ; openapi() 返回时
原始文档。 每个方法都接受 locale 财务报告和审定财务报表 signal 选项对象.
语言只发送到支持翻译的路径. withLocale 份额
与原始客户端的缓存和运输配置. site() 财务报告和审定财务报表
pushTopics() 从核心0.3.32开始使用一种语言; 旧网站拒绝它,所以
客户端再问一次没有它,并获得主语言. book()
发送客户端的 locale 因此,预定的电子邮件(和链接)
使用应用软件的语言; 在核心 0.3.38 拒绝该字段之前,
因此,客户的书 再次没有它, 用同样的钥匙, 在网站的主
语言。 更新网站后, 才能使用更新客户端发送应用程序构建 .
| 方法 | 选项/ 行为 |
|---|---|
site(), (中文(简体) ), theme(), (中文(简体) ), home() | 身份(包括 legal.privacy 编号 : legal.terms 要在应用程序中链接的 URL 、 符號、 已解析的主页 |
page(slug, opts) | 已解析的页面, 包括段落; 支持嵌入式 satters |
content(opts) | kind, (中文(简体) ), collection, (中文(简体) ), q, (中文(简体) ), page, (中文(简体) ), limit, (中文(简体) ), cursor |
contentById(id, opts) | 可选 kind 拆分导入的ID |
posts(opts) | 内容过滤到员额; collection, (中文(简体) ), tag, (中文(简体) ), q标记 |
post(slug, opts) | 扫描邮政摘要,然后用ID加载细节;通过 collection 对于模糊的子弹 |
pages(opts) | 页次 总结,带有页形;使用 page() 用于款次 |
search(text, opts) | 顶级匹配 posts, (中文(简体) ), pages 财务报告和审定财务报表 events附加时 more 每组; types, (中文(简体) ), collection, (中文(简体) ), limit (1–20,默认5). 空文本解决无请求 |
events(opts) | 发生情况 from, (中文(简体) ), to, (中文(简体) ), calendar, (中文(简体) ), tag, (中文(简体) ), excludeTag, (中文(简体) ), room, (中文(简体) ), q帕吉纳克 过滤器使用一个值或数组( 其中任何一个匹配) |
event(slug, opts) | 不论日期如何,用一发一发子弹GET /events/{slug}; 在超过核心0.3.40的站点上, 请求窗口中的第一个匹配) |
eventDetail(slug, { on? }) | 事件及其下一场,或者说 on (单位:千美元)YYYY-MM-DD(中文(简体) ) |
calendars() | 公共日历 |
resources(opts), (中文(简体) ), resource(slug, opts) | 公共资源; 搜索搜索列表页面 |
availability(opts) | 需求 resource, (中文(简体) ), link; 可选 from, (中文(简体) ), days, (中文(简体) ), minutes |
roomFree({ room, start, end }) | 自由或繁忙的一间房间,网站让人们检查,时间繁忙,没有名字;从未缓存(核心0.3.45+) |
rsvpStatus(slug, { start }) | 一个事件日期的RSVP状态: open 或者说 reason (单位:千美元)disabled, (中文(简体) ), ended, (中文(简体) ), closed 过了赛事的最后期限, cancelled, (中文(简体) ), full, (中文(简体) ), closesAt (当RSVPs到日期关闭时,或 null: 打开直到结束),活 going编号 :left (持有的等待列表点数已取出), maxSeats 现在有一个RSVP, autoConfirm, (中文(简体) ), waitlist (单位:千美元)enabled:全日期按顺序排列; waiting 人为 seats 席位; maxSeats 以一行为单位) form (问 题 version,或 null) (中文(简体) ). start 事件发生的起始时间与 events() 返回( UTC ISO 字符串, 如 2026-10-05T14:00:00.000Z) (中文(简体) ). 从未缓存 |
book(opts) | 资源预订或RSVP输入(RsvpInput 用于RSVP; waitlist: true 加入完整日期的等待列表, 状态 waitlisted),可选 idempotencyKey; 发送客户端的 locale (或 opts.locale用于电子邮件 |
openapi() | Raw OpenAPI 文档 |
registerDevice({ token, platform, locale?, topics?, channels? }) | 注册或刷新博览会推算符(每次启动应用程序都要呼叫); { created, device }。 。 。 . channels: Android 频道 所创建的应用程序(例如. ['urgent'](中文(简体) ) |
updateDevice(token, { topics?, locale?, channels? }) | 更改设备的主题、语言或频道 |
unregisterDevice(token) | 退出(专有);也解除工作人员测试电话的链接 |
pushTopics() | 以客户语言(内置和网站本身)命名的作为通知切换的题目, kind: 'site'(中文(简体) ) |
bibles(), (中文(简体) ), biblePassage(ref, { bible }), (中文(简体) ), bibleChapter(book, chapter, { bible }) | 在帖子(Settings → Bible, core 0.3.84+)中为圣经链接所写的圣经诗句: 站点的圣经,一个段落(一个链接) ?ref=),一个有标题和相邻的章节; 404 在关闭时 |
linkStaffPhone(token, code) | 仅工作人员:将这个已注册设备与管理员测试手机的6位数代码相接,从而获得此人的测试通知; { linked, staff: { name } }。错误的、使用过的或过期的代码是 400,太多尝试了 429; 从未重复过 |
unlinkStaffPhone(token) | 脱钩(专有); { linked: false, staff: null } |
links() | 链接页:简介、社交和可见块(link, (中文(简体) ), email, (中文(简体) ), phone, (中文(简体) ), contact, (中文(简体) ), heading, (中文(简体) ), text, (中文(简体) ), spacer, (中文(简体) ), live, (中文(简体) ), embed(中文(简体) ) |
live() | 现场视频:当前广播(snapshot 或者说 null, (中文(简体) ), serverNow 和拥有者 display 选择。 从未缓存; 404 时 site.features.live 错误 |
announcements({ kind?, pinned?, following?, scope?, page?, limit? }), (中文(简体) ), announcement(id), (中文(简体) ), paginate.announcements() | 以客户语言发布最新消息pinned: true:旗帜,紧急第一;404 site.features.announcements 错误 |
form(id), (中文(简体) ), submitForm(id, { answers, version?, idempotencyKey? }) | 已公布的窗体问题; 发送答案( 复选框组为数组) 。 每次呼叫都会发出一个键; 重复重用它 |
requestOptions(), (中文(简体) ), submitRequest(input, { idempotencyKey? }) | 没有说明的事件请求:表格提供的内容open, (中文(简体) ), types, (中文(简体) ), rooms, (中文(简体) ), replyTime, (中文(简体) ), notice, (中文(简体) ), phone, (中文(简体) ), timezone; 从未缓存),并请求新事件或更改({ id }. 404 同时 site.features.requests 是虚假的。 见下文事件要求 |
manageBooking(token), (中文(简体) ), cancelBooking(token) | 书商自己订的 manageToken,并取消它(或留下一个等待者,或释放一个被扣留的地方); can 说什么是允许的。 一个等待名单的RSVP有 booking.position 以物配主 booking.holdUntil |
confirmBooking(token) | 检查一下报到者can.confirm: confirmed,或 pending 当RSVP需要批准时. 410次 一次 holdUntil 通过了。 核心资源0.3.40或以后 |
rescheduleAvailability(token, { from?, days?, minutes? }), (中文(简体) ), rescheduleBooking(token, { start, minutes? }) | 移动它的开放时间(它自己的时间算作自由),然后移动 |
bookingIcs(token) | 预订为 iCalendar 文件( 字符串) |
requestGuestCode(email), (中文(简体) ), guestSignIn({ email, code, deviceName? }), (中文(简体) ), guestBookings(guestToken, { when? }), (中文(简体) ), guestSignOut(guestToken) | "我的预订"的来宾报名 |
服务器没有后断层,事件细节或资源细节端点. 这些
便利方法可提出多项请求. event() 只发现事件
在其窗口中出现; 未列出
需要日历 calendar它不会引起任意的历史事件.
如果弹出后弹出相撞而未提供收藏,则第一个列出的比赛获胜.
搜索需要 site.features.search (除非网站设置)
acms({ search: { enabled: false } })) (中文(简体) ). 它使用网站的搜索索引和
排名:标题数最多,再取出节选,标记和正文. 每个字
必须匹配,而最后一个匹配为前缀(birth 发现"出生日". q 打开 content()编号 :posts() 返回时
分级摘要、页前帖子和通过页数,每页最多200个
有点. search() 用于合并结果显示屏:每一次命中都带有简单文字
snippet 与 highlights (单位:千美元){ start, end } 区域)改为粗体,事件加
他们的下一个 occurrence启用 more.posts 提供"见",然后
posts({ q, collection })。匹配使用主语文本,所以
已翻译标题已显示,但没有搜索.
增殖用途 page编号 :nextPage,而不是不透明的光标. cursor 是数字
别名用于 page; 明示 page 赢了. tag 过滤器 每个获取的页面
当地,这样一页就可以空出一个非无 nextPage。继续
通过这些页面. paginate.content/posts/pages/events/resources 产量
单个项目(活动产生事件)。 他们不重复改变
服务器结果或提供快照一致性; 在您的应用程序中复制ID.
预订、错误和注销
import type { RsvpInput } from '@acms-subir/client';
// Persist a UUID v4 with this booking intent before submitting; reuse it until
// the outcome is known. Use your platform's secure UUID/storage facilities.
const idempotencyKey = crypto.randomUUID();
try {
const result = await client.book({
resource: 'hall', link: 'meeting', start: '2026-10-05T14:00:00Z', minutes: 60,
name: 'Example Guest', email: '[email protected]', idempotencyKey,
});
// Store result.manageToken securely; it grants booking-management access.
} catch (error) {
if (isAcmsError(error)) {
console.log(error.status, error.code, error.message, error.retryAfter);
// error.idempotencyKey preserves even an automatically generated key.
}
}
// RSVP to one date of an event (needs only the calendar: site.features.rsvp)
const { occurrences } = await client.events({ q: 'supper' });
const date = occurrences[0];
const status = await client.rsvpStatus(date.event.slug, { start: date.start });
if (status.open) {
const rsvp: RsvpInput = {
event: date.event.slug, start: date.start, seats: 2, name: 'Example Guest', email: '[email protected]',
// field id → answer; checkbox groups send a list
answers: { meal: 'Vegetarian', help: ['Setup', 'Music'] },
// required when the event asks questions: a changed form answers 409, so read the status again
...(status.form ? { formVersion: status.form.version } : {}),
};
// an email that already has an RSVP for this date: AcmsError 409 with code 'duplicate' (its link is emailed again)
const result = await client.book({ ...rsvp, idempotencyKey: crypto.randomUUID() });
// pending when the organiser approves RSVPs; status.form?.confirmation is the thank-you text.
// result.manageToken changes or cancels it later (manageBooking, cancelBooking), like the email's link:
// keep it in secure storage only.
console.log(result.booking.status);
} else if (status.reason === 'full' && status.waitlist.enabled) {
// a place in line (status 'waitlisted', no seats); when a spot opens the site emails them and holds it
// for 24 hours: then manageBooking shows status 'offered' and holdUntil, and confirmBooking(token) takes it
const result = await client.book({
event: date.event.slug, start: date.start, seats: 2, name: 'Example Guest', email: '[email protected]', waitlist: true,
...(status.form ? { formVersion: status.form.version } : {}), idempotencyKey: crypto.randomUUID(),
});
console.log(result.booking.status);
}
const controller = new AbortController();
const pending = client.home({ signal: controller.signal });
controller.abort();
await pending.catch(() => {});
安全重复的请求(GET、PUT、DELETE、设备注册和
带有Idepotency-Key的 POST:预订、表格回复和事件请求)再试网络
失败,5xx和429答复最多两次(总共三次尝试)
250/500ms 备份和至少服务器的 Retry-After 延迟( 秒或 HTTP)
(日期)。 等待会超过时间 timeoutMs 将429直接丢出
retryAfter。一发写出(请求和签名、取消和
移动预订、确认被扣位置、客人签出)永远不会自动重复:
网络出错, 请在再次尝试前检查状态 。 总是重复预订
重复使用同一序列体和UUID. 默认每个 book() 调用创建
使用键 crypto.randomUUID,返回到 Math.random UUID v4 中
无. 用于恢复呼叫/应用程序重新启动、供应和坚持
安全UUUID。 永远不要仅仅因为反应超时而产生新的意图.
timeoutMs 默认为 三万,并覆盖整个请求,包括复试,
缓存访问和身体读取。 较长的费率限制延迟可能使
预算。 明确中止和超时不自动重审;不确定
预订必须用它现有的钥匙重新测试. AcmsError 曝光情况 status, (中文(简体) ),
code, (中文(简体) ), message, (中文(简体) ), retryAfter (秒),可选验证 issues 财务报告和审定财务报表
idempotencyKey本地失败使用状态 0 和 network_error, (中文(简体) ), timeout, (中文(简体) ),
aborted,或 invalid_response; 非 JSON HTTP 错误使用 http_error。 。 。 .
方便查询对每一项基本请求分别适用超时.
事件请求
一个站点可以接收人们的事件请求
无账户( a)acms({ requests: true })这样 site.features.requests
(b) 工作人员在日历中回答请求。 有人问
新的东西,或者换成一个事件, 并给出一个名字,电子邮件和
可选择的电话。 在工作人员批准之前,日历上没有任何内容.
import type { RequestInput } from '@acms-subir/client';
const options = await client.requestOptions();
// { open, types: [{ id, label }], rooms: [{ slug, name, color }], replyTime: '3 days', notice, phone, timezone }
if (!options.open) {
// say "We aren't taking requests here right now." and show options.phone; keep the screen itself reachable
}
const ask: RequestInput = {
kind: 'new', type: options.types[0]?.id, title: 'Guitar workshop',
// the site's wall clock (options.timezone), in the future; the end may be just HH:mm on the same day
start: '2026-11-07T18:00', end: '20:00',
repeat: 'weekly', // 'once' (default), 'weekly', or 'other' with repeatText
rooms: ['fellowship-hall'], // slugs from options.rooms, or roomsUnsure: true
people: 12, message: 'We bring our own chairs.',
name: 'Example Guest', email: '[email protected]', phone: '555 0100',
};
const { id } = await client.submitRequest(ask, { idempotencyKey: crypto.randomUUID() });
// a change to a public event: one date of it (YYYY-MM-DD, the site's day), or leave date out for every date
await client.submitRequest({ kind: 'change', event: 'fall-fair', date: '2026-11-08', message: 'Could it start at 10?', name: 'Example Guest', email: '[email protected]' });
- 请检查access-date=中的日期值 (帮助)
roomFree({ room, start, end })(需要在该网站上预订; 时间需要一个区域,所以转换 时钟数options.timezone) (中文(简体) ). - 每个请求保留一个“同上不便键”并重新用于重试:重试
同样的回答
{ id },与其他细节相同的密钥是 409changed。每通电话没有idempotencyKey做了一个新的. - 客户的
locale(或input.locale进入身体, 所以电子邮件 关于请求 说应用程序的语言. - 拒绝是
AcmsError谁code说为什么:closed(409起请求) ),一个字段(400:title, (中文(简体) ),type, (中文(简体) ),start, (中文(简体) ),end, (中文(简体) ),past, (中文(简体) ),far, (中文(简体) ),repeat, (中文(简体) ),room, (中文(简体) ),people, (中文(简体) ),event, (中文(简体) ),date, (中文(简体) ),what(变化的 电文),name, (中文(简体) ),email, (中文(简体) ),phone, (中文(简体) ),changed(409个)或busy编号 :full(429个retryAfter10个请求,每个地址一分钟,加上 站点日封顶.
推动通知
当网站允许推时可用(acms({ mobile: { push: true } })(三);
否则,这些返回一个404 AcmsError。托肯斯来自
expo-notifications (单位:千美元)getExpoPushTokenAsync) (中文(简体) ). 网站存储的纪念品,
平台、语言和专题.
const { topics } = await client.pushTopics(); // [{ id: 'news', label: 'News', kind: 'general' }, …]
await client.registerDevice({ token, platform: 'ios', locale: 'es', topics: ['news', 'events'] });
await client.updateDevice(token, { topics: ['events'] });
await client.unregisterDevice(token);
在一次活动之后: event.follow.topic (单位:千美元)event:<id>当它不能是
随行)像其它任何设备的主题一样进入设备主题;站点放弃了它
一旦事件没有日期了。 活动列表 a
电话:
const topics = [...device.topics, event.follow.topic!];
await client.updateDevice(token, { topics });
const following = followedEvents(topics); // event ids
await client.announcements({ following }); // + their messages and notices (kind 'message')
await client.announcements({ following, scope: 'following' }); // only those ("My classes")
追随者也可以在每次约会之前,在
event.follow.reminders (单位:千美元)[{ id: '1h' | 'evening', topic, at }], (中文(简体) ), at 页:1
晚上时间 HH:mm现场时间; [] 当历法没有规定的时候
topic (单位:千美元)event:<id>:remind:1h在活动的主题之外,一个
一次; event.follow.reminderStart 新的跟踪者开始于
(单位:千美元)'none' =没有提醒,直到他们选择。 网站只保留一个提醒
它的话题, 当日历停止提供它时, 放弃它.
import { reminderOf, withReminder } from '@acms-subir/client';
reminderOf(device.topics, event.id); // '1h' | 'evening' | null
await client.updateDevice(token, { topics: withReminder(device.topics, event.id, 'evening') }); // "The evening before, at 7:00 PM"
await client.updateDevice(token, { topics: withReminder(device.topics, event.id, null) }); // no reminder
event.hosts 当其日历显示时, 列出其主机; event.hostsLabel
日历用请求的语言(“Maestro/a”,或
'' 当它没有设定的时候.
工作人员可以把自己的电话连接起来进行测试(“发送测试与我
在管理员中:管理员的测试手机面板显示一次性6位数
代码(10分,单用),应用程序随这款手机的代号发送.
device.staff (从) registerDevice 财务报告和审定财务报表 updateDevice和链接
反应名称与谁有关(仅与谁有关),或 null。测试推
携带 data.test: true。 。 。 .
const { staff } = await client.linkStaffPhone(token, '123456'); // { name: 'Ana López' }; 400 for a wrong, used or expired code
await client.unlinkStaffPhone(token); // turning notifications off unlinks it too
实况录像
site.features.live 网站现场视频(整合 ) 现场视频
视频)没有关闭. live() 返回网站显示的相同快照 :
status (单位:千美元)live, (中文(简体) ), upcoming 或者说 offline 为"注意我们的频道"倒下,
title, (中文(简体) ), watchUrl,绝对值 thumbUrl (空地当无),
startedAt, (中文(简体) ), expiresAt 和可选 channel 财务报告和审定财务报表 viewers。时间是 Unix
数以毫秒计 服务器 时钟: 保留 serverNow - Date.now() 作为
并停止一次将快照作为电流处理 expiresAt (非零时)
即使后来的请求失败了 也要按那个时钟来. display 携带
拥有者的选择(style数字 : poster, (中文(简体) ), split 或者说 row· ; bar, (中文(简体) ), header, (中文(简体) ), dock(中文(简体) )
因此,一个应用程序可以跟踪网站。 大约每30秒播放一次,而应用程序是
在前方; @acms-subir/native是因为 useLive() 做这一切。 这个
"我们现场直播"的通知是推题 live。 。 。 .
const { snapshot, serverNow } = await client.live();
const offset = serverNow - Date.now();
const current = snapshot && (!snapshot.expiresAt || snapshot.expiresAt > Date.now() + offset) ? snapshot : null;
if (current?.status === 'live') console.log(`Live: ${current.title} → ${current.watchUrl}`);
缓存和信头
ETag 重新验证默认为内存的 URL 键缓存 。 每个读取联系人
服务器 If-None-Match; 一个 304 重用保存的数据。 这不是一个
离线后退 将返回的缓存对象视为不可改变 。 使用 cache: false
来禁用它。 可用性,活状态,RSVP状态,预订结果和出错永远不会被缓存.
无ETag或与 no-store 取消旧条目.
import type { ClientCache } from '@acms-subir/client';
const cache: ClientCache = {
async get(url) { /* retrieve { etag, body } from app storage */ return null; },
async set(url, entry) { /* persist entry; null means delete */ },
};
const persistent = createClient({ baseUrl: 'https://example.com/api/v1', cache });
适配器可以是同步的或同步的. 将持续缓存限制为
一个站点的公开API 并账户存储限制。 缓存适配器失败
向打电话者宣传。 自定义 headers 支持; 不嵌入特权
证书。 请求省略浏览器权限 。 有条件的标题得到管理
由客户端; 逐个拨号的密钥会覆盖已配置的头键.
发展
npm run build -w packages/client
npm run test -w packages/client
npm run check:types -w packages/client
npm run generate:types -w packages/client # after reviewing core schema changes
合同测试使用 Core 虚拟导入实际核心 OpenAPI 生成器 模块测试根,将所有 GET/POST 路径与客户端方法注册进行对比, 和检查生成的请求/答复类型字节。 总部和备选方案 是运输业务,没有单独的数据返回方法。 无核心 代码,Zod,或节点内置的飞船 在这个包的运行时间.
管理预订和"我的预订"
// a booking the app made (result.manageToken from book()), or one from guestBookings()
const view = await client.manageBooking(manageToken);
if (view.can.reschedule) {
const open = await client.rescheduleAvailability(manageToken, { days: 7 });
await client.rescheduleBooking(manageToken, { start: open.days[0].slots[0].start });
}
if (view.can.cancel) await client.cancelBooking(manageToken);
// guest sign-in (check site.features.guestSignIn first)
await client.requestGuestCode('[email protected]');
const { accessToken } = await client.guestSignIn({ email: '[email protected]', code: '481203' });
const mine = await client.guestBookings(accessToken, { when: 'upcoming' });
预订和来宾信使以信封形式旅行(Booking-Token, (中文(简体) ),
Authorization: Bearer),从不在URL中,他们的回复也从不被缓存.
将两者安全储存(expo-secure-store) (中文(简体) ). can.closed 表示
预订类型更改截断已过:显示“联系我们”而不是“联系我们”
按钮。 一个来宾标志在未使用90天后结束;401表示再次签入.
编辑应用程序(工作人员登录)
用于网站 acms({ mobile: { editor: { redirectUris } } })工作人员在
在系统浏览器(OAuth 2.0 + PKCE);应用程序从未看到密码.
import { createEditorAuth, createEditorSession, createEditorClient } from '@acms-subir/client';
const auth = createEditorAuth({ baseUrl: 'https://example.com', clientId: 'org.example.staff', redirectUri: 'org.example.staff://auth' });
const session = createEditorSession({ auth, storage /* get/set backed by expo-secure-store */, onSignedOut });
const api = createEditorClient({ baseUrl: 'https://example.com', session });
const { url, verifier, state } = await auth.start({ deviceName: 'Ana’s iPhone' });
// open `url` with expo-web-browser openAuthSessionAsync, then:
const { code } = auth.parseRedirect(returnedUrl, state);
await session.signIn(await auth.exchangeCode({ code, codeVerifier: verifier }));
const doc = await api.getDocument('post', id);
await api.saveDocument('post', id, { revision: doc.revision, fields: { title: 'New title' } });
在反应土著通行证 crypto: { randomBytes, sha256 } 从前催眠到
createEditorAuth (Web Crypto是默认). 刷新为单飞;
401在刷新后再试一次 然后 onAuthError 运行。 写作携带
页:1 revision 你上膛了(第409次有人抢救).
时间安排和设置 : schedule({ from, to, resource? }) (至多63天)
预订、被封锁的时间、事件和资料), createBooking() (关于某人的)
代表, rescheduleBooking(id, …), (中文(简体) ), listResources() 编号 : getResource() 编号 :
createResource() 编号 : updateResource() 编号 : deleteResource(), (中文(简体) ),
createBookingLink() 编号 : updateBookingLink() 编号 : deleteBookingLink(), (中文(简体) ),
blockTime() 编号 : unblockTime(), (中文(简体) ), listCalendars() 编号 : createCalendar() 编号 :
updateCalendar() 编号 : deleteCalendar(), (中文(简体) ), listForms() 编号 :
listFormResponses(), (中文(简体) ), getLinksPage() 编号 : saveLinksPage()。 。 。 .
updateBookingLink(resourceId, linkId, { order: 0 }) 先放一个预订类型
在它的房间,一个它的预订页面和应用程序领先。 设置写入需要
me().permissions.schedule.manage; 窗体响应和 HTML 链接块需要
(a) 管理人或所有人(403人除外).