Quick-n-Dirty: How to add HTML 5 events

One way for adding unsupported events to an XPage or a component is the trick from Keith. But this is limited to CSJS only. If you need to execute a server side event, you just need change the name of the event to a new HTML 5 event name which does not exist in the DDE.

Here is an example for the new onSearch event:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:inputText id="inputTextSearch" type="search">
        <xp:eventHandler event="onsearch" submit="true"
            refreshMode="partial" refreshId="labelSearchFor">
            <xp:this.action>
               <![CDATA[#{javascript:print("onSearch");}]]>
            </xp:this.action>
        </xp:eventHandler>
    </xp:inputText>

    <xp:br />
    <xp:br />

    <xp:label id="labelSearchFor">
        <xp:this.value>
           <![CDATA[#{javascript:getComponent("inputTextSearch").value}]]>
        </xp:this.value>
    </xp:label>

</xp:view>

After entering a value and hitting enter…

… you can see on the server console that the event occurred:

This technique allows to add new events to the whole XPage, for example the onContextMenu event:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

        <xp:eventHandler event="oncontextmenu" submit="true"
            refreshMode="norefresh">
            <xp:this.action>
                <![CDATA[#{javascript:print("onContextMenu");}]]>
            </xp:this.action>
        </xp:eventHandler>

</xp:view>

Every time if the user opens the context menu in the browser, the event is triggered and executed on the server:

The name of the event must be lowercased. A list of HTML 5 events can be found here: http://www.tutorialspoint.com/html5/html5_events.htm. Not all browsers support every HTML 5 event in the list.

P.S. Keep in mind that this article has been posted in the “Quick-n-Dirty” category.

Veröffentlicht unter HTML, HTML5, Java Script, ServerSide JavaScript, Web, XPages | Verschlagwortet mit , , , , , , , , , , | 3 Kommentare

XPages: compositeData is undefined

An interesting question was asked on StackOverflow.com: The compositeData of custom control is undefined in beforeRenderResponse event. I have never noticed this before, but if you are accessing the compositeData object in the before-, afterRenderResponse or the afterRestoreView event, the object is undefined.

Here is a simple demonstration CC which just prints the type of the compositeData object to the console:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:this.beforePageLoad>
        <![CDATA[#{javascript:print( "beforePageLoad: " + typeof( compositeData )) }]]>
    </xp:this.beforePageLoad>

    <xp:this.afterPageLoad>
        <![CDATA[#{javascript:print( "afterPageLoad: " + typeof( compositeData )) }]]>
    </xp:this.afterPageLoad>

    <xp:this.afterRestoreView>
        <![CDATA[#{javascript:print( "afterRestoreView: "  + typeof( compositeData )) }]]>
    </xp:this.afterRestoreView>

    <xp:this.beforeRenderResponse>
        <![CDATA[#{javascript: print( "beforeRenderResponse: "  + typeof( compositeData )) }]]>
    </xp:this.beforeRenderResponse>

    <xp:this.afterRenderResponse>
        <![CDATA[#{javascript: print( "afterRenderResponse: "  + typeof( compositeData )) }]]>
    </xp:this.afterRenderResponse>

</xp:view>

And here is a screenshot of the console, including a partial refresh of the page:

Only in the PageLoad events the compositeData object is available. But why?

The answer is rather simple: The events are executed in a different context. The PageLoad events are running „inside“ of the UIInlcudeComposite (the Custom Control), the other events are running in the UIViewRoot:

That’s why the compositeData is undefined in these events. To use the object in the events, you have to do the following:

First you have to add an ID to your custom control:

<xc:ccWithId test="foo" id="ccWithId" />

This allows you to access the CC as a regular component with getComponent(). Now you can access the com.ibm.xsp.binding.PropertyMap of the component in the custom control’s event which holds the variable you want:

<xp:this.beforeRenderResponse>
   <![CDATA[#{javascript:
      var cmp:com.ibm.xsp.component.UIIncludeComposite = getComponent("ccWithId");
      print("Value of 'test' -> " + cmp.getPropertyMap().getString("test") )
   }]]>
</xp:this.beforeRenderResponse>
Veröffentlicht unter Java, JSF, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , , , | Schreib einen Kommentar

XPages: UI for editing localization files (1)

For one of my customers I am developing an easy way for editing the localization/translation of existing XPages applications: Instead of importing and exporting the property files with the Domino Designer (which is not an option for non-developers) it would be more suitable, if the users would have the possibility to edit the files directly in the browser. I am currently  working on a XPage solution for this, and this is my current beta version.

This is a proof-of-concept with some known bugs, f.e. HTML special chars are not encoded correctly. But this can be fixied easily. First, it is required that the users can choose a property file to edit. This can be realized with a small Java class I have created (based on a stackoverflow.com answer):

package ch.hasselba.xpages.localization;

import java.util.Vector;
import javax.faces.context.FacesContext;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NoteCollection;
import lotus.domino.NotesException;

public class DesignElements {

    private final String EMPTY_STRING = "";
    private final String[] FLAG_PROPERTIES = { "gC~4K2P", "gC~4K2"; "gC~4;2" };
    private final String FIELD_$FLAGS = "$Flags";
    private final String FIELD_TITLE = "$TITLE";

    /**
     * returns Vector containing all property files of a database
     * 
     * No error handling included!
     * 
     * @category Domino
     * @author Sven Hasselbach
     * @category Tools
     * @version 0.2
     */
    public Vector<String> getPropertFileList() {

        FacesContext fc = FacesContext.getCurrentInstance();

        Vector<String> data = new Vector<String>();
        try {

            // get DB
            Database db = (Database) fc.getApplication().getVariableResolver()
                    .resolveVariable(fc, "database");

            // get all design docs
            NoteCollection nc = db.createNoteCollection(false);
            nc.selectAllDesignElements(true);
            nc.buildCollection();

            // process all notes
            String noteId = "";
            noteId = nc.getFirstNoteID();

            Document doc = null;
            // 
            while (!(EMPTY_STRING.equals(noteId))) {

                // get design doc
                doc = db.getDocumentByID(noteId);

                // check if its a property file
                for (int i = 0; i < FLAG_PROPERTIES.length; i++) {
                    if (FLAG_PROPERTIES[i].equals(doc
                            .getItemValueString(FIELD_$FLAGS))) {
                        // add to Vector
                        data.add(doc.getItemValueString(FIELD_TITLE));
                    }
                }

                // next one
                noteId = nc.getNextNoteID(noteId);

                // recycle doc
                recycleObject(doc);
            }

        } catch (NotesException e) {
            e.printStackTrace();
        }

        return data;

    }

    /**
     * recycles a domino document instance
     * 
     * @param lotus
     *            .domino.Base obj to recycle
     * @category Domino
     * @author Sven Hasselbach
     * @category Tools
     * @version 1.1
     */
    public static void recycleObject(lotus.domino.Base obj) {
        if (obj != null) {
            try {
                obj.recycle();
            } catch (Exception e) {
            }
        }
    }
}

The difference to the answer on stackoverflow.com is the additional property flag I have added to this class (I know it would be better to check the flags instead of comparing the strings, but it seems to work this way).

Next step is to load the selected property file. This can be done with a managed bean and a helper class for the entries itself. Here is the helper class:

package ch.hasselba.xpages.localization;

import java.util.UUID;

/**
 * Locale Entry of a locale file
 * 
 * @author Sven Hasselbach
 * @category Localization
 * @version 1.0
 * 
 */

public class LocaleEntry {

    private String id;
    private String name;
    private String value;

    /**
     * initializes the object and
     * sets an unique identifier
     */
    public LocaleEntry(){
        this.id = UUID.randomUUID().toString();
        this.name = "";
        this.value = "";
    }

    /**
     * returns unique identifier of the object
     * @return String unique id
     */
    public String getId() {
        return id;
    }

    /**
     * sets the unique identifier of the entry
     * @param id String unique id
     */
    public void setId(final String id) {
        this.id = id;
    }

    /**
     * returns the name of the entry
     * @return String
     */
    public String getName() {
        return name;
    }

    /**
     * sets the name of the entry
     * @param String
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * returns the value of the entry
     * @return String
     */
    public String getValue() {
        return value;
    }

    /**
     * sets the value of the entry
     * @param value
     */
    public void setValue(String value) {
        this.value = value;
    }

}

And here comes the „Loader“ class:

package ch.hasselba.xpages.localization;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import com.ibm.xsp.context.FacesContextEx;

/**
 * "Loader" class to access the entries
 * of a .property file
 * 
 * @author Sven Hasselbach
 * @version 1.0
 */
public class Locale {

    private Vector<LocaleEntry> locales;

    /**
     * @return vector containing the LocalEntry objects
     */
    public Vector<LocaleEntry> getLocales() {
        return locales;
    }

    /**
     * sets vector containing the LocalEntry objects
     * @param Vector
     */
    public void setLocales(Vector<LocaleEntry> locales) {
        this.locales = locales;
    }

    /**
     * wrapper for the static method call
     * 
     * @param fileName name of the property file to load
     */
    public void loadFile(final String fileName) {
        Map<String, String> m = getPropertiesFromFile(fileName);
        this.locales = parseMap(m);
    }

    /**
     * loads a property file and parses it to a key/value map
     * 
     * @param fileName name of the property file to load
     * @return Map containing the key/values
     * 
     * The loading routine is shamelessly copied from Ulrich Krause:
     * http://openntf.org/XSnippets.nsf/snippet.xsp?id=access-.properties-files
     */
    public static Map<String, String> getPropertiesFromFile(String fileName) {
        Properties prop = new Properties();

        try {
            prop.load(FacesContextEx.getCurrentInstance().getExternalContext()
                    .getResourceAsStream(fileName));
            Map<String, String> map = new HashMap<String, String>((Map) prop);

            return map;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * parses a property map and create a vector
     * with LocalEntry objects
     * 
     * @param map to parse
     * @return Vector containing the LocalEntry objects
     */
    public static Vector<LocaleEntry> parseMap(final Map<String, String> map) {

        // init new vector
        Vector<LocaleEntry> localEntries = new Vector<LocaleEntry>();
        String key;
        String value;

        // get key set for iteration
        Iterator<?> it = map.keySet().iterator();

        while (it.hasNext()) {

            // extract key & value
            key = (String) it.next();
            value = (String) map.get(key);

            // create new entry and add to vector
            LocaleEntry lEntry = new LocaleEntry();
            lEntry.setName(key);
            lEntry.setValue(value);
            localEntries.add(lEntry);

        }

        // return vector
        return localEntries;

    }

    /**
     * dumps current object data to console
     * Just for debugging
     * 
     * @category Debug
     */
    public void dump() {
        for (int i = 0; i < this.locales.size(); i++) {
            LocaleEntry e = this.locales.get(i);
            System.out.println(e.getId() + " - " + e.getName() + " - "
                    + e.getValue());
        }

    }

}

Now the new managed bean can be added to the faces-config.xml:

<managed-bean>
  <managed-bean-name>locale</managed-bean-name>
  <managed-bean-class>ch.hasselba.xpages.localization.Locale</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

The resulting XPage can look like this:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    Property File:&#160;
    <xp:comboBox id="comboBoxFile" value="#{sessionScope.PropertyFile}">
        <xp:selectItems>
            <xp:this.value>
            <![CDATA[#{javascript:
               importPackage(ch.hasselba.xpages.localization);
               ch.hasselba.xpages.localization.DesignElements().getPropertFileList();
            }]]>
            </xp:this.value>
        </xp:selectItems>
    </xp:comboBox>
    &#160;
    &#160;
    <xp:button value="Load" id="buttonLoadFile">
        <xp:eventHandler event="onclick" submit="true"
            refreshMode="complete">
            <xp:this.action>
               <![CDATA[#{javascript:
                  importPackage(ch.hasselba.xpages.localization);
                  var bean = facesContext.getApplication().getVariableResolver()
                     .resolveVariable(facesContext, "locale");
                  bean.loadFile(sessionScope.get("PropertyFile") )}]]>
            </xp:this.action>
        </xp:eventHandler>
    </xp:button>
    <hr />
    <xp:label id="labelSelecteFile">
        <xp:this.value>
           <![CDATA[#{javascript:sessionScope.get("PropertyFile")}]]>
        </xp:this.value>
    </xp:label>
    <xp:br />
    <xp:br />
    <xp:repeat id="repeat1" rows="9999" var="lMap" value="#{locale.locales}">
        <xp:label value="#{lMap.name}" id="labelMapName" />
        <xp:inputText id="inputTextValue" value="#{lMap.value}" />
        <xp:br />
    </xp:repeat>

</xp:view>

And this is how it looks in the browser:

The next article describes the procedure to save the properties back the database.

Veröffentlicht unter Java, ServerSide JavaScript, Web, XPages | Verschlagwortet mit , , , , , , , , | Ein Kommentar

XPages: Disable iOS „autocorrect“ and „autocapitalize“ features

Two of the worst features ever implemented are the „autocapitalize“ and „autocorrect“ features from iOS devices, because they are enabled by default. It does not make fun to write a blog comment in another language… Or if you just want to enter your email address in a form: The first letter is always upper case, and you always have to kill the suggestion while writing some text.

To disable this behaviour, you have to add two attributes to the components, autocorrect=off and autocapitalize=off:

<xp:inputText id="inputText1">
   <xp:this.attrs>
      <xp:attr name="autocorrect" value="off" />
      <xp:attr name="autocapitalize" value="off" />
   </xp:this.attrs>
</xp:inputText>

To make life a little bit easier you can add these attributes by using a theme, and can disable the theme for every component for which this feature should be enabled:

<theme extends="webstandard"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:noNamespaceSchemaLocation="platform:/plugin/com.ibm.designer.domino.stylekits/schema/stylekit.xsd">

   <control>
      <name>InputField.EditBox</name>
      <property>
         <name>attrs</name>
         <complex type="xp_attr">
            <property>
               <name>name</name>
               <value>autocapitalize</value>
            </property>
            <property>
               <name>value</name>
               <value>off</value>
            </property>
         </complex>
         <complex type="xp_attr">
            <property>
               <name>name</name>
               <value>autocorrect</value>
            </property>
            <property>
               <name>value</name>
               <value>off</value>
            </property>
         </complex>
      </property>
   </control>
   <control>
      <name>InputField.TextArea</name>
      <property>
         <name>attrs</name>
         <complex type="xp_attr">
            <property>
               <name>name</name>
               <value>autocapitalize</value>
            </property>
            <property>
               <name>value</name>
               <value>off</value>
            </property>
         </complex>
         <complex type="xp_attr">
            <property>
               <name>name</name>
               <value>autocorrect</value>
            </property>
            <property>
               <name>value</name>
               <value>off</value>
            </property>
         </complex>
      </property>
   </control>

</theme>

So your users are able to use your application again  – without hating every keystroke. Thank you, Apple!

Veröffentlicht unter HTML, Mobile, Web, XPages | Verschlagwortet mit , , , , , | Schreib einen Kommentar

Quick-n-Dirty: Use your own Factory classes in XPages (2) – The VirtualPageTransformer

While playing a little bit with core XPages functionality I found the interesting VirtualPageTransformer interface. With this interface it is possible to implement „virtual“ XPages, and this allows to do something which can be described like an url mapping (the $$OpenDominoDocument.xsp url for example was implemented this way).

To do create your own transformations you have to do the following:

1. Read the first article about using Factory classes in XPages and create the required /WEB_INF/com.ibm.xsp.factories.properties file in your database

2. In this file add a new entry for the page transformer class to use:

PageTransformer=ch.hasselba.factory.PageTransformer

You can choose every key name you want for the factory. The class will be identified from the XPages runtime by the implemented interface com.ibm.xsp.page.VirtualPageTransformer.

3. Create a java class which implements this interface:

package ch.hasselba.factory;

import com.ibm.xsp.page.VirtualPageTransformer;
import com.ibm.xsp.FacesExceptionEx;
import javax.faces.context.FacesContext;

public class PageTransformer implements VirtualPageTransformer {

    public boolean isVirtualPage(FacesContext fc, String pStr) {
        return (pStr.indexOf("$$VirtualPage$$") > (-1));
    }

    public String transformPageName(FacesContext fc, String pStr) {
        try {
            return "/Test.xsp";
        } catch (Exception e) {
            throw new FacesExceptionEx("Error! " + e.getMessage());
        }
    }
}

This adds the „virtual“ page handler „$$VirtualPage$$“ to your database.

This means that every request to your  database is checked if the keyword is contained in the url. For this test, the method isVirtualPage is called with the current url as parameter. If this method returns true, the method transformPageName is called; this method must return a string which identifies the XPage to use. In this case, it is the XPage „Test.xsp

If you open this URL…

http://localhost/Factories.nsf/$$VirtualPage$$.xsp/blahblahblah/blubb/blubb/?etc=etc

… the XPage „Test.xsp“ is rendered instead.

You can add as much VirtualPageTransformers you want to a database.

P.S. Keep in mind that this article has been posted in the “Quick-n-Dirty” category.

Veröffentlicht unter Java, JSF, XPages | Verschlagwortet mit , , , , | 3 Kommentare

Quick-n-Dirty: Disable Validation for FileDownload control

If you are using the file download control,  you sooner or later want to allow your users to delete an existing file. But if you have some required fields on your XPage, a deletion is not possible because the validation is always fired before the attachment is removed.

To disable to validation you can use this little SSJS snippet which sets the „disableValidators“ property for all events of the UIFileDownload control. Just add the following code to the beforeRenderResponse event of your XPage and change the id of the component to fit your requirements.

<xp:this.beforeRenderResponse>
   <![CDATA[#{javascript:
      /***
       * disable validation for UIFileDownload control
       * 
       * @param UIFileDownload component
       * @author Sven Hasselbach
       * @category SSJS
       * @category UI
       * @version 0.2
       */
      function disableFileDownloadValidation( fDownload:com.ibm.xsp.component.UIFileDownload ){
         if( fDownload === null )
            return;

         rekDisableFileDownloadValidation( fDownload );
      }

      function rekDisableFileDownloadValidation( component:javax.faces.component.UIOutput ){
         try{
            var children:java.util.List = component.getChildren();
            var it:java.util.Iterator = children.iterator();
            var curChild:javax.faces.component.UIOutput;

            while( it.hasNext() ){
               curChild = it.next();
               if( typeof( curChild ) === 'com.ibm.xsp.component.xp.XspEventHandler' )
                  curChild.setDisableValidators( true );

               rekDisableFileDownloadValidation( curChild );
            }
         }catch(e){}    
      }

      disableFileDownloadValidation( getComponent( 'fileDownload1' ) );
   }]]>
</xp:this.beforeRenderResponse>

P.S. Keep in mind that this article has been posted in the “Quick-n-Dirty” category.

Veröffentlicht unter Java Script, ServerSide JavaScript, XPages, XSnippet | Verschlagwortet mit , , , , , | Ein Kommentar

Quick-n-Dirty: Non Closable Dialog for ExtLib

I have created a dirty hack for dialogs which prevents users to close the dialog by pressing the escape key or the close button. In my first approach  (StackOverflow.com)the hack has overwritten all dialogs for the whole XPage. This version allows to enable / disable it per dialog.

To use this hack you have to add the CSJS library to your XPage and call the dialog with an additional parameter ‚nonclosable‚:

XSP.openDialog("#{id:dialogNonClosable}", {'nonClosable': true } );

The hack was tested with IE 9 and FF 16, ExtLib 8.5.3.20120605-0921. Here is the CSJS library you have to attach to your XPage:

/**
 * ExtLib Dialog Hack to enable NonClosable Dialogs
 * 
 * This function overrides the exsiting openDialog method of the XSP object. It
 * adds a new option named 'nonclosable'
 * 
 * To open a non closable dialog, you have to use it like this
 * 
 * XSP.dialog('#{id:dialog}', {'nonclosable': true } );
 * 
 * @category CSJS
 * @category ExtLib
 * @author Sven Hasselbach
 */

function injectExtLibHack() {
    XSP.openDialog = function xe_od(dialogId, options, params) {
        dojo.addOnLoad( function() {
            var created = false;
            var dlg = dijit.byId(dialogId);
            if (!dlg) {
                var type = XSP._dialog_type;
                try {
                    if (options['nonClosable'])
                        type = XSP._dialog_type_nonClosable;
                } catch (e) {
                }

                options = dojo.mixin( {
                    dojoType : type || "extlib.dijit.Dialog"
                }, options);

                dojo.parser.instantiate( [ dojo.byId(dialogId) ], options);
                dlg = dijit.byId(dialogId);
                created = true;
            } else {
                if (dlg.keepComponents) {
                    dlg.show();
                    return;
                }
            }

            var onComplete = function() {
                dlg.show();
            };
            var axOptions = {
                params : dojo.mixin( {
                    $$showdialog : true,
                    $$created : created
                }, params),
                onComplete : onComplete,
                formId : dialogId
            };
            dlg.attr("content", "<div id='" + dialogId + ":_content'></div>");
            XSP.partialRefreshGet(dialogId + ":_content", axOptions);
            if (dojo.isIE < 8) {
                dojo.query(".lotusDialogBorder").style("width", "500px");
            }
        });
    }

    dojo.provide("extlib.dijit.OneUIDialogNonCloseableDialog");
    dojo.require("extlib.dijit.Dialog");
    dojo.declare(
       "extlib.dijit.OneUIDialogNonCloseableDialog",
       extlib.dijit.Dialog,
       {
          baseClass: "",
          templateString: dojo.cache("extlib.dijit", "templates/OneUIDialog.html"),
          disableCloseButton: true,
          _onKey: function(evt){
          if(this.disableCloseButton &&
             evt.charOrCode == dojo.keys.ESCAPE) return;
             this.inherited(arguments);
          },
          _updateCloseButtonState: function(){
             dojo.style(this.closeButtonNode,
             "display",this.disableCloseButton ? "none" : "block");
          },
          postCreate: function(){
             this.inherited(arguments);
             this._updateCloseButtonState();
             dojo.query('form', dojo.body())[0].appendChild(this.domNode);
          },
          _setup: function() {
             this.inherited(arguments);
             if (this.domNode.parentNode.nodeName.toLowerCase() == 'body')
                dojo.query('form', dojo.body())[0].appendChild(this.domNode);               
          }        
       }
    );

    // This is used by the picker dialog to grab the correct UI
    XSP._dialog_type_nonClosable="extlib.dijit.OneUIDialogNonCloseableDialog";

}

XSP.addOnLoad( injectExtLibHack );

And here is an example XPage which demonstrates how to use the hack:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
    xmlns:xe="http://www.ibm.com/xsp/coreex">
    <xp:this.resources>
        <xp:script src="/dialogHack.js" clientSide="true"></xp:script>
    </xp:this.resources>

    <xp:button value="Show Dialog Closable" id="button1">
        <xp:eventHandler event="onclick" submit="false">
            <xp:this.script><![CDATA[
            XSP.openDialog("#{id:dialogClosable}");]]></xp:this.script>
        </xp:eventHandler>
    </xp:button>
    <xp:button value="Show Dialog Non Closable" id="button2">
        <xp:eventHandler event="onclick" submit="false">
            <xp:this.script>
                <![CDATA[
                    XSP.openDialog("#{id:dialogNonClosable}", {'nonClosable': true } );
                ]]>
            </xp:this.script>
        </xp:eventHandler>
    </xp:button>

    <xe:dialog id="dialogClosable" title="This is a closable dialog">
        <xe:dialogContent>
            You can close me via Escape or the close button.
        </xe:dialogContent>
        <xe:dialogButtonBar>
            <xp:button value="Ok" id="button3"
                styleClass="lotusFormButton">
                <xp:eventHandler event="onclick" submit="false">
                    <xp:this.script><![CDATA[XSP.closeDialog('#{id:dialogClosable}')]]></xp:this.script>
                </xp:eventHandler>
            </xp:button>
        </xe:dialogButtonBar>
    </xe:dialog>

    <xe:dialog id="dialogNonClosable" title="This is a non-closable dialog">
        <xe:dialogContent>
            Try to close me via Escape or the close button ;-)
        </xe:dialogContent>
        <xe:dialogButtonBar>
            <xp:button value="Ok" id="button4"
                styleClass="lotusFormButton">
                <xp:eventHandler event="onclick" submit="false">
                    <xp:this.script><![CDATA[XSP.closeDialog('#{id:dialogNonClosable}')]]></xp:this.script>
                </xp:eventHandler>
            </xp:button>
        </xe:dialogButtonBar>
    </xe:dialog>

</xp:view>

This is the „standard“ ExtLib dialog:

And this is the „non-closable“ version:

P.S. Keep in mind that this article has been posted in the “Quick-n-Dirty” category.

Veröffentlicht unter Dojo Toolkit, ExtLib, Java Script, XSP | Verschlagwortet mit , , , , , , , , | Ein Kommentar

Quick-n-Dirty: Use your own Factory classes in XPages

Here is a easy way to use your own factory classes in XPages:

1. Create a file named „com.ibm.xsp.factories.properties“ in the WEB-INF-Folder of your NSF

 

 

2. In this file, define the factory classes you want to use in the format<NAME>=<Java Class>

HelloWorldFactory=ch.hasselba.factory.HelloWorld

3. Create a java class, in this example ch.hasselba.factory.HelloWorld

package ch.hasselba.factory;

public class HelloWorld 
{
   public HelloWorld(){
      System.out.println("Hello World Factory alive!");
   }

   public String getMessage(){
      return "Hello World!";
   }

}

The factory classes you are adding require a constructor. They will be instantiated during runtime.

4. After saving your java class and opening a XPage you should see the message on the server console (perhaps you have to do a clean first).

This means that the Factory is now accessible and is ready to use. The class will only be instantiated once during runtime.

5. To use your Factory, you have to do a lookup first, before you can use it. This can be done using the com.ibm.xsp.factory.FactoryLookup class which can be accessed via the current application (Please read the notice in the ExtAPI documentation!).

Here is a SSJS code snippet:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
   <xp:label id="labelHelloWorld">
      <xp:this.value>
         <![CDATA[#{javascript:
            var app = facesContext.getApplication();
            var facLookUp = app.getFactoryLookup();
            var hwFac = facLookUp.getFactory("HelloWorldFactory");
            hwFac.getMessage();
         }]]>
      </xp:this.value>
   </xp:label>
</xp:view>

The main difference between a managed bean and this approach is, that the class is instantiated as soon the JSF application starts running, not if you access the bean in your code for the first time. The factory class can do what you want, f.e. run a thread or whatever. The result of the operations can be accessed via the  factory lookup.

P.S. Keep in mind that this article has been posted in the „Quick-n-Dirty“ category.

Veröffentlicht unter Extensibility API, Java, JSF, Server, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , , , | 4 Kommentare

Quick-n-Dirty: Control Dojo generation for individual XPages

Another interesting question has been asked on stackoverflow.com: How to enable or disable the Dojo libraries for individual XPages, not the whole application?

Just adding the parameter as shown below to the XPage won’t work:

<xp:this.properties> 
 <xp:parameter name="xsp.client.script.libraries" value="none" />
</xp:this.properties>

The workaround is to add a single line to beforePageLoad or beforeRenderResponse event:

facesContext.getRequestParameters().setJsLibrary(0);

The parameter can be set as the following

  • „0“ disables Dojo Framework
  • „1“ enables Dojo Framwork
  • „2“ enables Dojo Lite Framework

If you are using the ExtLib, there is still a problem with the automatically added CSJS libraries. These headers will be added automatically (it is the same result if you are disabling dojo application wide):

<script type="text/javascript">dojo.registerModulePath('extlib', '/xsp/.ibmxspres/.extlib');</script>
<script type="text/javascript" src="/xsp/.ibmxspres/.mini/dojo/@Eya.js"></script>

Edit:

This can only be removed by disabling the ressource aggregation or disabling the Extension Library for the whole application.

You can disable it f.e. by adding this to your XPage beforeRenderResponse event:

facesContext.getRequestParameters()
   .setProperty("xsp.resources.aggregate", "false")
Veröffentlicht unter ExtLib, Server, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , , , | Ein Kommentar

Quick-n-Dirty: A simple isRecycled() method (2)

Tommy Valand improved the idea: In his solution the isDead method of the NotesBase class is accessed, and this works better, because the method addionally checks for the C object handle.

Here you can find the method: http://stackoverflow.com/questions/12740889/what-is-the-least-expensive-way-to-test-if-a-view-has-been-recycled

Update:

If you have problems with the java security manager, please add the following to the the java.policy file:

grant {
    permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};
Veröffentlicht unter Java, ServerSide JavaScript | Verschlagwortet mit , , , | Ein Kommentar