Sunday, January 3, 2016

Using controllers for development

Welcome to Oracle WebCenter Sites 12c development! After the introduction of MVC framework in Oracle WebCenter Sites, it is now very very easy to perform CRUD operations on assets as OOTB Controller assets are sufficient to carry out nearly 99% of your WCS development. Developers now need to learn little bit of groovy but its okay even if you know JAVA and jstl programming. But still in current release of 12c, legacy tags and asset api can also be used.

If you browse through Samples site, all new concepts are described aptly in the site's sections itself so its redundant to write the same thing in this post. I just wanted to list down few simple cases which are not present or not explained in detail in Samples or Avisports site.

1. Debugging using watchers:

Debugging is little bit difficult when it comes to WebCenter Sites; if not aware of various core functions which are used in background. And seems like that hasn't changed much as it is same case while using controllers. Whenever there is error thrown from controller, you get detailed error log in sites.log but sometimes it may be difficult to decipher the root cause. Thus, the role of watchers is very useful not only during debugging but also gives detail on output of your data in the model object. For every function, you can just add your own watchers and print them in your JSP code.

Syntax to define in controller: watch("__my_watch__")
Syntax to print output in JSP: $("__my_watch__")

Watchers are very useful when you just want to check output from your controller's method. For e.g. Create one controller and add any method which is defined in Samples site and then create one template (applies to various asset type, can be called from browser, uncached) and assign your controller. In template code, just add the following line of code within <cs:ftcs> opening and closing tag, which is basically default watcher: ${__$WATCH$__}
Now, call your template directly from browser using following url: <hostname>:<port>/sites/Satellite?pagename=<sitename where template was created>/<template name>&c=Page&cid=<Some random id> --> which should print the output of your controller's method. Note: Output will be from the asset of what c and cid is used in controller.

2. Accessing attribute values:

To fetch attributes of data type: string, text, number, float, date, int, money ; use the following syntax in JSP template or CSElement:
${asset.YOUR_ATTRIBUTE_NAME} or ${asset["YOUR_ATTRIBUTE_NAME"]}  
where asset is the model object containing the map values which was defined in controller.

To fetch asset of type blob, use the following syntax:  
${asset._bloblink_} or ${asset.YOURATTRIBUTENAME_bloblink_}

To fetch hyperlink for an asset: ${asset._link_}

To fetch attribute of type - asset: ${asset.YOURATTRIBUTENAME} but it will just provide your associated asset id. So to get detail/summary/link template fragment of this associated asset, fetch id in your controller using following syntax: models.asset.YOURATTRIBUTENAME whose output is basically an arraylist. Loop through list and get each id values. Create one empty List<Fragment> and add as TemplateFragment or SiteEntryFragment or ElementFragment to it to be used in Template or CSElement code. For eg: Suppose product assets are associated to one attribute: RelatedProducts and you want to show them using Summary template, then your code will be as followed:

package oracle.webcenter.sites.controller
import com.fatwire.assetapi.data.*
import com.openmarket.xcelerate.asset.*
import com.fatwire.assetapi.fragment.*
import com.fatwire.assetapi.data.search.*
/*
Note: This groovy class should extend AssetController otherwise you won't get list of related products
using: models.asset.RelatedProducts where "RelatedProducts" is attribute name
*/
public class PageController extends AssetController
{
/*
* List of template fragment for related products associated to Page attribute of type asset
*/
public void relatedProducts(){
if(models.asset.RelatedProducts != null){
List<Fragment> relatedProductsList = newFragmentList()
def relatedProducts = models.asset.RelatedProducts //Arraylist
for(AssetId assetId : relatedProducts){
relatedProductsList.add(
newTemplateFragment()
.forAsset(assetId)
.useTemplate("Summary")
.useStyle(Fragment.Style.EMBEDDED)
.useArguments("c,cid,locale")
)
}
models.putAt("relatedProducts", relatedProductsList)
}
}
@RequiredParams(query="c,cid")
public void doWork(Map models)
{
super.doWork(super.models)
relatedProducts()
}
}
 
and jsp code as followed (don't forget to include jstl core library at top of your element):

<c:if test="${not empty relatedProducts}">
   <c:forEach begin="0" end="${relatedProducts.size()-1}" var="product">
      <fragment:include name="relatedProducts" index="${product}"/>
   </c:forEach>
</c:if>

3. Formatting date is now very simple using jstl fmt:formatDate tag. Just pass ${asset.DATE_ATTRIBUTE_NAME} and desired format to get the formatted date.

4. Apart from watchers, you can directly log the desired output using already defined logger for controller:  
log.info("AssetMap: " + assetMap) OR log.info(JsonOutput.prettyPrint(JsonOutput.toJson(assetMap)))

5. Although methods related to search are explained but fails to provide info on how to sort and search against locale/dimension content. Following code searches against Page of only particular locale - en_US, sorts by updateddate descending and sets only the latest updated page into model object.
package oracle.webcenter.sites.controller
import groovy.json.JsonOutput;
import COM.FutureTense.Interfaces.Utilities;
import com.fatwire.assetapi.data.*
import com.fatwire.assetapi.data.search.BinaryRelationalOperator;
import com.fatwire.assetapi.data.search.Orders;
import com.fatwire.assetapi.data.search.Filter;
import com.fatwire.assetapi.data.search.SearchHelper;
import com.fatwire.assetapi.data.search.SortDirection;
import com.fatwire.assetapi.data.search.impl.OrderBuilder;
import com.openmarket.xcelerate.asset.*
import com.fatwire.assetapi.fragment.*
public class TestSearchController extends BaseController
{
public void searchContent(){
List<Map<String, String>> searchResults = null
//Order by updateddate first and then name
Orders order = newOrderChain("updateddate").thenBy(SortDirection.DESCENDING, "updateddate", "name").build();
//Add locale constraint
Filter localeFilter = newFilter("locale", BinaryRelationalOperator.EQUALS, "en_US");
//Search query for only latest updated page asset
searchResults = newSearcher()
.from("Page")
.inSite("FirstSiteII")
.orderBy(order)
.top(1) //This line provides only the first item
.selectAll()
.filter(localeFilter)
.search()
if(searchResults != null) {
List<Map<String, String>> results = new ArrayList<Map<String,String>>()
Map assetMap = null
for(Map<String, String> map in searchResults)
{
assetMap = newAssetReader()
.forAsset("Page", Long.parseLong(map.id))
.includeLinks(true)
.includeLinksForBlobs(true)
.selectAll(true)
.read()
//log.info(JsonOutput.prettyPrint(JsonOutput.toJson(assetMap)))
results.add(assetMap)
}
if(assetMap != null && !results.empty) {
models.put("results", results)
}
}
}
@RequiredParams(query="c,cid")
public void doWork(Map models)
{
searchContent()
}
}

That's all for now, will share as I explore more.

Need help? Click here.

6 comments:

  1. Nice Article.... really useful for me.

    Thanks,
    AjayKishore

    ReplyDelete
  2. Hi,

    how to retrieve flex attributes while doing the filter search
    ex: def results = newSearcher().from("Site_News_C").selectAll().search()
    the models.results --- it is showing only meta attributes,not the flex attributes.
    How to solve this please let me know if you have any clue on this.

    ReplyDelete
    Replies
    1. Use SearchHelper for better results

      Delete
    2. searcher with filter also getting the results now...

      Delete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I have a page attribute of blob type. How do i render its values in my template.

    ReplyDelete