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:
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:
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:
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
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):
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:
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.
2.2.4. Preview of XML Form
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:
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:
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:
Final result after XML transformation has been done by XSL:
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:
<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.
Web Resource Type. This configuration is done to map XML form to the template iView created in Step 2.3 c
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.
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.
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;
}
}
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;
}
}
Once the Editor component is deployed, perform the following:
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
37 | |
10 | |
5 | |
4 | |
4 | |
3 | |
3 | |
3 | |
2 | |
2 |