Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member
0 Kudos

1.     Document Objective

This document explains steps for customizing and configuring WPC (Web Page Composer) on Portal. The document will include steps to create custom forms, their styling and how these custom forms can be made available to the users for content editing:

  • Creating a custom form
  • Styling for custom form
  • Configure custom form to be made available in Portal
  • Create custom WPC Editor component  

2.     Customization and Configuration of WPC:

     2.1     Preview of WPC (Web Page Composer) functionality

Web Page Composer (WPC) is a tool for business users that facilitates the creation and management of portal pages that can combine business applications with user-generated Web content and static content. In this way, major content creation activities can be performed directly by business users.

The below flowchart depicts how a content is created and then displayed to end users.

As can be seen from the above flow, to display WPC content to users, two key components are required, viz:

    1. XML template which provides a skeleton of input fields where users add content.
    2. XSL which transforms the content created using XML template and displays it.

Any changes in look and feel of content would therefore be done in XSL, whereas any changes in the required fields or data would have to be done in XML.

The following scetions deal with this aspect and will cover in detail the following flow:

    1. Custom XML form creation.
    2. XSL creation to style content created using custom XML.
    3. Mapping of the above two custom XML and XSL so that the form is available to the content editors using WPC.

In addition, the last section touches upon the creation of custom WPC editor component on drop Down.


     2.2     Creating a custom WPC XML form

          2.2.1     Two major parts of XML

    • Property fields for form (These fields are usually not displayed to the end users. These fields are for the purpose of storing some meaningful information regarding the content created. An example can be, a property field which stores when the particular content was last modified).

Below is an example of properties created for a custom form (here some of the type values are customised, for more information,                       please read about standard WPC Editor Components):

    • Element fields for the form (These fields will contain the fields in which user will create content, and is typically styled for display by XSL).

Below is an example of elements created for custom form:

         2.2.2.     Description of various attributes used to create a custom form

          2.2.3.     Description of the custom form created:

    • Id: This attribute is used to define id of the form.
    • Description: This attribute is used to define a key of resource bundle which provides description of form.
    • Showpreview: This is a boolean flag provided by WPC framework used to define whether a preview button is to be shown in edit mode or not.
    • Showelementlist: This is a boolean flag provided by WPC framework used to define whether element list (defined in tag <elements>) will be visible in the form or not.
    • Properties: This tag is used to define all the properties that we want the form to have, define the following properties:
      1. Title
      2. Owner
      3. Modified By
      4. Modified On
      5. Created By
      6. Created On
    • Type: This attribute is used to define the type of WPC component used. There is a list of standard WPC components which can be used. Also we can create custom components (to be covered in Section xxx). Once the custom components have been created and configured, these components can also be used. In the above example properties c to f are using custom components created.
    • Isrequired: This is a boolean flag used to determine whether the particular element/field is mandatory or not.
    • Isfilename: This is a boolean flag, when this is set to true, then the property/element for which the boolean flag is true also becomes the file name at the time of saving in Portal.
    • Size: This attribute is available for certain WPC components. This attribute is used to control the size (width) of the element/property.
    • Property: In case of any custom WPC components, we also need to define a property for these components in order to store the values in the Portal KM.

Note: If the system property is not defined, then the values saved in the custom component cannot be read by the WPC editor during XML transformation.

    • Elements: This tag is used to define all the elements which will be used by a user in order to create content for the form. In this form I have defined the following elements:
      1. Name
      2. Introduction
      3. Image
      4. Body
    • Default: This is a boolean flag which is used to determine whether the element should be shown by default in the form or not.
    • Nodelete: This is a boolean flag which is used to determine whether the element can be deleted from the form instance or not.
    • Singleinstance: This is a boolean flag which is used to define whether the element can be added in the form instance from element list or not.


          2.2.4.     Preview of XML Form

  

     2.3     Creating XSLT (styling) for custom forms

The data created using forms is stored as XML. WPC utilizes XML transformations to transform XML data into HTML content. Custom XSL after creation below is the screenshot of the XSL head:

Description of the XSL head:

    1. xmlns:xsl: Expect elements from the W3 namespace called Transform.
    2. xmlns:wpc: Expect elements from standard WPC namespace for class: com.sap.nw.wpc.km.servicve.editor.xslt.XsltHelperCore.
    3. xsl:output method: This describes that the XML data read by WPC editor will be transformed into a HTML.

         

XSL display logic for the content stored for form described in Section 2.1:

XSL display logic for the form described in section 2.1:

    1. Div: a div tag is opened which will act as a container for all the data being displayed in the form.
    2. link type=”text/javascript”: This is used to include js files before execution of the transformation of XML.
    3. xsl:for-each: This function is used to run a for loop from the first element defined in select till the last element. For loop can be applied for both elements and properties. Select statement will vary as:    

                select=”document/elements/element” or select=”document/properties/property”

                Note: Nested for-each loops cannot be used as the context is lost. For nesting, you should use xsl:if or xsl:when.

          4.  As can be seen from the above screenshot, we can define different styles for various elements by checking for individual                elements using xsl:if

          5.  In xsl:if, current() will have the value of the element used in select. For example:

                                <xsl:if test="@type='name'">

                         <hr class="hr_staffProfile" style="clear: both; border: 1px #e5e5e5 solid; height:                                                       1px; margin-bottom:-5px;" />

                         <span class="staffProfileName" style="font-family: Verdana, Arial, Helvetica, sans-serif; line-height: 24px;                          padding: 0px; color: #549abb; margin: 0px 0px 0px 0px; font-size: 11px; font-weight: bold;"<xsl:value-of                          disable-output-escaping="yes" select="current()" />

                         </span>

      </xsl:if>

              The above snippet checks if the element id = name. If yes, then style is applied and the span holds the text which is extracted                               using: current().

          6.  XSLs have a limitation in that there cannot be any inline changes to local variables and there cannot be any inline function                definitions.               

               To overcome this limitation, we can define function in js files and call them on specific html actions. Two examples are shown in the                above XSL:

      • onclick of span class: minBtn a function: toggleDisplay() is called.
      • onload of blank.gif image a function: triggerInit_staff() is called.

               Final result after XML transformation has been done by XSL:

   

     2.4     Example of functionality achieved using JS

As explained in the earlier section, we have limitations in XSL where we cannot change values of variables, which causes some issues during certain calculations. Also we have limitations with nested loops. To counteract this, we can use a javascript. Below are a couple of ways by which we can call a function in JS through XSL:

    1. Adding javascript reference at the start of XSL as: <link type=”text/javascript” href=”/irj/go/km/docs/......”/>
    2. Adding javascript reference dynamically by using onload of a blank image as:

     <img id=”...” src=”/irj/go/km/docs/......” width=”0px” height=”0px” style=”display:none” onload=”javascript:var a =      createElement('script'); a.src='/irj/go/km/docs/....../jquery-1.11.1.min.js'; document.body.appendChild(a); var b =      createElement('script'); a.src='/irj/go/km/docs/......./promoTiles.js'; document.body.appendChild(b);”>

The functions within the javascript can be then called for various elements using attributes like onclick, or onload.


     2.5     Configure custom form to be made available in Portal

    1. Upload the XML form in KM Path: /etc/wpceditor/types.
    2. Upload the XSL in KM Path: /etc/wpceditor/styles.
    3. Delta copy standard WPC form template at path pcd:portal_content/com.sap.pct/templates/pcc.templates/content/com.sap.portal.pcc.article_default_iview, and paste the new iView in the folder where you want to maintain all the form templates
    4. Update the following iView properties with the custom form: com.sap.nw.wpc.WebContentType, com.sap.portal.ContentTemplate
    5. Perform the following mapping in System Administration:
    • Maintain Document Types: Content ManagementàWeb Page ComposeràEditoràDocument TypesàDocument TypesàDocument Types. This configuration is done to map the XML form with the corresponding XSL and Resource Bundle file.

    • Maintain Document Styles: Content ManagementàWeb Page ComposeràEditoràStylesheetsàStylesheetsàDocument Styles. This configuration is done to define XSL.

    • Maintain Stylesheet Groups: Content ManagementàWeb Page ComposeràEditoràStylesheetsàStylesheet groupsàDocument Style Groups. This configuration is done to define the stylesheet group.

    • Maintain Web Content Types: Content ManagementàWeb Page ComposeràWeb Content TypesàWeb Resource Type -->

               Web Resource Type. This configuration is done to map XML form to the template iView created in Step 2.3 c

     2.6     Create custom WPC component

This document provides step by step procedure on how to create Drop Down custom component in Web Page Composer (WPC):

Being able to create more complex editor components, developers can construct web forms that provide richer user experience.

The biggest benefit associated with using a dropdown menu on your web form is being able to organize all of your content into smaller sub-categories that are easy to locate and chose at design time.

This component gets data from assigned KM property and display it in a dropdown menu inside the forms.

It also take the defined size (attribute) to set the size of the dropdown.


          2.6.1     Pre-requisites

    • You have installed SAP Net Weaver 7.0 SPS 14 Usage Type EP
    • You have deployed the Web Page Composer Add-on More Information: SAP Note 1080110: “Installing the Web Page Composer”
    • You have the Content Administrator and the System Administrator roles
    • You have the WPC Editor Role assigned to your user (required only for testing)
    • You have installed the SAP NetWeaver Developer Studio


          2.6.2     Creating an Editor Component

Editor components extend com.sap.nw.wpc.km.service.editor.component. AbstractEditorComponent. The AbstractEditorComponent implements the basic functionality for all editor components that are simple enough and can be represented by a single input field.


Each editor component stores its value in its coreValue instance field. Since the goal is to have several editors, it is essential to provide a mechanism for storing and initializing them. In order to do that we add additional fields for the values of the extra editors.


In your portal application project in SAP NetWeaver Developer Studio, create a new class that extends the AbstractEditorComponent.


The following methods should be overridden: init (), initializeFromPageContext (), initializeFromXML (), getXMLElement ().


The skeleton of the DropDown class looks like this:

public class DropDownComponent extends AbstractEditorComponent

public void init(AbstractEditorObject editorObject, ComponentConfig cc) {}  --> the only thing the init() method does is to call the initializeFromPageContext()

public void initializeFromPageContext( AbstractEditorObject editorObject, IPageContext context, ComponentConfig cc) {} -->This method initializes the composite editor from the page context. The localization takes place in this method as well.

public void initialiseFromXML( String xmlContent, Attributes attributes, IPropertyMap properties, AbstractEditorObject object, IPortalComponentRequest request) {} –-> This performs the same actions as initializeFromPageContext() but initialization data is taken from the XML file.

private Component getComponent( TextEdit question, HtmlEditorComponent answer, AbstractEditorObject editorObject, Locale locale) {} --> This private method is invoked by initializeFromXML() and initializeFromPageContext() methods in order to create the UI widgets. In Web forms HTMLB UI components are used.

public Element getXMLElement( Document document, String elementId, String tagName, boolean includePropertyValues) {} --> This method creates an XML element to be added in the output XML file.


          2.6.3     Component for Drop Down - Code Snippet below

         public class DropDownComponent extends AbstractEditorComponent {

         private Hashtable values = new Hashtable();

         private static final String application = "net";

         private static final String component = "DropDownComponent";

         public void init(AbstractEditorObject editorObject, ComponentConfig cc) {

         initializeFromPageContext(editorObject, null, cc);

         }

         public void initializeFromPageContext(AbstractEditorObject editorObject, IPageContext context,         ComponentConfig cc) {

         this.editorObject = editorObject;

         this.componentConfig = cc;

         DropdownListBox menu = null;

         if (context != null) {

         this.values = new Hashtable();

         menu = (DropdownListBox) context.getComponentForId("menu_" + editorObject.getId());

         if (menu != null) {

         if (editorObject.getPropertyName().getName() != null) {

         String[] menuOptions = populateData(editorObject.getPropertyName().getName());

         for (int i = 0; i < menuOptions.length; i++) {

         menu.addItem(menuOptions[i], menuOptions[i]);

            }

         setCoreValue(String.valueOf(menu.getSelection())); //this adds the value as KM property

         this.values.put("menu", String.valueOf(menu.getSelection()));

            }

            }

            }

          Locale locale = Locale.getDefault();

          if (context != null) {

          IPortalComponentRequest request = (IPortalComponentRequest) context.getRequest();

          locale = Utils.getCurrentUserLocale(request);

          }

          Component comp = getCompoundComponent(menu, editorObject);

          setHtmlbComponent(comp);

           }

           public void initialiseFromXML(String xmlContent, Attributes attributes, IPropertyMap properties,

           AbstractEditorObject editorObject, IPortalComponentRequest request) {

           this.values = new Hashtable();

           DropdownListBox menu = new DropdownListBox("menu_" + this.editorObject.getId());

           if (editorObject.getPropertyName().getName() != null) {

           String[] menuOptions = populateData(editorObject.getPropertyName().getName());

           for (int i = 0; i < menuOptions.length; i++) {

           menu.addItem(menuOptions[i], menuOptions[i]);

            }

            }

           if (attributes.getValue(IEditorConsts.ATTR_VALUE) != null) {

           if (attributes.getValue(IEditorConsts.ATTR_VALUE).equals("")) {

           menu.setSelection("NA");

            this.values.put("menu", "NA");

            } else {

            menu.setSelection((attributes.getValue(IEditorConsts.ATTR_VALUE)));

            this.values.put("menu", attributes.getValue(IEditorConsts.ATTR_VALUE));

            }

            }

            Locale locale = Utils.getCurrentUserLocale(request);

            setHtmlbComponent(getCompoundComponent(menu,editorObject));

            }

           private Component getCompoundComponent(DropdownListBox menu, AbstractEditorObject editorObject) {

           FormLayout fl = new FormLayout();

           if (menu == null) {

           menu = new DropdownListBox("menu_" + this.editorObject.getId());

           if (editorObject.getPropertyName().getName() != null) {

           String[] menuOptions = populateData(editorObject.getPropertyName().getName());

           for (int i = 0; i < menuOptions.length; i++) {

           menu.addItem(menuOptions[i], menuOptions[i]);

           menu.setSelection(menuOptions[i]);

            }

            }

           menu.setEnabled(editorObject.isEnabled());

            }

           menu.setWidth(String.valueOf(editorObject.getSize()));

           FormLayoutCell flcMenu = fl.addComponent(1, 1, menu);

            flcMenu.setPaddingRight("10px");

            flcMenu.setPaddingLeft("0px");

            flcMenu.setColspan(2);

            return fl;

            }

            public Element getXMLElement(Document document, String elementId, String tagName, boolean includePropertyValues) {

            HtmlParser parser = new HtmlParser();

            Element element = document.createElement(tagName);

            element.setAttribute("type", elementId);

            element.setAttribute(IEditorConsts.ATTR_VALUE, this.values != null ? (String) this.values.get("menu") : "");

            Text text = document.createTextNode(this.values != null ? (String) this.values.get("menu") : "");

            element.appendChild(text); 

            return element;

            }

            public Element getDefaultXMLElement(Document document, String elementId, String tagName, String defaultValue,

            IPropertyName propName) {

            Element element = document.createElement(tagName);

            element.setAttribute("type", elementId);

            element.setAttribute(IEditorConsts.ATTR_VALUE, "");

            return element;

            }

            private String[] populateData(String propName) {

            String[] menuOptions = new String[10];

            try {

            IConfigClientContext context = IConfigClientContext.createContext();

            IConfigManager configManager = Configuration.getInstance().getConfigManager(context);

            if(configManager != null) {

            IConfigPlugin namespacePlugin = configManager.getConfigPlugin("/cm/services/properties_metadata");

            if (namespacePlugin != null) {

            IMutableConfigurable[] namespaceConfigurables = namespacePlugin.getConfigurables();

            for (int i = 0; i < namespaceConfigurables.length; i++) {

            if (namespaceConfigurables[i].getIdValue().equals(propName)) {

            Map<String, String> props = namespaceConfigurables[i].getProperties(true);

            menuOptions = ((String) props.get("values")).split(",");

            }

            }

            }

            }

            } catch (Exception e) {

            e.printStackTrace();

            }

            return menuOptions;

            }

            }


          2.6.4     Once your Editor Component is created, create a Service Wrapper:

The registration of the deployable unit is handled by a service. This service implements the IService and each deployable unit should have a unique key for the purpose of identification and class loading.


public interface IFAQServiceWrapper extends IService {

public static final String KEY = "com.sap.nw.wpc.core.faq";

}

public class ComponentServiceWrapper implements IFAQServiceWrapper {

private IServiceContext mm_serviceContext;

public void init(IServiceContext arg0) {

mm_serviceContext = arg0;

CrtClassLoaderRegistry.addClassLoader(

this.getKey(),

this.getClass().getClassLoader());

}

public void afterInit() {

}

public void configure(IServiceConfiguration arg0) {

}

public void destroy() {

}

public void release() {

}

public IServiceContext getContext() {

return null;

}

public String getKey() {

return null;

}

}


          2.6.5     Register the Editor Component:

Once the Editor component is deployed, perform the following:

  1. Go to System Administration > System Configuration > Knowledge Management > Web Page Composer > Editors > Editor Component > Components.

   2.   Choose New, add description and Implementation Class.

          As the new component would pick KM properties, create a new Property under Property metadata for input values to drop down:

   3.   Go to System Administration > System Configuration > Knowledge Management > Global Services > Property Metadata> Properties

          Create a new Property, enter the required fields such as Allowed Values, Namespace Alias should be wpc and Group should be           wpc_wcm.

          Use this newly created Editor Component (Type) and property Metadata (Property) in XML file as shown below:

Output:

You would be able to see and select the required Drop down values in XML form.         

Labels in this area