Contract Approval - Intelligent Viewer : can't publish PDF

While testing the intelligent viewer services in the contract approval application , faced an issue

when execution the action "Publish to PDF" , the issue related to "create_publications" sope

I tried with this scope defintion but still facing same error :

Answers

  • When you originally requested the access token for the app/tenant combo … did the response show the following scopes as being included?
    "scope": "create_publications view_publications search_publications delete_publications readwrite otds:roles otds:groups search"

  • @Roger K , yes , this is the scope

  • @Med_2024
    Did you add any redirect URLs to the service client, could you please share the steps you have performed?

    Any other steps you performed after the service client creation, please do share that as well.

  • @Eshan K , this is the redirect URLs in the service clients

    There was issues in the authentication , so I tried different things based on the thread I opened in the forum , I

    remember unassign/ reassign the app to the tenant

  • Hello @Med_2024 ,

    As far as I can remember, the export buttons are not present in the default viewer display in the contracts_approval application. When you enable them, you should also note that you need to add configurations for the pdfExport and pdfExportDetails configurations.

    Also, please check to see that you have the right token passed to the viewer:

    bravaApi.setHttpHeaders({

              Authorization: `Bearer ${authService.getAuthTokens().access_token}`     

    });

    And lastly, you will need to set the publishing host like this:

     bravaApi.setPublishingHost(window.ViewerAuthority);

    Just like the markupHost, searchHost.

    You also need to understand that the buttons in the viewer only initiate the PDF/TIFF transformation and wait for the publishing to be ready. Then, the viewer component is going to issue an event (similarly to the close event) that you will need to listen for. The event is called <id>_exportSuccess_<operation> where id is the id of the viewer instance (see the close event to see how we get the id) and the operation is what you define in the pdfExportActions configuration. The event detail will contain the publication JSON so you can get the blobId from there.

    You can find more information in the documentation: https://developer.opentext.com/imservices/products/viewingtransformationservices/documentation/viewingserviceapi/2

    Let me know if this solves your issue.

  • @LazarescuA , I am using the viewer with the full set of configuration settings as mentioned in the tutorial ,

    https://github.com/opentext/demo-contract-approval-app/blob/master/src/components/FileViewer.jsx-full

    should I modify something in the code ?

  • Yes, I see the token is set correctly but the Publication service URL is not set.

    Line 607 looks like this:

    bravaApi.setMarkupHost(window.ViewerAuthority);

    Add a new line after:

    bravaApi.setPublishingHost(window.ViewerAuthority);

    This will allow the viewer to successfully create the transformation and wait for it to complete. With the setting above you should be able to see a message in the viewer (after pressing PDF export button) that the publish was completed. But, in order to do something with the new file (like download or save as another rendition/version) you will need to catch the exportSuccess event of the viewer.

    I am going to give you a quick example for the PDF export.

    In the file above, at line 164, you have a JSON object containing the export actions. Keep the id in mind as we will need it later. Let's say we want to intercept the download success, so we keep in mind the id 'download'.

    Then, at line 577, you will see a function called bravaReadyEventListener. This function will be called once the viewerJS is fully loaded. Inside here you already see at line 583 how we treat the close event of our instance (pressing the x button at top right):

    window.addEventListener(${event.detail}-close, closeDialogEventListener);

    We need to do the same for the exportSuccess event, so add a new line after the one above:

    window.addEventListener(${event.detail}-exportSuccess-download, exportSuccessEventListener);

    The function exportSuccessEventListener will receive as input an event. The event.detail will contain the publication details - a JSON where you can get the CSS URL of the newly created file.

    So, for the above to work, you will need a last step, to create the function. Here is how it looks like for me:

    (the Publication JSON contains a lot of information, you will get a blob ID and a URL template that you need to combine in order to get the full URL. The ID is actually a CSS ID so you can also call CSS with that ID and download it)

    const exportSuccessEventListener  = (event) => {
        let publication = event.details;    
        if (publication.status==='Complete') {             //get to the URL       let tmpPdfUrl = '';       if (publication._embedded && publication._embedded['pa:get_publication_artifacts']) {         let artifactsArr = publication._embedded['pa:get_publication_artifacts'];         let foundX = false;         for (let i=0; i<artifactsArr.length; i++) {           if (artifactsArr[i].name==='pdf') {             if (artifactsArr[i].available===true) {               if (artifactsArr[i]._embedded && artifactsArr[i]._embedded['ac:get_artifact_content']) {                 tmpPdfUrl = artifactsArr[i]._embedded['ac:get_artifact_content'].urlTemplate;                 let contentLinks = artifactsArr[i]._embedded['ac:get_artifact_content'].contentLinks;                 for (let c=0; c<contentLinks.length; c++) {                   let curLink = contentLinks[c];                   for (const key in curLink) {                     tmpPdfUrl = tmpPdfUrl.replace(RegExp(`{${key}}`, 'g'), curLink[key]);                     if (key==='id') {                       tmpId = curLink[key];                     }                   }                 }
                    foundX = true;               } else {                 console.log('Found the PDF artifacts node but the ac:get_artifact_content does not exist.')               }             } else {               console.log('Found the PDF artifacts node but it is not available.');             }           }         }         if (!foundX) console.log('Publication complete but did not find the PDF artifacts node.');
          } else {         //cannot get pdf file         console.log('Error while reading the publication URL (/._embedded.pa:get_publication_artifacts[name=pdf]._embedded.ac:get_artifact_content)');       }
        } else {       if (publication.status==='Failed') {         console.log('There is an error in the Publication. Check the publication file.');       }     }
        if (foundX) {         //here is where you will call the CSS service to download the file to the browser     downloadFile(tmpPdfUrl);     }   }

    Lastly, you will need to create the downloadFile function and use AXIOS to do a GET to the URL above. The Axios call options are:

    {
           method: 'get',
           url: tmpPdfUrl,
           headers: { 'Authorization': `Bearer ${user.access_token}`,  'Accept': '*/*' },
          responseType: 'blob'
         };
    

  • Here is an example function for the download using the above URL:

    const downloadFile  = (pdfUrl) => {
        axios({
            method: 'get',
            url: pdfUrl,
            headers: {
              Authorization: `Bearer ${user.access_token}`,
            },
            responseType: 'blob'
          }).then((res) => {
            if (res.data) {
                // create file link in browser's memory
                const href = URL.createObjectURL(res.data);
                // create "a" HTLM element with href to file & click
                const link = document.createElement('a');
                link.href = href;
                link.setAttribute('download', 'Export.pdf'); //or any other extension
                document.body.appendChild(link);
                link.click();
                // clean up "a" element & remove ObjectURL
                document.body.removeChild(link);
                URL.revokeObjectURL(href);
            }
          }).catch((error) => {
            // eslint-disable-next-line no-alert
            alert(
              error.response != null && error.response.data != null
                ? error.response.data : error.message,
            );
          });
      }