Monday, April 27, 2009

Viewing PeopleCode Online

Is is possible to see PeopleCode asscociated with any Record Field/Component/Page etc, online( Through PIA) ?

Yes, thanks to %matadata API.

Use following code at behind a push button (Field Change) and you'll see the code as a message pop up. ( Please enter record, field and event name in below code.)


import %metadata:PeopleCodeProgram:PeopleCodeProgram;
import %metadata:PeopleCodeProgram:PeopleCodeProgram_Manager;
import %metadata:Key;

Local %metadata:PeopleCodeProgram:PeopleCodeProgram_Manager &mgr = create %metadata:PeopleCodeProgram:PeopleCodeProgram_Manager();
Local %metadata:Key &key = create %metadata:Key(Key:Class_Record, , Key:Class_Field, , Key:Class_Method, ;
Local %metadata:PeopleCodeProgram:PeopleCodeProgram &Prog = &mgr.GetDefn(&key);

If &Prog <> Null then

Local string &sProgramText = &Prog.GetProgram();
winmessage(&sProgramText ,0);

else

Winmessage("No PeopleCode Event Defined",0);

end-if;

Monday, April 20, 2009

Resume/File Upload Validations (Virus Scan)

Resume Upload functionality is an important part of Recruiting process. Likewise, in campus solution, file upload technique are used for different interface processing. I would like to highlight some of the validations which can be imposed over these file to meet business needs.

a) File Type Condition : Consider a requirement wherein business wants to put a constrain on resume upload functionality in terms of file type and they want only '.doc or .pdf format' files to be uploaded.

One can see this validation working in Reporting Tools > XML Publisher > Report Definition. Under Validation tab we select a Template Type and under Template tab we upload a Template File. If type of the file we're uploading is not matching with the one we selected in Template Type page, we'll get an error. Checkout Record PeopleCode PSXPRPTTMPF_WRK.PSXPUPLOADTMPLPB.FieldChange.

b) File Size Condition: Say, we've a limitation on upper size of file.

This one is simple as AddAttachment Function has an argument MaxSize which will take care of this validation. Syntax of AddAttachment goes like:
AddAttachment(URLDestination, DirAndFilename, FileType, UserFile, MaxSize [, PreserveCase, UploadPageTitle]).

When the file size will exceed the permissible value, the return code would be %Attachment_FileExceedsMaxSize ( Numeric value: 6)

Code Snippet ( From PBooks):

&retcode = AddAttachment(URL.MYFTP, ATTACHSYSFILENAME, "", ATTACHUSERFILE, 0);

If (&retcode = %Attachment_FileExceedsMaxSize) Then
MessageBox(0, "File Attachment Status", 0, 0, "AddAttachment failed: File exceeds the max size");
.....
.....
End-If;

c) Virus Scanning: Assume that client wants to scan the uploaded file for virus. This one is interesting as PeopleSoft doesn't support this out-of-box.
[ Updated on 5/12/09: In PT 8.50, Virus Scan is a delivered feature with AddAttachement ( We need to do the setup at Web Server Level) ]

You may ask why do we need this scan after uploading the file, why can't we scan it upfront before uploading it. Well, when external applicants uploads their resume, PeopleSoft has no control on those resume unless it's got uploaded into the server. Now how can be scan for virus for the uploaded file ? Here is the high level step :

1) Assume file is uploaded into the table PS_RESUME_TBL ( consists subrecord FILE_ATTDET_SBR) [ you may upload the file in file server but saving file as BLOB/CLOB in DB is advisable].

2) Create a file ( of the same type) in temp and write the content of file from the table using WriteRaw. Code would be something like...

&RS = CreateRowset(Record.RESUME_TBL);
&query = "WHERE ";
&RS.Fill(&query);
Local File &File = GetFile("C:\temp\Resume.pdf", "w", "a", %FilePath_Absolute);
&reqFile.WriteRaw(&RS.GetRow(1).GetRecord(Record.RESUME_TBL).GetField(Field.FILE_DATA).Value);
&reqFile.Close();
[PS: This code is for reference only & not been tested, you can write a better code than this ]

3) We need to have a Command Line Virus Scanner installed in the file server which can scan this file for virus. We need a mechanism through which a) we can run a Virus Scanner from Command Line and b) We can run it for any specific file.
There are many such Antivirus product in the market. For testing purpose I've installed McAfee VirusScan Command Line Scanner for Windows.

To scan a file using this antivirus from command prompt, the command is: SCAN
To scan a file using this antivirus from command prompt and if found infected then to be deleted , the command is: SCAN /DEL
So in peoplecode we'll need to execute the command SCAN C:\temp\Resume.pdf /DEL using Exec function in Synchronous mode. This piece of code will make (a) antivirus to scan the file Resume.pdf first and (b) if the file is found infected, it will be deleted.

4) Now we need to check if this file Resume.pdf exists in the file server or not. If it still exists, then it's virus free...else it's virus infected. In case the file is not found in the server, we need to delete the row from PS_RESUME_TBL for this file and send a message to user about the infected file.

Code should be written at FieldChange peoplecode.
Refer http://www.rexswain.com/eicar.html for test virus file.

Tuesday, April 7, 2009

Achieving Dynamic Search Record Functionality

This was an interesting requirement. Customer wanted the Search record of a Component to be changed dynamically- When different set of users accesses the same Component.( Assume, Different Search record for Different Permission List/Role).

One quick solution for similar problem is: Registering the component with another Menu and over-ridding the search record of component at Menu item level. PeopleSoft has used this approach at few places (e.g For Job Component), but this approach will invariably need another Portal Content Reference ( a different navigation) which customer might not agree for.

Analysis of this requirement:
a) Can't we change the search record of component the way we change the prompt table associated with a record-field (Remember %Edittable1,%Edittable2...) ? No, If we enter these values, error message pops up saying Invalid search record.
b) Can't we leave the search record field empty and fill it at run time ? No, this is a mandatory data while creating a component ( It has to be, and you know why)
c) Is there a way to change search record of a component at run time ? Yes, we can, but that will be 'changing Metadata' which is shared by all the users. So, if change search record 'REC1' for the component then this search record will be shared among all the users accessing that component (unless we keep on changing it for all different users). Please understand that PeopleTools table PSPNLGRPDEFN is not user (oprid or role or permission list) dependent and we should not alter it. (PS: We take this approach when we need to maintain User's role dynamically in PSROLEUSER table).
d) We are not quite sure which PeopleCode event should be used to achieve above mentioned three points.
e) We need to understand the purpose of Search record in a component and how PeopleSoft makes use of it.
(1) The search/alt srch fields in the search record are the fields that brings the Search Dialog Box to the User as a search criteria.
(2) The search record field populates the high-level, or level 0, key fields on a page.
(3) The search record for a component might differ from the primary record definition for a page and might contain any number of search keys, but it must contain all of the keys for each main level 0 record for a page.
(4) Out of the Box 'Row level Security' is implemented by PeopleSoft using Search Record only. We can add our own logic to filter out the data based on business rules.
(4) There is no Events outside the Component Processor flow which can manipulate the values returned by the search record. When component starts building up from reset state, the first two PeopleCode event which triggers are SearchInit and SearchSave written over Search Record of the Component.These PCode events have specific purposes and can't control the data fetched from the search record.

Proposed Solution:

Lets say, for Permission list 'HCCPHR3310' Search Record should be 'SEARCH_VW1', for 'HCCPHR3320'--> 'SEARCH_VW2' and for 'HCCPHR3330'--> 'SEARCH_VW3'.
( I assume these search records are custom sql views covering all the business rules and no more validation is required on top of this)

Step 1: By now we understand that changing search record of component dynamically is not advisable/feasible. However, we can control the data returned by the search record. For this, we'll create a Dynamic View (RKA_DYN_VW) which will serve our purpose. The Dynamic View will have all the search and alt search record required - level 0 key fields of a page. ( Assume EMPLID and EMPL_RCD are two search key fields in our Dynamic View)

Step 2: Create a Sql something like this (Sql Name: RKA_DYN_SQL):
SELECT EMPLID
,EMPL_RCD
FROM %Table(%P(1))-----> will Pass the Record Name
WHERE EXISTS (
SELECT 'X'
FROM PSROLECLASS A
, PSROLEUSER B
, PSOPRDEFN C
WHERE A.ROLENAME = B.ROLENAME
AND C.OPRID = B.ROLEUSER
AND C.OPRID = %OperatorId----> Will take Current user's Operator Id
AND A.CLASSID=%P(2))----> Will pass the Permission List

Step 3:
Create a new SQL RKA_UDYN_SQL, as below:

%Sql(RKA_DYN_SQL,SEARCH_VW1,'HCCPHR3310')
UNION %Sql(RKA_DYN_SQL,SEARCH_VW2,'HCCPHR3320')
UNION %Sql(RKA_DYN_SQL,SEARCH_VW3,'HCCPHR3330')

step 4:

View Defn of RKA_DYN_VW

SELECT DISTINCT ABC.EMPLID
, ABC.EMPL_RCD
FROM (%Sql(RKA_UDYN_SQL)) ABC

So, at run time this dynamic view will expand to:

SELECT DISTINCT A.EMPLID
, A.EMPL_RCD
FROM (
SELECT EMPLID
,EMPL_RCD
FROM PS_SEARCH_VW1
WHERE EXISTS (
SELECT 'X'
FROM PSROLECLASS A
, PSROLEUSER B
, PSOPRDEFN C
WHERE A.ROLENAME = B.ROLENAME
AND C.OPRID = B.ROLEUSER
AND C.OPRID = 'PSHC'
AND A.CLASSID='HCCPHR3310')
UNION
SELECT EMPLID
,EMPL_RCD
FROM PS_SEARCH_VW2
WHERE EXISTS (
SELECT 'X'
FROM PSROLECLASS A
, PSROLEUSER B
, PSOPRDEFN C
WHERE A.ROLENAME = B.ROLENAME
AND C.OPRID = B.ROLEUSER
AND C.OPRID = 'PSHC'
AND A.CLASSID='HCCPHR3320')
UNION
SELECT EMPLID
,EMPL_RCD
FROM PS_SEARCH_VW3
WHERE EXISTS (
SELECT 'X'
FROM PSROLECLASS A
, PSROLEUSER B
, PSOPRDEFN C
WHERE A.ROLENAME = B.ROLENAME
AND C.OPRID = B.ROLEUSER
AND C.OPRID = 'PSHC'
AND A.CLASSID='HCCPHR3330')) ABC

PS:
*** We can write SearchInit PeopleCode to handle Search record prompt record change (Using Edittable or by any other approach over %PermissionLists).
*** Validation can be taken care by SearchSave PeopleCode
*** There could to a simple approach to achieve the same result (the way Row Level Sec is implemented using SJT). But in this present solution there is a high degree of flexibility because of the sql object RKA_UDYN_SQL.

Wednesday, March 25, 2009

Auto Tab In PeopleSoft

We had an interesting requirement to implement Auto-tab functionality in PeopleSoft. In a grid with 3 columns, ( Say Field ABC, Field MNO, Field PQR from record XYZ each having length of 3 Character), when user will start inserting data in Field ABC, after entering 3 characters the cursor should automatically move to Field MNO and so on.

Approach:
To make generic solution, we added following piece of javascript code in delivered PT_EDITSCRIPT HTML definition:

/* Custom JavaScript Function */
function autotab(original,destination){
if (original.getAttribute&&original.value.length==original.getAttribute("maxlength"))
destination.focus()
return "";
}

And then added an HTML area in the requested page with a constant value as below:

and it worked well !

Friday, March 20, 2009

Random Password Generation

Many a times we generate random password for user in PeopleSoft ( eg. New Oprid) and then send this auto-generated password via Email. In PeopleCode, we can use the Rand function to generate a random number greater than or equal to 0 and less than 1. For example:
&password="ABC" | Int(Rand( )*1000);
In case we want to generate random password using SQL ( say in Set-based processing) we can use DBMS_RANDOM package in Oracle.
SELECT DBMS_RANDOM.STRING('P', 10) FROM PS_INSTALLATION;
(Reference: Generating random numbers and strings in Oracle)

Tuesday, March 10, 2009

About Fields

How can we find...
a) Type of a field ?
b) Attributes of a field in a record ( Key Field, Search Key Field, Required Field, etc... [Everything what we see in a record field property]) ?
c) Default value of a record-field ?
d) Prompt Table, Set Control Field associated with a record-field ?
e) and many such field/record-field attributes?

Prime source of information would be: PSDBFIELD, PSRECDEFN, PSRECFIELDALL, PSRECFIELDDB. However there are other tables (including Lang tables) which we might use based on our requirements but these 4 tables are being used more frequently.

If you wish to know how to work with USEEDIT, references are:
a) App Package EOEW: App Class Common-- Nicely written.
b) The Mystical USEEDIT -- Wisely explained.

*********************************************************************************
Update For 8.50 (11/11/2009)

Two new features have been added in 8.50 in terms of Record Field property:
a) Prompt Search Event
b) Type Ahead

Following Sql can be used to determine if these properties are set for ant record-field:

SELECT
A.RECNAME,
A.FIELDNAME,
CASE
WHEN bitand(A.USEEDIT, 134217728) > 0 THEN
'YES'
END AS PromptSearchEvent,
CASE
WHEN bitand(A.USEEDIT, 1073741824) > 0 THEN
'YES'
END AS TypeAhead
FROM PSRECFIELD A,
PSDBFIELD B
WHERE A.RECNAME = 'PS_ABC_TBL'
AND B.FIELDNAME='XYZ'
AND A.FIELDNAME = B.FIELDNAME