sharepointsharepoint-onlinepnp-js

Exporting a PPTX to PDF in Sharepoint Online with PnPjs and .pdfConversionUrl


We are interacting with Sharepoint Online's Document Library through the PnPjs framework and by using a Sharepoint App Add-In (We can't use Graph API at this time)

Our add-in has full control of our site:

<AppPermissionRequests AllowAppOnlyPolicy="true">
    <AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="FullControl" />
</AppPermissionRequests>

We are able to do almost every operation we want - create/delete folders, add files, copy files, etc. The one thing that we are stuck on is exporting a .pptx file that has been uploaded to Sharepoint to a .pdf file, from code.

Here is how I am retrieving the .pdfConversionUrl from the Document Library list to formulate a request to Microsoft's media transform service:

    let options: RenderListDataOptions = RenderListDataOptions.EnableMediaTAUrls | RenderListDataOptions.ContextInfo | RenderListDataOptions.ListData | RenderListDataOptions.ListSchema;
    const viewXml: string = `
    <View Scope='RecursiveAll'>
        <Query>
            <Where>
                <In>
                    <FieldRef Name='ID' />
                    <Values>
                        <Value Type='Counter'>{{OUR SP DOCUMENTS LIST ID}}</Value>                            
                    </Values>
                </In>
            </Where>
        </Query>
        <RowLimit>1</RowLimit>
    </View>`;
    
    const listStream = await sp.web.lists.getById({{OUR SP DOCUMENTS LIST ID}}).renderListDataAsStream({ RenderOptions: options, ViewXml: viewXml });
    /*
    * response contains this property:
    * '.pdfConversionUrl': '{.mediaBaseUrl}/transform/pdf?provider=spo&inputFormat={.fileType}&cs={.callerStack}&docid={.spItemUrl}&{.driveAccessToken}'
    *  mediaBaseUrl, callerStack and driveAccessToken values are also included
    */     
    const pdfTransformURL = `${listStream.ListSchema['.mediaBaseUrl']}/transform/pdf?provider=spo&inputFormat=pptx&cs=${listStream.ListSchema['.callerStack']}&docid={{LINK TO THE SP PPTX FILE}}&${listStream.ListSchema['.driveAccessToken']}`;        
    console.log(pdfTransformURL);

Here is the URL that is generated:

https://northcentralus1-mediap.svc.ms/transform/pdf?provider=spo&inputFormat=pptx&cs={{OUR SP CALLER STACK}}&docid={{LINK TO OUR SP PPTX FILE}}&access_token={{OUR ACCESS TOKEN}}

When we make a GET request to that URL, the response will contain the stream of the PDF file which we can then upload to Sharepoint. The process is outlined by the PnP team in this youtube video

However, when I make a GET request to that URL through node-fetch or directly in a browser, I get this error message:

{
    "error":
    {
        "code":"generalException",
        "message":"Exception: The remote server returned an error: (403) Forbidden. Access+denied.+Before+opening+files+in+this+location%2c+you+must+first+browse+to+the+web+site+and+select+the+option+to+login+automatically.",
        "innererror":{"code":"Web_403Forbidden"}
    }
}

We googled it, and found that we need to enable the option in the SP admin panel to 'Allow Apps that Don't use Modern Authentication'. We did that, but we still get the same error message.

If anyone out there has tried this before, please let us know how we can resolve this error. Thanks!


Solution

  • Found my issue- my CAML query was wrong. It should have been this:

    const viewXml: string = `
    <View Scope='RecursiveAll'>
        <Query>
            <Where>
                <In>
                    <FieldRef Name='ID' />
                    <Values>
                        <Value Type='Counter'>{{Document ID, NOT List Id}}</Value>                            
                    </Values>
                </In>
            </Where>
        </Query>
        <RowLimit>1</RowLimit>
    </View>`;
    

    Then, use the .spItemUrl that comes as part of the response inside of the ListData.Row elements

    Hope this helps someone