How to read a remote file and convert it to a Base64 string [JavaScript]

This code is based on the one found on StackOverflow — it’s compatible with IE8+, and all modern browsers:

    // get the remote file binary content
    function getBinary(file, callback) {
      var convertResponseBodyToText = function(e) { return e };
      var xhr = new XMLHttpRequest();  
      xhr.open("GET", file, true);
      if (xhr.overrideMimeType) xhr.overrideMimeType("text/plain; charset=x-user-defined")
      else {
        // for IE8 the binary file are not read correctly, and the only way is to use VBScript
        // see http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-for-binary-data-from-javascript-in-ie
        xhr.setRequestHeader("Accept-Charset", "x-user-defined");
        var VB_Fix_IE = '<script language="VBScript">'+"\r\n"
                      + "Function IEBinaryToArray_ByteStr(Binary)\r\n"
                      + "  IEBinaryToArray_ByteStr = CStr(Binary)\r\n"
                      + "End Function\r\n"
                      + "Function IEBinaryToArray_ByteStr_Last(Binary)\r\n"
                      + "  Dim lastIndex\r\n"
                      + "  lastIndex = LenB(Binary)\r\n"
                      + "  if lastIndex mod 2 Then\r\n"
                      + "    IEBinaryToArray_ByteStr_Last = Chr( AscB( MidB( Binary, lastIndex, 1 ) ) )\r\n"
                      + "  Else\r\n"
                      + "    IEBinaryToArray_ByteStr_Last = "+'""'+"\r\n"
                      + "  End If\r\n"
                      + "End Function\r\n"
                      + "\<\/script>\r\n";
        document.write(VB_Fix_IE);
        
        convertResponseBodyToText = function(binary) {
          var byteMapping = {};
          for ( var i = 0; i < 256; i++ ) {
            for ( var j = 0; j < 256; j++ ) {
              byteMapping[ String.fromCharCode( i + j * 256 ) ] = String.fromCharCode(i) + String.fromCharCode(j);
            }
          }
          var rawBytes = IEBinaryToArray_ByteStr(binary);
          var lastChr = IEBinaryToArray_ByteStr_Last(binary);
          return rawBytes.replace(/[\s\S]/g, function( match ) { return byteMapping[match]; }) + lastChr;
        };

      }
      xhr.onreadystatechange = function (aEvt) {
        if (xhr.readyState == 4) {
          if(xhr.status == 200) {
            var data = (xhr.overrideMimeType ? xhr.responseText : convertResponseBodyToText(xhr.responseBody));
            callback(data);
          } else {
            console.log("Cannot find the remote file")
          }
        }
      };
      xhr.send(null);
    }

    // convert the file content to a Base64 string
    function base64Encode(str) {
      var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      var out = "", i = 0, len = str.length, c1, c2, c3;
      while (i < len) {
        c1 = str.charCodeAt(i++) & 0xff;
        if (i == len) {
          out += CHARS.charAt(c1 >> 2);
          out += CHARS.charAt((c1 & 0x3) << 4);
          out += "==";
          break;
        }
        c2 = str.charCodeAt(i++);
        if (i == len) {
          out += CHARS.charAt(c1 >> 2);
          out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
          out += CHARS.charAt((c2 & 0xF) << 2);
          out += "=";
          break;
        }
        c3 = str.charCodeAt(i++);
        out += CHARS.charAt(c1 >> 2);
        out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
        out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
        out += CHARS.charAt(c3 & 0x3F);
      }
      return out;
    }

    getBinary('http://your.site.com/879258.jpeg', function(binary) {
      console.log(base64Encode(binary))
    })

« En attente d’éléments à copier » et « attente de l’application des modifications » [iOS]

Avec la sortie d’iOS 8 j’ai voulu mettre à jour mon iPad. Malheureusement, au moment de restaurer mes données, iTunes m’indique : « En attente d’éléments à copier » et tourne dans le vide pendant un long moment.

En fait j’ai découvert que cela était dû à des applications qui ne doivent pas être compatibles, ou qui posent problèmes. En l’occurrence celles des chaines françaises D8 et D17. Pour corriger le soucis je suis allé dans l’onglet Apps de mon iPad dans iTunes, puis j’ai supprimé les applications dont je n’avais pas besoin, et celles incriminées. Ensuite il suffit de rappuyer sur le bouton Synchroniser et de vérifier que cette fois ça fonctionne !

Pour tester, vous pouvez retirer toutes les applications, puis voir si ça passe. Si cela ne passe toujours pas, essayez de désactiver les autres synchronisations (comme les chansons, films, images, …) jusqu’à ce que vous trouviez ce qui fait buguer le processus. Si cela vient bien des applications, tentez de les installer une à une…

Problem with the People Picker of Sharepoint

For one of my list I’ve had a very weird issue: one people picker didn’t work properly. If I entered a name and clicked to check it, then it didn’t work. If I saved it and then got back to the form, the value didn’t appear.

This strange behavior is due to a missing SPAN element with _errorLabel as an ID. I have no clue why Sharepoint failed to create this SPAN element… And the error is related to the EntityEditorCallback function that tries to use the SPAN element, but as it doesn’t exist, it returns NULL and fails the function.

To fix it, I’m checking if the SPAN element exists, and if not I create it.
The below code must be called AFTER the line in your document that says :

document.write('<script type="text/javascript" src="/_layouts/entityeditor.js?rev=vXD0hrzDeHYHbfW8aRSMjA%3D%3D"></' + 'script>');

The code to fix this issue is:

// some People Picker are broken for an unknown reason...
// the behavior : we cannot check the name, and the "default" value is not set when editing
// the fix : the span with _errorLabel is not created, so we need to create it
if (typeof EntityEditorCallback === "function") {
  var _EntityEditorCallback = EntityEditorCallback;
  window.EntityEditorCallback = function(result,ctx,preventAutoPostBack) {
    var errorControl=document.getElementById(getSubControlID(ctx, 'errorLabel'));
    // if errorControl doesn't exist then we create it
    if (!errorControl) {
      var e=document.getElementById(ctx);
      var spans=e.getElementsByTagName("td");
      for (var i=0; i<spans.length; i++) {
        if (spans[i].getAttribute("colspan") == 3) {
          spans[i].innerHTML = '';
          break;
        }
      }
    }
    // call the original function
    _EntityEditorCallback(result,ctx,preventAutoPostBack);
  }
}

Communication between iframe and parent window [JavaScript]

In the iframe you’ll use the below code:

window.parent.postMessage("Hello World !", "http://your.site.web");

And in the parent window:

// function that will be called when the message is received
function receiveMessage(event) {
  alert(event.data); // it'll show "Hello World !"
}
// we listen to a message
if (window.addEventListener) {
  window.addEventListener('message', receiveMessage, false);
} else if (window.attachEvent) { // IE8
  window.attachEvent('onmessage', receiveMessage);
}

It’s working from IE8+, and all modern browsers, but make sure you have a HTML5 doctype (<!DOCTYPE html>)

Use hash word in the Sharepoint navigation

Let’s say you have the url http://my.site.com/myfolder/mypage.aspx#hash and you want to use it as a link into your left navigation bar in Sharepoint…. If you try putting that name, Sharepoint will not keep your link.

The trick here is to add a questionmark juste before the #. The URL to use will be http://my.site.com/myfolder/mypage.aspx?#hash and Sharepoint will accept the link 🙂

How to edit the default action for an icon in a Sharepoint Ribbon

I wanted to change the behavior of the « Edit Item » button from the Sharepoint 2010 ribbon of the Display Form (DispForm.aspx). It wasn’t really easy, so I finally found a solution that I’m going to share here.

You’ll need to use JavaScript for that. In the below example, the click on the « Edit Item » will open the EditForm in a new window:

function ribbonIsLoaded() {
  // find the button we want to change
  var a = document.getElementById('Ribbon.ListForm.Display.Manage.EditItem-Large');
  // remove the default action for this button
  Sys.UI.DomEvent.clearHandlers(a)
  // define your own action
  a.setAttribute("onclick","");
  a.setAttribute("target", "_blank");
  a.setAttribute("href", window.location.href.replace(/DispForm.aspx/,"EditForm.aspx").replace(/&IsDlg=1/,""))
}
// Note: 'SOD' is an abbreviation for "Script on Demand"

SP.SOD.executeOrDelayUntilScriptLoaded(function () {
  var pm = SP.Ribbon.PageManager.get_instance();
  pm.add_ribbonInited(function () {
    ribbonIsLoaded();
  });
  var ribbon = null;
  try
  {
    ribbon = pm.get_ribbon();
  } 
  catch (e) {
  }
  if (!ribbon) {
    if (typeof (_ribbonStartInit) == 'function')
    _ribbonStartInit(_ribbon.initialTabId, false, null);
  } 
  else {
    ribbonIsLoaded();
  }
}, 'sp.ribbon.js');

Installer Android KitKat 4.4.4 sur le Sony Xperia Z1 Compact

La dernière version d’Android est disponible pour le Xperia Z1 Compact, et on trouve tous les tutoriaux nécessaires sur http://www.phonandroid.com :

  1. D’abord il faut télécharger la ROM
  2. Ensuite on l’installe
  3. Puis on peut rooter son téléphone
  4. Et enfin on peut installer un recovery

Show/Hide a Sharepoint fields in the NewForm, EditForm and DispForm

With the Sharepoint WebServices it’s possible to hide a field into the NewForm, the Editform and/or the DispForm.
You’ll need to use JavaScript with jQuery and SPServices. It’s the UpdateList service that will do the trick.

Once you have loaded the both librairies you can use the below code:

var fieldsToUpdate = '<Fields>';
fieldsToUpdate += '<Method ID="1"><Field Type="Text" Name="My_x0020_Field" DisplayName="My Field" ShowInDisplayForm="FALSE" ShowInEditForm="FALSE" ShowInNewForm="FALSE"></Field></Method>';
fieldsToUpdate += '</Fields>';

$().SPServices({
  operation: "UpdateList",
  listName: "Name of the list",
  listProperties:"",
  updateFields: fieldsToUpdate,
  newFields: "",
  deleteFields: "",
  listVersion: "",
  completefunc: function (xData, Status){}
});

As explained into this Stackoverflow’s question you must provide a minimum of three mandatory properties, and in this order:

  1. Type
  2. Name
  3. DisplayName

The Type must reflect the type of your field. This information is available into the MSDN documentation. The most common values should be Boolean | Calculated | Choice | Currency | DateTime | Integer | Lookup | LookupMulti | MultiChoice | Number | Text | User | UserMulti.

The Name is the internal name (usually the spaces are replaced with « _x0020_ »).

And for each form (ShowInDisplayForm | ShowInEditForm | ShowInNewForm) you can set them to TRUE or FALSE (it seems to be case sensitive ?!). More properties are available into the MSDN documentation.

Delete a User Custom Action from Sharepoint [JavaScript]

The MSDN documentation is a nightmare…. For example Microsoft provides an example to create a User Custom Action for Sharepoint, however there is no code about how to delete it.

So here is the code to use to delete the usercustomaction just created with the above example:

var siteUrl = '/site collection/site/';

function deleteUserCustomAction() {
    this.clientContext = new SP.ClientContext(siteUrl);
    var oWebsite = clientContext.get_web();
    this.collUserCustomAction = oWebsite.get_userCustomActions();
    clientContext.load(oWebsite,'UserCustomActions','Title');
    clientContext.executeQueryAsync(Function.createDelegate(this, this.deleteCustomAction), Function.createDelegate(this, this.onQueryFailed));
}
function deleteCustomAction() {
    var customActionEnumerator = collUserCustomAction.getEnumerator();
    while (customActionEnumerator.moveNext())  {
      var oUserCustomAction = customActionEnumerator.get_current();
      if (oUserCustomAction.get_title() == 'New Menu Item') {
           oUserCustomAction.deleteObject();        
           clientContext.load(oUserCustomAction);
           clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
        }
    }
}
function onQuerySucceeded() {
    alert('Custom action removed');
}
function onQueryFailed(sender, args) {
    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
deleteUserCustomAction()