jump to navigation

Loading Photos into PeopleSoft in v9.2 June 17, 2014

Posted by Duncan in PeopleSoft 9.2, TW.
trackback

This is a guest post by Anton de Weger, a highly experienced PeopleSoft Consultant who works for StratusHR in Australia. Anton has been previously featured in the post “I’m Anton de Weger and This is How I Work“.

Introduction

As part of a v8.9 to v9.2 upgrade of PeopleSoft HCM my current client wanted to take the opportunity to load all of their employee photos from their security system into PeopleSoft so that they could take advantage of the photos showing on the Talent Summary and Manager Dashboard, as well as on the name mouse-over in the core pages. I thought this would have been a pretty straight forward process given that this is delivered functionality, but was surprised at the lack of examples on the internet and the difficulty in doing image manipulation through PeopleCode.

Requirements:

  • Load employee photos from site specific network directories which had different naming conventions (i.e. Employee ID in file name or random filename with link to employee in Active Directory).
  • Store photos in PeopleSoft delivered record (EMPL_PHOTO).
  • Fix the problem with the photos stretching depending on the size of the original photo when viewed in the mouse over. Numerous cameras were used over time and there were periods when manual clipping was performed on the photos.
  • Automatically pick up new employees when their photos become available.

The solution to this problem was to create an Application Engine to do a load of the photos and a custom Java class to pad out the photos to the 100 height x 80 width aspect ratio that is used by PeopleSoft in the mouse over pages.

Maxchunksize

The first thing that is required before the photos can be loaded is that the database MAXCHUNKSIZE has to be set. This done through the following:

UPDATE PSOPTIONS 
   SET MAXCHUNKSIZE = 500000

Remember to store the original MAXCHUNKSIZE value and reset it at the end of the process.

Run Control

A Run Control page was created with some basic options including the path to the image locations on the network.

image1

Dynamic SQL

Working out which employees to load was done via some dynamic SQL based on the run control parameters. This was then used with a

Local SQL &employees = CreateSQL(&employeesSQL);

and

While &employees.Fetch()…

to loop through all of the employees that need to be checked. This could have just as easily been done through a Do Select in step to give more control over the commit frequency, but I just used a CommitWork(); statement in the loop.

Getting the Filename

This was site specific, either using the Employee ID in the name of the photo file or via a field in Active Directory. The accessing Active Directory was interesting in that I used the delivered LDAP_SEARCH business interlink.

First step was to load up the active directory configuration and test it via the delivered page.

image2

image3

Then this was loaded in PeopleCode via

&LDAP_SRCH_SCHENTRY = GetInterlink(Interlink.LDAP_SEARCH);
&PSDSDIR = CreateRecord(Record.PSDSDIR);
&PSDSSRVR = CreateRecord(Record.PSDSSRVR);
&PSDSDIR.DSDIRID.Value = "MYADDOMAIN";
&PSDSDIR.SelectByKey();
&PSDSSRVR.DSDIRID.Value = "MYADDOMAIN";
&PSDSSRVR.DSSRVR.Value = "INTERNAL.MYADDOMAIN.COM.AU";
&PSDSSRVR.SelectByKey();

&LDAP_SRCH_SCHENTRY.Clear();
&LDAP_SRCH_SCHENTRY.UserID_Attribute_Name = "uid";
&LDAP_SRCH_SCHENTRY.URL = "file://psio_dir.dll";
&LDAP_SRCH_SCHENTRY.BIDocValidating = "Off";
&LDAP_SRCH_SCHENTRY.SSL = "NO";
&rootInDoc = &LDAP_SRCH_SCHENTRY.GetInputDocs("");

&ret = &rootInDoc.AddValue("Connect_DN", &PSDSDIR.DSCNCTDN.Value);
&ret = &rootInDoc.AddValue("Connect_Password", Decrypt("dirmgrpwd", &PSDSDIR.DSCNCTPWD.Value));
&DirSrchParmsDoc = &rootInDoc.AddDoc("Directory_Search_Parms");
&ret = &DirSrchParmsDoc.AddValue("Host", &PSDSSRVR.DSSRVR.Value);
&ret = &DirSrchParmsDoc.AddValue("Port", &PSDSSRVR.LDAPPORT.Value);
&ret = &DirSrchParmsDoc.AddValue("Base", "DC=internal,DC=MYADDOMAIN,DC=com,DC=au");
&ret = &DirSrchParmsDoc.AddValue("Scope", "Sub");
&ret = &DirSrchParmsDoc.AddValue("Filter", "(employeeNumber=" | &emplid | ")");
&ret = &DirSrchParmsDoc.AddValue("Attributes", "comment");
&EXECRSLT = &LDAP_SRCH_SCHENTRY.Execute();

This then brought back the code for the comments field from Active Directory from which the file name was extracted. A simplified version of the code that accessed the comments is as follows:

If (&EXECRSLT = 1) Then
 &rootOutDoc = &LDAP_SRCH_SCHENTRY.GetOutputDocs("");
 &DirEntriesDoc = &rootOutDoc.GetDoc("Directory_Entries");
 &AttributeDoc = &DirEntriesDoc.GetDoc("Attribute");
 &ret = &AttributeDoc.GetValue("Value", &userActiveDirComment);
 &userActiveDirComment = RTrim(&userActiveDirComment);
 /* Process Comments */
End-If;

Pre-Processing the Photos

The ClipImage() PeopleCode function doesn’t work from Application Engine, and I couldn’t find an easy way to pad out the photos in PeopleCode, via a Component Interface or directly in the database, so I decided to use Java to do it. After a bit of research I decided on the imgscalr library to perform the required image manipulation.

Initially I tried calling the imgscalr class directly from PeopleCode, but I couldn’t easily manipulate the image in memory using the “any” PeopleCode variable type or the IMAGE field (via AET) into the bufferedimage type required by the imgScalr class and I was getting overloading issues. I probably could have persisted down this path, either using reflective type conversion or by writing a wrapper java class to remove the function overloading of imgscalr, but in the end I took the quicker (to code) approach of manipulating the image file and saving in a temporary file before loading into PeopleSoft. This was done via a custom java class (PhotoSync.java). This pads out either the height or width of the photo with white to achieve a 100 height x 80 width aspect ratio, while maintaining the full resolution of the original image.

import java.awt.image.*;
import java.awt.Color;
import java.io.*;
import javax.imageio.*;
import static org.imgscalr.Scalr.*;
public class PhotoSync {
public static void main(String[] args) throws IOException {
 }
 
 public static void padToPeopleSoft(String sourceFileName, String targetFileName) throws IOException {
 File sourceImageFile = new File(sourceFileName);
 BufferedImage img = ImageIO.read(sourceImageFile);
int height = img.getHeight();
 int width = img.getWidth();
 int expectedWidth = (int) Math.floor((double)height * 80 / 100);
 
 if (width > expectedWidth) {
 /* Add padding to the top of the image */
 
 int newHeight = (int) Math.floor((double)width / 80 * 100);
 int padding = (int) Math.floor((double)(newHeight - height) / 2);
 newHeight = height + (padding * 2);
 img = pad(img, padding, Color.WHITE);
 img = crop(img, padding, 0, width, newHeight);
 } else {
 if (width < expectedWidth) {
 /* Add padding to the side of the image */
 
 int newWidth = (int) Math.floor((double)height / 100 * 80);
 int padding = (int) Math.floor((double)(newWidth - width) / 2); 
 newWidth = width + (padding * 2);
 img = pad(img, padding, Color.WHITE);
 img = crop(img, 0, padding, newWidth, height); 
 }
 }
 img.createGraphics().drawImage(img, 0, 0, null);
 ImageIO.write(img, "jpg", new File(targetFileName));
 }
}

This was then compiled to create the Class file. Just a note that I ended up having to compile using jdk1.7.0 as the process scheduler was not able to run the Java 8 compiled class.

And the resulting PhotoSync.class file plus the imgScalr classes (in their org\imgScalr directory) were all copied to the PeopleTools\class directory on the process scheduler. In this case it was only distributed to the windows process scheduler, but theoretically it should have worked with other operating systems. This was called from the Application Engine PeopleCode as follows:

Local JavaObject &Photo = GetJavaClass("PhotoSync");
&Photo.padToPeopleSoft(&filename, &targetFilename);

As the Java code has not got error capture built in you have to check the availability of the image first.

Loading the Photo into PeopleSoft

The final step was to load the photo, now resized to the right aspect ratio into PeopleSoft. This was done as follows.

&fPhoto = GetFile(&targetFilename, "E", "", %FilePath_Absolute); 
&sTempFileName = &emplID | ".jpg";
SQLExec("DELETE FROM PSFILE_ATTDET WHERE ATTACHSYSFILENAME = :1", &sTempFileName);
&sResult = PutAttachment("record://PSFILE_ATTDET", &sTempFileName, &fPhoto.Name);
If PHOTO_LOAD_AET.REPLACE_IND = "Y" Then
SQLExec("DELETE FROM PS_EMPL_PHOTO WHERE EMPLID = :1", &emplID);
End-If;
SQLExec("SELECT FILE_DATA FROM PSFILE_ATTDET WHERE ATTACHSYSFILENAME = :1 AND VERSION = 1", &sTempFileName, &Data);
&rec = CreateRecord(Record.EMPL_PHOTO);
&rec.EMPLID.Value = &emplID;
&rec.PSIMAGEVER.Value = (Days365(Date3(1999, 12, 31), %Date) * 86400) + (%Time - Time3(0, 0, 0));
&rec.EMPLOYEE_PHOTO.Value = &Data;
&rec.Insert();
SQLExec("DELETE FROM PSFILE_ATTDET WHERE ATTACHSYSFILENAME = :1", &sTempFileName);

The Result

image4

This shows the padding in the image in yellow (rather than white). The photos retained the original resolution so were either large or small depending on when they were taken. It would be possible using this method to standardise the size of the photos as well.

image5

This is the mouse-over on the name for the employee.

Accreditation

Anton de Weger has been working with PeopleSoft since 1994 and currently works for Stratus HR (www.stratushr.com) a small specialist consultancy with a couple of other experienced colleagues.

 

Advertisement
%d bloggers like this: