Posts Tagged: ‘8.5.3’

XPages: Execute Events with HTTP Get

30. September 2014 Posted by Sven Hasselbach

To execute an event on the server, you normally have to send a POST request, because actions will be executed in the Invoke Application phase of the JSF lifecycle. A GET request will only process the Restore View and the Render Response phase, that why you can not execute an event with a GET request.

But with the help of a PhaseListener, the execution can be done earlier in the Restore View phase:

package ch.hasselba.xpages.util;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import com.ibm.xsp.component.xp.XspEventHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import com.ibm.xsp.util.FacesUtil;

public class ExecuteOnServerPhaseListener implements PhaseListener {

    private static final long serialVersionUID = 1L;

    public void beforePhase(PhaseEvent event) {}

    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    public void afterPhase(PhaseEvent event) {
        
        FacesContextExImpl = FacesContextExImpl.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();
        String url = ec.getRequestPathInfo();
        String[] pathes = url.split( "/" );
        
        try{
            if( pathes.length > 2 ){
                if( "executeOnServer".equals( pathes[pathes.length -2 ] ) ){
                    String[] fullId = pathes[ pathes.length - 1 ].split(":");
                    String actionId = fullId[ fullId.length - 1 ];
                    XspEventHandler eventHandler = (XspEventHandler)
                        FacesUtil.findComponentWithFullId( fc.getViewRoot(), actionId );
                    if( eventHandler != null ){
                        eventHandler.getAction().invoke( fc, null );
                        fc.responseComplete();
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        
    }

}

To activate the PhaseListener, it has to be enabled in the faces-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
   <lifecycle>
      <phase-listener>ch.hasselba.xpages.util.ExecuteOnServerPhaseListener</phase-listener>
   </lifecycle>
</faces-config>

The following Javascript snippet extends the XSP object and adds the new function executeOnServerGet to it. The parameter is the if of the event to invoke.

XSP.executeOnServerGet = function( eventId ){
    var viewId = dojo.query('[name="$$viewid"]')[0].value;
    var url = document.forms[0].action;
    url += "/executeOnServer/" + eventId;
    url += "?$$viewid=" + viewId;
    url += "&$$ajaxid=@none";
    dojo.xhrGet({url: url});
}

When calling the function, it sends a GET request and adds the current view id to the request. With the parameter $$ajaxId set to @none, the XPages Engine is forced to send no HTML code back to the client.

And here is an example XPage:

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

    <xp:eventHandler id="helloworld" event="helloworld" submit="false">
        <xp:this.action>
            <![CDATA[#{javascript:
               print("hello world " + java.lang.System.currentTimeMillis() );
            }]]>
        </xp:this.action>
    </xp:eventHandler>


    <xp:scriptBlock id="scriptBlock1">
        <xp:this.value><![CDATA[
            dojo.addOnLoad( function(){
                XSP.executeOnServerGet = function( eventId ){
                      var viewId = dojo.query('[name="$$viewid"]')[0].value;
                    var url = document.forms[0].action;
                    url += "/executeOnServer/" + eventId;
                    url += "?$$viewid=" + viewId;
                    url += "&$$ajaxid=@none";
                    dojo.xhrGet({url: url});
                  }
            });
        ]]></xp:this.value>
    </xp:scriptBlock>
    
    <xp:button value="Execute" id="button1">
        <xp:eventHandler event="onclick" submit="false">
            <xp:this.script>
                <![CDATA[XSP.executeOnServerGet( "#{id:helloworld}" );]]>
            </xp:this.script>
        </xp:eventHandler>
    </xp:button>
</xp:view>

When clicking the button, the following URL is opened in the background:

http://example.com/db.nsf/EventGet.xsp/executeOnServer/view:_id1:helloworld?$$viewid=!dwjldz64w0!&$$ajaxid=@none

A GET request was sent to the server:If you look on the server console, you will see that the Action was invoked:

XPages: Create a Database without Template

30. Mai 2014 Posted by Sven Hasselbach

On stackoverflow.com, an interessting topic was asked about how to create a notes database programmatically without using a template. The problem is, that it will not contain a Icon document. But in this document are all database properties stored. So the question is: How can you create this document?

Jesse Gallagher came up with the idea to use the DXL import and create the Icon document this way, which works fine. But the next problem is, that there is no ACL note in the database and no default view.

That’s why I modified his idea and created a Java Utility class. This class creates a new database which includes all of the required design elements.

package ch.hasselba.core;

import lotus.domino.ACL;
import lotus.domino.Base;
import lotus.domino.Database;
import lotus.domino.DbDirectory;
import lotus.domino.DxlImporter;
import lotus.domino.Session;

/**
 * DB Utilities
 * 
 * @author Sven Hasselbach
 * @version 1.1
 */
public class DBUtil {

    /**
     * creates a new database 
     * the database is identically to a database created by the -blank- template in designer.
     * The default access is set to Manager
     *  
     * @param session
     *             the session used to create the database
     * @param dbTitle
     *             the database title
     * @param dbPath
     *             the path of the database
     * @param dbServer
     *             the server 
     * 
     * @version 1.1
     */
    public static void createDatabase( Session session,  final String dbTitle, final String dbPath, final String dbServer ) {
        DbDirectory dbDir = null;
        Database db = null;
        DxlImporter importer = null;
        ACL acl = null;

        try{
            // create a new database
            dbDir = session.getDbDirectory( dbServer );
            db = dbDir.createDatabase( dbPath );

            // initialize dxl importer
            importer = session.createDxlImporter();
            importer.setDesignImportOption( DxlImporter.DXLIMPORTOPTION_REPLACE_ELSE_CREATE );

            // generate DXL
            String dxl = generateDXL( dbTitle, dbPath, db.getReplicaID() );

            // import DXL
            importer.importDxl(dxl, db);

            // set ACL: Default to Manager
            acl = db.getACL();
            acl.getFirstEntry().setLevel(ACL.LEVEL_MANAGER);
            acl.save();

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            recycleObj( acl );
            recycleObj( importer );
            recycleObj( db );
            recycleObj( dbDir );
        }

    }

    /**
     * generates the DXL for a blank database
     * 
     * @param dbTitle
     *            the title of the database
     * @param dbPath
     *             the path of the database
     * @param dbReplicaId
     *             the replica of the database
     * @return    String with DXL
     * 
     * @version 1.1
     *          
     */
    private static String generateDXL( final String dbTitle, final String dbPath , final String dbReplicaId ){

        StringBuilder str = new StringBuilder();

        str.append("<?xml version='1.0' encoding='utf-8'?>");
        str.append("<!DOCTYPE database SYSTEM 'xmlschemas/domino_8_5_3.dtd'>");
        str.append("<database xmlns='http://www.lotus.com/dxl' version='8.5' maintenanceversion='3.0' ");
        str.append("replicaid='");
        str.append( dbReplicaId );
        str.append("' path='");
        str.append( dbPath );
        str.append("' title='");
        str.append( dbTitle );
        str.append("' allowstoredforms='false' ");
        str.append("usejavascriptinpages='false' increasemaxfields='true' showinopendialog='false'>");
        str.append("<databaseinfo dbid='");
        str.append( dbReplicaId );
        str.append( "' odsversion='51' ");
        str.append("numberofdocuments='0'></databaseinfo>");
        str.append("<note default='true' class='icon'>");
        str.append("<noteinfo noteid='11e'>");
        str.append("</noteinfo>");
        str.append("<item name='IconBitmap' summary='true'>");
        str.append("<rawitemdata type='6'>");
        str.append("AiAgAQAA///////wD///gAH//gAAf/wAAD/4AAAf8AAAD+AAAAfgAAAHwAAAA8AAAAPAAAADgAAA");
        str.append("AYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAHAAAADwAAAA8AAAAPgAAAH4AAAB/AAAA/4AAAf");
        str.append("/AAAP/4AAH//gAH///AP//////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiAAAAAAAAAAAAAAI");
        str.append("jPZmZm/IgAAAAAAAAAAIjGZmZmZmZsiAAAAAAAAAjGZmZmZmZmZmyAAAAAAACMZmZmZmZmZmZmyA");
        str.append("AAAAAIxmZmZmZmZmZmZmyAAAAAjGZmZmZmZmZmZmZmyAAAAIZmbyL2byL2byL2ZmgAAAjGZmIiJm");
        str.append("IiJmIiJmZsgAAIZmZiIiZiIiZiIiZmZoAADGZmYiImYiImYiImZmbAAI9mZmIiJmIiJmIiJmZm+A");
        str.append("CGZmZiIiZiIiZiIiZmZmgAhmZmYiImYiImYiImZmZoAIZmZmIiJmIiJmIiJmZmaACGZvIiIiZiIi");
        str.append("ZiIiIvZmgAhmYiIiImYiImYiIiImZoAIZm8iIi9m8i9m8iIi9maACPZmZmZmZmZmZmZmZmZvgADG");
        str.append("ZmbyL2byL2byL2ZmbAAAj2ZmIiJmIiJmIiJmZvgAAIxmZiIiZiIiZiIiZmbIAAAI9mbyL2byL2by");
        str.append("L2ZvgAAACMZmZmZmZmZmZmZmbIAAAACMZmZmZmZmZmZmZsgAAAAACMZmZmZmZmZmZmyAAAAAAACM");
        str.append("9mZmZmZmZm/IAAAAAAAACIz2ZmZmZm/IgAAAAAAAAAAIiMZmZmyIgAAAAAAAAAAAAACIiIiIAAAA");
        str.append("AAAAUEECICABAAD/////+A4DgA==");
        str.append("</rawitemdata></item>");
        str.append("<item name='$Daos'><text>0</text></item>");
        str.append("<item name='$TITLE'><text>");
        str.append( dbTitle );
        str.append("</text></item>");
        str.append("<item name='$Flags'><text>7f</text></item>");
        str.append("<item name='$FlagsNoRefresh'><text/></item></note>");
        str.append("<view xmlns='http://www.lotus.com/dxl' version='8.5' maintenanceversion='3.0' ");
        str.append("replicaid='");
        str.append( dbReplicaId );
        str.append("' showinmenu='true' publicaccess='false' default='true' noviewformat='true'>");
        str.append("<noteinfo noteid='11a' sequence='1'></noteinfo>");
        str.append("<code event='selection'><formula>SELECT @All</formula></code>");
        str.append("<item name='$FormulaClass'><text>1</text></item></view>");
        str.append("</database>");

        return str.toString();

    }

    /**
     * recycles notes objects
     * 
     * @param obj
     *             the notes object to recycle
     */
    private static void recycleObj( Base obj ){
        try{
            if( obj != null )
                obj.recycle();
        }catch( Exception e ){}
    }
}

Here is an example XPage how to use the class:

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

    DB Path:<xp:inputText id="inputTextDBPath" value="#{viewScope.dbPath}" /><xp:br />
    DB Title:<xp:inputText id="inputText1" value="#{viewScope.dbTitle}" /><xp:br />

    <xp:button value="Create DB" id="buttonCreateDB">
        <xp:eventHandler event="onclick" submit="true" refreshMode="complete">
            <xp:this.action>
                <![CDATA[#{javascript:
                    importPackage( ch.hasselba.core );
                    var dbUtil = new ch.hasselba.core.DBUtil();
                    dbUtil.createDatabase(session, viewScope.dbTitle, viewScope.dbPath, "");
                }]]>
            </xp:this.action>
        </xp:eventHandler>
    </xp:button>

</xp:view>

You can enter the database Title and the database path. After clicking the button “Create DB”, the database is created on the server (assuming you have the right to do that).

XPages: Bootstrap File Input

26. März 2014 Posted by Sven Hasselbach

When using the default file upload control in a Bootstrap application, the default file upload button does not fit anymore to the design:

To fix this issue, you can use a small jQuery plugin named Twitter Bootstrap File Input. When this plugin is added to your XPage, the button will look like this: