Quantcast
Channel: Jim's PeopleSoft Journal
Viewing all 201 articles
Browse latest View live

Alliance 2014 Agenda

$
0
0

It is hard to believe it has been a year since the HEUG Alliance 2013. We had a lot of fun in Indianapolis. I have been reviewing the 2014 session lineup and it looks pretty exciting. I must say that I am very pleased to see several sessions on REST, iScripts, and User Experience. With Alliance 2014 just a couple of weeks away, I wanted to make sure I posted my schedule:

  • Monday at 1:45 PM - 2:45 PM 33718 PeopleTools Developer: Tips and Techniques in Room 312-317
  • Tuesday 11:00 AM - 12:00 PM Meet the Experts

I also plan to spend some time in the PeopleSoft demo grounds. Please stop by and visit with me or one of my colleagues. We would love to say hello and show you some new ideas for configuring PeopleSoft applications.

If you are presenting at Alliance, please feel free to promote your session in the comments below.


Collaborate 2014 Schedule

$
0
0

I am just getting caught up after HEUG Alliance. What a great conference! As always, it was a lot of fun visiting with customers, partners, and colleagues. I heard some amazing stories and learned a few new tricks. Now it is time for the next conference: Collaborate 2014 is only a couple of weeks away. I love hearing your stories and challenges. Hunt me down and share your stories with me. Besides the demo grounds, here are two places you will find me:

  • Tuesday, 8 Apr 3:00 PM-4:00 PM So You Think You Know PeopleSoft? Do You Know the PeopleSoft Interaction Hub? – You Own It !, session ID 109490, Level 4, Lando 4301B
  • Thursday, 10 Apr 08:30 AM-09:30 AM PeopleTools Developer: Tips and Techniques, session ID 108600, Level 4, Marcello 4401A

I fly out Thursday night, but if I can make it, I plan to attend Mike Doyle's session Advanced PeopleSoft Development Techniques, which contains examples based on my book PeoleSoft PeopleTools Tips & Techniques.

PeopleTools 8.54 is GA!

$
0
0

On Friday the PeopleTools blog announced that PeopleTools 8.54 is now Generally Available (GA). PeopleTools 8.54 brings several usability features including responsive design for mobile devices as well as development features such as Mobile Application Platform (MAP). One of my favorite new features is component specific branding. You can now attach stylesheets and JavaScript to components as described in PeopleBooks Applying Branding to Other Objects. Another great branding enhancement is Attribute-based branding. This is very similar to role based branding, but more flexible and easier to administer. You can read about it in the PeopleBooks entry Administering System Branding.

To learn more read the PeopleTools announcement or visit the hosted 8.54 PeopleBooks. I can't wait for the new demo images!

PeopleCode Coding Discipline

$
0
0

Java, JavaScript, C++, C Sharp, Objective C, Groovy... what do these languages have in common? Yes, curly braces, but besides that... actually, there are a lot of similarities between these languages. Throw Visual Basic, Perl, Python, or any other well-known language into the mix and the number of similarities drops significantly. Setting semantics and syntax aside, a common attribute of all well-known languages is standards and best practices. Some of those best practices (such as coding style) differ by language. For example, bash scripts can either look like, uh... bash scripts or they can look like c-style logic statements. Obviously, bash best practices prefer you make bash code look like bash code. Other standards are personal: do you prefer real tabs or spaces? How many spaces does your tab consume? Do you put curly braces on a new line?

How does all of this fit into PeopleCode? Forget about code formatting preferences. Application Designer has its own code formatting ideas. But there are other best practices that can help you write better code with fewer defects (fewer defects = better code). By following best practices your code will be easier to read, you will be more productive, and your users will be happier because you deliver better solutions faster.

Even though best practices usually result in code that is more efficient to process, that isn't really the point. Computers can interpret just about anything. Compilers and interpreters are really good at eliminating useless words and resolving seemingly incomprehensible logic. I love Martin Fowler's quote, "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." Best practices are really about writing code that humans can easily comprehend. For example, avoid complex logic (including double negatives, or any negative logic, for that matter), keep your method and function code short, etc. If you write some code, leave it for a night, and then come back the next day and either need to read lots of comments to figure it out or spend a few minutes "remembering" what that code does, then the code is probably too complex. The problem with complex code is that it is easily misinterpreted by humans. Another problem with complex code is we actually ignore it when trying to resolve problems. We know it takes time to digest complex code, so we avoid it, preferring to test simple code first. Why waste time trying to understand complex code if it might be functioning properly?

Today's Quest Newsletter contained a link to 10 Bad Coding Practices That Wreck Software Development Projects. These are language agnostic practices that we can easily apply to PeopleSoft development.

If I were to summarize Coding best practices, I think I would do it like this: two.sentenc.es. Now, arguably, short does not equal comprehensible. There are programmers that err on the terse side because it is clever. This is true, often short code is clever. It is also hard to read. Most of us, however, err the other way. E. F. Schumacher said, "Any fool can make things bigger, more complex, and more violent. It takes a touch of genius — and a lot of courage — to move in the opposite direction." Schumacher died in 1977, so this problem is not new.

Computer programming is about communication. As programmers we have two audiences:

  • Computers (which can interpret anything -- even complex stuff)
  • Humans (who have a limited attention span, distractions, and a preference for simplicity)

Here is why I think discipline and best practices are critical for good PeopleCode:

We use PeopleCode to create business rules, but PeopleCode is NOT a business rules language. PeopleCode is a Metadata manipulation language. (Note: this is purely my opinion)

Here is why I believe PeopleCode is for metadata, not business rules: PeopleCode only has Metadata objects: records, fields, SQL, components, menus, etc. These are all metadata. These are the low level API's we use to write business logic. Consider the following PeopleCode:

Localrecord&rec =CreateRecord(Record.PSOPRDEFN);
Local field &descr;

&rec.SelectByKey("jimsoprid");
&descr =&rec.GetField(Field.OPRDEFNDESC);

&descr.Value="JimMarion";

&rec.Update();

This code implements business logic, but does so by manipulating metadata objects. PeopleCode metadata objects are building blocks for business logic. If we were to rewrite this using a business logic language, it would probably look something like this:

Local User &u = GetUser("jimsoprid");

&u.descr ="JimMarion";
&u.Update();

And this is why discipline and best practices are SO important for PeopleCode developers: We are trying to speak business logic with a metadata vocabulary. We start with a communication deficit. It is like trying to teach advanced weaving using an automobile mechanics vocabulary. The two subjects have different vocabularies. But if you combine the words correctly, you can communicate the same meaning.

Unlimited Session Timeout

$
0
0

There are a lot of security admins out there that are going to hate me for this post. There are a lot of system administrators, developers, and users, however, that will LOVE me for this post. The code I'm about to share with you will keep the logged in PeopleSoft user's session active as long as the user has a browser window open that points to a PeopleSoft instance. Why would you do this? I can think of two reasons:

  • Your users have several PeopleSoft browser windows open. If one of them times out because of inactivity at the browser window level, then it will kill the session for ALL open windows. That just seems wrong.
  • Your users have long running tasks, such as completing performance reviews, that may require more time to complete than is available at a single sitting. For example, imagine you are preparing a performance review and you have to leave for a meeting. You don't have enough information in the transaction to save, but you can't be late for the meeting either. You know if you leave, your session will time out while you are gone and you will lose your work. This also seems wrong.

Before I show you how to keep the logged in user's session active, let's talk about security... Session timeouts exist for two reasons (at least two):

  • Security: no one is home, so lock the door
  • Server side resource cleanup: PeopleSoft components require web server state. Each logged in user session (and browser window) consumes resources on the web server. If the user is dormant for a specific period of time, reclaim those resources by killing the user's session.

We can "lock the door" without timing out the server side session with strong policies on the workstation: password protected screen savers, etc.

So here is how it works. Add the following JavaScript to the end of the HTML definition PT_COMMON (or PT_COPYURL if using an older version of PeopleTools) (or even better, if you are on PeopleTools 8.54+, use component and/or role based branding to activate this script). Next, turn down your web profile's timeout warning and timeout to something like 3 and 5 minutes or 5 and 10 minutes. On the timeout warning interval, the user's browser will place an Ajax request to keep the session active. When the user closes all browser windows, the reset won't happen so the user's server side session state will terminate.

What values should you use for the warning and timeout? As low as possible, but not so low you create too much network chatter. If the browser makes an ajax request on the warning interval and a user has 10 windows open, then that means the user will trigger up to 10 Ajax requests within the warning interval window. Now multiply that by the number of logged in users at any given moment. See how this could add up?

Here is the JavaScript:

(function(root){
//xhradaptedfromhttp://toddmotto.com/writing-a-standalone-ajax-xhr-javascript-micro-library/
var xhr =function(type, url, data){
var methods ={
success:function(){
},
error:function(){
}
};

var parse =function(req){
var result;
try{
result = JSON.parse(req.responseText);
}catch(e){
result = req.responseText;
}
return[result, req];
};

var XHR = root.XMLHttpRequest || ActiveXObject;
var request =newXHR('MSXML2.XMLHTTP.3.0');
request.open(type, url,true);
request.setRequestHeader('Content-type','application/x-www-form-urlencoded');
request.onreadystatechange =function(){
if(request.readyState ===4){
if(request.status ===200){
methods.success.apply(methods,parse(request));
}else{
methods.error.apply(methods,parse(request));
}
}
};

request.send(data);
return{
success:function(callback){
methods.success = callback;
return methods;
},
error:function(callback){
methods.error = callback;
return methods;
}
};
};//ENDxhr


var timeoutIntervalId;
var resetUrl;

/*replacewarningmessagetimeoutwithAjaxcall
*
*clearoldtimeoutafter30seconds
*macsdon'tsettimeoutuntil1000ms
*/
root.setTimeout(function(){
/*somepagesdon'thavetimeoutsdefined*/
if(typeof(timeOutURL)!=="undefined"){
if(timeOutURL.length >0){
resetUrl = timeOutURL.replace(/expire$/,"resettimeout");
if(totalTimeoutMilliseconds !==null){
root.clearTimeout(timeoutWarningID);
root.clearTimeout(timeoutID);

timeoutIntervalId =
root.setInterval(resetTimeout /*definedbelow*/,
root.warningTimeoutMilliseconds);
}
}
}
},30000);

var resetTimeout =function(){
xhr("GET", resetUrl)
.success(function(msg){
/*donothing*/
})
.error(function(xhr, errMsg, exception){
alert("failedtoresettimeout");
/*error;fallbacktodeliveredmethod*/
(root.setupTimeout || root.setTimeout2)();
});
};
}(window));

A special "shout out" to Todd Motto for his Standalone Ajax/XHR JavaScript micro-library which is embedded (albeit modified) in the JavaScript above.

List of PeopleSoft Blogs

$
0
0
It has been amazing to watch the exponential growth in the number of PeopleSoft community bloggers. It seems that most of them have links to other PeopleSoft blogs, but where is the master list of PeopleSoft blogs? Here is my attempt to create one. Don't see your blog on the list? Add a comment and I'll review your blog. If your blog is education oriented, then I will put it in the list... and probably delete your comment (that way you don't have to feel awkward about self promotion). There are some PeopleSoft related blogs that I really wanted to include, but they just weren't educational (more marketing than education). I suppose some could say that the Oracle blogs I included were primarily marketing focused. That is true. I included them, however, because those product release announcements are so valuable.
I have not read all of these blogs. I can't, don't, and won't attest to the quality of the content in those blogs. Each reader should evaluate the content of these blog posts before implementing suggestions identified in these blogs and their corresponding comments.

    "Private" App Class Members

    $
    0
    0

    I was reading Lee Greffin's post More Fun with Application Packages -- Instances and stumbled across this quote from PeopleBooks:

    A private instance variable is private to the class, not just to the object instance. For example, consider a linked-list class where one instance needs to update the pointer in another instance.

    What exactly does that mean? I did some testing to try and figure it out. Here is what I came up with:

    1. It is still an instance variable which means each in-memory object created from the App Class blue print has its own memory placeholder for each instance member.
    2. Instances of other classes can't interact with private instance members.
    3. Instances of the exact same class CAN interact with private members of a different instance.
    4. Private instance members differ from static members in other languages because they don't all share the same pointer (pointer, reference, whatever).

    I thought it was worth proving so here is my sample. It is based on the example suggested in PeopleBooks:

    For example, consider a linked-list class where one instance needs to update the pointer in another instance.

    The linked list is just an item with a pointer to the next item (forward only). A program using it keeps a pointer to the "head" and then calls next() to iterate over the list. It is a very common pattern so I will forgo further explanation. Here is a quick implementation (in the App Package JJM_COLLECTIONS):

    class ListItem
    method ListItem(&data As any);
    method linkTo(&item As JJM_COLLECTIONS:ListItem);
    method next() Returns JJM_COLLECTIONS:ListItem;
    method getData() Returnsany;
    private
    instance JJM_COLLECTIONS:ListItem &nextItem_;
    instanceany&data_;
    end-class;

    method ListItem
    /+&dataasAny+/
    %This.data_ =&data;
    end-method;

    method linkTo
    /+&itemasJJM_COLLECTIONS:ListItem+/
    &item.nextItem_ =%This;
    end-method;

    method next
    /+ReturnsJJM_COLLECTIONS:ListItem+/
    Return%This.nextItem_;
    end-method;

    method getData
    /+ReturnsAny+/
    Return%This.data_;
    end-method;

    Notice the linkTo method sets the value of the private instance member of a remote instance (its parameter), NOT the local instance. This is what is meant by private to the class, not private to the instance. Each instance has its own &nextItem_ instance member and other instances of the exact same class can manipulate it. Here is the test case I used to test the remote manipulation implementation:

    import TTS_UNITTEST:TestBase;
    import JJM_COLLECTIONS:ListItem;

    class TestListItem extends TTS_UNITTEST:TestBase
    method TestListItem();
    method Run();
    end-class;

    method TestListItem
    %Super=create TTS_UNITTEST:TestBase("TestListItem");
    end-method;

    method Run
    /+Extends/implementsTTS_UNITTEST:TestBase.Run+/
    Local JJM_COLLECTIONS:ListItem &item1 =
    create JJM_COLLECTIONS:ListItem("Item1");
    Local JJM_COLLECTIONS:ListItem &item2 =
    create JJM_COLLECTIONS:ListItem("Item2");

    &item2.linkTo(&item1);

    %This.AssertStringsEqual(&item1.next().getData(), "Item2",
    "ThenextitemisnotItem2");
    %This.Msg(&item1.next().getData());
    end-method;

    The way it is written requires you to create the second item and then call the second item's linkTo method to associate it with the head (or previous) element.

    Now, just because you CAN manipulate a private instance member from a remote instance doesn't mean you SHOULD. Doing so seems to violate encapsulation. You could accomplish the same thing by reversing the linkTo method. What if we flipped this around so you created the second item, but called the first item's linkTo? It is really the first item we want to manipulate in a forward only list (now, if it were a multi-direction list perhaps we would want to manipulate the &ampprevItem_ member?). Here is what the linkTo method would look like:

    method linkTo
    /+&itemasJJM_COLLECTIONS:ListItem+/
    %This.nextItem_ =&item;
    end-method;

    Now what if we wanted a forward AND reverse linked list? Here is where maybe the ability to manipulate siblings starts to seem a little more reasonable (I still think there is a better way, but humor me):

    class ListItem
    method ListItem(&data As any);
    method linkTo(&item As JJM_COLLECTIONS:ListItem);
    method next() Returns JJM_COLLECTIONS:ListItem;
    method prev() Returns JJM_COLLECTIONS:ListItem;
    methodremove()ReturnsJJM_COLLECTIONS:ListItem;
    method getData() Returnsany;
    private
    instance JJM_COLLECTIONS:ListItem &nextItem_;
    instance JJM_COLLECTIONS:ListItem &prevItem_;
    instanceany&data_;
    end-class;

    method ListItem
    /+&dataasAny+/
    %This.data_ =&data;
    end-method;

    method linkTo
    /+&itemasJJM_COLLECTIONS:ListItem+/
    REM**manipulateprevioussibling;
    &item.nextItem_ =%This;
    %This.prevItem_ =&item;
    end-method;

    method next
    /+ReturnsJJM_COLLECTIONS:ListItem+/
    Return%This.nextItem_;
    end-method;

    method prev
    /+ReturnsJJM_COLLECTIONS:ListItem+/
    Return%This.prevItem_;
    end-method;

    methodremove
    /+ReturnsJJM_COLLECTIONS:ListItem+/
    %This.nextItem_.linkTo(%This.prevItem_);
    REM**Ormanipulatebothsiblings;
    REM%This.prevItem_.nextItem_=%This.nextItem_;
    REM%This.nextItem_.prevItem_=%This.prevItem_;
    Return%This.prevItem_;
    end-method;

    method getData
    /+ReturnsAny+/
    Return%This.data_;
    end-method;

    And here is the final test case

    import TTS_UNITTEST:TestBase;
    import JJM_COLLECTIONS:ListItem;

    class TestListItem extends TTS_UNITTEST:TestBase
    method TestListItem();
    method Run();
    end-class;

    method TestListItem
    %Super=create TTS_UNITTEST:TestBase("TestListItem");
    end-method;

    method Run
    /+Extends/implementsTTS_UNITTEST:TestBase.Run+/
    Local JJM_COLLECTIONS:ListItem &item1 =
    create JJM_COLLECTIONS:ListItem("Item1");
    Local JJM_COLLECTIONS:ListItem &item2 =
    create JJM_COLLECTIONS:ListItem("Item2");
    Local JJM_COLLECTIONS:ListItem &item3 =
    create JJM_COLLECTIONS:ListItem("Item3");

    &item2.linkTo(&item1);

    %This.AssertStringsEqual(&item1.next().getData(), "Item2",
    "Test1failed.ThenextitemisnotItem2");
    %This.AssertStringsEqual(&item2.prev().getData(), "Item1",
    "Test2failed.TheprevitemisnotItem1");

    &item3.linkTo(&item2);
    %This.AssertStringsEqual(&item1.next().next().getData(), "Item3",
    "Test3failed.Thenext.nextitemisnotItem3");
    %This.AssertStringsEqual(&item1.next().next().prev().getData(), "Item2",
    "Test4failed.TheprevitemisnotItem2");

    Local JJM_COLLECTIONS:ListItem &temp =&item2.remove();
    %This.AssertStringsEqual(&item1.next().getData(), "Item3",
    "Test5failed.ThenextitemisnotItem3");
    %This.AssertStringsEqual(&item1.next().prev().getData(), "Item1",
    "Test6failed.TheprevitemisnotItem1");

    end-method;

    I hope that helps clear up some of the confusion around the term "private" as it relates to Application Classes.

    PeopleTools 8.54 Upgrade now Available


    Accessing Database Photos from Query Access Service

    $
    0
    0

    I have been working with the PeopleTools 8.54 REST Query Access Services. I would absolutely LOVE them if they returned JSON instead of XML. With a little help from x2js I am able to work around this "shortcoming." One specific challenge I faced was accessing image data. For example, with PeopleSoft query I can see who has photos in PS_EMPL_PHOTO, but I can't see the actual uploaded photo. With a little help from Oracle and a query expression, however, I can convert the photo blob into base64:

    SELECTUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(EMPLOYEE_PHOTO))
    FROM PS_EMPL_PHOTO
    WHERE EMPLID ='KU0003'

    The only problem with this approach is that Oracle database has a maximum size limit on data that can be encoded and most of the photos I have seen exceed that maximum. The way I chose to work around this limitation is to substring the blob and encode it in fragments. I create a separate column for each fragment, and then concatenate them together in the REST client. Here is some sample SQL from a PeopleSoft query. Each of the CASE statements is a query expression.

    SELECT
    CASE
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)>1455THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 1)))
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)<=1455THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(EMPLOYEE_PHOTO))
    ENDAS C1,
    CASE
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)>2910THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 1456)))
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)<=2910ANDDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)>1455THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)-1455, 1456)))
    ENDAS C2,
    CASE
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)>4365THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 2911)))
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)<=4365ANDDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)>2910THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)-2910, 2911)))
    ENDAS C3,
    CASE
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)>5820THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 4366)))
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)<=5820ANDDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)>4365THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)-4365, 4366)))
    ENDAS C4,
    CASE
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)>7275THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 5821)))
    WHENDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)<=7275ANDDBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)>5820THENUTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO)-5820, 5821)))
    ENDAS C5
    FROM PS_EMPL_PHOTO
    WHERE EMPLID ='KUL704'

    On the client I use something like this:

    var data = data:image/jpeg;base64," + columns.join("");

    The end result is something like this (right-click to see base64 data):

    Mikko,Jill's Photo

    Up and Running with HCM 9.2 on PT 8.54 via PUM "Demo Image"

    $
    0
    0

    Yes, you read that correctly. PUM is the new demo image. According to MOS Doc ID 1464619.1"As of HCM 9.2.006, the PeopleSoft Image and PeopleSoft Demo Image have been combined into one PeopleSoft Update Image. You can use the PeopleSoft Update Image for both your patching and demonstration purposes." If you are current on your PUM images, you probably already knew that. If you are like me, however, and haven't downloaded a demo image for a while, then you may have been looking for demo images on the old MOS demo image page.

    Since I use the image for prototyping and demonstrations, I turned off SES and reduced the memory requirements to 2048 MB. It is working great at this lower memory setting.

    There are a lot of new and great features in the PT 8.54 PUM:

    • Attribute based branding,
    • Component Branding (add your own CSS and JavaScript to components without hacking a delivered HTML definition)
    • REST Query Access Service,
    • Mobile Application Platform (MAP), and
    • Fluid homepages

    Tip: Access the fluid homepages by visiting the URL http://<yourserver>:8000/psc/ps/EMPLOYEE/HRMS/c/NUI_FRAMEWORK.PT_LANDINGPAGE.GBL. For example, if you have a hosts entry mapping your PUM image to the hostname hcmdb.example.com, then use the URL http://hcmdb.example.com:8000/psc/ps/EMPLOYEE/HRMS/c/NUI_FRAMEWORK.PT_LANDINGPAGE.GBL.

    OpenWorld 2014

    $
    0
    0

    OpenWorld is only a couple of weeks away. As always, this promises to be an outstanding conference. Whether your focus is functional or technical, OpenWorld has a lot of PeopleSoft sessions. The Focus on PeopleSoft OpenWorld document contains a good list of PeopleSoft focused sessions and events. I look forward to seeing you at OpenWorld this year. Here are some of the places and times where you can find me:

    Monday

    Demo Grounds: PeopleSoft User Experience from 12:00 PM to 2:00 PM

    Tuesday

    Demo Grounds: PeopleSoft User Experience from 9:45 AM to 11:00 AM


    Session ID: CON7568
    Session Title: PeopleSoft PeopleTools Developer: Tips and Techniques
    Venue / Room: Moscone West - 3004/3006
    Date and Time: 9/30/14, 17:00 - 17:45

    Wednesday

    Demo Grounds: PeopleSoft User Experience from 9:45 AM to 12:00 PM

    Thursday


    Session ID: CON7537
    Session Title: Connecting PeopleSoft HCM, Oracle Taleo, Oracle HCM Cloud, and More
    Venue / Room: Palace - Twin Peaks South
    Date and Time: 10/2/14, 9:30 - 10:15

    Meet the Authors Book Signing at the OpenWorld Bookstore in Moscone South Upper Hall Lobby from 1:00 PM to 1:30 PM.

    See you there!!

    Alliance 2015

    $
    0
    0

    I am looking forward to seeing everyone at the Alliance conference in Nashville next month. I was just reviewing my schedule and see lots of interesting technical sessions (as always). If you have room in your schedule, I invite you to attend my session on Tuesday, Mar 17, 2015 from 11:15 AM to 12:15 PM in Presidential D. The session is titled PeopleSoft PeopleTools Developer: Tips and Techniques. If you can't make it to my session, then perhaps I'll see you shortly thereafter at Meet the Experts from 1:45 to 2:45 (table 11)? I'll be around the conference all week and will be working in the demo grounds when I'm not attending sessions. See you in a few weeks!

    Using RequireJS to Manage Dependencies

    $
    0
    0

    I have a handful of blog posts describing how to deal with JavaScript dependency conflicts such as multiple library inclusions, namespace conflicts, etc. These workarounds are necessary in portal environments that mash up content from various providers that may use the same libraries. On a portal (Interaction Hub) homepage, for example, you may have a Pagelet Wizard pagelet that uses the jQuery Cycle plugin as well as a Pagelet Wizard pagelet that uses jQuery UI. Both of these pagelets will include the jQuery library, but different jQuery plugins. As each pagelet loads, it will load jQuery and then its required plugins. Both pagelets will try to put $ and jQuery into the global (window) namespace. The last pagelet to load will reload window.$ and window.jQuery causing the browser to drop any previously loaded jQuery plugins.

    One technique JavaScript developers use to manage dependencies in a scenario like this is to use RequireJS with Asynchronous Module Definitions (AMD). With RequireJS and AMD, you would define a RequireJS configuration file pointing to each JavaScript library and plugin and then write JavaScript that uses these libraries with a RequireJS closure. This approach keeps custom libraries out of the global namespace and ensures that libraries only load once (not once for each pagelet). PeopleTools 8.54 makes implementing this really easy through the new Branding Objects module and Branding System Options. Let's review an example. Let's say that I have RequireJS, jQuery, and jQuery UI loaded into JavaScript objects in the Branding Objects module as shown in the following image

    Now let's say you have your Branding System Options configured to include RequireJS and the RequireJS configuration file as described in the following screenshot:

    You could then create a Pagelet Wizard pagelet containing HTML like the following and not worry about dependencies or namespace pollution. Everything would just work

    <divid="NAA_DIALOG_TEST_html">
    <styletype="text/css">
    @import url(%StyleSheet(NAA_JQ_UI_1_11_2));
    #NAA_DIALOG_TEST_html.dialog{display:none};
    </style>
    <script>
    require(['jquery','jqueryui'],function($){
    $(document).ready(function(){
    console.log("dialogpageletisusingversion"+ $.fn.jquery);
    $("#NAA_DIALOG_TEST_html").find("button")
    .button()
    .click(function( event ){
    event.preventDefault();
    $("#NAA_DIALOG_TEST_html.dialog").dialog();
    });
    });
    });

    </script>
    <button>Show Dialog</button>
    <divclass="dialog"title="Basicdialog">
    <p>This is the default dialog which is useful for displaying information.
    The dialog window can be moved, resized and closed with the 'x' icon.</p>
    </div>
    </div>

    Of course, this assumes that your RequireJS configuration file looks something like this:

    /**
    *RequireJSglobalconfiguration.IncludeafterRequireJSinbrandingsettings
    *
    *@returns{undefined}
    */
    (function(){
    /**
    *BuildaURLbasedonthecurrentcomponent'sURL
    *@param{type}scriptId
    *@returns{String}derivedURLforJavaScript
    */
    var getScriptUrl =function(scriptId){
    var mainUrl =/*window.strCurrUrl||*/ window.location.href;
    var parts =
    mainUrl.match(/ps[pc]\/(.+?)(?:_\d)*?\/(.+?)\/(.+?)\/[chs]\//);
    return window.location.origin +"/psc/"+ parts[1]+"/"+ parts[2]+
    "/"+ parts[3]+
    "/s/WEBLIB_PTBR.ISCRIPT1.FieldFormula.IScript_GET_JS?ID="+ scriptId;
    };

    require.config({
    paths:{
    /*Usingnon-standardnamebecause1.6.2isnotAMDcompliantwhereas
    *laterversionsarecompliant.Don'twantconflictwithlaterversion
    */
    'jquery':getScriptUrl("NAA_JQ_1_11_2_JS"),
    'jqueryui':getScriptUrl("NAA_JQ_UI_1_11_2_JS"),
    'jquery-private':getScriptUrl("NAA_JQ_PRIVATE_JS")
    },
    map:{
    //'*'meansallmoduleswillget'jquery-private'
    //fortheir'jquery'dependency.
    '*':{'jquery':'jquery-private'},

    //'jquery-private'wantstherealjQuerymodule
    //though.Ifthislinewasnothere,therewould
    //beanunresolvablecyclicdependency.
    'jquery-private':{'jquery':'jquery'}
    }
    });
    }());

    And your jQuery-private module looks something like this:

    //http://requirejs.org/docs/jquery.html#noconflictmap
    define(['jquery'],function(jq){
    return jq.noConflict(true);
    });

    What's up with the getScriptUrl function? JavaScript HTML definitions do not yet support %JavaScript Meta-HTML. The getScriptUrl JavaScript function attempts to perform the same task, but using client-side JavaScript.

    Why do we need a jquery-private module? The point is to hide all of our dependencies and just expose them within the RequireJS closure. That way we avoid conflicts with older code that uses jQuery as well as any PeopleTools delivered JavaScript that may user

    This technique also works well for loading dependencies. I often use a JavaScript library in a pagelet, with JavaScript executed directly in the pagelet. One challenge I have had is ensuring that my browser parses and processes any JavaScript libraries before JavaScript embedded in a pagelet. RequireJS solves this by first loading the dependencies, and then executing the JavaScript within the define/require function.

    Note: For this to work properly, it is important that your JavaScript libraries are either AMD compliant or can be appropriately shimmed. Current versions of jQuery and jQuery UI are AMD compliant. The older version of jQuery UI that ships with PeopleTools 8.54 and earlier is NOT AMD compliant. Instead, I downloaded the latest jQuery UI and uploaded it using the new Branding Objects module. To work successfully, the jQuery UI CSS must be updated to use %Image() for each image resource and each image must be uploaded using the Branding Objects component.

    Collaborate 2015

    $
    0
    0

    Collaborate 2015 is just a few weeks away. I will be presenting PeopleSoft PeopleTools Developer: Tips and Techniques on Tuesday, 4/14/2015 at 3:15 PM in Surf B. If you are presenting a PeopleTools technical session, then please post a comment with your session title, date, time, and venue. I look forward to seeing you next month!

    HIUG Interact 2015 Presentations Available

    $
    0
    0

    The HIUG Interact conference is one of my favorite annual PeopleSoft conferences. I learn a lot from the customer and partner presentations delivered each year. As always, my highlight is interacting with customers.

    All of my presentations are now available through the HIUG conference site. Be sure to download the slides because these slide decks contain a lot of information we didn't have time to cover in our sessions. For reference purposes, here is a list of sessions I presented at Interact 2015:
    • What's New with PeopleSoft Reporting - Query, Pivot Grids, BI Publisher
    • Configuring and Utilizing the Interaction Hub -- Deep Dive
    • PeopleTools Developer Tips and Techniques
    • Fluid User Interface and Mobile Application Platform - Deep Dive

    New PeopleTools Mobile Book

    $
    0
    0

    My wife and I have been writing another book. We are reviewing proofs now, which means we are getting close to publication. I did a quick search on Amazon and see that Amazon is taking pre-orders: PeopleSoft PeopleTools: Mobile Applications Development. Publication date is currently set for October 16, 2015, which means it will publish just before OpenWorld. Fluid and MAP have been out for about a year. If you guessed that a new PeopleTools mobile book would cover these mobile technologies, you guessed correctly. But I saw no reason to stop there. After describing how to use Fluid and MAP, the book moves on to building responsive mobile applications using standard HTML5 development tools and libraries including jQuery mobile and AngularJS. Just today I spoke with a customer still using PeopleTools 8.50. What are the odds that customer will be using PeopleTools 8.54 in the next year? The second section of this book, using HTML5 is perfect for a customer in this situation because it describes how to connect a modern single page application to a PeopleSoft back end using iScripts and REST services (one chapter for each back end solution). The book finishes with examples of building native and hybrid applications for PeopleSoft using the Android SDK, Apache Cordova (my personal favorite), and Oracle's Mobile Application Framework. Here is a rough outline:

    Chapter 1 shows you how to prepare your workstation for mobile development. This includes configuring HTML5 browsers, developer tools, and emulators.

    Chapter 2 digs into Fluid, showing two examples of creating Fluid pages. The first is a basic page whereas the second is a two-column responsive page. This chapter covers search pages; toolbar actions; and fluid field, page, and component features. The point of this chapter is to help the reader feel comfortable with Fluid. Fluid includes a lot of new features based on HTML5, CSS3, and JavaScript. I really want customers to understand, however, that they can build Fluid pages using core PeopleTools without any knowledge of the modern web concepts. Of course, you can build some really amazing solutions if you know HTML5, CSS3, and JavaScript.

    Chapter 3 explains the new Mobile Application Platform (MAP): what it is, when to use it, and how to use it. A chapter wouldn't be complete without examples, so there are plenty of examples to help you start your next MAP project.

    Chapter 4 segues into modern mobile development. The rest of the book takes the user interface outside of PeopleTools. Before moving away from Application Designer, however, we need a data model and a scenario. This chapter presents the scenario and lays the foundation for the rest of the chapters. In this chapter you will work with SQL and the Documents module.

    Chapter 5 shows us how to create our first HTML5 front end. I wanted to make this chapter as simple as possible so I used jQuery Mobile. In this chapter the reader will write very basic HTML and have the opportunity to see how jQuery Mobile progressively enhances simple markup to create impressive mobile solutions.

    Chapter 6 is the exact opposite of chapter 5. Here I wanted to demonstrate flexibility and performance. This chapter is intentionally designed to provide a challenge for developers. Readers tell me it is a good chapter, perhaps a little intimidating, but very worthwhile. In this chapter you will work with AngularJS, Topcoat, and FontAwesome.

    Chapter 7 shows the reader how to build back-end services for Chapters 5 and 6 using iScripts.

    Chapter 8 is the same as Chapter 7 but uses REST services instead of iScripts. If you are new to PeopleSoft REST services and want to learn how to configure REST services as well as how to work with Documents to serve JSON from Integration Broker, then you will find this chapter very valuable.

    Chapter 9 shifts from HTML5 to native. In this chapter the reader will learn how to use the Android SDK to consume the services built in chapter 8. The point of this chapter is not to teach Android development but rather how to consume PeopleSoft services from Android.

    Chapter 10 turns to a native application category described as Hybrid applications. In this Chapter the reader will learn how to convert the Chapter 6 prototype into an on-device application that has access to device specific features such as the camera. In fact, the example shows how to use the Cordova API to take a selfie.

    Chapter 11 brings us back to Oracle-specific technology by showing how to build a hybrid application using Oracle's Mobile Application Framework (MAF). I chose to spend a little more time in this chapter to teach some of the specifics of MAF. For example, I wasn't very excited about the default appearance of buttons on Android so I included steps showing how to extend the MAF skin.

    Publication is still a few months away, but we are getting close. I'm really hoping to be able to give away copies during my OpenWorld session this year.

    ReConnect 2015

    PeopleSoft Spotlight Series: Fluid

    $
    0
    0

    Have you seen the new PeopleTools Spotlight series? I just finished the Developing Fluid Applications lesson. This 1-hour video walks you through creating a fluid component, including some of the special CSS style classes, such as psc_column-2. The example is very well done and should get new fluid developers headed down the right path.

    PeopleTools Mobile Book Now Shipping

    OpenWorld 2015 Conference Schedule

    $
    0
    0

    Just a couple more weeks and we will be enjoying the great weather and hospitality of San Francisco. I am anxiously anticipating another great OpenWorld conference. As always, I look forward to meeting with you between sessions or in the demo grounds. I will be presenting "PeopleSoft Developer: Tips and Techniques [CON8596]" on Monday, Oct 26 at 12:15 p.m. in Moscone West—3007.

    I find the OpenWorld/JavaOne content catalog a little intimidating. If you are presenting a PeopleTools topic, please post your session details in the comments below to help the rest of us find PeopleTools-related sessions.

    Viewing all 201 articles
    Browse latest View live