Tuesday, July 21, 2015

Developing website navigational section

Recently, one of our template was taking too much time to render left navigation section on a webpage, which displays all the pages under certain parent page upto level 3.

For reference, you can consider the following case:

Parent Page
- 1
  - 1.1
     - 1.1.1
     - 1.1.2
     - 1.1.3
  - 1.2
     - 1.2.1
  - 1.3
     - 1.3.1
     - 1.3.2
     - 1.3.3
- 2
  - 2.1
  - 2.2
     - 2.1.1
     - 2.1.2
     - 2.1.3
  - 2.3
- 3
  - 3.1
  - 3.2
     - 3.2.1
     - 3.2.2
     - 3.2.3
  - 3.3
     - 3.3.1
     - 3.3.2
and so on...

where, blue ones are level 1 page assets (placed under parent page), dark purple ones are level 2 page assets (obviously placed under level 1 page asset) and orange ones are level 3 page assets within FatWire/Oracle WebCenter Sites.

Similar configuration is present for other sites too as this project is configured for many countries (meaning many sites within OracleWCS and 2 or more dimensions per site)

I am discussing in detail on this topic so whomsoever reads this doesn't make the same mistake which our previous developer did.

Functionality required:
1. To render a particular page with left menu section listing page assets as shown above
2. The current page should be highlighted in left menu
3. On removing/adding page asset below Parent page asset within WCS at any level should reflect in left menu on next page load
4. Same functionality should be present for other sites (if there are multi-lingual sites)
5. And obviously dependencies and caching should be taken care automatically (no publishing of assets or flushing dependencies/caches)

Let me first discuss on how usually developers code navigational elements. Considering the above example i.e. to render left menu; one can think of a logic like: to call certain Template/SiteEntry (say LeftNav, assettype-Page, usage: Element is used within HTML page, cached) by adding following parameters to cache criteria: c, cid, site, locale and other common parameters (if required); which will be called from Layout template.
Now to build left navigation from top-to-bottom, one needs to know parent page id. So there are 2 ways, either you add parent page id to MAP tab of Template/CSElement for every site or load the current page's siteplan and find the parent page by traversing loaded siteplan (One can argue that same can be achieved if there is basic asset which stores only key-value pairs and parent page's asset id can be stored per site but again there is risk of duplicity of asset name, not to mention it is always prone to human-errors).

First case (getting value from MAP) is generic. For latter case, one can do the following:
<asset:load> - Load current asset from the param - cid
<asset:getsitenode> - Get currently loaded asset's node id in SitePlan Tree
<siteplan:load> - Load siteplan by passing the node id obtained above
<siteplan:notepath> - Get list of ancestors
Last entry in the IList will fetch the parent page asset's asset id and node id

Once you have parent page, start by loading the siteplan from this parent page id or node id and build the left navigation:
<asset:load> - Load current parent page asset
<asset:getsidenode> - Get node id of parent page
<siteplan:load> - Load the site plan
<siteplan:children> - Get first level of children
and so on... repeating same process till required level is reached.

Drawbacks of using above logic:
1. If the Template/SiteEntry for LeftNav is cached, there would be many left menu pagelets generated resulting in non-desirable unnecessary over-caching of a page section. For e.g. if you have 50 page assets under parent page asset, then logically you will have 50 cached enteries for left menu per site. And suppose, there are 10 sites configured (multi-lingual), then total number of cached pagelets = 500. Futhermore, consider this -> Even if there are 2 dimensions configured per site within your WebCenter Sites/FatWire project, then there would be 1000 cached pagelets only for left navigation/menu!! At last, one would think that if there are so many cached enteries, developer's end up leaving LeftNav Template/SiteEntry uncached.

2. If there is only site within project, then retrieving & handling value from MAP or using basic asset should not be a problem, but if there are more than 10 sites present in your project, then it may or may not problem; again depends on project design. If you are using basic asset to store key-value, one needs to always make sure if correct page asset id is added as value or not; which is always prone to human-errors. Hence, one can't escape from loading the current page siteplan and then finding the parent page's asset id or node id. There might be some other simple way too.

3. Last but not the least is the issue with loading siteplan for every page asset as you loop through IList from top-to-bottom. Hence, if considered 50 page assets per site under Parent Page, then 51 siteplan loads!! (including parent page) Furthermore, if 10 sites are present within a project, 510 siteplan loads!!  Also, one doesn't know when to stop loading children pages until it is cleared upto which level page assets are to be searched for. Some page asset may or may not have child page assets but still siteplan:children tag is loaded while looping from top-to-bottom (also have to add extra if-else condition for child pages existence). This unnecessary loads using siteplan:load tag will definitely affect page responsiveness for every new page request. Developers should be really good in developing such elements and the depth needs to be fixed before implementation too. Additionally, one not only needs page id, but also require page attributes or association and generate url to show in left navigation. Also, you will add some condition to highlight the current page in left navigation which may or may not be complex (depends on HTML prototype). Considering all this, if you check the load time using <time:set> and <time:get> tags, that would be high compared to other elements.

In your project, if this is how navigation/menu is built; then this is really a bad implementation on developer's part and requires inspection.

So how to avoid this?
Coding such scenario is not much of challenge until you know the proper way to implement it. If you check FirstSiteII, similar case is present: Loading product categories in left navigation on product page but it was using searchstate tags whereas in this case, siteplan tags are used.
I am listing few suggestions which can be implemented to increase page performance and reduce unnecessary generation of pagelets.

1. Pagelet caching: Considering that every page request goes through layout (or wrapper+Layout) template and each layout template calls various parts of your webpages - header, footer, navigation and body. For our case, layout template will call an extra pagelet element - LeftNav. Create LeftNav template (same configuration as before but make it as uncached). Create another template - LeftNavChildren (or whatever name) which will be called from LeftNav template; having same configuration (cached, for page asset and usage as HTML pagelet, include same params). When a page having left navigation is requested,  LeftNav (uncached) will just calculate the parent page's asset id or node id from the current page cid's value and call LeftNavChildren template by passing this calculated parent page's asset id. As we will always get same parent's page asset id from LeftNav template for a site for every page asset in left navigation, there would be only one cached LeftNavChildren per site per locale. Hence, considering same scenario as 1st point in drawbacks section, we will now have only 1 pagelet * 2 dimensions * 10 sites = 20 cached pagelets!! (Awesome, isn't it??). Hence, in one word, try to cache on parent's page cid not on every page asset in left navigation.

2. Loading parent page: There is not much to say for this case as its very obvious - if more sites are present within project, then its better to use tags to get parent page id or else use MAP tab or some other way.

3. Loading child pages: Use siteplan:listpages tag rather than loading child page assets using siteplan:children tag. This tag has ability to search all the child page assets in one go; given that you provide parent page node id and the level of depth upto which search is required. Hence, no messy code. Following few lines will completely fetch siteplan from top-to-bottom for left navigation.
<asset:load> - Load current parent page asset
<asset:getsidenode> - Get node id of parent page
<siteplan:load> - Load the site plan
<siteplan:listpages> - Get list of child page assets of this parent page upto whatever level of depth required.

Resulting IList contains list of child page assets containing important columns: node level (level 1, 2, 3...), id (page asset id) and hasChildren (page asset has children or not). Hence, if coded efficiently, LeftNav execution time can be reduced drastically. Here is a sample element which can load any siteplan by providing page id and level of depth to search & shows tree like navigation.

4. Highlighting current page: As we are not passing current page id, one may ask how to highlight current page in left navigation? Well, its very simple. By Comparing URL or part of URL generated in left navigation with the current page URL (as URL in left navigation for the current page will be same as the URL in the browser, not to mention that URLs will be unique for a site). Hence, a little knowledge on JavaScript/jQuery can do the trick.

There might be few more other tweaks which I may have missed but these are the essentials to know as every project has requirement to develop navigational section like footer, left navigation, breadcrumb, top navigation or sitemap. I really hope this make things clear on developing navigational part of a website and helps many FatWire/Oracle WCS developers.

Furthermore, this article is a must read for any WCS developer.

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Friday, July 17, 2015

Dump variables

If you want to know what all variables are present on a particular webpage, you can run the following code to find all the available variables.


If you want to know what are columns and values present in an IList


Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Friday, July 10, 2015

Adding constraint on existing asset list

In one of my project, I faced dire situation where in I had to add search condition on existing attribute (say A1) of type asset on already searched assetlist.
As we were using lucene search, to search against this attribute: A1, default way will be to add one filedcopier filter, so that it can copy id from custom attribute, A1 to some other custom attribute & then should add the condition for it in search element. Basically, I had to show results by previous conditions + search on A1's attribute value (if exists). For e.g. Previously, if I got 500 results on searching, I should now get results by searching against previous conditions + search results from searching against custom attribute, A1; basically adding one more constraint on searching assets.

Sounds straightforward and easy, right?? But no, clients will be clients. Issue with adding filter is that you have to edit and save all the assets so that filter is executed. But by doing so, updateddate field value's changes and which client did not wanted that to happen at all for auditing purposes. Damn!!

So, basically now I had only one option i.e. to add one if condition while iterating through previous results. Meaning i would have to load each asset, get its attribute: A1 value and check if A1 attribute's value exists for that assets or not & matches with desired value or not. Obviously desired result will be generated but it would be worst non-desirable algorithm.

Hence, I started looking for Tags and APIs to avoid the such bad scenario. And to my luck, there are 2 tags which does what was required for my case - <assetset:setsearchedassets> and <assetset:setlistedassets>

<assetset:setlistedassets> tag can build assetset by consuming assetlist (as input list) but should have columns: assetid and assettype. Even if you don't have this columns within your IList, you can build your IList using <listobject> tags with columns: assetid and assettype

Once you have  assetset, it becomes straightforward to add constraint by creating searchstate and passing this constraint to <assetset:setsearchedassets> tag

Overall code would become like following (assetlist is input list with columns: assetid and assettype):

<assetset:setlistedassets name='thisassetset' assets='assetlist' deptype='exists'/>
<searchstate:create name="ss" op="or"/>
<listobject:create name="lo" columns="value" />
<listobject:addrow name="lo">
    <listobject:argument name="value" value="[some value]"/>
</listobject:addrow>
<listobject:addrow name="lo">
    <listobject:argument name="value" value="[some other value]"/>
</listobject:addrow>
<listobject:tolist name="lo" listvarname="matchlist"/>
<searchstate:addstandardconstraint name="ss" attribute="A1" list="matchlist" immediateonly="true" typename="[Attribute type name]" bucket="filterBucket"/>
<assetset:setsearchedassets name="thisassetset" constraint="ss" assettypes="Content_C" site="
    <%= siteId%>"/>
<assetset:getassetlist name="thisassetset" listvarname="assetlist"/>

Voila!! Now, this output list - assetlist returned is desired filtered list with criteria passed.

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Tuesday, July 7, 2015

Implementing pagination on search results

I have been searching an easy way to implement pagination which requires no jQuery/JavaScript or java class implementation. And to my surprise, I found one element - Search.jsp which was present with product installation itself!! It's located at OpenMarket/Xcelerate/Search/Search.jsp which gave me basic idea on how pagination can be implemented with ease within WCS or FatWire.

Requirements:
1. Your search should be implemented via some search engine like lucene or endeca as they have ability to get results by setting starting index and max results. I have used by default lucene search which is installed out-of-the-box with JSK.
2. Knowledge on implementing forms within WCS. Check details here.
3. Knowledge on implementing lucene search. Check details here.

Procedure:
1. Basic idea is to generate page url with some params which lets your application know what results to show (in other words its just passing different params to your page asset templates). Generate URL same as which you generated for search button - Use <render:gettemplateurl> - Either you get vanity url if configured else you will get url by your assembler or generic long url

2. Append all the input params as query strings - Just append all the input params which were passed to search the current page. You might want to consider using packedargs if many parameters are required to be passed. Use tag: <render:unpackarg> and <render:packargs>

3. Update lucene search logic to search assets by setting start and end index - Simply add the following lines:

int startIndex = (null == pageNum || pageNum.length() == 0 ) ? 0 : (Integer.parseInt( pageNum )-1) * 10;
int endIndex = (null == pageNum || pageNum.length() == 0 ) ? 10 : (Integer.parseInt( pageNum ) * 10);
finalQuery.setStartIndex( startIndex );
finalQuery.setMaxResults( 10 );

where, pageNum = page number like 1,2... if null then just pass 0 as starting index and 10 as endIndex. I have considered that I would be showing 10 results per page. By adding this condition, lucene fetches only those results which are asked for.

4. Atlast, a simple logic to generate pagination

if(numOfArticles>10){%>   
    <ul id="pagination" style="padding-right: 50%;padding-left: 50%;display:inline;">   
    <% int pageCount = 0;   
     for(int p=0; p<numOfArticles; ) {   
        pageCount++;   
        String url = ics.GetVar("actURL")+"?keyword="+keyvalue+"&n="+pageCount;    
       if(Utilities.goodString(pageNum) && Integer.valueOf(pageNum) == pageCount ||       !Utilities.goodString(pageNum) && pageCount==1){   
        %><li style="margin: 0 10px;display:inline;"><a style="font-size: large;"><%= pageCount       %></a></li><%   
        } else {   
           %><li style="margin: 0 10px;display:inline;"><a style="font-size: larger;text-      decoration:underline;" href="<%= url %>"><%= pageCount %></a></li><%   
        }   
        p = p + 10;%>   
     <%} %>   
     </ul>   
<%} 

where, numOfArticles = total number of assets found on search
actURL = search results page url
pageNum = pageNumber like 1,2... which will be passed to URL for all the pagination links
rest of code is simply beautification.

If you have followed instructions mentioned in the posts for Satellite form and lucene, then building pagination should not be tough. 
I have developed and shared one sample JSP element which works with latest 11g JSK + Vanity URL configured + patch2 applied. Make changes in template according to your installation and configuration.

Add the param - "n" to cache-criteria to your previous SearchLayout.jsp and the replace code with the new pagination based search results - SearchLayoutPagination.jsp. Your page should look like following:




Note: There are differences in rendering forms for latest 11g and old versions, do check out how to work with them before trying this example.  

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Friday, July 3, 2015

A simple ajax call

Often there is a requirement to populate results on basis of jquery/js + ajax call to populate data on a web page.

A simple use case:
Suppose you have following hierarchy for your flex parent

Main Parent
 - Parent1
   -- SubParent 11
       -- SubSubParent111
       -- SubSubParent112
  -- SubParent 12
       -- SubSubParent121
       -- SubSubParent122
 - Parent 2
   -- SubParent 2
       -- SubSubParent1
.. and so on

and on your webpage you would like show parents and subparents on basis of  user selection on dropdown. For eg: There are 3 dropdowns present for parent, subparent and subsubparent, if first parent is selected as "Parent1", you have to populate 2nd dropdown with "SubParent11" and "SubParent12" and similarly on selection of subparent, have to show corresponding subsubparent dropdown without page load. In such scenario, you can make use of ajax call to populate parents dynamically.

A simple procedure can be:

1. Create one Template or SiteEntry/CSElement and add required params in cache criteria, in our case only one argument would be "parent". If template, make its usage as "Can be called externally via browser"
2. A sample code from calling Template/CSElement can be like following:

//within script tag for localhost can be:
var url = "http://localhost:8080/cs/ContentServer?pagename=GetParent";var selectedParam;
//On parent dropdown selection selectedParam = "&parent="+$(".parent").val();
//On subparent dropdown selection selectedParam = "&parent="+$(".subparent").val();
url = url+selectedParam;
$.ajax({
url: url
}).done(function() {
// Get the output from your Template/CSElement  
// and populate corresponding child parents
});


3. Your called element which you created in 1st step, should output values in either JSON format or XML format or simply text or whatever, its upto you/your HTML.

Note: Further optimization can be done like only one call is sufficient to populate your dropdowns, etc, which depends on your knowledge of JavaScript/Jquery.

Thus, you can call SiteEntry or Template to dynamically inject data from WCS/FatWire on your webpage.

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Sample code for insite editing

1. How to code insite edit for image attribute (BLOB) ?

a. For Flex asset - blob attribute

<insite:edit field="largeThumbnail" assetid='<%=ics.GetVar("imageId")%>' assettype="AVIImage" list="largeThumbnail" column="value" > 
        <img class="photo left" src='<%=ics.GetVar("imageURL")%>' />
</insite:edit>

b. For Basic asset - blob attribute (url field)

<render:getbloburl
        c='<%=ics.GetVar("c") %>'
        cid='<%=ics.GetVar("cid") %>'
        asset="<asset instance name>"
        field="<asset field name>"
        outstr="theURL"/>
 
<insite:edit field="
<asset field name>" variable="theURL" assetid='<%=ics.GetVar("cid") %>' assettype='<%=ics.GetVar("c") %>' editor="upload">
    <img src='<%=ics.GetVar("theURL") %>'/>
</insite:edit>

2. How to code insite edit for recommendation asset?

<commercontext:getrecommendations collectionname="[RECO NAME]" listvarname="recoList" />
<insite:slotlist slotname="RecommendationLink" assetid='<%=ics.GetVar("recoId")%>' assettype="AdvCols" field="Manualrecs">
<ics:listloop listname="assetlist">   
        <ics:listget listname="assetlist" fieldname="assetid" output="Id"/>   
        <ics:listget listname="assetlist" fieldname="assettype" output="Type"/> 
                <insite:calltemplate tname="[Template Name]" c='<%=ics.GetVar("Type")%>'     cid='<%=ics.GetVar("Id")%>'/>
        </ics:listloop>
</ics:slotlist>

Note: Above code is applicable for only static list of recommendation.

3. How to limit characters for insite edit field?

<insite:edit variable="title" field="titleparams="{maxLength:20}"/>

4. How to code insite edit for CKEditor?

<insite:edit field="body"
              value="${asset.body}"
              editor="ckeditor"
              params="{noValueIndicator: 'Enter body ', width: '600px', height: '500px', toolbar: 'SITES', enableEmbeddedLinks:'1'}"/>

You can also create your own toolbar and add your toolbar name in params attribute in above code.

General instructions:
1. Add clegal param to restrict users from adding other asset type
2. Add cssstyle param for using any particular css file
3. Make use of <insite:ifedit> tag when extra slots/additional js or css are required to be loaded
4. Slot data gets lost when using CSDT+eclipse after editing template: Populate slots in CS and then sync with eclipse. If slots are changed, before coding for template in eclipse, first re-sync and then make changes.

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Sample code for auditing tasks

Sometimes we need to check info on certain attribute, content, templates, etc. for their existence or shared/unshared  or status, etc. For such cases, you can write your own utility to do batch audit operations. I am listing sample code for few cases:

1. Any asset of a particular asset type with a name exists or not?

2. If a particular asset is present in site or not?
3. Check all variables of a particular asset
Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Important list of tags

Listing few important tags required in any FatWire/Oracle WebCenter Sites project:

1. <ics:geterrno/> or ics.GetErrno - Get error number, if error number is not equal to Zero, something error occured. One can check againt errno description here.
<asset:load name="thisAsset" field="name" value="SomeAssetName"/>
if(ics.GetErrno == 0) success
else print error

2. <ics:LogMsg> or ics.LogMsg - To print any info in logs. Use with <ics:debug> tag.

3. <time:set> and <time:get> - Finding time response for any particular Template/CSElement in milliseconds. Enable logging.cs.time=DEBUG
<time:set name="timeVar"/>
//Call template
<time:get name="timeVar" output="elapsedTime"/>

4. <string:stream> - This tag is important when you want text to be shown as exactly as its saved in database. Useful in case of translated asset as it take cares of all other characters which contains diacrtic for other languages like spanish. Furthermore, this tag can retreive value from both ics variable and IList.

5. <render:stream> - All body or summary attributes which may contain hyperlinks or embedded asset can be rendered properly with use of this tag. Similarly, like <string:stream> tag, it can also take both ics variable and IList tag.

6. <asset:children> - It can find associated assets to an asset without loading the asset.

7. <asset:filterassetsbydate> - Very important tag when you want to show versions of an assets. It acts on basis of end date and start date. Read full details here.

8. <asset:setdimparents>, <asset:getdimparents> and <asset:removedimparents> are bunch of tags to set translation for asset, gets dimensionable asset parents and removes dimensional asset parents respectively.

9. <asset:getassettypeproperties> - Get all the properties related to assets as described here.

10. <asset:getlocale> and <asset:locales> - Get asset locale/s.

11. <asset:getsubtype> - Get subtype (definition) of an asset. Usually used in layout template for dispatching request to correct template.

12.  <asset:load>, <asset:get> and <asset:scatter> - Tags related to Basic asset details

13. <assetset:*> - Tags related to Flex asset details

14. <searchstate:*> - Tags related to searching flex assets

15. <asset:revision> and <asset:rollback>: Useful when something goes wrong with batch update/edit and saving assets.

16. <asset:edit>, <asset:checkout>, <asset:save> and <asset:undocheckout> - useful during mass editing and saving assets.

17. <asset:search> and <asset:list> - Tags related to searching basic assets

18. <asset:delete>, <asset:deleterevision> and <asset:void> - Tags related to deleting assets

19. <render:logdep> and <render:unknowndeps> - Tags for generating dependencies.

20. <ics:sql>, <ics:callsql>, <ics:sqlexp> and <ics:selectto> - Tags to get data drom database.

21. <insite:*> - In-context editing tags

22. <ics:getproperty> and <property:get> - Get property from properties files

23. <render:*> and <satellite:*> - Mostly all tags are used to render content. Note: use ics tags if asset is not rendered. For e.g. if your CSElement is just performing some calculation and NOT rendering any data, then use ics:callelement rather than render:callelement.

24. <ics:setssvar>, <ics:getssvar> and <ics:removessvar> - Session related tags

25. <dimensionset:filter> and <dimensionset:filtersingleasset> - Gets translation of asset/s by applying dimensionset filter

26. <listobject:*>, <ics:listget> and <ics:listloop> - List tags

27. <ics:if>, <ics:then> and <ics:else> - Conditional tag. Note: Don't use <ics:if> alone, you always with <ics:then>

28. <render:packargs> and <render:unpackarg> - Send args in form of one string and retreive them later by unpacking them. Usage depends on different situations, check tag library.

29. <satellite:cookie> and <ics:getcookie> - Cookies related tags

30. <vdm:*> and <commercecontext:*> - Engage related tags

31. <siteplan:*> - Tags related to loading siteplan tree

32. <satellite:normalizeurl> - Generates satellite safe url

33. <proxy:*> - Tags related to proxy asset

Page: Placed or Unplaced

There are times when one needs to know if a page asset is under default node or unplaced node of siteplan; be it while generating sitemap, left navigation, top navigation, breadcrumb or finding page asset which contains a particular associated asset.

Mostly anyone can find if a particular page is placed or unplaced using following query:

Select ncode from SitePlanTree where otype='Page' and oid='[Page id]'

But, there seems to be bug for this within Oracle WCS product for the following case. Suppose you have following setup in your siteplan under Default node.

DEFAULT
Page1
- Subpage1
- Subpage2

And if you were to unplace Page1 (along with chilren page assets) from "Default" to "Unplaced Pages" node, ncode for Page1 changes to "Unplaced" but all its children page assets ncode's value remains same i.e. "Placed" as information in SitePlanTree is not updated for children page assets. Hence, running the above query will yield false info.

I faced similar situation when in certain project, there was a requirement to find placed page asset whose association is some asset. As Contributors added that asset to only one placed page asset, it should be unique according to them. But it seemed that somebody had moved that page asset from Default to Unplaced page node which happened to be same case as above and created new set of page asset which was valid as there was only one placed page asset with such association.

Solution: To counter this, there is tag which can decipher if all the parent page assets of a particular page asset are under DEFAULT or UNPLACED node: siteplan:unplacedancestor

<asset:load name="pageAsset" type="Page" objectid='<%=ics.GetVar("PageId")%>'/>
<asset:getsitenode name="pageAsset" output="PageNodeId"/>
<siteplan:load name="pageNode" nodeid='<%=ics.GetVar("PageNodeId")%>'/>
<siteplan:unplacedancestor name="pageNode" output="hasunplacedparent"/>
Parent pages are placed?: <ics:getvar name="hasunplacedparent" />

If value is true, then the loaded page asset is under UNPLACED node.

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Engage: Flushing visitor's data

If you are using Engage, then is one caveat which needs to be taken care of:

"Data in visitor's tables can increase exponentially (obviously depends on how many times your site is visited) which can affect performance"

FatWire/Oracle WCS has no way to know which data is to be deleted and on what basis so basically you have to flush data from tables either manually or by programming. 

Hence, following sample code snippet can help to flush data:

But wait, does admin has to run this code again and again? No. WebCenter Sites has a very important table: SystemEvents where one can add SiteEntry+CSElement or Template and set a time to call this element. Simple, isn't it? So just create an entry in SystemEvents table and some logs, if required.

Tag info: http://docs.oracle.com/cd/E29542_01/apirefs.1111/e39371/JSP/vdm-flushinactive.html
http://docs.oracle.com/cd/E29542_01/apirefs.1111/e39371/JSP/vdm-mergeinactive.html

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Batch edit and save assets

Sometimes its required to achieve some task which requires just mass editing and saving assets (no content update) within FatWire/Oracle WCS.

A simple use case can be like this: Suddenly there is a requirement of adding Flex Filter to certain flex definition to perform certain tasks like extracting file info from BLOB attached. In such cases, you will have to edit and save asset as filters only work after assets are SAVED. Hence, if there are many assets (even if  ~ 20-30 assets), its better to have simple program which can help.

Sample code:

//get list of assets in form of IList (you should know which columns are retrieved, I am assuming I will get values in assetid and assettype as column names)
<ics:listloop name="editSaveList">
<ics:listget listname="editSaveList" name="assetid"/>
<ics:listget listname="editSaveList" name="assettype"/>
<%
String assetType = ics.GetVar("assettype");
String assetid = ics.GetVar("assetid");

// Check if the asset is editable or not
%><asset:canedit type="<%=assetType %>" objectid="<%=assetid %>"/><%
//if ics.GetErrno == 0 proceed else show the reason
if(ics.GetErrno == 0) {

//2 cases to consider
//Revision tracking on or off for the asset type

// if asset type is revision tracked
<asset:checkout objectid="<%=assetid %>" type="<%=assetType %>"/>
//if checkout successful ( i.e. ics.GetErrno == 0) proceed else show error
<asset:load name="assetToEdit" type="<%=assetType %>" objectid='<%=assetid%>' option="editable" />
<asset:save name="assetToEdit" />
//if save successful ( i.e. ics.GetErrno == 0) proceed else show error
<asset:checkin name="assetToEdit" annotation="Edited and saved using EditSaveAsset utility" flush="true"/>
//if checkin successful ( i.e. ics.GetErrno == 0) proceed else show error

// else if asset type is NOT revision tracked
<asset:load name="assetToEdit" type="<%=assetType %>" objectid='<%=assetid%>' option="editable" />
<asset:save name="assetToEdit" />

}  else {
//print or log error
}%>
</ics:listloop>

Thus, implementing above code with proper improvements (check at every step for any errors) can be useful in certain scenarios. It is better to test your utility in Test environment or JSK to avoid unexpected results on management environment.

One question I often get is how to run this utility. There are many ways, I am listing few:
1, Just create one template, add your logic to it and make its usage as "Can be called from External browser". Now call this template via browser in following format:

http://<hostname>:<portnum>/<context>/ContentServer?pagename=<SiteCatalog name of Template>&assetid=<AssetId>&assettype=<AssetType>

E.g. http://localhost:8080/cs/ContentServer?pagename=AviSports/EditSaveAsset&assetid=1234567&assettype=AVIArticle

Additionally, you can code satellite:form within which shows form and you can enter values within this form and submit this form to call the same template and then run this utility. In form, you can pass assetids in list or pass query to get the asset ids and then build your list using <ics:listobject> tag

If you are using form, then simple call like following is enough:
http://localhost:8080/cs/ContentServer?pagename=AviSports/EditSaveAsset

2. Create CSElement and add legal parameters like assettype, assetids, query, etc. and then add you logic to "Element" tab. These params which you added are now available to CSElement and can be retrieved using ics.GetVar(""). So basically you can get single valued assettype, list of assetids OR assetids by performing query, then create your IList and perform batch edit&save. To run this CSElement, save it and then scroll down till you see "Preview", click on it, a form will load up in either new window or new tab. Enter values and submit to run this utility.

Tag info: http://docs.oracle.com/cd/E29542_01/apirefs.1111/e39371/JSP/asset-checkout.html
http://docs.oracle.com/cd/E29542_01/apirefs.1111/e39371/JSP/asset-checkin.html

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

IList: Tags and Asset API

Mostly all output after data retrieval in FatWire and Oracle WCS is in form of IList and one needs to know how to work with IList. Hence, just simple case of how one can retrieve values using IList with tag and api.

Using TAG:

<ics:listloop list="mylist">  <ics:listget listname="mylist" fieldname="assetid" /> <ics:listget listname="mylist" fieldname="assettype" /> <ics:listget listname="mylist" fieldname="#curRow" /> //Current Row <ics:listget listname="mylist" fieldname="#numCols" /> //Number of columns <ics:listget listname="mylist" fieldname="#numRows" /> //Number of rows

//Sometimes it is required to use particular field to perfor some operation
//For eg: If you want to use asset id to pass in template
//Calling in following way will always result in same assetid
<render:calltemplate c="Type" cid='<%= ics.GetList("mylist").getValue("assetid")%>'/>

//instead use "output" attribute to save id in variable as followed:
<ics:listget listname="mylist" fieldname="assetid" output="thisId"/> <% String thisId = ics.GetVar("thisId"); %> <render:calltemplate c="Type" cid='<%= thisId%>'/>
</ics:listloop>

Users can also use this attribute while using ics:listloop tag as required - maxrows, endrow and startrow as required

Tag info: http://docs.oracle.com/cd/E29542_01/apirefs.1111/e39371/JSP/ics-listloop.html

Using Asset API:

IList iList = ics.GetList("ListName");
if(null != iList && iList.hasData()){
int numRows = iList.numRows();
String assetid = "";
String assettype = "";
    for(int currentRow=1; currentRow<=numRows; currentRow++){
             iList.moveTo(currentRow);
            assetid = iList.getValue("assetid");
             assettype = iList.getValue("assettype");
             //Do something
}
ics.RegisterList("ListName",null);
}


API info: http://docs.oracle.com/cd/E29542_01/apirefs.1111/e39356/toc.htm

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

A simple code compare functionality

One of the most important aspect of any development cycle is deployment and while deployment, it is very important to note the changes don...