Sonntag, 27. März 2011

Building LOVs with popup search form and user defined search attributes

Using LOVs in ADF is both a blessing and a curse. On the one hand they are easy to use, on the other hand they can be very frustrating because of their ostensible mysterious behavior. In our specific case we ran into NullPointerException (ADF_FACES-60096) after migrating an application to a JDeveloper 11.1.1.4.0 project.

This blog post contains 2 parts. First we show how we implemented a LOV with a popup search region including user defined search attributes using a view critaria and bind variables. This approach has been working fine with JDeveloper 11.1.1.2.0 and also after migrating project to 11.1.1.3.0. The second part describes our workaround to get the LOV running within a JDeveloper 11.1.1.4.0 project.

Our sample bases upon Oracle's hr schema. It contains one Entity Object "EmployeesEO", a corresponding View Object "EmployeesVO" and a read-only View Object "LOVJobsVO" (SELECT * FROM jobs).    




A basic LOV with popup search dialog has already been configured in our previous LOV post.  
So we will use that sample application and extend it!

Our basic LOV sample works, but there is one disadvantage! Because of defining our search region bases upon "All Queryable Attributes" ...



... our LOV Search Region popup contains search fields for JobId, JobTitle, MinSalary and MaxSalary.



In our use case we only need to filter for attributes JobId and JobTitle, so our job is to remove the salary fields!


Part 1 - View Criteria and Bind Variables for user defined search attributes in LOV popup
To achieve that behavior, we have to create a view criteria on LOVJobsVO. To do this, edit LOVJobsVO, select section Query and add a new view criteria by clicking the related green plus symbol.




In Create View Criteria Dialog click Add Item. Set attribute to "JobId", operator to "contains" and operand to "Bind Variable". After that click the green plus next to field parameter to add a new bind variable




Configure the bind variable in the following way:



Create another bind variable JOB_TITLE for attribute JobTitle in the same way. Therefore select () Group in the LOVJobsVOCriteria-Tree before clicking button Add Item 






After creating the bind variables, our view criteria should be equal to that:



Now we are able to use the view criteria fields JobId and JobTitle as search fields in our LOV popup search form instead of "All Queryable Attributes". Therefore we edit the LOV in EmployeesVO...



... and choose the tab UI Hints of the upcoming dialog. The "Include Search Region" ComboBox contains a new entry "Use LOVJobsVOCriteria". We select that entry an close the edit dialog via OK button.



We are done!

Really? No!
As mentioned in the introduction, that approach was working until JDeveloper 11.1.1.3.0. When running it as a JDev 11.1.1.4.0 project it seems to run, but it does not!  When we run the application and click the magnifier symbol next to JobsLOV's input field everything seems to be fine. We are able to search for JobId and JobTitle:





That is the beavior we intended. So what's going wrong? When clicking a second time on the LOV's magnifier symbol a NullPointerException is thrown:



Part 2 - Workaround to avoid NullPointerException
We were able to fix the problem by substituting the bind variables from our view criteria with empty strings. For doing this edit the LOVJobsVO and mark the view criteria...


... After that select the source tab from the bottom of the overview page. We can see the XML-representation of our view criteria within the selected textblock.


Search for our view criteria items that contain bind variables :JOB_ID and :JOB_TITLE....



... and modify the attribute "IsBindVarValue" in both of the criteria items to "false". After that set the criteria items' "value" attributes  to "". The source code should now be equal to the following screenshot:



After modifying the source, don't forget to click "save all" icon.


Now, we are done!

If we run the application with the modified view criteria, we are able to use the LOV's popup search dialog more than one time!



Download extended sample application

Building LOVs with popup search form

This post is about using LOVs within an ADF application. Although there are similar posts in other blogs, we need this post to build a basic application we will extend in a further blog post.

Our sample bases upon Oracle's hr schema. It contains one Entity Object "EmployeesEO", a corresponding View Object "EmployeesVO" and a read-only View Object "LOVJobsVO" (SELECT * FROM jobs). We want to use LOVJobsVO as our LOV source within EmployeesVO.     


Configure Business Components dependent LOV
First, we configure our EmployeesVO to use LOVJobsVO as source of our LOV. The requirement is to show JobTitle as the LOV's display value, whereas JobId is returned as referenced key value. Andrejus Baranovskis describes this approach in his famous blog, so we just focus on the main steps that are necessary for our sample:

Edit EmployeesVO and add a new transient attribute JobsLOV



Select new transient attribute and add new LOV by clicking the green plus in section List of Values


Create a new View Accessor to make our LOVJobsVO accessible to EmployeesVO



Define JobTitle and JobId as values that are returned from LOV to EmployeesVO after selecting a value from the LOV


Select Tab UI Hints and configure it as follows


Create a ne jspx-page and drag & drop the data control as a form into the page




When running the page and clicking the magnifier symbol next to JobsLOV's input field we get the following popup Search Form

Mission accomplished! 

Please notice: Our next LOV-Post will base upon this sample application!
Download: Popup search region LOV with all queryable attributes sample application

Donnerstag, 24. März 2011

Referencing images when creating reusable templates

If your going to create a template that's exported to a ADF Library JAR to be reusable, you'll get trouble to refer to images within the template.
We might want to add a background image to our beautiful template. So we set a inline style for the first Panel Stretch Layout in our application.

 
<af:panelstretchlayout id="pt_psl1" 
inlinestyle="padding:10px;background-image:url('/oracle/ui/pattern/dynamicShell/images/back.jpg');">
</af:panelstretchlayout>
 
In our design view everything looks nice but after using the template within our application the background image is missing. This is because it's not within our applications directory path at /oracle/ui/pattern/dynamicShell/images/back.img. If you create the structure and put the images there it would be working. But it's against all rules of reusability. So we keep the image within our templates JAR file and refer to it in a different way.
It's quiet simple. Just use
#{facesContext.externalContext.requestContextPath} 
to refer to your image and everything works fine.
 
<af:panelstretchlayout id="pt_psl1" 
inlinestyle="padding:10px;background-image:url('#{facesContext.externalContext.requestContextPath}/oracle/ui/pattern/dynamicShell/images/back.jpg');">
</af:panelstretchlayout>
 

Donnerstag, 17. März 2011

Providing Input Parameters for Taskflows in UI Shell

When trying out to build your own UI Shell based application you possibly have used the pre-implemented source code for your launcherBean that can be found under http://www.oracle.com/technetwork/developer-tools/adf/uishell-093084.html.

This code works well, but if you want to pass parameters into a taskflow the Launcher class does not provide any interface. The solution is simple but powerful. Of course, it is already implemented in our customized ADF Juggernaut Shell Test Application. Please notice: To run our sample application you might have to add the ADF Juggernaut Shell Template to your CLASSPATH.


After decompiling the dynamic tabs shell template we took a look into UI Shell's TabContext class. There you can see two appearances of a method called addTab:

public void addTab(String localizedName, String taskflowId);
public void addTab(String localizedName, String taskflowId, Map<String, Object> parameters);


As you can see in the signature of the second addTab-Method, UI Shell provides the possibility to pass a Map<String, Object> that holds parameters for our task flow. Keeping that knowledge in mind we are able to modify the Launcher class provided by OTN in two steps:

1. Overload method _launchActivity
In order to overload the pre-implemented _launchActivity method edit Launcher.java and insert the following code snippet:
private void _launchActivity(String title, String taskflowId, boolean newTab,
                               Map<String, Object> parametersMap) {
    try {
      if (newTab) {
        TabContext.getCurrentInstance().addTab(title, taskflowId,
                                               parametersMap);
      } else {
        TabContext.getCurrentInstance().addOrSelectTab(title, taskflowId,
                                                       parametersMap);
      }
    } catch (TabContext.TabOverflowException toe) {
      toe.handleDefault();
    }
}

2. Simplify old _launchActivity method
For consistency reasons we should modify the "original" _launchActivity method in the following way:
private void _launchActivity(String title, String taskflowId,
                               boolean newTab) {
    this._launchActivity(title, taskflowId, newTab, null);
}

Now we are able to pass parameters into taskflows! 

An example of passing a parameter into a taskflow is shown in our ADF Juggernaut Shell Test Application.  In the sample application we define an action listener method for our "Parameter Task Flow" button .

public void launchWithParameterTaskFlow(ActionEvent actionEvent) {
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("inParameter", "I'm the Juggernaut!");

    _launchActivity("ParameterTaskFlow",
                    "/WEB-INF/flows/parameterTaskFlow.xml#parameterTaskFlow",
                    false, map);
}

When the button is clicked we generate a HashMap that contains the parameter inParameter with its value "I'm the Juggernaut!". This map is used to pass the containing parameter into the taskflow via _launchActivity method.

The taskflow itself contains a single view activity with a corresponding page fragment. To use the inParameter we passed, we have to define an input paramter in our taskflow. To do this open the taskflow and click somewhere, but not onto the view activity. The property inspector should show the task flow definition attributes now. Under section Parameters we can see the subsection Input Parameter Definitions. Click the green plus to add a new parameter. Name the parameter inParameter and define that its class is java.lang.String. The fields "value" and "required" should be empty. As you can see, the name of the task flow input parameter must match the key we passed in our parameters map via _launchActivity method.



As intended, we are able to use the input parameter within the taskflow by using the EL expression #{pageFlowScope.inParameter}

Please notice: To run our sample application you have to add the ADF Juggernaut Shell Template to your CLASSPATH.

Dienstag, 15. März 2011

ADF Juggernaut Shell

As described in our last post, we created our own implementation of the UI Shell Template for test purposes. It's less complex compared to the original. And of course it looks better ;)



Just download the template and test application to see how it works.
Download: ADF Juggernaut Shell Template (Add this to the classpath of your ViewController Project)
Download: ADF Juggernaut Shell Test Application

Montag, 14. März 2011

Decompiling the Oracle UI Shell Template

The oracle dynamic tabs shell template is one of the best pattern to design your application in a modular way. Additionally the template looks very smooth, so it's a real eye catcher. But not every corporate identity have this blue touch. There might be many people who want to design their own implementation of the template.
This is what we are going to build today. Before we can start, we have to download the template's sources, so we can easily apply our own template.

Unzip the contained fusion web application named SamplePatterns to your workspace and open it with the jdeveloper. 


Before we can start, we have to remove the write protection from the files. Otherwise we wouldn't be able to create our own page definition file. Linux users can just chmod.

~/jdeveloper/mywork/DynamicTabSehll $ sudo chmod 755 -R ./


After we renamed the application and project, we can take a look at the original UI Shell Template. This is located at /oracle/ui/pattern/dynamicShell/dynamicTabSehll.jspx. If you only want to do small changes, it should be easy to do them directly within the template. 
Another solution, is to create a complete new template for your own CI. So we create a new JSF Page Template with the wizard and call it juggernautShell.jspx.




Before we can start to create our own design, we have to create some components and bind them via EL to the tabContext bean. Here is a list of the needed components:



componentbinding
af:panelStretchLayout#{viewScope.tabContext.contentArea}
af:navigationPane#{viewScope.tabContext.tabsNavigationPane}
af:panelGroupLayout#{viewScope.tabContext.innerToolbarArea}
af:panelStretchLayout#{viewScope.tabContext.toolbarArea}
af:popup#{viewScope.tabContext.tooManyTabsPopup}
af:popup#{viewScope.tabContext.tabDirtyPopup}

First we start to create a place for our dynamic tabs. Place a af:decorativeBox at the center of your template and put a af:panelStretchLayout inside the center facet and set its binding attribute this EL value #{viewScope.tabContext.contentArea}.




Even if it looks like a af:panelTabbed, the real ui shell template uses a af:panelStretchLayout with a navigation pane in the top facet and regions in the center facet. To achieve something that looks like a tab panel we put a af:navigationPane the top facet of our just created af:panelStretchLayout and a af:decorativeBox with a welcome facet in the center. Set the navigation panes binding to #{viewScope.tabContext.tabsNavigationPane}. Now it should look like this. 



To Display the proper Tab for our currently active region, we have to do some modifications on our just created af:navigationPane. First surround it with a af:panelBorderLayout und set its rendered attribute to #{viewScope.tabContext.tabsRendered}. Then insert a af:commandNavigationItem inside the node stamp facet. Inside of this Item we need a f:attribute. Take a look at the following code snippet and edit the components attributes.


<af:panelBorderLayout id="pt_psl6"
                      styleClass="AFStretchWidth"
                      rendered="#{viewScope.tabContext.tabsRendered}"
                      inlineStyle="margin-left:5px;">
  <af:navigationPane id="pt_np1"
                     binding="#{viewScope.tabContext.tabsNavigationPane}"
                     rendered="#{viewScope.tabContext.selectedTabIndex != -1}"
                     value="#{viewScope.tabContext.tabMenuModel}"
                     var="tab"
                     inlineStyle="filter: alpha(opacity=100);-moz-opacity: 1.0;opacity: 1.0;">
    <f:facet name="nodeStamp">
      <af:commandNavigationItem id="tabIndex"
                                rendered="#{tab.active}"
                                actionListener="#{viewScope.tabContext.tabActivatedEvent}"
                                inlineStyle="#{tab.dirty ? 'font-style: italic' : ''}"
                                partialSubmit="true"
                                text="#{tab.title}"
                                immediate="true">
        <f:attribute name="tabIndex"
                     value="#{tab.index}"/>
      </af:commandNavigationItem>
    </f:facet>
  </af:navigationPane>
  <f:facet name="end">
    <af:commandImageLink icon="/oracle/ui/pattern/dynamicShell/images/close.png"
                         hoverIcon="/oracle/ui/pattern/dynamicShell/images/closeHover.png"
                         actionListener="#{viewScope.tabContext.tabRemovedEvent}"
                         rendered="#{viewScope.tabContext.selectedTabIndex != -1}"
                         immediate="true"
                         partialSubmit="true"
                         id="pt_cil1"/>
  </f:facet>
</af:panelBorderLayout>


To create the tabbing mechanism, we have to create regions within the center facet. This regions will be rendered dependent on the currently selected tab index. The index will be managed by oracles tab context bean, so we only need to read its value.


To hide the af:decorativeBox containing our welcome facet, we have to set its rendered attribute to #{viewScope.tabContext.selectedTabIndex == -1}.


Surround the af:decorativeBox with a af:group and copy the following XML code inside this group.

<af:region value="#{viewScope.tabContext.tabs[0].binding.regionModel}"
           id="pt_region0"
           rendered="#{viewScope.tabContext.selectedTabIndex == 0}"/>
<af:region value="#{viewScope.tabContext.tabs[1].binding.regionModel}"
           id="pt_region1"
           rendered="#{viewScope.tabContext.selectedTabIndex == 1}"/>
<af:region value="#{viewScope.tabContext.tabs[2].binding.regionModel}"
           id="pt_region2"
           rendered="#{viewScope.tabContext.selectedTabIndex == 2}"/>
<af:region value="#{viewScope.tabContext.tabs[3].binding.regionModel}"
           id="pt_region3"
           rendered="#{viewScope.tabContext.selectedTabIndex == 3}"/>
<af:region value="#{viewScope.tabContext.tabs[4].binding.regionModel}"
           id="pt_region4"
           rendered="#{viewScope.tabContext.selectedTabIndex == 4}"/>
<af:region value="#{viewScope.tabContext.tabs[5].binding.regionModel}"
           id="pt_region5"
           rendered="#{viewScope.tabContext.selectedTabIndex == 5}"/>
<af:region value="#{viewScope.tabContext.tabs[6].binding.regionModel}"
           id="pt_region6"
           rendered="#{viewScope.tabContext.selectedTabIndex == 6}"/>
<af:region value="#{viewScope.tabContext.tabs[7].binding.regionModel}"
           id="pt_region7"
           rendered="#{viewScope.tabContext.selectedTabIndex == 7}"/>
<af:region value="#{viewScope.tabContext.tabs[8].binding.regionModel}"
           id="pt_region8"
           rendered="#{viewScope.tabContext.selectedTabIndex == 8}"/>
<af:region value="#{viewScope.tabContext.tabs[9].binding.regionModel}"
           id="pt_region9"
           rendered="#{viewScope.tabContext.selectedTabIndex == 9}"/>
<af:region value="#{viewScope.tabContext.tabs[10].binding.regionModel}"
           id="pt_region10"
           rendered="#{viewScope.tabContext.selectedTabIndex == 10}"/>
<af:region value="#{viewScope.tabContext.tabs[11].binding.regionModel}"
           id="pt_region11"
           rendered="#{viewScope.tabContext.selectedTabIndex == 11}"/>
<af:region value="#{viewScope.tabContext.tabs[12].binding.regionModel}"
           id="pt_region12"
           rendered="#{viewScope.tabContext.selectedTabIndex == 12}"/>
<af:region value="#{viewScope.tabContext.tabs[13].binding.regionModel}"
           id="pt_region13"
           rendered="#{viewScope.tabContext.selectedTabIndex == 13}"/>
<af:region value="#{viewScope.tabContext.tabs[14].binding.regionModel}"
           id="pt_region14"
           rendered="#{viewScope.tabContext.selectedTabIndex == 14}"/>

Now you can see where the limitation of 15 open tabs comes from. Now it should look like this:




Because of the rendered attribute, we always see only the region of the currently activated tab index. Now everything is set for loading our dynamic regions to the specified place. What's left, is to define those regions in our binding container. Go to your templates page definition file (If it's not there, let jdeveloper create it). You can create every task flow binding by hand or copy the following code to the xml file.


<executables>
    <variableIterator id="variables"/>
    <taskFlow id="r0" taskFlowId="${viewScope.tabContext.tabs[0].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[0].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r1" taskFlowId="${viewScope.tabContext.tabs[1].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[1].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r2" taskFlowId="${viewScope.tabContext.tabs[2].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[2].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r3" taskFlowId="${viewScope.tabContext.tabs[3].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[3].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r4" taskFlowId="${viewScope.tabContext.tabs[4].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[4].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r5" taskFlowId="${viewScope.tabContext.tabs[5].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[5].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r6" taskFlowId="${viewScope.tabContext.tabs[6].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[6].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r7" taskFlowId="${viewScope.tabContext.tabs[7].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[7].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r8" taskFlowId="${viewScope.tabContext.tabs[8].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[8].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r9" taskFlowId="${viewScope.tabContext.tabs[9].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[9].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r10" taskFlowId="${viewScope.tabContext.tabs[10].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[10].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r11" taskFlowId="${viewScope.tabContext.tabs[11].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[11].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r12" taskFlowId="${viewScope.tabContext.tabs[12].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[12].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r13" taskFlowId="${viewScope.tabContext.tabs[13].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[13].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="r14" taskFlowId="${viewScope.tabContext.tabs[14].taskflowId}"
              parametersMap="${viewScope.tabContext.tabs[14].parameters}"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="tabContext" value="${bindings.tabContext}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
  </executables>


Now, everything that's left, is to create the remaining components with their binding to the tab context bean. Otherwise the initialization of the bean will fail. Where you put those components and if you use them is you choice. You can even put their rendered attribute to false and it'll still work.


It's a good advise to check the used template attributes and facets in the original UI Shell Template and adapt to its way of working. 


How to dynamically start your task flows is pretty detailed described in an article from oracle and can be found here


Official Oracle UI Shell Template Site