Skip to content

Instantly share code, notes, and snippets.

@richie5um
Created August 1, 2022 11:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richie5um/a59303a401cbd8c16a6d81c49ff32c3a to your computer and use it in GitHub Desktop.
Save richie5um/a59303a401cbd8c16a6d81c49ff32c3a to your computer and use it in GitHub Desktop.
name: Smz-CustomXml-v2
description: ''
host: WORD
api_set: {}
script:
content: |
$("#get-xml").click(() =>
tryCatch(() => {
getCustomJson();
})
);
$("#get-contentcontrols").click(() =>
tryCatch(() => {
getContentControls();
})
);
$("#delete-contentcontrol").click(() =>
tryCatch(() => {
deleteContentControl();
})
);
$("#list-xml").on("click", ".delete", function() {
const id = $(this).attr("id");
console.log(id);
removeCustomJsonById(id);
$(this)
.parent()
.remove();
});
// $("#list-contentcontrols").on("click", ".delete", function () {
// const id = $(this).attr("id");
// console.log(id);
// removeCustomJsonById(id);
// $(this)
// .parent()
// .remove();
// });
async function getCustomJson() {
await Word.run(async (ctx) => {
const promise = new Promise((resolve, reject) => {
const smzNamespace = "https://schemas.summize.com/token/1.0";
Office.context.document.customXmlParts.getByNamespaceAsync(smzNamespace, async (result) => {
if (result.status !== "succeeded") {
return reject(result);
}
const xmlParts = result.value;
let customJsons = [];
for (let xmlPart of xmlParts) {
const id = xmlPart.id.replace("{", "").replace("}", "");
const customJson = await this.getCustomJsonById(id);
customJsons.push({ id, json: customJson });
}
customJsons = customJsons.filter((x) => x !== undefined);
return resolve(customJsons);
});
});
await promise.then((items) => {
$("#list-xml").empty();
for (let item of items) {
const json = JSON.stringify(item, null, 2);
$("#list-xml").append(
`<li><pre>${json}</pre><button id="${item.id}" class="delete" value="Delete">X</button></li>`
);
console.log(json);
}
});
});
}
async function getCustomJsonById(id) {
if (typeof Word === "undefined") {
return Promise.reject();
}
return Word.run(async (ctx) => {
id = `{${id}}`;
const promise = new Promise((resolve, reject) => {
Office.context.document.customXmlParts.getByIdAsync(id, async function(result) {
if (result.status !== "succeeded" || result.value === undefined) {
return reject(result);
}
const xmlPart = result.value;
xmlPart.getNodesAsync("//*", async (result) => {
if (result.status !== "succeeded") {
return reject(result);
}
console.log(result);
// This doesn't feel right, but I can't get the xpath working. So, hey ho.
const nodes = result.value.filter((x) => x.baseName === "json");
if (nodes.length === 0) {
return reject(result);
}
nodes[0].getTextAsync((result) => {
if (result.status !== "succeeded") {
return reject(result);
}
const json = JSON.parse(decodeForXml(result.value));
return resolve(json);
});
});
});
});
return promise;
}).catch((err) => {
console.log("OfficeService-Error: " + JSON.stringify(err));
if (err instanceof OfficeExtension.Error) {
console.log("OfficeService-Debug info: " + JSON.stringify(err.debugInfo));
}
});
}
async function removeCustomJsonById(id: string) {
if (typeof Word === "undefined") {
return Promise.reject();
}
return Word.run(async (ctx) => {
id = `{${id}}`;
const promise = new Promise((resolve, reject) => {
Office.context.document.customXmlParts.getByIdAsync(id, async (result) => {
if (result.status !== "succeeded" && result.value !== undefined) {
return reject(result);
}
const xmlPart = result.value;
xmlPart.deleteAsync((result) => {
if (result.status !== "succeeded") {
return reject(result);
}
console.log(`Deleted SmzToken => ${result.value.id}`);
return resolve(true);
});
});
});
return promise;
}).catch((err) => {
console.log("OfficeService-Error: " + JSON.stringify(err));
if (err instanceof OfficeExtension.Error) {
console.log("OfficeService-Debug info: " + JSON.stringify(err.debugInfo));
}
});
}
async function deleteContentControl() {
if (typeof Word === "undefined") {
return Promise.reject();
}
return Word.run(async (ctx) => {
let selection = ctx.document.getSelection();
ctx.load(selection);
await ctx.sync();
ctx.load(selection.paragraphs, "*");
await ctx.sync();
for (const paragraph of selection.paragraphs.items) {
ctx.load(paragraph.contentControls);
}
await ctx.sync();
for (const paragraph of selection.paragraphs.items) {
if (!paragraph.contentControls.isNull) {
for (let contentControl of paragraph.contentControls.items) {
console.log("Deleting Content Control");
contentControl.delete(true);
}
}
}
await ctx.sync();
}).catch((err) => {
console.log("OfficeService-Error: " + JSON.stringify(err));
if (err instanceof OfficeExtension.Error) {
console.log("OfficeService-Debug info: " + JSON.stringify(err.debugInfo));
}
});
}
async function getContentControls() {
if (typeof Word === "undefined") {
return Promise.reject();
}
return Word.run(async (ctx) => {
let contentControls = ctx.document.body.contentControls;
ctx.load(contentControls);
await ctx.sync();
$("#list-contentcontrols").empty();
if (!contentControls.isNull) {
for (let item of contentControls.items) {
console.log(`${item.title}:${item.tag}`);
$("#list-contentcontrols").append(`<li><div>${item.title}</div><pre>${item.tag}</pre></li>`);
}
}
}).catch((err) => {
console.log("OfficeService-Error: " + JSON.stringify(err));
if (err instanceof OfficeExtension.Error) {
console.log("OfficeService-Debug info: " + JSON.stringify(err.debugInfo));
}
});
}
function encodeForXml(xml: string) {
return xml
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&apos;");
}
function decodeForXml(xml: string) {
return xml
.replace(/&apos;/g, "'")
.replace(/&quot;/g, '"')
.replace(/&gt;/g, ">")
.replace(/&lt;/g, "<")
.replace(/&amp;/g, "&");
}
/** Default helper for invoking an action and handling errors. */
async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user through your add-in's UI.
console.error(error);
}
}
language: typescript
template:
content: "<section class=\"samples ms-font-m\">\n\t<button id=\"delete-contentcontrol\" class=\"ms-Button\">Delete ContentControl</button>\n\t<button id=\"get-xml\" class=\"ms-Button\">Read CustomXml</button>\n\t<ul id=\"list-xml\">\n\t\t<li>Empty</li>\n\t</ul>\n\n\t<button id=\"get-contentcontrols\" class=\"ms-Button\">Read ContentControl</button>\n\t<ul id=\"list-contentcontrols\">\n\t\t<li>Empty</li>\n\t</ul>\n</section>"
language: html
style:
content: |-
section.samples {
margin-top: 20px;
}
section.samples .ms-Button, section.setup .ms-Button {
display: block;
margin-bottom: 5px;
margin-left: 20px;
min-width: 80px;
}
.delete {
background: red;
color: white;
}
language: css
libraries: |-
https://appsforoffice.microsoft.com/lib/1/hosted/office.js
@types/office-js
office-ui-fabric-js@1.4.0/dist/css/fabric.min.css
office-ui-fabric-js@1.4.0/dist/css/fabric.components.min.css
core-js@2.4.1/client/core.min.js
@types/core-js
jquery@3.1.1
@types/jquery@3.3.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment