feat(wordaddin): add e-Court pass-through sign-in
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 4s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 1m8s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 6s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 4s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 1m8s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 6s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
ไฟล์ที่ e-Court stamp custom properties token/owner มาใช้งานได้เลย ไม่ต้องกด login — detect() ของ TaskpaneApp อ่านค่าทั้งสองตอนเปิด task pane แล้วเรียก signInFromDocument(): username จาก owner → uid ใน JWT payload → NEXT_PUBLIC_ADDIN_MOCK_USERNAME, token จาก ไฟล์เก็บใน memory ให้ getAccessToken() ใช้ต่อ decodeJwtField ถอด base64url เป็น bytes แล้วผ่าน TextDecoder ก่อน JSON.parse — uid ที่ไม่ใช่ ASCII (เช่น ชื่อไทย) ไม่เป็น mojibake อัปเดตเอกสาร CLAUDE.md + docs/WORDADDIN-DEV.md พร้อมวิธีทดสอบ เองใน Word ผ่าน Advanced Properties Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
dc851fbb73
commit
2b70a50c62
5 changed files with 78 additions and 9 deletions
|
|
@ -51,7 +51,7 @@ lib/openapi.ts ← OpenAPI 3.0 spec ของ API ทดสอบ (กล
|
||||||
wordaddin/ ← **client-only zone ของ Word add-in** (ห้าม import จาก lib/ หรือถูก import โดย server component — พอร์ตดิบจาก D:\word-addin-poc\addin\src, spec: docs/addin-ui-flow.md, คู่มือ dev: docs/WORDADDIN-DEV.md)
|
wordaddin/ ← **client-only zone ของ Word add-in** (ห้าม import จาก lib/ หรือถูก import โดย server component — พอร์ตดิบจาก D:\word-addin-poc\addin\src, spec: docs/addin-ui-flow.md, คู่มือ dev: docs/WORDADDIN-DEV.md)
|
||||||
wordaddin/config.ts ← env NEXT_PUBLIC_ADDIN_API_BASE_URL ("" = ยังไม่ต่อ backend จริง) + NEXT_PUBLIC_ADDIN_MOCK_USERNAME (mock auth)
|
wordaddin/config.ts ← env NEXT_PUBLIC_ADDIN_API_BASE_URL ("" = ยังไม่ต่อ backend จริง) + NEXT_PUBLIC_ADDIN_MOCK_USERNAME (mock auth)
|
||||||
wordaddin/models/versionRecord.ts ← types VersionRecord/FieldsSnapshot + readSnapshotField (พอร์ตดิบ)
|
wordaddin/models/versionRecord.ts ← types VersionRecord/FieldsSnapshot + readSnapshotField (พอร์ตดิบ)
|
||||||
wordaddin/services/ ← documentService/fileService/metadataService/autoSaveService/versionFlow/apiClient/errors พอร์ตดิบทั้งไฟล์ · **authService = MOCK** (login ผ่านทันที คง surface เดิมครบไว้สลับของจริง — ต้นฉบับ Keycloak PKCE อยู่ที่ D:\word-addin-poc\addin) · **versioningService = MOCK** (memory + **metadata persist ลง localStorage ต่อ documentId** เพื่อทดสอบวงปิดเอกสาร→เปิดใหม่ได้ — blob ไฟล์จริงเฉพาะ session; คงชื่อ/ลายเซ็น uploadVersion/getVersions/getDownloadLink/newClientVersionId เป๊ะ, download-link คืน about:blank)
|
wordaddin/services/ ← documentService/fileService/metadataService/autoSaveService/versionFlow/apiClient/errors พอร์ตดิบทั้งไฟล์ · **authService = MOCK** (login ผ่านทันที คง surface เดิมครบไว้สลับของจริง — ต้นฉบับ Keycloak PKCE อยู่ที่ D:\word-addin-poc\addin + **`signInFromDocument` e-Court pass-through 2026-10-16: ไฟล์ที่ stamp custom properties `token`/`owner` มา (`readTrackedProperties` อ่านรวม) = `detect()` ของ TaskpaneApp ผ่านเข้าเลยไม่ต้องกด login — username จาก owner → uid ใน JWT → mockUsername, token ไฟล์เก็บ memory ให้ getAccessToken ใช้ต่อ**) · **versioningService = MOCK** (memory + **metadata persist ลง localStorage ต่อ documentId** เพื่อทดสอบวงปิดเอกสาร→เปิดใหม่ได้ — blob ไฟล์จริงเฉพาะ session; คงชื่อ/ลายเซ็น uploadVersion/getVersions/getDownloadLink/newClientVersionId เป๊ะ, download-link คืน about:blank)
|
||||||
utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine
|
utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine
|
||||||
utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %)
|
utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %)
|
||||||
utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + TYPE_SHORT/TYPE_ORDER ชื่อย่อ/ลำดับชนิด (บรรทัดสรุปหน้าของหน้าสรุป + แผงตัวกรอง explorer — ย้ายจาก 2 ที่ซ้ำกันมารวมที่นี่ 2026-09-24) + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/groups/CSV ใช้ร่วม)
|
utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + TYPE_SHORT/TYPE_ORDER ชื่อย่อ/ลำดับชนิด (บรรทัดสรุปหน้าของหน้าสรุป + แผงตัวกรอง explorer — ย้ายจาก 2 ที่ซ้ำกันมารวมที่นี่ 2026-09-24) + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/groups/CSV ใช้ร่วม)
|
||||||
|
|
|
||||||
|
|
@ -149,20 +149,33 @@ const TaskpaneApp: React.FC<{ demo?: "untracked" | "tracking" }> = ({ demo }) =>
|
||||||
|
|
||||||
/** ตรวจตอนเปิด task pane (FR-1): token → SavedByAddIn → history
|
/** ตรวจตอนเปิด task pane (FR-1): token → SavedByAddIn → history
|
||||||
* FR-16 — อ่าน properties ครั้งเดียวทั้งชุดแล้ว derive tracked เอง เพราะไฟล์ร่าง (untracked)
|
* FR-16 — อ่าน properties ครั้งเดียวทั้งชุดแล้ว derive tracked เอง เพราะไฟล์ร่าง (untracked)
|
||||||
* ต้องได้อ่าน TemplateId ไปใช้ตอน Start Versioning ด้วย (ข้ามทั้งหมดในโหมด demo) */
|
* ต้องได้อ่าน TemplateId ไปใช้ตอน Start Versioning ด้วย (ข้ามทั้งหมดในโหมด demo)
|
||||||
|
* e-Court pass-through — ยังไม่มี session แต่ไฟล์ stamp token/owner มา = ผ่านเข้าเลยไม่ต้องกด login */
|
||||||
const detect = useCallback(async () => {
|
const detect = useCallback(async () => {
|
||||||
setPhase("checking");
|
setPhase("checking");
|
||||||
setBootError(null);
|
setBootError(null);
|
||||||
try {
|
try {
|
||||||
const token = await authService.getAccessToken();
|
let properties: metadataService.TrackedDocumentProperties | null = null;
|
||||||
if (!token) {
|
if (!(await authService.getAccessToken())) {
|
||||||
setPhase("signed-out");
|
// e-Court pass-through — อ่านไฟล์ก่อนตัดสิน: ไฟล์ที่ stamp token/owner มา = ผ่านเลยไม่ต้องกด login
|
||||||
return;
|
// (นอก Word เช่น browser ธรรมดา → อ่านไม่ได้ = ไป signed-out ตามเดิม)
|
||||||
|
try {
|
||||||
|
properties = await metadataService.readTrackedProperties();
|
||||||
|
} catch {
|
||||||
|
properties = null;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!properties ||
|
||||||
|
!authService.signInFromDocument({ token: properties.ecourtToken, owner: properties.ecourtOwner })
|
||||||
|
) {
|
||||||
|
setPhase("signed-out");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
authService.startAutoRefresh();
|
authService.startAutoRefresh();
|
||||||
setUsername(authService.getUsername() ?? null);
|
setUsername(authService.getUsername() ?? null);
|
||||||
|
|
||||||
const properties = await metadataService.readTrackedProperties();
|
properties ??= await metadataService.readTrackedProperties();
|
||||||
const tracked = properties.savedByAddIn === metadataService.ADDIN_SIGNATURE_GUID;
|
const tracked = properties.savedByAddIn === metadataService.ADDIN_SIGNATURE_GUID;
|
||||||
const id = tracked ? properties.documentTrackingId : undefined;
|
const id = tracked ? properties.documentTrackingId : undefined;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,15 @@ Deploy จริง (intranet): rewrite URL ทุกตัวใน manifest
|
||||||
- **`/docedit/wordaddin?addindemo=untracked`** / **`?addindemo=tracking`** — dev-only: ข้าม Office.js + sign in mock ทันที + ตั้ง phase ตามค่า (tracking มี history ตัวอย่าง 3 รายการ) — ทุกปุ่มกดได้ แต่ flow ที่แตะ Word จริง (Start Versioning / Save & Version / บันทึก) จะจบที่ error ตาม expected
|
- **`/docedit/wordaddin?addindemo=untracked`** / **`?addindemo=tracking`** — dev-only: ข้าม Office.js + sign in mock ทันที + ตั้ง phase ตามค่า (tracking มี history ตัวอย่าง 3 รายการ) — ทุกปุ่มกดได้ แต่ flow ที่แตะ Word จริง (Start Versioning / Save & Version / บันทึก) จะจบที่ error ตาม expected
|
||||||
- หมายเหตุ: ใน browser ปกติ (ไม่ใช่ demo) banner "บันทึกอัตโนมัติไม่สำเร็จ" จะโผล่หลัง ~30 วิ เพราะ auto-save (FR-17) พยายาม save ทุก 30 วิแต่ไม่มี Word — ถือว่าถูกต้อง
|
- หมายเหตุ: ใน browser ปกติ (ไม่ใช่ demo) banner "บันทึกอัตโนมัติไม่สำเร็จ" จะโผล่หลัง ~30 วิ เพราะ auto-save (FR-17) พยายาม save ทุก 30 วิแต่ไม่มี Word — ถือว่าถูกต้อง
|
||||||
|
|
||||||
|
## ทดสอบ e-Court pass-through (ข้าม login)
|
||||||
|
|
||||||
|
ไฟล์ที่ e-Court stamp custom properties `token` และ/หรือ `owner` ไว้ จะเข้าใช้ได้เลยโดยไม่ต้องกด Sign in — `detect()` อ่านค่าทั้งสองตอนเปิด task pane แล้วเรียก `authService.signInFromDocument()` (username ได้จาก `owner` → ถอด `uid` ใน JWT payload → `NEXT_PUBLIC_ADDIN_MOCK_USERNAME`, token จากไฟล์เก็บใน memory ให้ `getAccessToken()` ใช้ต่อ)
|
||||||
|
|
||||||
|
ทดสอบเองใน Word โดยไม่ต้องรอ e-Court: เปิดไฟล์ → **File → Info → Properties → Advanced Properties → แท็บ Custom** → เพิ่ม property ชื่อ `token` (ค่า JWT ตัวอย่าง `eyJ...` — payload มี `uid` ก็แสดงเป็นชื่อผู้ใช้ได้) และ/หรือ `owner` (เช่น `u1001`) → เปิด task pane ใหม่ → ต้องเข้าเฟส untracked/tracking ทันทีไม่ผ่านหน้า login
|
||||||
|
|
||||||
## การสลับกลับของจริง (วันหน้า)
|
## การสลับกลับของจริง (วันหน้า)
|
||||||
|
|
||||||
- **Auth:** `wordaddin/services/authService.ts` เป็น mock คง surface เดิมครบ (`login/getAccessToken/refreshAccessToken/getUsername/signOut/startAutoRefresh/...`) — เขียน implementation จริงทับ (ต้นฉบับ Keycloak PKCE + Office Dialog API อยู่ที่ `D:\word-addin-poc\addin\src\services\authService.ts`; ถ้าใช้ ต้องเพิม DialogAPI กลับเข้า manifest + หน้า auth-start/auth-callback)
|
- **Auth:** `wordaddin/services/authService.ts` เป็น mock คง surface เดิมครบ (`login/getAccessToken/refreshAccessToken/getUsername/signOut/startAutoRefresh/...`) — เขียน implementation จริงทับ (ต้นฉบับ Keycloak PKCE + Office Dialog API อยู่ที่ `D:\word-addin-poc\addin\src\services\authService.ts`; ถ้าใช้ ต้องเพิม DialogAPI กลับเข้า manifest + หน้า auth-start/auth-callback) · `signInFromDocument` (pass-through จาก token/owner ในไฟล์) เป็น mock-only — วันหน้า token ในไฟล์ควรเป็นแค่ตัวจับคู่เอกสาร ส่วนสิทธิ์ผู้ใช้พิสูจน์ตอน runtime
|
||||||
- **API:** `wordaddin/services/versioningService.ts` เก็บชื่อ/ลายเซ็นเหมือนต้นฉบับเป๊ะ — ก้อบ body เดิม (multipart POST ผ่าน `apiClient` ซึ่งพอร์ตไว้แล้ว) กลับมา + ตั้ง `NEXT_PUBLIC_ADDIN_API_BASE_URL`
|
- **API:** `wordaddin/services/versioningService.ts` เก็บชื่อ/ลายเซ็นเหมือนต้นฉบับเป๊ะ — ก้อบ body เดิม (multipart POST ผ่าน `apiClient` ซึ่งพอร์ตไว้แล้ว) กลับมา + ตั้ง `NEXT_PUBLIC_ADDIN_API_BASE_URL`
|
||||||
- ชุด unit test ของ services (jest → vitest) ยังไม่พอร์ต — ต้นฉบับอยู่ที่ `D:\word-addin-poc\addin\src\services\*.test.ts` + mock ที่ `src/test/`
|
- ชุด unit test ของ services (jest → vitest) ยังไม่พอร์ต — ต้นฉบับอยู่ที่ `D:\word-addin-poc\addin\src\services\*.test.ts` + mock ที่ `src/test/`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ export type LoginResult = { ok: true } | { ok: false; error: string };
|
||||||
|
|
||||||
/** state เดียวของ mock — null = signed out */
|
/** state เดียวของ mock — null = signed out */
|
||||||
let username: string | null = null;
|
let username: string | null = null;
|
||||||
|
/** token จาก custom properties ของไฟล์ (e-Court pass-through) — เก็บใน memory เท่านั้น */
|
||||||
|
let docToken: string | null = null;
|
||||||
|
|
||||||
function delay(ms: number): Promise<void> {
|
function delay(ms: number): Promise<void> {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
@ -31,7 +33,40 @@ export function signInForDemo(): void {
|
||||||
username = config.mockUsername;
|
username = config.mockUsername;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** ถอด field จาก JWT payload (base64url) โดยไม่ตรวจ signature — ใช้ดู uid ประกอบชื่อผู้ใช้เท่านั้น */
|
||||||
|
function decodeJwtField(token: string, field: string): string | undefined {
|
||||||
|
const payload = token.split(".")[1];
|
||||||
|
if (!payload) return undefined;
|
||||||
|
try {
|
||||||
|
// atob คืน binary string (1 char = 1 byte) — ต้องถอด UTF-8 ด้วย TextDecoder ก่อน JSON.parse
|
||||||
|
// ไม่งั้น uid ที่ไม่ใช่ ASCII (เช่น ชื่อไทย) จะกลายเป็น mojibake
|
||||||
|
const bytes = Uint8Array.from(atob(payload.replace(/-/g, "+").replace(/_/g, "/")), (char) => char.charCodeAt(0));
|
||||||
|
const json: unknown = JSON.parse(new TextDecoder().decode(bytes));
|
||||||
|
const value = typeof json === "object" && json !== null ? (json as Record<string, unknown>)[field] : undefined;
|
||||||
|
return typeof value === "string" && value !== "" ? value : undefined;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* e-Court pass-through (ระยะแรก) — ไฟล์ที่ e-Court stamp `token`/`owner` ไว้ใน custom properties
|
||||||
|
* ใช้งานได้เลยโดยไม่ต้องกด login: username จาก owner → uid ใน token → mockUsername
|
||||||
|
* คืน true = ผ่านเข้าได้ (มี token หรือ owner อย่างน้อยหนึ่งตัว)
|
||||||
|
*/
|
||||||
|
export function signInFromDocument(input: { token?: string; owner?: string }): boolean {
|
||||||
|
const token = input.token?.trim() || undefined;
|
||||||
|
const owner = input.owner?.trim() || (token ? decodeJwtField(token, "uid") : undefined);
|
||||||
|
if (!token && !owner) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
username = owner ?? config.mockUsername;
|
||||||
|
docToken = token ?? null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getAccessToken(): Promise<string | null> {
|
export async function getAccessToken(): Promise<string | null> {
|
||||||
|
if (docToken) return docToken;
|
||||||
return username ? `mock-token-${username}` : null;
|
return username ? `mock-token-${username}` : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,4 +93,5 @@ export function startAutoRefresh(): void {
|
||||||
|
|
||||||
export function signOut(): void {
|
export function signOut(): void {
|
||||||
username = null;
|
username = null;
|
||||||
|
docToken = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ export const LAST_VERSION_TIMESTAMP_PROPERTY = "LastVersionTimestamp";
|
||||||
/** FR-16 — stamp เฉพาะไฟล์ร่าง (draft) ที่เว็บสร้าง: template ต้นทาง (ค่าเดียวกับ AddInMetadata.TemplateIdPropertyName ฝั่ง backend) */
|
/** FR-16 — stamp เฉพาะไฟล์ร่าง (draft) ที่เว็บสร้าง: template ต้นทาง (ค่าเดียวกับ AddInMetadata.TemplateIdPropertyName ฝั่ง backend) */
|
||||||
export const TEMPLATE_ID_PROPERTY = "TemplateId";
|
export const TEMPLATE_ID_PROPERTY = "TemplateId";
|
||||||
|
|
||||||
|
/** e-Court pass-through — ไฟล์ที่ e-Court stamp มาใน custom properties เข้าใช้ได้เลยไม่ต้องกด login */
|
||||||
|
export const ECOURT_TOKEN_PROPERTY = "token";
|
||||||
|
export const ECOURT_OWNER_PROPERTY = "owner";
|
||||||
|
|
||||||
/** ค่า property ที่หาไม่เจอเป็น `undefined` (ไม่ใช่ throw) */
|
/** ค่า property ที่หาไม่เจอเป็น `undefined` (ไม่ใช่ throw) */
|
||||||
export interface TrackedDocumentProperties {
|
export interface TrackedDocumentProperties {
|
||||||
savedByAddIn?: string;
|
savedByAddIn?: string;
|
||||||
|
|
@ -31,6 +35,9 @@ export interface TrackedDocumentProperties {
|
||||||
lastVersionTimestamp?: string;
|
lastVersionTimestamp?: string;
|
||||||
/** มีเฉพาะไฟล์ร่าง (FR-16) — ส่งต่อให้ backend ตอน Start Versioning เพื่อผูก Document กับ template */
|
/** มีเฉพาะไฟล์ร่าง (FR-16) — ส่งต่อให้ backend ตอน Start Versioning เพื่อผูก Document กับ template */
|
||||||
templateId?: string;
|
templateId?: string;
|
||||||
|
/** e-Court pass-through — มีอย่างใดอย่างหนึ่ง = ข้ามหน้า login (username จาก owner หรือ uid ใน token) */
|
||||||
|
ecourtToken?: string;
|
||||||
|
ecourtOwner?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** item ใน Custom XML Part `<VersionHistory>` */
|
/** item ใน Custom XML Part `<VersionHistory>` */
|
||||||
|
|
@ -41,7 +48,8 @@ export interface XmlVersionEntry {
|
||||||
user: string;
|
user: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** อ่าน custom properties ทั้ง 5 ตัวที่ระบบสนใจ (4 ตัวของ tracked + TemplateId ของไฟล์ร่าง FR-16) */
|
/** อ่าน custom properties ทั้ง 7 ตัวที่ระบบสนใจ (4 ตัวของ tracked + TemplateId ของไฟล์ร่าง FR-16
|
||||||
|
* + token/owner ที่ e-Court stamp มากับไฟล์) */
|
||||||
export async function readTrackedProperties(): Promise<TrackedDocumentProperties> {
|
export async function readTrackedProperties(): Promise<TrackedDocumentProperties> {
|
||||||
return Word.run(async (context) => {
|
return Word.run(async (context) => {
|
||||||
const properties = context.document.properties.customProperties;
|
const properties = context.document.properties.customProperties;
|
||||||
|
|
@ -50,12 +58,16 @@ export async function readTrackedProperties(): Promise<TrackedDocumentProperties
|
||||||
const lastVersionId = properties.getItemOrNullObject(LAST_VERSION_ID_PROPERTY);
|
const lastVersionId = properties.getItemOrNullObject(LAST_VERSION_ID_PROPERTY);
|
||||||
const lastVersionTimestamp = properties.getItemOrNullObject(LAST_VERSION_TIMESTAMP_PROPERTY);
|
const lastVersionTimestamp = properties.getItemOrNullObject(LAST_VERSION_TIMESTAMP_PROPERTY);
|
||||||
const templateId = properties.getItemOrNullObject(TEMPLATE_ID_PROPERTY);
|
const templateId = properties.getItemOrNullObject(TEMPLATE_ID_PROPERTY);
|
||||||
|
const ecourtToken = properties.getItemOrNullObject(ECOURT_TOKEN_PROPERTY);
|
||||||
|
const ecourtOwner = properties.getItemOrNullObject(ECOURT_OWNER_PROPERTY);
|
||||||
// load "value" ของ null-object proxy ก็ทำให้ isNullObject แม่นหลัง sync
|
// load "value" ของ null-object proxy ก็ทำให้ isNullObject แม่นหลัง sync
|
||||||
savedByAddIn.load("value");
|
savedByAddIn.load("value");
|
||||||
documentTrackingId.load("value");
|
documentTrackingId.load("value");
|
||||||
lastVersionId.load("value");
|
lastVersionId.load("value");
|
||||||
lastVersionTimestamp.load("value");
|
lastVersionTimestamp.load("value");
|
||||||
templateId.load("value");
|
templateId.load("value");
|
||||||
|
ecourtToken.load("value");
|
||||||
|
ecourtOwner.load("value");
|
||||||
await context.sync();
|
await context.sync();
|
||||||
|
|
||||||
const read = (property: Word.CustomProperty): string | undefined =>
|
const read = (property: Word.CustomProperty): string | undefined =>
|
||||||
|
|
@ -69,6 +81,8 @@ export async function readTrackedProperties(): Promise<TrackedDocumentProperties
|
||||||
lastVersionId: read(lastVersionId),
|
lastVersionId: read(lastVersionId),
|
||||||
lastVersionTimestamp: read(lastVersionTimestamp),
|
lastVersionTimestamp: read(lastVersionTimestamp),
|
||||||
templateId: read(templateId),
|
templateId: read(templateId),
|
||||||
|
ecourtToken: read(ecourtToken),
|
||||||
|
ecourtOwner: read(ecourtOwner),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue