Thursday, May 28, 2009

Adding Flexibility and Re-use to your Web Site Design

We are frequently asked, “How many layout templates should we have in our Site Studio site?”  The (short) correct answer is always, “As many as you need.”  But a more realistic response includes some additional thought behind it:

Take into consideration the features and functions of your site.  Are you replicating functionality in a similar manner?  Do you have pages, or a series of pages, that are remarkably similar, but functionally or structurally different in one or two areas?  (For example, do you have a special section that creates an exception to your navigation rule or has an additional dynamic list?)   These structural differences often get interpreted as a need for additional layout templates.  However, adding new layout templates comes with an added cost.
When adding layout templates, keep the following in mind:

  • These templates must be maintained.  If your site design changes, or if there is some structural change to your web site, someone will need to modify the template
  • These templates must be accounted for.  This is one more file that you will need to track during the publication process
  • These templates must maintain consistency, and the more files you have, the more difficult this task becomes. 

Ideally, a small number of templates will serve a greater good on your website.  However, companies

Fragments and parameters offer a great starting ground for allowing greater flexibility with layout templates, but they often fall short of a full solution.  When you add a fragment to a layout template and assign parameter values, those values are stored on the layout template itself.  For example, if you have a fragment that displays a dynamic list, and the query powering that list is assigned in a parameter, that value is saved directly on your layout template.  If you were to use that layout template in another section, you would be unable to change the query without affecting the page as it appears in the original section.  This severely limits the reusability and flexibility of this layout template.

Luckily, the Oracle CMS provides an alternative method of assigning that dynamic query value.  The tool used for that is “Custom Section Properties.”  Just like you can define and assign parameters to a specific fragment, you can define and assign properties to the sections of your web site.  These properties are then available on your layout templates and in your fragments.

Let’s take closer look at the dynamic query example.  Assume that your original fragment was set up in the following manner:

You have a single parameter “ssQueryText” that defines your query:



You have the following code inside your snippet:

<!--$ ssQueryText = getValue("#active", ssFragmentInstanceId & "_ssQueryText") -->
<!--$ QueryText = ssQueryText -->
<!--$ executeService("GET_SEARCH_RESULTS") -->
<!--$ loop SearchResults -->
      <!--$ dDocTitle --><br />
<!--$ endloop -->


Note: Granted, this is an overly-simplistic fragment.  You can certainly expand this to include styles, sorting, additional logic, etc.

If you were to put this fragment on a layout page and assign that layout page to two different sections of your web site, there would be no way to use the same fragment to display different lists.  This can be extremely limiting.

Let’s take a new approach with a different fragment.  Before we create the fragment though, we’ll need to set up our site to accommodate a Custom Section Property.  In the top menu of Site Studio, there is a button for “Define Custom Section Properties.”  Click that button to access the Custom Section Properties setup screen.



From the setup screen, click the “Add” button.  This will allow you to add a custom section to the Properties pane for the sections.  This process is very similar to adding a parameter to a fragment.



You’ll need to provide three values for the definition:

  • Name: must be a valid XML name, so no spaces or special characters.  We’ll call ours “SectionQuery”
  • Type: you have a wide variety of formats from which to choose.  In this case, let’s select “managedquery”
  • Description: any descriptive text to help understand the property better.  We’ve used “The query that is special to this sections


Once you’ve filled this out, click “OK.”



Your Custom Section Property is now defined, so just click “OK” again to return to Site Studio.  Click on a section and look at the bottom of the Properties pane.  You should see a new set of properties labeled “Custom section properties,” and your property should be present in the list:



Just as you can assign a different title to each section, you now have the ability to assign a different query to each section.  Using your same two sections as before, enter two different queries for those sections.  Notice that, because we selected “managedquery” as the variable type, Site Studio automatically assists us in creating a valid query.

Let’s take another look at the fragment and incorporate this new property into the code.  Create a new fragment, but do not include a parameter.  Remember, we want to get the query value from the Custom Section Property that we created.  The only thing that really needs to change in this fragment is how QueryText gets assigned.  In the previous example, we took it from a parameter.  Here, we can leverage our Custom Section Property easily:

<!--$ QueryText = ssGetNodeProperty("SectionQuery") -->

We’re using the ssGetNodeProperty function to read the value from the Properties panel and incorporate it into our code execution.  From here on out, the code is the same:

<!--$ executeService("GET_SEARCH_RESULTS") -->
<!--$ loop SearchResults -->
      <!--$ dDocTitle --><br />
<!--$ endloop -->


Save your new fragment and put it on your layout page.  Check your results.  You should see the same layout page, but with different query results!
This feature has endless possibilities.  Some common uses include:

  • Changing navigation structures; perhaps some sections require 2 levels of navigation while some require three
  • Adding a true/ false option to show an area of the layout page
  • Assigning different style sheets to different sections
  • Managing parameters for Flash objects
One caveat to Custom Section Properties:  Remember that once you create a Custom Section Property, it is available for all sections of the web site.  Having an abundance of CSPs can lead to some confusion, so choose and name your CSPs well.

Monday, April 27, 2009

Expanding Your Search Results

When you run a search in Oracle UCM, the system will max out the number of documents it returns according to the MaxResults variable.  The default value for this is 200, and you can change this number in your config.cfg file.  Be warned that increasing this number beyond 200 can have adverse effects on your search performance, so use this with caution.

Another Method of Expansion

But what if, on a rare occasion, you do need to get extra search results, but don’t want to modify the configuration file so as to affect all searches?  This post demonstrates some simple IDOC code that will expand your search results.

Sample HCSP

For this example, we’ll put the code in a simple HCSP page.  The same strategies would work just as well inside fragments, components, or other code sources.
Create a basic SearchResults.hcsp page.  I always like to put in some basic HTML code first to aid with display.   Once you have that set up, let’s start with the parameters for the search:

<!--$ QueryText = “” -->
<!--$ desiredResults = 500 -->
<!--$ maxResultsPerSearch = 200 -->


The QueryText variable is required to execute the search service.  By setting it to blank, we are getting all documents in the system.  You can put in your own properly formatted query here if you like.

The desiredResults variable is how many results we’d really like to see.  The maxResultsPerSearch variable is set to 200, the default maximum that we’ll get from a single search.  We can expand this code a little to accommodate a custom setting here:

<!--$ if MaxResults and MaxResults gt 0 -->
     <!--$ maxResultsPerSearch = MaxResults -->
<!--$ endif -->


This tests for the presence of the MaxResults configuration setting and, if it finds one, will swap that value in for maxResultsPerSearch.

We’re going to need one more variable to keep track of how many total search results we’ve found to date.

<!--$ runningTotal = 0 -->

Now that we’ve got these variables established, we can get our search results.  Remember, each time we run a search, we’ll be capped at 200 results, but we want to get 500.  This means we’ll need to run two 200-result searches and one 100-result search.  We’ll accomplish this by executing the search inside a loop.

<!--$ loopwhile runningTotal lt desiredResults -->

What we’re setting up here is a method to keep track of the total number of results we get.  As long as that number is fewer that the desiredResults, we have more searching to do.

We need to add some additional search parameters that are specific to each iteration of the search execution:

<!--$ ResultCount = desiredResults - runningTotal -->
<!--$ StartRow = runningTotal + 1 -->


I’m cheating a little bit on the ResultCount here.  Theoretically, this number could be well in excess of the search result cap.  I’m relying on Oracle’s internal processes to enforce the limit of 200 documents.  The StartRow will need to be different each time I run the search so that I don’t get duplicates.

Now that this is all set up, I can run the service and print the results:

<!--$ executeService("GET_SEARCH_RESULTS") -->
<!--$ loop SearchResults -->
     <li><!--$ dDocTitle --></li>
<!--$ endloop -->


I have a touch of housekeeping to do to make sure the loop continues (and more importantly, ends) properly:

<!--$ runningTotal = runningTotal + SearchResults.#numRows -->
<!--$ if SearchResults.#numRows eq 0 -->
     <!--$ break -->
<!--$ endif -->


I’m incrementing the runningTotal so I can keep track of my progress.  I’m also checking for a zero result set.  If that happens, then I’ve run out of results and need to break out of the loop.

All that’s left to do is end the loop!

<!--$ endloop -->

When you run this code, you should see 500 documents in your results.
Here is the code in its entirety (plus some minor formatting):

<html>
<head></head>
<body><!--$ QueryText="" -->
<!--$ desiredResults = 300 -->
<!--$ maxResultsPerSearch = 200 -->
<!--$ if MaxResults and MaxResults gt 0 -->
     <!--$ maxResultsPerSearch = MaxResults -->
<!--$ endif -->
<!--$ runningTotal = 0 -->
<ol>
<!--$ loopwhile runningTotal lt desiredResults -->
     <!--$ ResultCount = desiredResults - runningTotal -->
     <!--$ StartRow = runningTotal + 1 -->
     <!--$ executeService("GET_SEARCH_RESULTS") -->
     <!--$ loop SearchResults -->
          <li><!--$ dDocTitle --></li>
     <!--$ endloop -->
     <!--$ runningTotal = runningTotal + SearchResults.#numRows -->
<!--$ endloop -->
<ol>
<body>
<html>


One word of warning: this code will be executing a search service several times, so you may get some slower results. 

Friday, April 10, 2009

Security Groups and Accounts Overview (Part 3)

This is the third entry of a three-part series on Oracle UCM (Stellent) security.

How Access Works

Access to documents is defined by both the Security Group and the Account.  In order to perform an action (such as view the document or check in a revision), a user needs to have permissions to do so by BOTH the Role the user has (and, by extension, the Security Groups the user has) and the Accounts the user has.  Both the Role and the Account settings for that user must grant permission to perform an action.  If either the Role or the Account permission does not exist, the user will be unable to perform the action.

Note: if Accounts are not enabled, then the user needs access via the Role only; accounts are not considered.

Practical Usage

The following roles are defined in the system:

Role Security Groups
Employee Intranet (R)
IntranetManager Intranet (RW)
ExtranetManager

Extranet (RW)
Partner Extranet (R)

The following users exist in the system:

User Roles Accounts
John Employee dept (R)
Sally Employee
IntranetManager
dept (R)
dept/hr (RW)
Beth Employee
Intranet Manager
dept (R)
dept/legal (RW)
Mike Partner partner/all (R)
partner/acme (R)
Hugh Employee
IntranetManager
dept (RW)
Brian Employee
ExtranetManager
dept (R)
partner (RW)
Anne Employee
IntranetManager
ExtranetManager
#all (RWDA)


Let’s look at some sample documents in the system.

Document A

Security Group Intranet
Account dept/legal
Here’s the breakdown of how people can access this document:

User Highest Permission by Role Highest Permission by Account Final Permission
John

R

R

R

Sally

RW

R

R

Beth

RW

RW

RW

Mike

None

None

None

Hugh

RW

RW

RW

Brian

R

R

R

Anne

RWDA

RWDA

RWDA

Sally cannot check out this document, even though her Role allows it. Her account settings are too granular (dept/hr) to write to a document in the dept/legal account.  On the other hand, Hugh can check out this document, because his role allows it and his account setting of /dept (RW) will cascade down to accounts beneath it; so he has write permissions to all documents under the /dept account.

Document B

Security Group Extranet
Account partner/acme
Here’s the breakdown of how people can access this document:

User  Highest Permission by Role Highest Permission by Account Final Permission
John

R

None

None

Sally

R

None

None

Beth

R

None

None

Mike

R

R

R

Hugh

None

None

None

Brian

RW

RW

RW

Anne

RWDA

RWDA

RWDA

Most of these users cannot see this document at all because they do not have the account requirement.  Mike, the partner, can see this document just fine however.

Document C

Security Group Extranet
Account partner/abc
Here’s the breakdown of how people can access this document:

User Highest Permission by Role Highest Permission by Account Final Permission
John

R

None

None

Sally

R

None

None

Beth

R

None

None

Mike

R

None

None

Hugh

None

None

None

Brian

RW

RW

RW

Anne

RWDA

RWDA

RWDA

This document is similar to Document B, but the account is different.  Using the account, this system is able to put a document on the Partner Extranet that only some partners can see.  Notice how Mike is denied access to this document.