Why do we create conflicts?

Personally I feel that I’m involved in less micro conflicts than many of the people around me.
I think I have an answer to that.
My focus is on what people are saying and not how!
Personally I believe that if we all could stop focusing on how and try to understand what people are saying, we would have less conflicts.
To reduce the personal impact on how things are told I believe it’s necessary to understand how to communicate with less violence and above all become more prestigeless.

DLNA videos on ZTE MF93D

Bought a unlocked ZTE MF93D to take with me on our vacation. Selected this over the Huawei E5776 since it was available at a local store to very good price.

Have been reeding a lot compering the two and in the end my conclusion was that there are no big difference between them. ZTE you can use MiMo (dual external antenna) in low signal condition but the HUWEI has a bit better internal antenna and max throughput on 4G. But what difference does 150 Mbps for Huawei and 100Mbps for the ZTE really do when you do not intend to stream multiple movies over 4G.

Have been trying to get the dlna to work, but to get the movies to show up in the Video folder has not been easy. Google didn’t have the answer.

No manual saying what limitation there are and support could not help directly, they needed to escalate it.

So after some testing I found that the following works:

  • .mp4
  • .wmv

Did not work:

  • .m4v
  • .avi
  • .mkv
  • .mov
  • .divx
  • .xvid

So now I have to turn on the converter at home, so that my kids can stream movies on our iPads when we drive the 2000km down to Munich.

In Germany I buy prepaid surf card for the ZTE router and we can all surf without using expensive roaming. With some extra battery pack we can be on the move the whole day.

Vacation here we come!

Finding your XPage generated elements byId client side

Ok so when you create your fields or divs in XPage and you give them a sane Id, when XPage then renders them you get ids like:

id=”view:_id1:_id2:ContentControl:CompanyName”

I do see why it is necessary, since you can have multiple forms in one XPage and it would be impossible to keep them apart if fields did not have unique names.

But what if you have your JavaScript libraries which uses functions like getElementById or if you are smarter than that and uses the dojo.byId which is browser independent.

There is no way using the client side syntax (#{id:comboBox}) you can use in the XPage and custom controls.

At the moment I see two approaches, the first is to create a global variable which is then used in the JavaScript library. This method is described here!

http://www-10.lotus.com/ldd/ddwiki.nsf/dx/element_ids_in_client_side_javascript_on_xpages.htm

The second method is to use dojo.query().

dojo.query can be used to “query” the DOM tree, a really powerful function which is well worth digging into.

But in this case we will use it to find our html element by querying for the elements where id ends with the id we are looking for.

The syntax would be dojo.query(“[id$=’:CompanyName’]”).

Let’s take it apart. The brackets means we are searching for a tag with the name “id” ([id$=’…), which value ends with our search string, ‘$=’ stands for ends with.

The search string begins with a colon since we want to be sure we do not get fields with ids like ‘FirstCompanyName’, since they also ends with ‘CompanyName’.

The result will be an array of html elements, with multiple entries if more than one element id ends with ‘:CompanyName’, which very well may be the case, see when to use section below.

So when do we use which one:

The first method is preferred if you have multiple forms as data sources for your fields or when you cannot be absolutely sure that the ids are unique over all elements you need to use in your JavaScript library!

In all other cases the second method should work fine.

So now you have to rewrite all your JavaScript libraries! Yes you do and when you do that try to skip everything which takes an id string as a parameter, rewrite it to use elements or better to take both types as an argument.

Function doSomthingWithElement(elmnt){
 if(typeof(elmnt) === ‘string’){
   elmnt = dojo.byId(elmnt);
 }
 // use the element
 alert(elmnt.innerHTML);
}

What you also can do is to replace the XSP objects method getElementById with your own implantation of it using the dojo.query().

Either put the code inside addOnLoad in the client side XPage event onClientLoad or better put the code below in to a JavaScript library which is added as a resource into the XPage. Be sure to put in the JavaScript library early in the code so that it is loaded before used.

The XSP object is created by Domino server and contains different convenient functions like a getElementsById which actually only is a wrapper around dojo.byId()!

dojo.addOnLoad(function(){
 dojo.mixin(XSP, {
   getElementById: function(idName){
     var result = dojo.byId(idName);
     if (!result){
       result = dojo.query("[id$=':" + idName + "']");
       if (result){
         result = result[0];
       }
     }
     return result;
   }
 })
});

I’ll try to explain what happens: First the dojo.addOnLoad() will run the function given as a parameter when the page has loaded in the client. Then by using dojos mixin the script replaces the object literal entry for “getElementById” with the code given as a second parameter. The code itself doing the magic first tries to find the element the classic way by using dojo.byId(). In this way we do not break the previous functionality. If nothing is found it tries to find the element by using the dojo.query() method described above.

So now if you want to find an element where the id has been replaced by the Domino XPage rendering engine you can use XSP.getElementById(‘CompanyName’) as you hopefully are used to.

An alternativ can be to mixin xspById() into dojo.

If someone knows of a function which can be used instead in custom JavaScript libraries please tell me since I’ve been searching a lot for a solution to this problem.

The XPage vs. JavaScript library and getElementById issues

Ok so when you create your fields or divs in XPage and you give them a sane Id, when XPage then renders them you get ids like:

id=”view:_id1:_id2:ContentControl:CompanyName”

Please do notice that this applies only to script code which is loaded into the XPage. I’m well aware of that JavaScript entered directly into the Xpage or custom control will not have the problem, since there are many diffrent way to reference the rebuilt XPage-id.

I do see why it is necessary, since you may have multiple forms in one XPage and it would be impossible to keep them apart if fields did not have unique names.

But what if you have your old JavaScript libraries which uses functions like getElementById or if you are smarter than that and uses the dojo.byId() which is browser independent.

There is no way using the client side syntax like #{id:comboBox} in JavaScript libraries. Works fine in XPages and custom controls though.

At the moment I see two approaches, the first is to create a global variable which is then used in the JavaScript library. This method is described here!

The second method is to use dojo.query()!

dojo.query can be used to “query” the DOM tree, a really powerful function which is well worth digging into.

But in this case we will use it to find our html element by querying for the elements where XPage-id ends with the id we are looking for.

So the syntax is dojo.query(“[id$=’:CompanyName’]”).

Let’s take it apart. The brackets means we are searching for a tag with the name “id” ([id$=’…), which value ends with our search string, ‘$=’ stands for ends with.

The search string begins with a colon since we want to be sure we do not get fields with ids like ‘FirstCompanyName’, since they also ends with ‘CompanyName’.

The result will be an array of html elements, with multiple entries, which may be the case!
I’ll try to explain why and when to use which?

The first method is preferred if you have multiple forms as data sources for your fields in the XPage or when you cannot be absolutely sure that the ids are unique over all elements used in your JavaScript library!
In all other cases the second method should work fine!

So now you have to rewrite all your JavaScript libraries! Yes you do! And when you do that try to skip everything which takes an id string as a parameter, rewrite it to use elements or better accepts both types as an argument.

Function doSomthingWithElement(elmnt){
  if(typeof(elmnt) === ‘string’){
    elmnt = dojo.byId(elmnt);
  }
  // use the element
  alert(elmnt.innerHTML);
}

Now with a little twist of customization!

What you also can do is to replace the XSP objects method getElementById with your own implantation of it, using the dojo.query().

Either put the code you find as parameter to dojo.addOnLoad in the client side XPage event onClientLoad or better put the code below in to a JavaScript library where you have your common functions, then add it as a resource into the XPage.

The XSP object is created by Domino server and contains different convenient functions like getElementsById which actually is a wrapper for dojo.byId()!

dojo.addOnLoad(function(){
  dojo.mixin(XSP, {
    getElementById: function(idName){
      var result = dojo.byId(idName);
      if (!result){
        result = dojo.query("[id$=':" + idName + "']");
        if (result){
          result = result[0];
        }
      }
      return result;
    }
  })
});

I’ll try to explain what happens.

First the dojo.addOnLoad() will run the function given as a parameter when the page has loaded in the client. Then by using dojos mixin the script replaces the object literal entry for “getElementById” with the code given as a second parameter. The code itself doing the magic first tries to find the element the classic way by using dojo.byId(). In this way we do not break the functionality. If no elements are found it tries to find the element by using the dojo.query() as described above.

So now if you want to find an element where the id has been replaced by the Domino XPage rendering engine you can use XSP.getElementById(‘CompanyName’).

It’s also possible to add it to the dojo object by altering the code to:

dojo.mixin(dojo, {
    xspById: function(idName){

I’ve did not manage to replace the dojo.byId() since it references it self in a “strange” way which is broken when you use dojo.mixin

If  someone knows of a function which can be used instead in custom JavaScript libraries please comment!

Linking Notes and Domino directories and application

A very powerful feature in linux is linking files and directories. Used in the right way it can save you a lot of space and keep thing often used or changed in one directory structure to be easily find.

All Administrators knows the feature in the Admin client where you can create links to directories and nsf files. For some developers it may be new! For some admins and developers it may also be new that it works really well within the notes client on the local machine.

You can easily create links to directories and nsf files which is then resolved by the notes client.

Personally I try to keep a folder structure on my PC where important stuff is located under one directory.
Doing a back of the machine is then much easier and the number of folders to consider in a backup set is greatly reduced.

So how does it work!

  1. Create a text file in the domino/notes data directory and name it eg.  BackupedData.txt.
  2. Open the text file in your preferred text editor and type in the path to the source directory eg. C:\Documents and Settings\Tomas\My Documents\DominoData.
  3. change the file type from .txt to .dir. (“BackupedData.dir”)

The linked folder will be shown as “BackupedData” when browsing for files in the Notes client.

This can also be done on single NSF files. Instead of naming it .dir change it to .nsf and the content in the file should contain the full path to the target nsf file.

Step by step guide linking nsf file:

  1. Create a text file in the domino/notes data directory and name it eg.  myApplication.txt.
  2. Open the text file in your prefered text editor and type in the path to the application eg. C:\Documents and Settings\Tomas\My Documents\DominoData\myApplication.nsf.
  3. change the file type from .txt to .nsf

The link files can be  put anywhere in the data directory structure!

NotesNames in LotusScript

Just wanted to show how easy creating names, authors, and readers fields can be in LotusScript?

As always there are different ways to accomplish that. Here’s two of them.

1) Use New NotesItem:
Set item = new NotesItem(“SendTo”, sendToArray,NAMES) ‘ For other type use the SpecialType parameter with AUTHORS or READERS instead of NAMES depending on what you want to create.

This is useful if you want to do something more with the field/item.
Eg. Change some properties.

2) Using ReplaceItemValue
set item = doc.replaceItemValue(“SendTo”,sendToArray)

This will only create a text field item. We need to change it to an Names item.
item.isNames = true

This can be shortened since ReplaceItemValue returns a notes item:
doc.replaceItemValue(“SendTo”,sendToArray) .isNames = True

If you want it to be readers field or authors field do this.

doc.replaceItemValue(“SendTo”,sendToArray) .isReaders = True

doc.replaceItemValue(“SendTo”,sendToArray) .isAuthors = True

Personally I tend to use the shortened version since I find it easier to read. That is if I do not need the NotesItem further down in the code.

It saves me a dummy variable which may not be using further down in the code.
I do not like to set variables which are never used.

It’s like in a movie where you get to see a shotgun in the beginning of the film. You then sort of expect it to be used before the end.

NotesAgent.ServerName with consequences

NotesAgent.ServerName may not give you the server you expect!

In the properties for scheduling the agent, you can set on which server the agent shall run.

But from time to time you may want to trigger it manually.

If so, be careful when you use NotesAgent.ServerName, since it will give you the server name set in the agent property described above and not the server on which it was manually triggered.

To get the current server be sure to use NotesSession.CurrentDatabase.Server instead.

JavaScript in Domino – Unobtrusive JavaScript and mandatory fields

Finally I found a name for it. Chris Blatnick named it on his blog Interface Matters.

The technique was presented to me by Patrick Kwinten a while back. He as also blogged about it here.

Since then I find the technique really appealing since it in my opinion makes the coding in domino a lot cleaner. You can create a javascript library and put all the event management in there instead of having it in forms and subforms. Personally it goes along very well with my belief in separating style and content.

Here’s an example how to dynamically change the behavior on mandatory fields. In this case it uses css to show a red border around the field which is mandatory and is missing a value. I have not included the css here. But it is basic knowledge so it should not be hard to create one your self.

This example requires prototype.js

First a function is used to get a Hash of mandatory fields. In this case the mandatory fields are different depending of a previous selection in a field. The selections are “IBAN”, “US-ABA”, “SE-BG”…
There is a function for this so that the source of the information easily can be changed. Later its easy to implement a configuration document and fetch the information via ajax.

function getMandatoryFields(){

var mandatoryFields = {

"":{"mandatory":["Tx_BankName","Tx_BankID","Tx_AccountNumber
"IBAN":{"mandatory":["Tx_BankName","Tx_BankID","Tx_AccountNumber"]},
"US-ABA":{"mandatory":["Tx_BankName",
"Tx_BankID", "Tx_AccountNumber"]},

"SE-BG":{"mandatory":["Tx_AccountNumber"]},
"SE-PG":{"mandatory":["Tx_AccountNumber"]},
"SE-Clearing":{"mandatory":["Tx_BankName",
"Tx_BankID","Tx_AccountNumber"]},

"DE-BLZ":{"mandatory":["Tx_BankName", "Tx_BankID","Tx_AccountNumber"]},
"CANADA":{"mandatory":["Tx_BankName",
"Tx_BankID", "Tx_AccountNumber"]}};

return mandatoryFields;

}

Here’s the code:
Function refreshMandatoryBankInfo(accountType, mandatoryFields){

//Get the list of fields to the specific account type
mandatoryList = mandatoryFields[accountType].mandatory;

//This is not very modular for now.
var allFields = ["Tx_Recipient","Tx_BankName","Tx_BankID","Tx_AccountNumber"];

//Now we reset all fields. Removing onFocus and onBlur events and styles for mandatory fields
for (var i = 0; i < allFields.length; i++){

$(allFields[i]).setAttribute('onFocus','');
$(allFields[i]).setAttribute('onBlur','');
$(allFields[i]).removeClassName('mandatoryField');
$(allFields[i]).removeClassName('required');

};

//Go through all mandatory fields and assign an onFocus and onBlur event.
for (var i = 0; i < mandatoryList.length; i++){

Event.observe($(mandatoryList[i]), 'focus', mandatoryOnFocusEvent);
Event.observe($(mandatoryList[i]), 'blur', mandatoryOnBlurEvent);

//Set initial class for the field.
if (IsEmpty($(mandatoryList[i]))){

$(mandatoryList[i]).addClassName('mandatoryField');
$(mandatoryList[i]).addClassName('required');

}else{

$(mandatoryList[i]).removeClassName('mandatoryField');
$(mandatoryList[i]).removeClassName('required');

}

}

}

//Function to check if field is empty
function IsEmpty(aTextField) {

if ((aTextField.value.length==0) || (aTextField.value==null)){

return true;

}else {

return false;

}

}

//The onFocus function
function mandatoryOnFocusEvent(e){

//Find source element (field which event was triggered) read more about the technique here. The difference is due to different handling in ie and mozilla.
if (typeof e == undefined) {

var e = window.event;

}
var source;
if (typeof e.target != undefined) {

source = e.target;

} else if (typeof e.srcElement != undefined) {

source = e.srcElement;

} else {

//Since it is a target type we do not handle, exit function
return true;

}

//Set class for active field (You can define a special style to show in which field the cursor is placed).
source.addClassName('activeField');

}

function mandatoryOnBlurEvent(e){

if (typeof e == undefined) {

var e = window.event;

}
var source;
if (typeof e.target != undefined) {

source = e.target;

} else if (typeof e.srcElement != undefined) {

source = e.srcElement;

} else {

return true;

}

//Remove class for active field
source.removeClassName('activeField');

//Different handle for text and textarea and a select object
if (source.type =="text" || source.type =="textarea"){

if (IsEmpty(source)){

source.addClassName('mandatoryField')

} else{

source.removeClassName('mandatoryField')

}
source.value = Trim(source.value);

//Check if select statement is empty. Select-one is for on selection if you have a multiple selection object test for select-multiple. The test if it is empty may then have to be rewritten to work.
}else if (source.type =="select-one"){

if(source.options[source.selectedIndex].text == ""){

source.addClassName('mandatoryField')

}else{

source.removeClassName('mandatoryField')

}

}

}

How the function is called:
refreshMandatoryBankInfo($F("Tx_AccountType"), getMandatoryFields());

Easy implementation of multiple configurable global stylesheets

The concept is to have a profile document with a multi value field containing the stylsheet path. First entry has the lowest priority the last the highest priority.

First create a profile document and a multi value field
In my example the profile form is named “(DbSetup)” and the field is named “globalCSS”.

In the Form you add the following code into to the “HTML Head Content” form object. Explanation follows.

startTagCSS := "<link rel=\"stylesheet\" type=\"text/css\" href=\"";
endTagCSS := "\">";
s := @Name([CN]; @ServerName);
@If( s= "" ; varGlobalCSS:="css/global.css";varGlobalCSS:=@GetProfileField("(DbSetup)"; "GlobalCSS") );
@Implode((startTagCSS) + varGlobalCSS + (endTagCSS);@NewLine) + @NewLine

First I define the start and end tags. I then try to find out if it runs local or on a server. If it is local it gets the css path from the local application it self, otherwise it gets it from the profile document. Needed that to be able to debug without network access.

Then, on each entry in the GlobalCSS field, the start and end tag is added in front and back of the path. By using @Implode and @NewLine each entry will be separated with a new line. Last but not least I add an extra new line after. To make it look more readable.

If the field GlobalCSS contains [http://myserver.com/css/global.css”, http://myserver.com/css/second.css, http://myserver.com/css/third.css%5D we would get the following in the “<head>” tag on the web page.

<link rel="stylesheet" type="text/css" href="http://myserver.com/css/global.css">
<link rel="stylesheet" type="text/css" href="http://myserver.com/css/second.css">
<link rel="stylesheet" type="text/css" href="http://myserver.com/css/third.css">

This is only for absolute paths, since the relative path changes depending on if the document is new or already exists.
In this case each entry has to be tested for the existence of http or “//” in the beginning of the path and add “../” in the beginning if it is not a new document. Hint use @IsNewDoc in combination with @Transform.

Logout button

When using session based authentication on the web, it is possible to create a logout button in a simple way.

Just append ?logout to the URL path. By using redirectto it is possible to forward the user to a logout page. Only catch, since you then are logged-out, the page you want to show must be available for the user anonymous.

Just create a button with the the following syntax in the onClick event:
window.open(pathToDb + "?logout&redirectto=//" + logoutURI ,"_self")

“_self” opens the url in the current window.

Pitfalls:
If you want to redirect to the same application (logout and get the login dialog directly) don’t forget to end logoutURI with a slash “/”.

This would logout and direct you to the login page again:
window.open("//ldserver.com/webapps/testdb.nsf?logout
&redirectto=//ldserver.com/webapps/testdb.nsf/","_self")