This is an article to document how to produce a report which when exported to PDF will display images without a user wall.
Why?
Our client needs to send a report to suppliers that include photos of the faulty products. These display fine in a Zoho Creator report and even show up on the preview when exporting to PDF. But when you download the PDF and view this outside of the Zoho instances, the photos don't display.
This is because the photos were just links in the Zoho Creator image field, links to another Zoho app that is behind a user wall (ie. login and password as a Zoho User).
How?
So I'll go over what happens in the real world process; then cover some technical information; and even include the function that makes it work:
Business Process
- Customer visits website to report a faulty product; using website form or chat.
- Customer can upload or send to agent to upload to a Zoho Desk support ticket as an attachment.
- The ticket will appear in a Quality Control report in Zoho Creator
- Staff can access the report via Zoho Creator and export it to PDF.
- They can then send the PDF to the supplier.
Technical Information
- Automations in the background, on update of ticket or attachment webhook, will push some of the details of the ticket and all/any attachments to a Zoho Creator form.
- The ticket form has the ticket reference, product information, factory/supplier, the fault/description, and a subform to a form called "Documents" linked by a bi-directional ticket lookup.
- The document form is a subform derived from an existing form (called "Document"). Mainly, because I don't know how to refer to the public URL of a subform row other than making the subform link to an existing form and not a blank form.
- The document form contains the fields: Ticket (lookup to Ticket form), Image, File, Local Image, Subform Row ID (prevents duplicates). Note: I create the form then add it as a subform on the parent form first, then add the lookup to the ticket field on the subform so that I get the popup to enable bi-directional rather than via the tickbox.
- The below code is added to a trigger whenever the ticket form is submitted either on creation or modification.
The function to rule-them-all
This code was used for testing, then for correcting any historical records, and then when given just one ticket reference; will create or update the images associated in the subform:
void Standalone.fn_DownloadAndCacheAttachments(string p_TicketRef) { v_TicketRef = ifnull(p_TicketRef,""); c_TicketRecords = QC_Report_Table[Ticket_Number == v_TicketRef]; for each c_TicketRecord in c_TicketRecords { info "Ticket Ref: " + c_TicketRecord.Ticket_Number; v_CountImages = 0; v_CountNew = 0; for each c_ImageRow in c_TicketRecord.Images { v_ImageRowID = c_ImageRow.ID.toString(); v_CountImages = v_CountImages + 1; v_ImageHtml = ifnull(c_ImageRow.Image,""); if(v_ImageHtml.contains("\"")) { v_ImageSrc = v_ImageHtml.getSuffix("\""); v_ImageSrc = v_ImageSrc.getPrefix("\""); if(!isBlank(v_ImageSrc)) { // use a try...catch as some file types may cause this to partially fail try { v_CountNew = v_CountNew + 1; f_DownloadFile = invokeurl [ url :v_ImageSrc type :GET connection:"zdesk" ]; f_DownloadFile.setParamName("file"); // // add into subform (actual form "Document"). Uses row.ID as the identifier to prevent duplicates. c_CheckDoc = Document[Subform_Row_ID == v_ImageRowID]; // // if exists then update if(c_CheckDoc.count() > 0) { c_CheckDoc.Image=c_ImageRow.Image; c_CheckDoc.File_field=f_DownloadFile; v_UploadFileID = c_CheckDoc.ID; } else { v_UploadFileID = insert into Document [ Added_User=zoho.loginuser Ticket_field=c_TicketRecord.ID Image=c_ImageRow.Image File_field=f_DownloadFile Subform_Row_ID=c_ImageRow.ID ]; } // // store this after the record is created/updated (was getting error if included in record creation - if in same row). // c_ImageRow.File_field = f_DownloadFile; // // let's get the local image cache reference so it is exportable to PDF c_Document = Document[ID == v_UploadFileID]; // // publish key of "Documents" report v_PublishKey = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; v_DocFileName = ifnull(c_Document.File_field,""); // // used for blocking certain file extensions (not used in this function) v_DocExtension = v_DocFileName.subString(v_DocFileName.lastIndexOf(".") + 1).toLowerCase(); // // All_Documents is the report, File_field is the field. v_ThisDocSrc = "https://creator.zoho.eu/file" + zoho.appuri + "All_Documents/" + c_Document.ID + "/File_field/download/" + v_PublishKey + "?filepath=/" + v_DocFileName; // // Store the link to the local file as the local image - this is where we could check on file extension and substitute with PDF, DOC, XLS extensions c_Document.Local_Image = "<img src=\"" + v_ThisDocSrc + "\" />"; info "File: " + v_DocFileName; } catch (e) { info e.message; } } } } info "Created/Updated " + v_CountNew + " of " + v_CountImages; info "----------------"; } }
- void Standalone.fn_DownloadAndCacheAttachments(string p_TicketRef)
- {
- v_TicketRef = ifnull(p_TicketRef,"");
- c_TicketRecords = QC_Report_Table[Ticket_Number == v_TicketRef];
- for each c_TicketRecord in c_TicketRecords
- {
- info "Ticket Ref: " + c_TicketRecord.Ticket_Number;
- v_CountImages = 0;
- v_CountNew = 0;
- for each c_ImageRow in c_TicketRecord.Images
- {
- v_ImageRowID = c_ImageRow.ID.toString();
- v_CountImages = v_CountImages + 1;
- v_ImageHtml = ifnull(c_ImageRow.Image,"");
- if(v_ImageHtml.contains("\""))
- {
- v_ImageSrc = v_ImageHtml.getSuffix("\"");
- v_ImageSrc = v_ImageSrc.getPrefix("\"");
- if(!isBlank(v_ImageSrc))
- {
- // use a try...catch as some file types may cause this to partially fail
- try
- {
- v_CountNew = v_CountNew + 1;
- f_DownloadFile = invokeUrl
- [
- url :v_ImageSrc
- type :GET
- connection:"zdesk"
- ];
- f_DownloadFile.setParamName("file");
- //
- // add into subform (actual form "Document"). Uses row.ID as the identifier to prevent duplicates.
- c_CheckDoc = Document[Subform_Row_ID == v_ImageRowID];
- //
- // if exists then update
- if(c_CheckDoc.count() > 0)
- {
- c_CheckDoc.Image=c_ImageRow.Image;
- c_CheckDoc.File_field=f_DownloadFile;
- v_UploadFileID = c_CheckDoc.ID;
- }
- else
- {
- v_UploadFileID = insert into Document
- [
- Added_User=zoho.loginuser
- Ticket_field=c_TicketRecord.ID
- Image=c_ImageRow.Image
- File_field=f_DownloadFile
- Subform_Row_ID=c_ImageRow.ID
- ];
- }
- //
- // store this after the record is created/updated (was getting error if included in record creation - if in same row).
- // c_ImageRow.File_field = f_DownloadFile;
- //
- // let's get the local image cache reference so it is exportable to PDF
- c_Document = Document[ID == v_UploadFileID];
- //
- // publish key of "Documents" report
- v_PublishKey = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
- v_DocFileName = ifnull(c_Document.File_field,"");
- //
- // used for blocking certain file extensions (not used in this function)
- v_DocExtension = v_DocFileName.subString(v_DocFileName.lastIndexOf(".") + 1).toLowerCase();
- //
- // All_Documents is the report, File_field is the field.
- v_ThisDocSrc = "https://creator.zoho.eu/file" + zoho.appuri + "All_Documents/" + c_Document.ID + "/File_field/download/" + v_PublishKey + "?filepath=/" + v_DocFileName;
- //
- // Store the link to the local file as the local image - this is where we could check on file extension and substitute with PDF, DOC, XLS extensions
- c_Document.Local_Image = "<img src=\"" + v_ThisDocSrc + "\" />";
- info "file: " + v_DocFileName;
- }
- catch (e)
- {
- info e.message;
- }
- }
- }
- }
- info "Created/Updated " + v_CountNew + " of " + v_CountImages;
- info "----------------";
- }
- }