Thursday, November 21, 2019

Fluid Attachment Framework

I recently had a requirement to have a run control page with attachment(s) so that an Application Engine can process the file.

I decided to implement this as a Fluid page and using the Fluid Attachment Framework, which was implemented as part of Image 25, and there are a couple of write ups on the feature:


Implementing this was fairly easy, and the only issues I had were:
  1. The subpage which was inserted into the run control page was the first item, and saving the page in Application Designer gave an error "More than one data record in scroll".
    • Once I changed this to not be the first page item, i.e. I moved it down to the bottom in the page order, the error went away.
  2. My page was navigated to by way of another page and the Transfer function, and I originally didn't register it as a portal content reference. This caused one of the queries related to security to not return a value, and give a null exception error.
    • I registered the component and made it hidden.
  3. As referenced in the red paper, there are two component scoped Rowsets which need to be declared and initialized, otherwise errors occur.
    • Ensure that these objects are initialized prior to calling the event.
  4. Originally I had my run control record as level 0 (it had OPRID and RUN_CNTL_ID and the required subrecord). But then when saving the page, the SEQ_NBR on the subrecord is a key and caused a NULL insert error.
    • The solution was to just use PRCSRUNCNTL as the level 0 record, and then the level 1 record is the custom run control record (with the attachment sub record).
  5. The 'Maintain Definitions' page needed to have the 'All values' checkbox selected for the Context Keys. 

In the end I think this is a nifty feature that allows you to plug in an attachment framework fairly easily.


Wednesday, May 29, 2019

Action Menu Items

The 'View All' notifications page (PTPN_VIEWALL_GRID) has a good example of this.

It is embedded with a Custom Grid Header group box type. Inside this group box is another one (and in this case labelled Actions via PTPN_WRK.PTPN_GBOX1), and has a group box type of Popup and a style class of 'psc_button-simple'.

Then there is another group box with a style class of 'ps_menusection', and a group box type of Menu. Then within this is a group box for each link or menu item. This group box has a type of Menu Item and a style class of 'ps_menuitem'. The hyperlink fields within this group box have no special fluid attributes.


Order Field Type Style Record/Field Label
First Group Box Custom Grid Header psc_button
Second Group Box Popup psc_button PTPN_WRK.PTPN_GBOX1 Actions
Third Group Box Menu ps_menusection
Fourth Group Box Menu Item ps_menuitem
Fifth Hyperlink PTPN_WRK.PTPN_READ Mark as Read

What is also interesting is that in PeopleTools 8.56 the Group Box Types "Menu" and "Menu Item" do not exist. Instead they are using "Layout Only" with the HTML Tag of UL and LI respectively.

But the effect seems to be the same.

There is also PeopleCode hiding and displaying by making use of the following:
 If &bAtleastOneSelected Then
    PTPN_WRK.PTPN_GBOX1.FreeFormStyleName = "";
 Else
    PTPN_WRK.PTPN_GBOX1.FreeFormStyleName = "psc_force-hidden";
 End-If;


Java to get the file separator

There are several different techniques for getting the file separator, i.e. "/" for Linux and "\" for Windows.

In the PSXPFUNCLIB.FUNCLIB.FFo there is a function called GetDirSeparator(), which just looks at the first character of the PS_SERVDIR environment variable. On Linux it will be "/", so it will return this, otherwise if it is not "/" it will return "\".

You can also check the OS environment variable, which on Windows seems to be set to "Windows_NT". So this can be checked and return "\" if it is, otherwise "/".

  • Equally, you could check something like HOME for starting with "/".


Lastly you can also do something like this:
class Utilities;
   property string sDirSeparator get;
   
end-class;

get sDirSeparator
   /+ Returns String +/
   Return GetJavaClass("java.io.File").separator;
end-get;

Tuesday, May 28, 2019

Fluid Activity Guide step validation

Navigation Validation

The navigation at the top of the page will allow users to click through, which may not be what you want if you want the users to enter mandatory information before moving to the next step.
The MOS Doc ID 2309032.1 describes how to do it, especially when you are just moving from one page to another within a component.

Essentially you can add navigation buttons to the pages, e.g. add the fields to a subpage which can be added to each page. There should be one button per page.
I used fields named PTGP_CUSTBTN_PB_nn and added them to a Group Box (layout only), and added two specific style classes to each field:

  • ps_ag-custom-step-button-
  • psc_hidden
You can run something like this to get the item id:

SELECT LOWER(A.PTAI_ITEM_ID),A.PTAI_SEQ
FROM PS_PTAI_ITEM A
WHERE 1 = 1
AND PTAI_LIST_ID = 'FSU_KK_UPD'
ORDER BY A.PTAI_SEQ

Then in the FieldChange event you add the following to your navigation buttons:
Declare Function genOpenStepScript PeopleCode FUNCLIB_PTGP.FUNCLIB FieldFormula;

AddOnLoadScript(genOpenStepScript("fsukkudoct1", True));


So if you are on the first page, and you don't want people to skip to the second page without completing the first page you can add any validation you want before invoking the AddOnLoadScript() function.

A nice touch is to use a secondary modal page, and add all the messages (e.g. one for each field) to a long edit box. The Payment Request wizard in FSCM is a good example of this.


Fluid Notes

Page Titles

The Activity Guide seems to suppress the page title, and although there are numbered stops at the top of the page, it may not always be the most helpful.

So you can add a Group Box and use the Group Box Type of "Page Title". This will add text like "Step x of y: Page Title".


Custom Grid Headings

If you want to use your own buttons for the grid, e.g. insert row which invokes a modal page or an activity guide, then you can add a Group Box to the page with a Group Box Type of "Custom Grid Header". Then in the Fluid tab on the grid page control you can update the Custom Grid Header to use your page field number for the group box.
  • This seems to be a common technique used by Oracle.
  • It does not seem to require any styles to be applied to the page fields.


Wednesday, May 1, 2019

Database grants

Depending on how you query your tables or views, and with which database user you are doing that querying with, it is possibly that a table alter or a rebuild of the view will strip the object of your Select (and possibly other) permission(s).

An easy way to resolve this is to have a SQLExec() function run which reapplies the missing grants. This also assumes that:
a) It is easier to do this than to contact your DBA team, and
b) You have an easy way to run the SQLExec(), e.g. using an IScript.
c) This is in Development only :)
SQLExec("GRANT SELECT ON MY_OBJECT TO MY_USER_OR_ROLE");

Tuesday, April 2, 2019

Fluid and Search Pages

According to the 'Fluid Programming Fundamentals Red Paper', there are three types of search alternatives:

  1. Real Time Search
    This uses a Pivot Grid with the Component as the source, and the Pivot Grid Wizard will create a query in the background using the search record.

    To enable this, on the Internet tab the Search Page section should be 'Search', and on the Fluid tab the Search Type should be 'Standard'.

  2. Keyword Search
    This uses a Search Index.

    To enable this, on the Internet tab the Search Page section should be 'Keyword', and on the Fluid tab the Search Type should be 'Standard'.

  3. Search Page
    This is a custom implementation of the PTS_NUI_SEARCH or PTS_NUI_SEARCH_S Fluid 'Search' pages.

    This would be custom page development, and you would insert the page into the component.

Also under Search Type (on the Fluid tab) there is the option for 'None' and 'Master/Detail'.
You can see these options under PSPNLGRPDEFN.INCSEARCH:

  • 0 = None. This seems to be the default , and the vast majority of components have this.
  • 1 = Standard. 
  • 2 = Master/Detail. In the FSCM and HCM databases I haven't seen any of these.

The main difference between 1 and 2 seems to be that 1 -> PTS_NUI_SEARCH as the Search Page, and 2 -> PTS_NUI_SEARCH_S as the search page. I didn't notice any difference in the search between the two when using the Pivot Grid search. Although you can see in the HTML references to the search page, so you can see which one you are on.

For the Master/Detail implementation, there is also the option of using the PT_MDSTARTPAGE_NUI component. With this you define the Portal CREF pointing to this, and add additional parameters like so:

nRow=0&GMenu=FSU_DB_PRIVS&GComp=MY_COMPONENT&GPage=MYPAGE

This component must also be a Fluid component, with Search Page Type of 'None'. The page will be a 'Standard' Fluid page.

The page will have at least two 'Layout Only' groupboxes with the outer one having the following styles:
psc_scroll psa_md_grouplet

Inside the inner groupbox there should be a grid with at least the following fields.

  • A 'Layout Only' groupbox with PTLAYOUT.GROUPBOX1, with the style 
    ps_vtab
  • A hyperlink which will be used to contain the link.
  • (Optionally) an edit box used for the counter, with the style 
    psc_list_count
You would then write some PeopleCode on Page Activate (or Component Postbuild perhaps) which will populate the grid with the links you want to add.

Then on the groupbox field you need to set the following properties:

  • SetGroupletActionUrl
  • SetGroupletDisplayIn
  • IsGroupletLabelOnly
  • Visible



For example:
&sMenu = MenuName.MY_MENU;
&sComp = Component.MY_TARGET_CMP;
&sPage = Page.MY_TARGET_PG;
&sNewURL = GenerateComponentContentURL(%Portal, %Node, @("MenuName." | &sMenu), %Market, @("Component." | &sComp), @("Page." | &sPage), %Action_UpdateDisplay);
&sNewURL = EncodeURL(&sNewURL | "&SEARCH_KEY=" | &SearchKeyValue); /* If necessary */
&rs(&n).PTLAYOUT.GROUPBOX1.SetGroupletActionUrl(&sNewURL);
&rs(&n).PTLAYOUT.GROUPBOX1.SetGroupletDisplayIn(%GroupletActionMDTargetFluid);
&rs(&n).PTLAYOUT.GROUPBOX1.IsGroupletLabelOnly = True;
&rs(&n).PTLAYOUT.GROUPBOX1.Visible = False;
&rs(&n).MY_RECORD.LINE_NBR.Value = &nRow;
&rs(&n).MY_RECORD.LINE_NBR.Label = &Value;
&rs(&n).MY_RECORD.LINE_NBR.LabelImage = @("Image.CHECKLIST_64");
&rs(&n).MY_RECORD.COUNTER.Value = &NumOfRows;

Note: You can see this in detail under the 'Implementing Master/Detail Components' section in PeopleBooks 'Working with Master/Detail Components'.

Monday, April 1, 2019

Way to use Java to grab classpath

Easy to combine with the IScript from before.

Function Test_Java_Path();
   %Response.SetContentType("text/plain");
   %Response.WriteLine("");
   %Response.WriteLine("------ Java -------");
   Local JavaObject &sys = GetJavaClass("java.lang.System");
   Local string &cp = &sys.getProperty("java.class.path");
   Local array of string &tokens = Split(&cp, &sys.getProperty("path.separator"));
   For &i = 1 To &tokens.Len
      Local string &token = Lower(&tokens [&i]);
      %Response.WriteLine("&token:  " | Lower(&tokens [&i]));
   End-For;
End-Function;

Page and Field Configurator

I had a requirement to disable all of the fields on a run control page, and to populate two of the run control fields with values.

I thought that this would be a good opportunity to test the Page and Field Configurator.

As there were a dozen or so fields, I tried at first to use the 'Configure Page Visibility' grid, as you can make the page Display Only. But the problem with this was that the run control links and the 'Run' button were also disabled (as they didn't have the options set to be enabled when display only).

Instead I used the 'Configure Field Properties' grid and selected the fields from the lookup (a modal component) and selected the 'Disable Entry' option.

However, I noticed that the Pay Cycle field which was a series of radio buttons, was not available to select. From memory, the PeopleCode is querying the PSPNLFIELD table but skipping the Radio Button fields.

To get around this I was able manually insert the value into the database, e.g.
INSERT INTO PS_EOCC_CONFIG_FLD (PNLGRPNAME,MARKET,SEQUENCE_NBR,RECNAME,FIELDNAME,OCCURSLEVEL,RECNAME1,FIELDNAME1,LBLTEXT,PNLNAME,STDPNLNAME,PTCS_PNLFLDNAME,EOCC_LBL_OVERRIDE,EOCC_IS_REQUIRED,EOCC_DFLT_VALUE,EOCC_VISIBLE_FLAG,EOCC_DISABLED,FIELDTYPE,FIELDUSE,EOCC_FIELD_TYPE,EOCC_SBP_LEVEL,RECNAME_BASE,PTAI_SYSTEM_DATA) 
VALUES (...)

The 'Map to Portal Registry' page allows you to automatically create the Event Mapping using the EOCC% services.

So this catered for the first part of the requirement, and to cater for the second part of the requirement I was able to create another Event Mapping PeopleCode which sets the value of the two fields. I was able to configure this to run PostBuild after the EOCC_POSTBUILD service.

So there were no customizations to the delivered objects!

This was migrated by using the Data Migration Workbench and an Application Designer project.

  • The Application Designer project contained the Application Packages, and the PeopleCode.
  • The DMW project contained data sets EOCC_CONFIGURATION, RCF_SERVICES, and RCF_SERVICE_DEFINITIONS.

IScript for testing PeopleCode

When your Application Package PeopleCode doesn't have any Component Buffer references, then creating an IScript (and registering it to a portal CREF) allows you to test your Application Package PeopleCode.
Function Disable_Tracing();
   SetTraceSQL(0);
   SetTracePC(0);
   
End-Function;

Function Enable_Tracing();
   SetTraceSQL(31);
   SetTracePC(2060);
   
End-Function;

Function Test_ConfigureClassicPlusComponent();
   Enable_Tracing();
   %Response.SetContentType("text/plain");
   %Response.WriteLine("Turn Off");
   %Response.WriteLine("COMPONENT: " | String(ConfigureClassicPlusComponent(Component.COMPONENT, "GBL", 0)));
   Disable_Tracing();
End-Function;

rem Register this to the CREF and to a Permission List;
Function IScript_Test();
   rem Test_Environment_Variables();
   rem Test_ConfigureClassicPlusComponent();
End-Function;

I find it especially useful when testing for Web Services PeopleCode, as it allows you to build out the logic before testing the Service Operation itself.

The URL Type will be 'PeopleSoft Script', which will give you the fields to enter the Record Name (e.g. WEBLIB_ACC_TEST), Field Name (e.g. ISCRIPT1), PeopleCode Event Name (e.g. FieldFormua), and the PeopleCode Function Name (e.g. IScript_Test).

Friday, March 29, 2019

Use a content reference to point to a Query

Add a Content Reference link.

  • For URL Type, select 'PeopleSoft Generic URL' 
  • For the Portal URL, type: 'q/?ICAction=ICQryNameURL=PUBLIC.MY_QUERY_NAME'