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

XSnippets: viewPanelHelper

I have added a new XSnippet, the viewPanelHelper. The code helps to keep the selection of the selected documents in a view panel even if pager is used or categories are expand or collapsed.

It is not required to modify the view: Only the script and the hidden field must be added to the facets section of the panel and the CSJS class „viewPanelHelper“ must be added to the XPage (the class is loaded only once and can be stored in a separate CSJS resource file).

The selected documents are stored in the hidden field „viewPanelSelectedIds“.
Tested in IE 9 and FF 15 – 17.

Veröffentlicht unter Allgemein, Dojo Toolkit, Java Script, ServerSide JavaScript, XPages, XSnippet | Verschlagwortet mit , , , , , , , , | Schreib einen Kommentar

XPiNC and Attachments: A better PDF handling

For one of my customers I have created a small XPiNC application with contains a lot of documents with attached PDFs. One of the key requirements was the posibility to open and read a PDF directly in Notes Client, without storing it on the desktop.

The first thing to know is that XPiNC applications are running in XULRunner, which means that there is a compatibility to Firefox. That’s why you can use FF plugins*, for example the Foxit Reader plugin (npFoxitReaderPlugin.dll) or the original Acrobat Reader plugin.

These plugins have to be copied to the XULRunner folder, f.e.

C:\Lotus\Notes\framework\rcp\eclipse\plugins\com.ibm.rcp.xulrunner.runtime.win32.x86_6.2.3.20110915-1350\xulrunner\plugins

The plugin has to be installed/copied to each Notes client. For my customer this was no problem and could be done manually because only a small group of users will use the application.

In a second step I have created a XPage which creates the output of the attached PDF:

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

   <xp:this.data>
      <xp:dominoDocument var="documentAttachment" action="openDocument" />
   </xp:this.data>

   <xp:this.afterRenderResponse>
      <![CDATA[#{javascript:
         importPackage( ch.hasselba.xpages.xpinc );
         var exCon = facesContext.getExternalContext();
         var response = exCon.getResponse().getDelegate();

         AttachmentHelper.sendFile( response, session,
            documentAttachment.getDocument(), param.get("attachment") );
      }]]>
   </xp:this.afterRenderResponse>
</xp:view>

As you can see the main trick is to use the delegated response instead of the default XPages response object. And here comes the Java backend class, the attachment handler:

package ch.hasselba.xpages.xpinc;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import com.ibm.designer.runtime.util.MIME;
import lotus.domino.Document;
import lotus.domino.MIMEEntity;
import lotus.domino.MIMEHeader;
import lotus.domino.NotesException;
import lotus.domino.Session;
import lotus.domino.Stream;
import java.io.IOException;

public class AttachmentHelper {

   private final static String MIMEHEADER_CONTENTDISPOSITION = "Content-Disposition";
   private final static String MIMEHEADERVAL_ATTACHMENT = "attachment";
   private final static String MIMEHEADERPARAM_FILENAME = "filename";
   private final static String SPLIT_CHAR = "\"";

   public static void sendFile(final HttpServletResponse response,
      Session session, Document doc, final String attachmentName) {

      ServletOutputStream out = null;
      MIMEEntity mime = null;
      MIMEHeader mimeHeader = null;
      boolean flagConvertToMime;
      try {
         // get the output stream
         out = response.getOutputStream();

         // store MIME convert settings
         flagConvertToMime = session.isConvertMime();

         // switch MIME convert mode
         session.setConvertMIME(false);

         // convert Doc to MIME
         doc.convertToMIME();

         // process document as MIME
         mime = doc.getMIMEEntity();

         while (mime != null) {
            mimeHeader = mime.getNthHeader(MIMEHEADER_CONTENTDISPOSITION);
            if (mimeHeader != null) {
               if (MIMEHEADERVAL_ATTACHMENT.equals(mimeHeader
                  .getHeaderVal(true))) {

               // extract filename
               String fileName = mimeHeader.getParamVal(MIMEHEADERPARAM_FILENAME);

               // remove double slashes
               fileName = fileName.split(SPLIT_CHAR)[1];

               // set content type
               response.setContentType(MIME
                  .getMIMETypeFromExtension(MIME
                     .getFileExtension(fileName)));

               // check if this is the requested attachment
               if (attachmentName.equals(fileName)) {

                  // get MIME data as stream and send to output
                  Stream stream = session.createStream();
                  mime.getContentAsBytes(stream);
                  stream.getContents(out);
                  stream.close();
               }
            }
         }

         // next MIME entity
         mime = mime.getNextEntity(MIMEEntity.SEARCH_DEPTH);
      }

      // revert MIME conversion
      session.setConvertMIME(flagConvertToMime);

      } catch (IOException e) {
         e.printStackTrace();
      } catch (NotesException e) {
         e.printStackTrace();
      } finally {
        recycleObject(mimeHeader);
        recycleObject(mime);
      }
   }
   /**
   * 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 document can contain more than one attachment at once. Now you can create links to this XPage in the format

PDF.xsp?documentId=<DOCUNID>&attachment=<NAME_OF_ATTACHMENT>

If you are clicking the link, the PDF will be displayed in the Notes Client:

*: there is no gurantee that other plugins are working correctly!

Veröffentlicht unter Allgemein, Infrastruktur, Java, ServerSide JavaScript, XPages, XPiNC | Verschlagwortet mit , , , , , | Schreib einen 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