Monday, December 08, 2025

Branding System Options for Fluid

 The PeopleTools Branding System Options component allows a developer to inject custom CSS and custom JavaScript globally. This is fantastic, but it only applies to Classic/Classic+ (see Oracle Support document 2827970.1). Wouldn't it be nice to have the same functionality for Fluid? Now you can! PeopleTools 8.60 included Global Event Mapping, a feature that allows us to attach JavaScript and CSS to all Fluid components at once! We published a video a couple of years ago describing this PeopleTools feature. You may want to watch the video before continuing with this post:



Let's apply what we learned in the video to our scenario:

1. Create an Event Mapping Service App Class

In Application Designer, let's create a Global Event Mapping App Class. A Global Event Mapping App Class is just like any other Event Mapping App Class. They all start with the same PeopleCode. You may download our Event Mapping PeopleCode template from our GitHub repository. Here is a PeopleCode template to get you started:

import PT_RCF:ServiceInterface;

class FluidBrandingSystemOptions extends PT_RCF:ServiceInterface
   method execute();
end-class;

method execute
   /+ Extends/implements PT_RCF:ServiceInterface.execute +/
   
   If (IsFluidMode()) Then
      REM ** Insert JavaScripts;
      REM AddJavaScript(HTML.ABC123);
      
      REM ** Insert Stylesheets;
      REM AddStylesheet(Stylesheet.DEF456);
   End-If;
end-method;

2. Create an Event Mapping Service Definition

Every Event Mapping solution requires a Related Content Service Definition. As of PeopleTools 8.59, we can use a dedicated page for Event Mapping, but we don't have to. We can create our Global Event Mapping service definition the same way we have always created Related Content Service Definitions. Here is a screenshot of my service definition:



3. Run PeopleCode to Apply Global Event Mapping

Since there is no user interface to manage Global Event Mapping, we must invoke a few lines of PeopleCode to register our Global Event Mapping solution. I like to use App Engines for this, because I can run them directly from App Designer. Here is the PeopleCode I placed in my App Engine:

import PTCS_GLOBALEVENTMAPPING:*;

Local PTCS_GLOBALEVENTMAPPING:GlobalEventMapping &gem = create PTCS_GLOBALEVENTMAPPING:GlobalEventMapping();
Local boolean &bstatus = &gem.CreateGlobalEventMappingConfig("JSM_GBL_BRAND", "JSM_GBL_FL_BRAND");

4. Refactor Your Solution into a Table-Driven Framework (Optional)

Fantastic! We can now add or remove CSS and JavaScript globally by adding and removing lines from our App Class PeopleCode. Alternatively, would you like to maintain your list of assets through an online configuration page? The solution requires a bit more effort, but would involve the following:

  • A table to store JavaScript and Stylesheet names,
  • A page for configuring (likely with grids for JavaScript and Stylesheet assets), and
  • An online component.

I'll leave this final step to you. Personally, I prefer the static code listing, as it performs better (critical for Global Event Mapping), and my list of Stylesheet and JavaScript resources doesn't change very often. I also like the change control required by PeopleCode that would not exist if I had an online configuration table.

As noted in the video, be careful not to break Global Event Mapping. One misstep and your entire system is broken! As also noted in the video, correcting an error is trivial. Just comment out the offending code.


Are you interested in learning more about Event Mapping? Check out our Event Mapping on-demand course, just one of many on-demand classes included with our subscription service.

Tuesday, December 02, 2025

When AI Writes PeopleCode... Fact or Fiction?

I just asked Gemini the following question:

"Is there a PeopleCode variable to detect Fluid Mode vs Classic mode?"

Gemini responded with, "Yes!" I thought, "FANTASTIC!"

Here is the code snippet Gemini suggested:

If %Component.IsFluid Then
    /* Logic specific to Fluid Mode */
    MessageBox(0, "", 0, 0, "Running in Fluid Mode");
Else
    /* Logic specific to Classic Mode */
    MessageBox(0, "", 0, 0, "Running in Classic Mode");
End-If;

Wow! That is some fantastic PeopleCode! But AI is known to hallucinate. Before trusting this code, we have to ask: Fact or Fiction?

Let's start with line 1:

If %Component.IsFluid Then
It appears Gemini thinks %Component is an object. What does PeopleBooks say?
%Component returns an uppercase character string containing the name of the current component, as set in the component definition.
To confirm, let's try compiling it in Application Designer. The following is a screenshot of the compilation result:

Verdict: Fiction.

%Component contains a primitive string, not an object. Since a primitive string does not have methods. The IsFluid method would be invalid.

I followed up with Gemini by asking:

"Can you share your reference for %Component?"

Instead of sharing a bibliography of cited sources, Gemini shared what appeared to be very legitimate documentation that describes Gemini's understanding of %Component. I attached a screenshot for your reference:




I circled the last statement, because it was the only part of this listing that was true:

"Gemini can make mistakes, so double-check it"

But I have to ask: Where did Gemini get its information? 🤔 Like a crack reporter, Gemini would not reveal its sources.

The good news is that Gemini is a fast learner! I fed Gemini a few authoritative PeopleBooks, such as a reference to PeopleCode System Variables, and Gemini generated a new response. This version is clever, but does it work?

Local ContentReference &oCRef;
Local boolean &isFluidMode;

/* Get the ContentReference object for the currently running content.
This object holds the metadata for the component's setup.
*/
&oCRef = %Session.GetContentReference();

If (&oCRef <> Null) Then
    /* Check the IsFluid property on the ContentReference object */
    &isFluidMode = &oCRef.IsFluid;
End-If;

If &isFluidMode Then
    /* Execute PeopleCode specific to Fluid UI */
    /* ... */
Else
    /* Execute PeopleCode specific to Classic UI */
    /* ... */
End-If;

What do you think? Fact or Fiction?

Let's start with the first line:

Local ContentReference &oCRef;

Unfortunately, ContentReference is not a built-in PeopleCode data type. What about this line?

&oCRef = %Session.GetContentReference();

Strike 2! GetContentReference is not a documented method of the Session object.

Verdict: Fiction.

But Gemini is on to something! The Fluid Mode attribute is visible in Structure and Content, so with a bit of PortalRegistry PeopleCode and the new %CRef system variable, you could write a lot of PeopleCode to determine if the current component is using Fluid mode.

I wanted to spend a few more minutes helping Gemini find the correct answer, so I fed Gemini several more PeopleBooks entries, hoping it would derive the correct answer. Each time, Gemini gave me a new, seemingly authoritative but entirely fictitious response, inventing new App classes and creating new functions.

After a few iterations, I decided to share the correct answer with Gemini. PeopleCode has a built-in function to determine Classic from Fluid: IsFluidMode. Here is Gemini's reply:


"This built-in function provides the clean separation of logic needed when supporting the same component in both Fluid and Classic modes (emphasis added)..."

Should we tell Gemini that a single component cannot be both Fluid and Classic?

This was a fun exercise. It was like a PeopleCode puzzle: find the hidden AI hallucination, and solve the puzzle.

How about you? Do you have interesting stories about AI and PeopleSoft? If so, please share them in the comments. We love hearing your stories!

Today, there is no substitute for experience and a strong personal understanding of PeopleCode. Want to become a PeopleSoft development expert so you can better determine fact from fiction? Enroll in the JSMpros All-access training pass and start learning!

Thursday, October 23, 2025

PeopleSoft Reconnect 2025 | Dive Deep



The premiere PeopleSoft conference, Reconnect | Dive Deep, begins in just a few days. If you are not already registered for this live virtual conference, be sure to do so ASAP. If you are already registered, then be sure to log in to the conference app to build your agenda. Some sessions have capacity limits, and you don't want to miss them!

Here is the list of sessions I am presenting at this year's conference:

Monday

  • 4:45pm EDT P-051853, PeopleSoft Test Framework: More than Automated End-user Testing


Tuesday

  • 11:15 am EDT P-051448, Replacing Customizations with Configurations in Campus Solutions
  • 1:45 pm EDT P-051447, Enterprise Components: The "Other" Toolset
  • 4:15 pm EDT P-051446, PeopleSoft Fluid Best Practices


Wednesday

  • 12:30pm EDT P-051861, What's New in 8.62 for Campus Solutions!


Thursday

  • 12:30 pm EDT P-051442, Getting the Most out of PeopleSoft PeopleTools: Tips and Techniques
  • 1:45 pm EDT P-051445, Isolate and Configure: Don't Customize!

As a registered attendee, be sure to check out our virtual booth to watch replays from prior conference sessions. I look forward to seeing you online next week!

Monday, September 08, 2025

Parsing JSON Arrays with PeopleCode

PeopleCode includes built-in support for parsing JSON through a native object called the JsonParser. Let's review a couple of JSON strings and then see how the JsonParser interprets them. First, let's review a JsonObject:

{
  "course": "PT1",
  "description": "PeopleTools",
  "courseType": "T",
  "duration": 4
}

Assuming that the JSON text is in the string variable &jsonStr, then we might parse it with code similar to:

Local JsonParser &p = CreateJsonParser();

If (&p.Parse(&jsonStr)) Then
  REM ** Woo Hoo! It parsed!;
End-If;

Parsing means the JsonParser created an in-memory structure. To leverage the JSON, we need to access that in-memory structure. The JsonParser includes one documented method to access the JSON Structure: GetRootObject(). The GetRootObject method returns a JsonObject. According to the documentation, the following PeopleCode should give us the value of the description attribute from the JSON above:

Local JsonObject &course = &p.GetRootObject();
Local string &descr; = &course.GetString("description");

What about a JSON Array? Here is my concern: There is no GetRootArray method. What if the "root object" is not an "object," but an Array? Here is what a valid JSON Array might look like:

[{
  "course": "PT1",
  "description": "PeopleTools",
  "courseType": "T",
  "duration": 4
}, {
  "course": "PC",
  "description": "PeopleCode",
  "courseType": "T",
  "duration": 5
}]

We would parse it using the same code as above. But how would you access the Array? Since the Parser has a GetRootObject method, let's invoke it, and then ToString the root object to see its JSON output. Here is the PeopleCode:

MessageBox(0, "", 0, 0, "%1", &p.GetRootObject().ToString());

... and here is the JSON:

{[
    {
        "course": "PT1",
        "description": "PeopleTools",
        "courseType": "T",
        "duration": 4
    },
    {
        "course": "PC",
        "description": "PeopleCode",
        "courseType": "T",
        "duration": 5
    }
]}

Do you notice anything unusual about that JSON? Notice the extra curly braces ({}). The "root" object is a JSON object. But here is where it gets interesting. The printed JSON is not valid. An object must have an attribute. The array should be assigned to an attribute. Now, does this matter? I think Oracle is allowed to internally represent JSON any way they desire. You might say that what we did was unexpected. We asked PeopleSoft to print an internal representation, not a true, expected JSON Object. But my question is the same: How do you access the Array that is now inside the root object? Here is the answer:

&p.GetRootObject().GetJsonArray("");

The GetJsonArray method expects an attribute name. We don't have one. So we don't give it one. Just use a zero-length string.

Want hands-on experience with REST services and PeopleCode parsing techniques? Join us on Tuesday, September 23, for two hands-on, live virtual workshops focused on integration! Details for the entire September series are available online.

We teach PeopleTools tips like this every week. Check out our website to see what we are offering next. Prefer to learn at your own pace? Our entire catalog is available online.

Wednesday, August 13, 2025

Five Reasons to Adopt the Application Services Framework

PeopleSoft's Integration Broker has support for REST and JSON. But, it is clear from the metadata, design, and history that Integration Broker favors SOAP and XML (Web Services). Is there a better alternative? YES! As of PeopleTools 8.59, we have a module designed specifically for REST: the Application Services Framework (ASF).

Here are five reasons you should consider ASF for your next integration project:

1. URL Design

REST focuses on objects and data. In an enterprise system, business objects might be Employees, Students, or Vouchers. In ASF, we might call these root resources. Continuing with the Voucher example, we might have the following URLs:

  • .../ap/vouchers
  • .../ap/vouchers/000000012567

When constructing an Application Service, we would have the "ap" service, which would then have a "vouchers" root resource. We could then define URI templates for:

  • vouchers (get a list of all vouchers)
  • vouchers/000000012567 (get a specific voucher)

We would further define HTTP methods, such as GET, PUT, PATCH, or POST, to fetch, update, or create business objects within the respective collections.

The Application Services framework helps us design URLs by first thinking about the module → then the collection → and then the specific object (generic to specific).

When we browse the Internet, we expect to find an organized collection of documents (endpoints) conveniently filed in meaningful folders. Computers browse the Internet, and those computers should expect an organized collection of business "documents" as well.

2. 201 Created Success Status Code

Web Services and SOAP use a protocol within a protocol. The typical Web Service request uses HTTP as the transport protocol and SOAP for the transaction protocol. Therefore, a Web Service might return a 200 HTTP status code to report success even though the SOAP transaction failed.

REST HTTP status codes have meaning. HTTP status codes in the 200 range represent successful responses. The most important HTTP status codes for a PeopleSoft developer are 200, 201, 202, and 204. These are the ONLY HTTP success status codes supported by ASF. Integration Broker REST-based Service Operations, on the other hand, support several other 200-level status codes, but with one critical omission: REST-based Service Operations do not support 201. 201 is the status code for "created." Assuming a PUT or a POST, the proper response may be a 201 - Created. This is critical. If the service handler immediately creates a transaction, then it should return a 201. The Application Services Framework supports this, but traditional REST Service Operations do not.

3. 401 Unauthorized Bad Request Status Code

PeopleSoft takes control of the HTTP Authorization header for several reasons. Here are a couple:

  • To determine if the requester is authorized to access the requested service.
  • To assume the identity of the user making the request, allowing PeopleCode to run as the proper user.

If PeopleSoft determines the requester does not have access (based on roles and permission lists), then PeopleSoft will return the 401 Unauthorized HTTP status code. This happens at the Integration Broker level, and it is fantastic!

But what if your business logic needs to return a 401 Unauthorized? Traditional REST-based Service Operations do not allow this.

Consider the following example. Let's say that a user is authorized for our /ap/vouchers service (the example above). That user might be authorized to access certain vouchers, such as .../ap/vouchers/000000012567, but not .../ap/vouchers/000000012568. This is called row-level security. In this scenario, we should return a 401 - Unauthorized. The user is authorized for the service, but not the data.

Traditional REST-based Service Operations do not allow you to return a 401 Unauthorized EVER. ASF does. 401 is an acceptable failure response from an Application Service.

4. OpenAPI

Metaphorically speaking, OpenAPI is the WSDL of REST. ASF generates OpenAPI specifications for us. We can plug these OpenAPI URLs or downloaded descriptors into various consumers, including Oracle Digital Assistant, Oracle Integration Cloud, Oracle Visual Builder, and more!

5. PeopleCode

ASF was designed to expose Application Classes as REST services. The framework includes an API designed to construct single-row and multi-row responses. The API was designed for REST with support for HTTP status codes and HTTP methods.

6. (Bonus) Metadata Design

ASF metadata consists of:

  • Module
  • Root Resource
  • URI Templates
  • Parameters
  • Result States
  • HTTP Headers
All of these are common and expected REST metadata concepts. Traditional REST Service Operations, on the other hand, include Messages, Services, Service Operations, and Documents; metadata structures that are more appropriate for Web Services than REST.


What to learn more? Create a free account at jsmpros.com and explore our free webinar replays to learn the basics of the Application Services Framework. Next, join us for our three-day live virtual Integration Tools Update course. Prefer to learn at your own pace? This same material is available on demand! Watch the videos whenever and wherever you like, and then complete the hands-on activities on your own server.

Wednesday, July 23, 2025

PTF: Recorder is unable to load... Now What?

I love PTF! With Selective Adoption, Continuous Delivery, and Customization Isolation strategies, PTF is more important than ever. Since Event Mapping, Drop Zones, and Page and Field Configurator don't appear on compare reports (and that is the point), we need a tool like PTF to expose regressions we would have found through the traditional retrofit analysis. The traditional retrofit approach required us to analyze and retrofit every customization. Event Mapping, Drop Zones, and Page and Field Configurator free us to focus on just what broke during the upgrade. And this is why PTF exists. The PTF regression test is how we find what broke. PTF is the linchpin that holds the whole isolated customization strategy together. Without it, we either go live with undiscovered errors or we continue to analyze and retrofit everything.

But what if you launch the PTF recorder and suddenly see this?


What happened? The PTF recorder is a Chrome/Edge plugin. That plugin needs to be loaded for the recorder to function. The PTF application attempts to install this plugin each time it launches the recorder. Depending on your enterprise settings, however, Chrome may deny that request. This is what happened to me. Enterprise customers have been dealing with this since PeopleSoft switched to the Chrome recorder. However, this is what surprised me: I'm simply using a standard Chrome download on an unmanaged server. In fact, PTF used to work just fine on this very server, and this behavior is a recent development. Perhaps Chrome altered its security policy?

Fortunately, this is a known and documented issue. Enterprise customers with highly controlled Chrome environments have been experiencing this issue since PTF switched to the Chrome recorder. Take a look at MOS Doc ID 2922127.1. This document outlines the steps necessary to correct the issue. Following those steps, I launched Chrome as an Administrator by:

  1. Typing Chrome into the Windows Menu and
  2. Right-clicking the Google Chrome entry and choosing Run as Administrator from the popup menu

I then navigated to chrome://extensions/ and turned on Developer Mode:



Finally, I dragged the Chrome extension psTstRecCh.crx file onto the Chrome extension window:



But after a restart, it still didn't work. I could now see the extension listed in Chrome, but it was disabled, and no matter how many times I clicked, it wouldn't enable itself!




Even though the extension was installed, Chrome wouldn't trust it. Even as an Administrator, I could not enable the extension. The final step is to override Chrome's behavior by encouraging it to trust Oracle's PTF extension. We do this through the Windows Registry. The appropriate Windows Registry keys are listed in PeopleBooks under Installing a PTF Client > Configuring Browser Settings. Here is the contents of my *.reg file I imported into my Windows Registry.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome]

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionAllowedTypes]
"1"="extension"

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallAllowlist]
"1"="boainbfkaibcfobfdncejkcbmfcckljh"

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallBlocklist]
"1"="*"

Please note that editing the Windows registry can be risky and potentially cause serious problems, including system instability or even rendering Windows unbootable. Therefore, it's crucial to proceed with extreme caution and only if you are confident in your actions. Always back up the registry before making any changes, and keep detailed records of modifications.

And that was all it took! My PTF recorder is now working as well as ever!

At JSMpros, we teach PeopleSoft tips like this every week. Check out our schedule to see what we are offering next! Have a large group you would like to train? Contact us for scheduling and group pricing.

Monday, June 02, 2025

Have you Considered the Application Services Framework?

 We recently asked the PeopleSoft LinkedIn Community:

Which would you choose to expose a PeopleSoft REST endpoint?

Here were the responses:


The answers surprised me. On the one hand, the IB Service/Service Operation approach was the only option until PeopleTools 8.59. This makes it familiar. However, the Application Services Framework was specifically created for REST. Unlike traditional IB Services and Service Operations, which were designed for SOAP and Web Services, the Application Services Framework focuses on HTTP concepts, such as URL design, status codes, data structures, and more. Here are some benefits the Application Services Framework offers over traditional IB Service Operations:

  • Generates an OpenAPI specification for import by consumer applications.
  • Provides additional HTTP status codes.
  • Uses a PeopleCode API that aligns more closely with REST service design.
  • Eliminates irrelevant metadata creation, such as Message Definitions and Documents.

The Application Services Framework is a REST-specific, REST-focused layer over the top of service-oriented Integration Broker metadata. If you haven't done so already, we recommend reviewing the Application Services Framework. It definitely simplifies REST Service development.

The Application Services Framework is part of our standard Integration Tools training. Join us for our next class to learn more! Already a subscriber? Log in and Get Started!