Customizing File Uploads

This section describes, in a single place, the collection of user custom exits which facilitate the handling of cloud-resident attachments, image field values, and document field values.

The basic requirement described in this section is to make cloud-based or repository-based files behave like uploaded file objects in ExtraView. Generally, ExtraView stores upload objects in the file system or the database; some user custom exits permit storage of upload objects in user-defined external storage.

User custom exits support cloud-based file systems due to the wide variety of cloud storage types and the assumptions of the current upload process. No two cloud-based systems have the same interface.

Upload Process Operations

Each of these operations requires specific processing by user custom exits when the upload object is in the cloud or a non-ExtraView repository. The user custom exits defined in this section are the only ones used for cloud/repository upload objects. There are other, specific exits that deal directly with file Attachments, for example, ucDeleteAttachment, but these should not be used since they may or may not be invoked for cloud/repository upload objects. The only legacy interfaces used for these upload types are ucViewAttachment and ucViewDocument.

The upload process consists of the following high-level, user-initiated, operations:

Upload Screen Interaction - ucUploadSetForm

A user custom exit is made available to populate an HTML DIV on the upload page with HTML created within the user custom exit. The DIV will become visible based on the MODE of the upload screen. This is selectable by the user from one of three modes: Drag and Drop, Standard and Repository. All messages and button titles visible by the user are customizable.

The user custom exit modifies a tags HashMap that is used with the inbuilt addAttachment.html template to produce the upload page. There are two mandatory tags of interest to the user custom callout:

  • NO_REPOSITORY – should be set to “false” to include the repository DIV
  • REPOSITORY_DIV_HTML – should be set to the HTML to include inside the DIV used for repository upload

Other tags of interest are:

  • REPOSITORY_TOOLTIP – is the tooltip shown in a balloon for mouseover event on the repository anchor image
  • USE_REPOSITORY_UPLOAD_MSG – is the text shown for the repository mode switch anchor

The submit button used for standard mode form submission is in the tags HashMap with the key SUBMIT_BUTTON. This can be reused for inclusion in the repository html.

The parameter uploadObjectType is either ATTACHMENT, IMAGE or DOCUMENT. Interface:

public void ucUploadSetForm ( HashMap tags, 
                              SesameSession session, 
                              Connection dbconn, 
                              String uploadObjectType ) throws Exception ;

Submitted Upload Form Actions - ucUploadProcessForm

While it is possible for the repository DIV to generate a form submission that bypasses all existing base ExtraView code, there would be substantial work involved in the new user custom service that simply replicated existing code. Therefore, it is strongly recommended that the upload HTML designer use the existing action/option and HTML FORM for submission to the server.

A user custom exit is made available to process the form parameters and request object received from the upload page. This user custom exit is responsible for:

  • Parameter validation
  • Upload object creation (attachment, image_item_udf, or document_item_udf) – this includes thumbnail generation
  • Upload object insertion to the database

This custom exit is not responsible for:

  • Providing the service action/option method
  • Collecting the parameters from the multipart form
  • Closing the upload popup

Once the user has entered the necessary information to the upload screen, the form is submitted as a multipart form, and the ExtraView base code parses the request. The result consists of form parameters in a HashMap, representing the values entered, or pre-seeded on the upload page. If the page is submitted in the Repository mode, the form parameters and the request are passed to the user custom exit ucUploadProcessForm defined here. A new, partially constructed uploadObject is passed as a parameter. This will be either an Attachment, a DocumentItemUDF, or an ImageItemUDF object that can be used in creating the upload object in the database. In this object the required fields that are populated are: ITEM_ID, UDF_ID (if any), CREATED_BY_USER and UPDATED_BY_USER. Other system-generated fields such as LAST_DATE_UPDATED are populated during transactions on the upload object. Aside from the pre-populated fields and the system-generated fields, the user custom exit is responsible for setting the object values using its setter methods.

The form parameters here do not support multi-valued parameters. This is a legacy-based restriction due to the way the parameters are handled internally in ExtraView. If multiple values are needed for a specific parameter, it is recommended that the parameter name be uniquefied with numbers, e.g., file1, file2, file3, …. in order to work around this restriction.

The string returned by this method is an error message that will alert the user in the upload screen; null for successful upload. Interface:

public String ucUploadProcessForm ( HashMap formParameters, 
                              SesameSession session, 
                              Connection dbconn, 
                              String uploadObjectType,
                              Object uploadObject ) throws Exception

There are special considerations for the ADD screen because the ITEM_ID is 0 as the issue has yet to be added to the database and assigned its ITEM_ID. For image and document data types, when the ITEM_ID is 0, the executeTransactionUncommitted should be used to store the imageItemUDF; otherwise, use the executeTransaction method.

For attachments, when the ITEM_ID is 0, the attachment is stored in an attachment list in the current session. When the issue is later committed to the database, the proper fields will be filled in and the attachment will be stored in the database.

Render Upload Object in Browser - ucViewDocument & ucViewAttachment

Two user custom exits are available to view the upload object in the browser according to its MIME type. This stage also includes the rendering of a thumbnail image of the object.

There are two methods for rendering an upload object from a repository:

  • ucViewDocument – used for viewing image and document data types
  • ucViewAttachment – used for viewing attachments

Interface:

public boolean ucViewDocument (OutputStream out, ImageItemUDF iiu) ;

public boolean ucViewAttachment (OutputStream out, Attachment attachment);

Edit Upload Object Metadata - ucUploadEditObject

For attachments, some parts of the object metadata are editable, e.g. the file description. There is a user custom exit for validation/modification of the edited metadata for cloud/repository upload objects. User modification of upload objects is restricted to the file description, also known as ALT_TEXT, and the charset encoding. A user custom exit is provided to process the modifications before they are made to the object. This exit may validate the parameters, produce an error message, or allow the transaction to proceed. Interface:

public String ucUploadEditObject ( HashMap formParameters, 
                              SesameSession session, 
                              Connection dbconn, 
                              String uploadObjectType,
                              Object uploadObject ) throws Exception

Delete Upload Object - ucUploadDeleteObject

All upload objects may be deleted based on actions by the user, e.g. the deletion of containing issue. There is a user custom exit for cloud/repository object deletion to permit recycling of resources owned by the upload object.

public String ucUploadDeleteObject ( SesameSession session, 
                              Connection dbconn, 
                              String uploadObjectType,
                              Object uploadObject ) throws Exception

The user custom exit ucDeleteAttachment is not invoked with cloud upload objects. The string returned by these methods is an error message that will alert the user in the edit/delete screen; null for successful transaction.

Clone Upload Object - ucUploadClone

When an upload object is contained in an issue that is being cloned, and the upload object is a cloud/repository-based object, a user custom method can be invoked to populate a cloned object. At a minimum, any thumbnails should be copied, and other repository operations may be needed.

In the clone operation on the edit screen, contained upload objects must be replicated into the new issue. In the interface, the newUploadObject is the partially populated new upload object (Attachment, ImageItemUDF, or DocumentItemUDF) that can be used to perform the transaction to store it in the database. Interface:

public String ucUploadClone ( Object uploadObject, 
                              SesameSession session, 
                              Connection dbconn, 
                              String uploadObjectType,
                              Object newUploadObject ) throws Exception

Thumbnail images are considered to be contained in the upload object; therefore, they must not be shared between objects, and in a clone operation, the thumbnail must be created as a new object in the database.

Finish Upload Operation - ucUploadFinish

After the completion of an upload operation, there may be resources that were allocated during the upload that need to be recycled. The Finish Upload operation permits cleanup, as it is the last operation performed after the upload is complete. Interface:

public String ucUploadFinish ( Object uploadObject, 
                              SesameSession session, 
                              Connection dbconn, 
                              String uploadObjectType ) throws Exception

The String return value is an error message or null if successful.

Get Upload Object Content - ucUploadGetContent

The binary content of the uploaded object must be made available for email inclusion. Emails that include attachments, images, or documents generally have a thumbnail of the object in addition to the binary object in its entirety, not just a link. The ucUploadGetContent method is invoked to populate the email message with the content of the object. The thumbnail is obtained directly from the object in the database. Interface:

public InputStream ucUploadGetContent ( Object uploadObject, 
                              SesameSession session, 
                              Connection dbconn, 
                              String uploadObjectType ) throws Exception

The InputStream returned value must stream the binary contents of the upload object for inclusion in emails.

Implementation Considerations

The metadata associated with document or image field object consists of the following (those preceded by “*” are values that should be defined by user custom code):

  • IMAGE_ITEM_UDF_ID – auto-generated primary key for document and image data types
  • UDF_ID – the UDF id of the containing field
  • DATE_CREATED – auto-generated timestamp
  • LAST_DATE_UPDATED – auto-generated timestamp
  • LAST_UPDATED_BY_USER – auto-generated user id
  • CREATED_BY_USER – auto-generated user id
  • ITEM_ID – the item id of the containing item
  • *MIME_TYPE – the MIME type of the object
  • *STORED_INTERNAL – Y for database-resident objects; N for file system objects; R for repository-based objects
  • *EXTERNAL_IDENT – string containing enough key information to identify the object externally; this is opaque to ExtraView base code
  • *ALT_TEXT – also known as File Description, this is text that is important for accessibility guideline compliance
  • VALUE_BLOB – when stored in the database, the blob identifier
  • *THUMBNAIL_BLOB – when thumbnail is present, the blob identifier of the thumbnail image
  • *FILE_NAME – a file name, usually the one specified in the upload screen; must not be null or blank
  • *CHARSET – the charset name for the character set in which the file is stored. This is used ONLY for text files (usually with an extension of “.txt”, but may be other files based on the MIME type)
  • *FORMAT – IGNORE, TEXT or BINARY: used by quick search to generate an index for this object
  • *EXTENSION – a file extension, e.g., “.txt” to identify the file type for quick search

The metadata associated with an attachment object consists of the following (those preceded by “*” are values that should be defined by user custom code):

  • ATTACHMENT_ID– auto-generated primary key for attachments
  • *FILE_DESC – also known as ALT TEXT, this is text that is important for accessibility guideline compliance
  • *FILE_NAME– a file name, usually the one specified in the upload screen; must not be null or blank
  • *PATH– a file directory path, usually the one specified in the upload screen
  • *CONTENT_TYPE– the MIME type of the object
  • *FILE_SIZE – the size in bytes of the object
  • DATE_CREATED– auto-generated timestamp
  • CREATED_BY_USER– auto-generated user id
  • ITEM_ID – the item id of the containing item
  • *CHARSET – the charset name for the character set in which the file is stored. This is used ONLY for text files (usually with an extension of “.txt”, but may be other files based on the MIME type)
  • *STORED_INTERNAL – Y for database-resident objects; N for file system objects; R for repository-based objects
  • *EXTERNAL_IDENT – string containing enough key information to identify the object externally; this is opaque to ExtraView base code
  • LAST_DATE_UPDATED– auto-generated timestamp
  • LAST_UPDATED_BY_USER– auto-generated user id
  • *TYPE_ID – 0 for attachment object, 1 for thumbnail
  • *THUMBNAIL_ID – ID of the thumbnail attachment object associated with this attachment (non-null only in attachment objects, null for thumbnail objects)

Implementation Notes

  1. The user custom code should not modify any database tables directly. The developershould use the internal Attachment, DocumentItemUDF and ImageItemUDF objects to set values and transact with the database
  2. The CHARSET for documents and attachments may be entered by the user at the time the object is uploaded. If the cloud/repository cannot determine the correct character set of the object, then the form should include the CHARSET drop-down list as is done for the Standard upload mode.

QuickFind for Cloud/Repository Upload Objects

QuickFind is not capable of building indexes based on the contents of cloud/repository objects. A future release will cover this functionality.  

Code Example - Thumbnail Creation for ImageItemUDF

This is a Java source code example of how to create a thumbnail and store it into an ImageItemUDF object in the database. In this example, the image file is assumed to be readable (iiu.getTempFile()).

int maxPixelsPerDimension = 4000;
String appDefaultMax = ApplicationDefault.getAttribute("MAX_IMAGE_DIMENSION_PIXELS");
if (isANumber(appDefaultMax)) {
  maxPixelsPerDimension = Integer.parseInt(appDefaultMax);
}
// check against max dimensions...
int[] dimensions = ImageProcess.getDimensions( iiu.getTempFile(), "");
if (dimensions[0] > maxPixelsPerDimension || dimensions[1] > maxPixelsPerDimension)
       throw new Exception(Z.m.msg(session, "Image too large"));
  // make the thumbnail...
  thumbFile = File.createTempFile("thumb", iiu.getFileName(), new File(Z.getTempDir()));
  status = (new ImageProcess()).makeThumbnail(iiu.getTempFile(), thumbFile, 
                                         iiu.getFileName());
  if (status) {
    iiu.setThumbnailSize( (int) thumbFile.length());
    thumbIs = new FileInputStream(thumbFile);
    iiu.setThumbnailBlobIS(thumbIs);
    is = new FileInputStream(iiu.getTempFile());
    iiu.setValueBlobIS(is);
    String userId = session.getUserId();
    iiu.executeTransactionUncommitted(userId);
    is.close();
    is = null;
    // If we got here with no exceptions commit and return;
    dbconn.commit();
    status = true;
  } else throw new Exception(Z.m.msg(session, 
               "The most probable reason is that it is not an image file."));

Code Example - Creating a Thumbnail for Attachments

Thumbnails for Attachments are separate Attachment objects with a type_id = 1. To create a thumbnail, the following example may be used:

public boolean generateThumbnail(Attachment orig, Connection dbConn) {
  boolean status = false;
  String origFilename = orig.getFileName();
  String outFilename = null;
  File strtsFile = orig.getTempFile();
  String strtsFilename  = orig.getOrigFileName();
  if (origFilename != null) 
            outFilename = origFilename.substring(0,origFilename.lastIndexOf(".")) +
            "_thm" + origFilename.substring(origFilename.lastIndexOf("."));
  File thumbnailFile = new File(strtsFile.getParent(),outFilename);
  String test = thumbnailFile.getAbsolutePath();

  if (ImageProcess.canMakeThumbnail(origFilename)){
    try {
      DbTime dbt = new DbTime(dbConn);
      ImageProcess ip = new ImageProcess();
      status = ip.makeThumbnail(strtsFile,thumbnailFile,origFilename);
      this.tempFile = thumbnailFile;
      if (this.tempFile != null) this.tempFilePath = thumbnailFile.getAbsolutePath();
      this.origFileName = outFilename;
      this.fileName = outFilename;
      this.fileSize = thumbnailFile.length();
      this.problemId = orig.getProblemId();
      this.dateCreated = dbt.getDbNowTimestamp();
      if (!status) {
        this.errorMsg = Z.m.msg(session, 
                    "Cannot generate thumbnail. The file is not the right type.");
      }
    } catch (Exception e) {
      //ErrorWriter.write(e, ErrorWriter.LOGERR);
      status = false;
    }
  }
  // Log the failure here, success will be logged later
  return status;
}