Word addin doesn't work on word online

2019-09-12 05:56发布

Developing a Word addin were you can pick a docx-file and it gets inserted in the new blank document - a templateHandler sort of.

Every docx-file has a footer that is containing several RTF - contentControlers. Before a document is loaded (insertFileFromBase64) there is a thisDocument.body.clear() in the code.

In local wordclient there is no problem to load the different docs, but in word online I get an error regarding body.clear() : The action is not supported with Word Online

EDIT: The code is adjusted so that header and footer are separated from docx-file and is inserted with insertOoxml. They are loaded initialy when you open the addin. body.clear() is not an issue anymore.

There is also isues with the contenControllers in the footer. They sease to exits in the online version.

EDIT: They are visible now as the footer is loaded separatly from the docx. Only big problem with addin now is - that you only can edit on one line in the document when you open the addin. If you klick on text it gets invisible. If you change to another docx file via loading one from the addin the text get invisible in body.

I'm using office.js 1.1 The office365 that I'm running is an evaluationversion for developers: V 1609 (Build 7369.2127)

This is the content of main.js:

 console.log("mains.js is present! ");

(function () {
"use strict";

// Initialize
Office.initialize = function (reason) {
    $(document).ready(function () {
        app.initialize();
        // Use this to check whether the new API is supported in the Word client.
        if (Office.context.requirements.isSetSupported("WordApi", "1.1")) {

            console.log('Den här koden är optimerad för Word 2016.');

            // Load the doc names from the service into the UI. 
            getDocNames();
            console.log("Getting Documents");



            addFooter(); 
            //console.log("Adding Footer");

            addHeader();
            //console.log("Adding Header");

            //getDataFromSelection();
            //getAllContentControls();

            // Setup the event handlers for UI.
            $('#selectDocument').change(selectDocumentHandler);
            $('#changeContent').click(contentControlHandler); //TODO:validateFields
            $('#clearContent').click(clearContent);
            $('#get-data-from-selection').click(getDataFromSelection);

        }
        else {
            console.log('Du måste använda Word 2016 för att det här ska fungera.');
        }
    });
};
function clearContent() {
    $('input[type=text]').val('');
}

        // Function for getting header and footer

function getHeaderFooter(fileName) {

    var myOOXMLRequest = new XMLHttpRequest();

    var myXML;

    myOOXMLRequest.open('GET', fileName, false);

    myOOXMLRequest.send();

    if (myOOXMLRequest.status === 200) {

        myXML = myOOXMLRequest.responseText;
        return myXML;
    }
Office.context.document.setSelectedDataAsync(myXML, { coercionType: 'ooxml' });
    //return "";
}

/* Word JS functions */

function addHeader() {
    // Run a batch operation against the Word object model.
    Word.run(function (context) {

        // Create a proxy sectionsCollection object.
        var sections = context.document.sections;
        //mySections.body.clear();
        // Queue a commmand to load the sections.
        context.load(sections, 'body/style');

        return context.sync().then(function () {

            var header = sections.items[0].getHeader("primary");
            //header.clear();
            var templateHeader = getHeaderFooter('/xml/simrisHeader.xml');

            header.insertOoxml(templateHeader, Word.InsertLocation.replace);

            return context.sync().then(function () {
                console.log("Added a header to the first section.");
            });
        });
    })
        .catch(function (error) {
            console.log('Error: ' + JSON.stringify(error));
            if (error instanceof OfficeExtension.Error) {
                console.log('Debug info: ' + JSON.stringify(error.debugInfo));
            }
        });
}

function addFooter() {
    Word.run(function (context) {

        var sections = context.document.sections;
        //context.document.clear();
        context.load(sections, 'body/style');
        return context.sync().then(function () {

            var footer = sections.items[0].getFooter("primary");
            //context.footer.clear();

            var templateFooter = getHeaderFooter('/xml/simrisFooter.xml');

            footer.insertOoxml(templateFooter, Word.InsertLocation.replace);
            // var contentControls = footer.contentControls;
            //getAllContentControls();

            return context.sync().then(function () {
                console.log("Added a footer to the first section.");
            });
        })
            .catch(function (error) {
                console.log('Error: ' + JSON.stringify(error));
                if (error instanceof OfficeExtension.Error) {
                    console.log('Debug info: ' + JSON.stringify(error.debugInfo))
                }
            });

    });
}

    function displayContents(myBase64) {
        Word.run(function (context) {

            var thisDocument = context.document;

            thisDocument.body.clear();

            var mySelection = thisDocument.getSelection();

            mySelection.insertFileFromBase64(myBase64, "replace");

            return context.sync()
                .then(function () 
                    getAllContentControls();          
                });
        })
            .catch(function (error) {
                app.showNotification('Error: ' + JSON.stringify(error));
                if (error instanceof OfficeExtension.Error) {
                    app.showNotification('Debug info: ' + JSON.stringify(error.debugInfo));
                }
            });
    }

// Using the Word JS API. Gets all of the content controls that are in the loaded document. 
function getAllContentControls() {
    Word.run(function (context) {

        var thisDocument = context.document;

        var contentControls = thisDocument.contentControls;

        contentControls.load('tag, title');

        return context.sync(contentControls).then(function () {

            var uniqueFields = removeDuplicateContentControls(contentControls);

            // Create HTML inputfields based on the content controls.
            createFields(uniqueFields);

        })
    })
        .catch(function (error) {
            app.showNotification('Error: ' + JSON.stringify(error));
            if (error instanceof OfficeExtension.Error) {
                app.showNotification('Debug info: ' + JSON.stringify(error.debugInfo));
            }
        });
}

// Using the Word JS API. Set the values from the INPUT elements into the associated
// content controls to make the doc. 
function contentControlHandler() {

    var entryFields = document.getElementById("entryFields").querySelectorAll("input");

    // Loading the contentcontrols again.
    Word.run(function (context) {

        // Create a proxy object for the document.
        var thisDocument = context.document;

        // Create a proxy object for the contentcontrol collection in the document.
        var contentControls = thisDocument.contentControls;

        // Queue a command to load the contentcontrols collection with the tag and title properties.
        contentControls.load('tag, title');

        return context.sync()
            .then(function () {

                var i, j;

                // Map each input element to the associated content control.
                for (i = 0; i < entryFields.length; i++) {
                    for (j = 0; j < contentControls.items.length; j++) {
                        // Matching content control tag with the tag set as the id on each input element.
                        if (contentControls.items[j].tag === entryFields[i].id && entryFields[i].value != 0) {
                            contentControls.items[j].insertText(entryFields[i].value.trim(), Word.InsertLocation.replace)
                        }
                    }
                }
            })
            .then(context.sync);

    })
        .catch(function (error) {
            app.showNotification('Error: ' + JSON.stringify(error));  //console.log
            if (error instanceof OfficeExtension.Error) {
                app.showNotification('Debug info: ' + JSON.stringify(error.debugInfo)); //console.log
            }
        });
}

// Handles the doc selection in the add-in UI. Results in a call to the service to get
// a docx file that contains a doc. The doc gets displayed in the Word UI.
function selectDocumentHandler() {

    // Form the URL to get the docx file. Need to remove the host information by slicing
    // off the host information beginning at ?_host_Info.
    var fileName = this.value;
    var currentUrl = location.href.slice(0, location.href.indexOf('?'));
    var getFileUrl = currentUrl + 'getfile?filename=' + fileName;

    // Call the helper to get the selected file then insert the file into Word.
    httpGetAsync(getFileUrl, function (response) {
        displayContents(response);
    });
}

/* UI functions */

function getDocNames() {

    // Form the URL to get the docx file list. Need to remove the host information by slicing
    // off the host information beginning at ?_host_Info.
    var currentUrl = location.href.slice(0, location.href.indexOf('?'));
    var getFileNamesUrl = currentUrl + 'getfilenames';

    // Call the helper to get the file list and then create the dropdown listbox from the results.
    httpGetAsync(getFileNamesUrl, function (rawResponse) {

        // Helper that processes the response so that we have an array of filenames.
        var response = processResponse(rawResponse);

        // Get a handle on the empty drop down list box.
        var select = document.getElementById("selectDocument");

        // Populate the drop down listbox.       
        for (var i = 0; i < response.length; i++) {
            var opt = response[i];
            var el = document.createElement("option");
            el.text = opt;
            el.value = opt;
            select.appendChild(el);
        };

        //$(".ms-Dropdown").Dropdown();
    });
}

// Creates HTML inputfields
function createFields(uniqueFields) {

    // Get the DIV where we will add out INPUT-controls.
    var entryFields = document.getElementById("entryFields");

    // Clear the contents in case it has already been populated with INPUT controls.
    while (entryFields.hasChildNodes()) {
        entryFields.removeChild(entryFields.lastChild);
    }

    // Create a unique INPUT element for each unique contentcontrol-tag.
    for (var i = 0; i < uniqueFields.length; i++) {

        var newLabel = document.createElement("label");
        newLabel.appendChild(document.createTextNode(uniqueFields[i].title + ': '));
        document.getElementById('entryFields').appendChild(newLabel);

        var input = document.createElement("input");

        newLabel.className = 'ms-label';

        input.type = "text";

        input.id = uniqueFields[i].tag;

        input.className = 'ms-TextField-field';

        entryFields.appendChild(input); //, input.value

        entryFields.appendChild(document.createElement("br"));

    }

}

// Get data from AAD and displays user information
function getDataFromSelection() {
    var baseEndpoint = 'https://graph.microsoft.com';
    var authContext = new AuthenticationContext(config);

    Office.context.document.getSelectedDataAsync(Office.CoercionType.Text,
        function (result) {
            if (result.status === Office.AsyncResultStatus.Succeeded) {

                authContext.acquireToken(baseEndpoint, function (error, token) {
                    if (error || !token) {
                        app.showNotification("Ingen token: ", "Du får logga in igen."); // + error
                    }
                    var email = authContext._user.userName;
                    var url = "https://graph.microsoft.com/v1.0/" + config.tenant + "/users/" + email;

                    var html = "<ul>";
                    $.ajax({
                        beforeSend: function (request) {
                            request.setRequestHeader("Accept", "application/json");
                        },
                        type: "GET",
                        url: url,
                        dataType: "json",
                        headers: {
                            'Authorization': 'Bearer ' + token,
                        },
                        success: function (response) {
                            console.log('Hämtar inehåll och populerar Kontroller!');
                            //utilize your callback function
                            postDataToContentControlers(response);
                        }
                    }).done(function (response) {
                        html += getPropertyHtml("Namn", response.displayName);
                        html += getPropertyHtml("Titel", response.jobTitle);
                        html += getPropertyHtml("Avdelning", response.officeLocation);
                        html += getPropertyHtml("Telefon jobb", response.businessPhones);
                        $("#results").html(html);
                    }).fail(function (response) {
                        app.showNotification('Inloggningen slutade att fungera!', 'Du får logga ut och prova att logga in igen'); //response.responseText
                    });
                });
            } else {
                app.showNotification('Error:', 'Något gick fel. Du får logga in igen.'); //result.error.message
            }
        }
    );
}

function getPropertyHtml(key, value) {
    return "<li><strong>" + key + "</strong> : " + value + "</li>";
}

function postDataToContentControlers(response) {
    // Loading the contentcontrols again.
    Word.run(function (context) {

        // Create a proxy object for the document.
        var thisDocument = context.document;

        // Create a proxy object for the contentcontrol collection in the document.
        var contentControls = thisDocument.contentControls;

        // Queue a command to load the contentcontrols collection with the tag and title properties.
        contentControls.load('tag, title');

        return context.sync()
            .then(function () {

                var i, j;

                var responseArrayKey = Object.keys(response).map(function (v) { return v });

                // Map each data element to the associated content control.
                for (i = 0; i < responseArrayKey.length; i++) {

                    for (j = 0; j < contentControls.items.length; j++) {

                        console.log("responseArrayKey:  ", responseArrayKey);
                        // Matching content control tag with the index of each responseArray.
                        if (contentControls.items[j].tag === responseArrayKey[i]) {

                            var responseArrayValue = Object.keys(response).map(function (k) { return response[k] });

                            contentControls.items[j].insertText(responseArrayValue[i], Word.InsertLocation.replace)

                        }
                    }
                }
            })
            .then(context.sync);
    })
        .catch(function (error) {
            app.showNotification('Error: ' + JSON.stringify(error));  //console.log
            if (error instanceof OfficeExtension.Error) {
                app.showNotification('Debug info: ' + JSON.stringify(error.debugInfo)); //console.log
            }
        });
}

/* Helper functions */

// Helper that deduplicates the set of contentcontrols.
function removeDuplicateContentControls(contentControls) {

    var i,
        len = contentControls.items.length,
        uniqueFields = [],
        currentContentControl = {};

    for (i = 0; i < len; i++) {
        currentContentControl[contentControls.items[i].tag] = contentControls.items[i].title;
    }

    for (i in currentContentControl) {

        var obj = {
            tag: i,
            title: currentContentControl[i]
        };

        uniqueFields.push(obj);
    }

    return uniqueFields;
}

// Helper for calls to the service. 
function httpGetAsync(theUrl, callback) {
    var request = new XMLHttpRequest();
    request.open("GET", theUrl, false);
    request.onreadystatechange = function () {
        if (request.readyState === 4 && request.status === 200)
            callback(request.responseText);
    }
    request.send(null);
}

    // Function for getting the HeaderTemplate

    function getHeaderTemplate(fileName) {

    var myOOXMLRequest = new XMLHttpRequest();

    var myXML;

    myOOXMLRequest.open('GET', fileName, false);

    myOOXMLRequest.send();

    if (myOOXMLRequest.status === 200) {

        myXML = myOOXMLRequest.responseText;
        return myXML;
    }

    return "";
}

// Helper that processes file names into an array. 
function processResponse(rawResponse) {

    // Remove quotes.
    rawResponse = rawResponse.replace(/"/g, "");

    // Remove opening brackets.
    rawResponse = rawResponse.replace("[", "");

    // Remove closing brackets.
    rawResponse = rawResponse.replace("]", "");

    // Return an array of file names.
    return rawResponse.split(',');
}

// Helper function for treating errors
function errorHandler(error) {
    showNotification(error, JSON.stringify(error.debugInfo));
    console.log("Error: " + error);
    if (error instanceof OfficeExtension.Error) {
        console.log("Debug info: " + JSON.stringify(error.debugInfo));
    }
}

function validateFields() {
    //TODO: validating fields
    console.log('Validating fields');
}

})();

Content of index.html

Content of manifest

Getting the docs is handled with nodejs

Hope someone has an answer to the problem. Thanks =)

2条回答
小情绪 Triste *
2楼-- · 2019-09-12 06:19

Thanks for providing the docx file.

The 'Tab' before the content control in body is not supported and it makes the content control to be unsupported. Therefore, the body.clear() is not allowed since there is something unsupported inside.

I suggest you to delete the 'Tab' in local wordclient and upload the docx file online. Body.clear() would work well in this way.

PS: Paragraph-Center could be used to help align content control center.

查看更多
相关推荐>>
3楼-- · 2019-09-12 06:33

I am an engineer from MS working on Word Rich APIs.

And I have tried your script code in displayContents. As you said above, the script here would call body.clear() before loading the file (calling insertFileFromBase64). The body.clear() operation is not allowed in word online.

I think there might be some content which is not supported on word online, such as field range, table row content control.

If you can provide the docx file you're using, it would be better for us to help validate this opinion and find the root cause.

查看更多
登录 后发表回答