Check permissions in Sharepoint based on a PermMask

In some cases you could get this kind of hexadecimal numbers from Sharepoint for the PermMask: 0xb008431061, or 0x1b03c4313ff, and so on.

To verify the PermMask against the different level of permissions you can proceed with the below method (using functions from core.js):

var permMask = '0x1b03c4313ff';
var permissions = new SP.BasePermissions();
permissions.initPropertiesFromJson({High:GetPermMaskH(permMask), Low:GetPermMaskL(permMask)});
// we can now check permissions using SP.BasePermissions.has()
// and we can compare with SP.PermissionKind — see https://msdn.microsoft.com/en-us/library/office/ee556747(v=office.14).aspx
var canEdit = permissions.has(SP.PermissionKind.editListItems);

On Sharepoint 2010 GetPermMaskH and GetPermMaskL are not defined, so here is their code:

function GetPermMaskH(b) {
    var a = b.length;
    return a <= 10 ? 0 : parseInt(b.substring(2, a - 8), 16)
}

function GetPermMaskL(b) {
    var a = b.length;
    return a <= 10 ? parseInt(b) : parseInt(b.substring(a - 8, a), 16)
}

It took me many hours to find how to proceed, so I hope it will help some others.

Redirect after Deleting [Sharepoint]

When I open a dialog for an EditFrom and use the “Delete Item” button from the ribbon, then the main page is redirected to the related Sharepoint list. This behavior is very bad for the user experience.

To change it I used the below Javascript code:

// replace the Delete Item default action when into a popup
$(document).ready(function() {
  // if it's in a popup
  if (window !== window.top) {
    $('a[id$="_toolBarTbl_RptControls_diidIODeleteItem"]').attr("href","javascript:commonDeleteItem(); return false")
  }
})
// now when clicking on the Delete item it will call my own function
// here I use http://aymkdn.github.io/SharepointPlus/ to show a waiting message and to delete the current item, and then close the modal
function commonDeleteItem() {
  $SP().waitModalDialog("Deleting...");
  $SP().list("{"+WPQ2FormCtx.ListAttributes.Id+"}").remove({ID:GetUrlKeyValue("ID")}, {
    after:function() {
      $SP().closeModalDialog();
      $SP().closeModalDialog(2);
    }
  })
}

How to open an Excel document from SharePoint into Chrome/Firefox in readonly/edit mode

With Sharepoint 2010, when you use Internet Explorer you can open an Office document in readonly or in edit mode. Also, when trying to open an Excel file in Firefox/Chrome it will probably open the ugly xslviewer and won’t let you deal with your file.

After searching a bit about it, I found a clue that helped me to find a good solution: on Firefox/Chrome when you click on an Excel file, you’ll now have a modal asking if you want to open it in edit or readonly mode!

To achieve this result you have to insert the below Javascript code somewhere into your masterpage:

/**
 * fix problem with Excel documents on Firefox/Chrome
 * @param  {HTMLElement} p the <A> element
 * @param  {HTMLEvent} a the click event
 * @param  {Boolean} h TRUE
 * @param  {Boolean} e FALSE
 * @param  {Boolean} g FALSE
 * @param  {Strin} k the ActiveX command (e.g. "SharePoint.OpenDocuments.3")
 * @param  {Number} c 0
 * @param  {String} o the activeX command, here we look at "SharePoint.OpenDocuments"
 * @param  {String} m
 * @param  {String} b the replacement URL to the xslviewer
 */
var bak_DispEx;
var modalOpenDocument; // it will be use with the modal
SP.SOD.executeOrDelayUntilEventNotified(function() {
  bak_DispEx = _DispEx;
  _DispEx=function(p, a, h, e, g, k, c, o, m, b, j, l, i, f, d) {
    // if o==="SharePoint.OpenDocuments" && !IsClientAppInstalled(o)
    // in that case we want to open ask the user if he/she wants to readonly or edit the file
    var fileURL = b.replace(/.*_layouts\/xlviewer\.aspx\?id=(.*)/, "$1");
    if (o === "SharePoint.OpenDocuments" && !IsClientAppInstalled(o) && /\.xlsx?$/.test(fileURL)) {
      // if the URL doesn't start with http
      if (!/^http/.test(fileURL)) {
        fileURL = window.location.protocol + "//" + window.location.host + fileURL;
      }
      var ohtml = document.createElement('div');
      ohtml.style.padding = "10px";
      ohtml.style.display = "inline-block";
      ohtml.style.width = "200px";
      ohtml.style.width = "200px";
      ohtml.innerHTML = '<style>'
                      + '.opendocument_button { background-color:#fdfdfd; border:1px solid #ababab; color:#444; display:inline-block; padding: 7px 10px; }'
                      + '.opendocument_button:hover { box-shadow: none }'
                      + '#opendocument_readonly,#opendocument_edit { float:none; font-size: 100%; line-height: 1.15; margin: 0; overflow: visible; box-sizing: border-box; padding: 0; height:auto }'
                      + '.opendocument_ul { list-style-type:none;margin-top:10px;margin-bottom:10px;padding-top:0;padding-bottom:0 }'
                      + '</style>'
                      + 'You are about to open:'
                      + '<ul class="opendocument_ul">'
                      + '  <li>Name: <b>'+fileURL.split("/").slice(-1)+'</b></li>'
                      + '  <li>From: <b>'+window.location.hostname+'</b></li>'
                      + '</ul>'
                      + 'How would like to open this file?'
                      + '<ul class="opendocument_ul">'
                      + '  <li><label><input type="radio" name="opendocument_choices" id="opendocument_readonly" checked> Read Only</label></li>'
                      + '  <li><label><input type="radio" name="opendocument_choices" id="opendocument_edit"> Edit</label></li>'
                      + '</ul>'
                      + '<div style="text-align: center;margin-top: 20px;"><button type="button" class="opendocument_button" style="background-color: #2d9f2d;color: #fff;" onclick="modalOpenDocument.close(document.getElementById(\'opendocument_edit\').checked)">Open</button> <button type="button" class="opendocument_button" style="margin-left:10px" onclick="modalOpenDocument.close(-1)">Cancel</button></div>';
      // show the modal
      modalOpenDocument=SP.UI.ModalDialog.showModalDialog({
        html:ohtml,
        dialogReturnValueCallback:function(ret) {
          if (ret!==-1) {
            if (ret === true) { // edit
              // reformat the fileURL
              var ext;
              if (/\.xlsx?$/.test(b)) ext = "ms-excel";
              if (/\.docx?$/.test(b)) ext = "ms-word"; // not currently supported
              fileURL = ext + ":ofe|u|" + fileURL;
            }
            window.location.href = fileURL; // open the file
          }
        }
      });
      a.preventDefault();
      a.stopImmediatePropagation()
      a.cancelBubble = true;
      a.returnValue = false;
      return false;
    }
    return bak_DispEx.apply(this, arguments);
  }
}, "sp.scriptloaded-core.js")

[2] Bypass a lookup field not displayed because of threshold on NewForm and EditForm [Sharepoint 2013]

This is an update of this article.

This time the JS Link file’s content is the one below:

// loadExt permits to load JS and CSS files
// https://gist.github.com/Aymkdn/98acfbb46fbe7c1f00cdd3c753520ea8
function loadExt(e,t){var s=this;s.files=e,s.js=[],s.head=document.getElementsByTagName("head")[0],s.after=t||function(){},s.loadStyle=function(e){var t=document.createElement("link");t.rel="stylesheet",t.type="text/css",t.href=e,s.head.appendChild(t)},s.loadScript=function(e){var t=document.createElement("script");t.type="text/javascript",t.src=s.js[e];var a=function(){++e<s.js.length?s.loadScript(e):s.after()};t.onload=function(){a()},s.head.appendChild(t)};for(var a=0;a<s.files.length;a++)/\.js$|\.js\?/.test(s.files[a])&&s.js.push(s.files[a]),/\.css$|\.css\?/.test(s.files[a])&&s.loadStyle(s.files[a]);s.js.length>0?s.loadScript(0):s.after()}

(function() {
  // some "global" variables
  var lookupFieldsToFix = [];
  var storageThrottledLookup = localStorage.getItem("ThrottledLookup");
  if (storageThrottledLookup) storageThrottledLookup=JSON.parse(storageThrottledLookup);

  // onLoad() is called when all the fields in the form have been proceed
  function onLoad(ctx) {
    // we'll need jQuery (or nanoajax... see SharepointPlus doc) and SharepointPlus
    loadExt([
      "/Toolbox/Documents/js/jQuery/jquery-1.12.4.min.js",
      "/Toolbox/Documents/js/SharepointPlus/3.14/sharepointplus-3.14.js"
    ], function() {
      // if some lookup fields need to be fixed
      if (lookupFieldsToFix.length>0) {
        var aDeferred=[];
        for (var i=0; i<lookupFieldsToFix.length; i++) {
          aDeferred.push(fixLookup(lookupFieldsToFix[i]));
        }
        $.when.apply(this, aDeferred).done(function() {
          var save={}, count=0, i;
          for (i=arguments.length; i--;) {
            if (arguments[i]) {
              save[arguments[i].field] = arguments[i].choices; // save it as {fieldName:choices}
              count++;
            }
          }
          if (count > 0) {
            // we use localStorage to store the data retrieve from the Lists
            localStorage.setItem('ThrottledLookup', JSON.stringify(save));
            // now reload the page
            $SP().closeModalDialog();
            $('#aspnetForm').hide().after('<h1 style="margin-top:50vh;transform:translateY(-50%);text-align:center">Reloading...</h1>');
            window.location.reload();
          }
        })
      } else {
        // delete all existing localStorage
        localStorage.removeItem('ThrottledLookup');

        // here you can start doing anything you want on this form
      }
    });    
  }
  
  /**
   * Fix the broken lookup fields
   * @param  {String} field Name of the field to fix
   * @return {Deferred}
   */ 
  function fixLookup(field) {
    var deferred = jQuery.Deferred();
    var choices=[], modal;

    WPQ2FormCtx.ListSchema[field].ChoiceCount=0;
    WPQ2FormCtx.ListSchema[field].Choices=[];
    // show a Waiting message
    modal = $SP().getModalDialog('loading-throttled');
    if (!modal) {
      $SP().showModalDialog({
        wait:true,
        title:"Loading some data...",
        id:"loading-throttled"
      });
    }
    // and we get data from the list
    // here we'll use "ID" as the data returned by our field in the form
    var fieldID="ID";
    $SP().list('{'+WPQ2FormCtx.ListSchema[field].LookupListId+'}').get({fields:fieldID, paging:true}, function(data) {
      var res=[];
      for (var i=0, len=data.length; i<len; i++) {
        res.push({LookupId:data[i].getAttribute(fieldID), LookupValue:data[i].getAttribute(fieldID)});
      }
      deferred.resolve({field:field, choices:res});
    });

    return deferred;
  }

  // do some actions as soon as the fields are shown
  var loadAfterForm = {
    Templates: {
      OnPreRender:function(ctx) {
        // we want to show Lookup fields even if there are more than 5000 items in those lists
        if (ctx.ListSchema.Field[0].Throttled === true) {
          if (storageThrottledLookup) {
            // we use the stored data to create our dropdown
            ctx.ListSchema.Field[0].Throttled=false;
            ctx.ListSchema.Field[0].ChoiceCount=storageThrottledLookup[ctx.ListSchema.Field[0].Name].length;
            ctx.ListSchema.Field[0].Choices=storageThrottledLookup[ctx.ListSchema.Field[0].Name];
          }
          else {
            lookupFieldsToFix.push(ctx.ListSchema.Field[0].Name);
          }
        }
      },
      OnPostRender:function(ctx) {
        // only trigger when everything is loaded
        // --> ctx.ListData.Items[0] all the fields
        if (ctx.ListSchema.Field[0].Name === "Attachments") {
          onLoad(ctx)
        }
      }
    }
  }

  // don't do it when editing the page
  if (GetUrlKeyValue("PageView") !== "Shared" && GetUrlKeyValue("DisplayMode") !== "Design") SPClientTemplates.TemplateManager.RegisterTemplateOverrides(loadAfterForm);
})();

Yes, you can be lazy and get more than 5000 SharePoint Items

To reply to this blog post I wanted to share the same thing but with SharepointPlus.

To use the paging option and get all the content from a large Sharepoint list you can simply do the below code:

$SP().list("My List").get({
  fields:"ID,Title",
  rowlimit:5000,
  paging:true,
  progress:function progress(nbItemsLoaded) {
    // for each new page this function will be called
    console.log("It's still loading... already "+nbItemsLoaded+" items have been loaded!");
  }
}, function(data) {
  console.log(data.length); // -> 23587
  for (var i=0, len=data.length; i<len; i++) {
    console.log(data[i].getAttribute("ID"))
  }
})

If you only want to get the first 15,000 items, you can use the option page:3. See the documentation to know more.

Détecter si le Freebox Player est allumé ou éteint (en veille) via les API de Free [Programmation]

Il n’existe pas de commande directe qui permette de savoir si le Player est éteint (en veille) ou allumé… Cependant il existe une astuce qui consiste à faire une requête en utilisant les paramètres suivants :

{
  url:"http://mafreebox.freebox.fr/api/v3/airmedia/receivers/Freebox%20Player/",
  headers:{
    "X-Fbx-App-Auth": client.app.session_token
  }, 
  method:"POST",
  json: {
    "action":"stop",
    "media_type":"video"
  },
  encode:"utf-8"
}

La Freebox va alors retourner { success:true } si elle est allumée, ou { success:false } si elle est éteinte/en veille.

Execute an action before saving a form but after the form validation [Sharepoint]

Let’s say you want to do an asynchronous request just before saving a Sharepoint form, but after Sharepoint verified all the fields in your form (for mandatory ones, or for format, …). Here is my solution to do it (using jQuery.Deferred):

// this function will do our asynchronous check
function beforeSaving() {
  var deferred=jQuery.Deferred();
  setTimeout(function() {
    alert("Test Complete!")
    deferred.reject(); // if the test failed
    // or deferred.resolve() to valid the test
  }, 2000)
  return deferred;
}
// force PostBackRequired to true in the context, otherwise it won't work if you open it into a modal
WPQ2FormCtx.PostBackRequired=true;
// we override SPClientForms.ClientFormManager.SubmitClientForm
SPClientForms.ClientFormManager.SubmitClientForm=function(b){
  var a=SPClientForms.ClientFormManager.GetClientForm(b);
  var res = (a!=null&&a.SubmitClientForm());
  // if the form is not valid, then res===true
  if (res === true) return true;
  else {
    // at this stage, all fields' value have been saved into a hidden input
    // e.g. document.querySelector('input[type="hidden"][id$="Field_x0020_Name"]')
    // all these saved values will be sent back to the server when "WebForm_DoPostBackWithOptions" is executed

    // if the form is valid we now want to do our asynchronous check
    beforeSaving().done(function() {
      // our test is valid too so we can send the form to the server
      WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions($get(WPQ2FormCtx.SubmitButtonID).name, "", true, "", "", false, true))
    }).fail(function() {
      // if it fails we just unblock the save button
      $get(WPQ2FormCtx.SubmitButtonID).disabled=false;
    })
  }
  // to make sure the form won't be submitted yet
  return true;
};

Disable X-Frame-Options on Sharepoint

I needed to communicate with an iframe on Sharepoint, however Sharepoint returned Load denied by X-Frame-Options … To override this limitation you have to create a Webpart page where you’ll add the code <WebPartPages:AllowFraming runat="server" /> into the <asp:Content> section (that might work somewhere else, but I did this way).

You can also add it into your masterpage to allow it everywhere on your website. Add it into your <head> block:

Now you can use postMessage() to communicate with your iframe.

Capture HTTP(S) traffic from Android using a sniffer

I wanted to debug an Android app that uses HTTPS requests with a JSON API. Thanks to this article I’ve been able to use my Windows 10 computer to get all the network from my Android phone thru my local network and decode the HTTPS requests !

I’m going to summarize the steps from the above article:

  1. Download and Install Fiddler on your computer
  2. Once Fiddler is installed, launch it and:
    • Click menu Tools > Options, then select the Connections tab
    • Make note of the “Fiddler listens on” port (normally it’s 8888)
    • Make sure the check box for “Allow remote computer to connect” is checked
    • Switch to the HTTPS tab
    • Make sure the check boxes for “Capture HTTPS Connects” and “Decrypt HTTPS traffic” are both checked
    • Restart Fiddler
  3. Go to your Android phone then:
    • Tap on Settings, then Wi-Fi
    • Find the network on which you’re connected (normally the first one listed), then tap and hold
    • Choose Modify network from the pop-up
    • Scroll down and enable “Show advanced options”
    • Change “Proxy settings” to Manual
    • Under “Proxy host name” enter the Windows PC IP address from above
    • Under “Proxy port” enter the Fiddler port from above (usually 8888)
    • Tap Save and wait a moment for the network to reconnect
  4. Now we need to add the certificate in Android to have the HTTPS working:
    • On Android start the Chrome browser
    • Navigate to http://IP_ADDRESS_WHERE_FIDDLER_IS:8888/ or http://ipv4.fiddler:8888
    • Tap on the link for the “Fiddler Root Certificate”
    • Name the certificate “Fiddler” and install it (entering your PIN or password if prompted)

You’re now ready to capture the traffic on Fiddler!

Once you’re done you can switch back to normal by following the below steps:

  1. Tap on Settings, then Wi-Fi
  2. Find the network on which you’re connected (should be the first one listed), then tap and hold
  3. Choose Modify network from the pop-up
  4. Scroll down and select (enable) “Show advanced options”
  5. Change “Proxy settings” to None
  6. Tap Save and wait a moment for the network to reconnect
  7. Go up a level in settings to Security
  8. Tap Trusted credentials, then select the User tab
  9. Tap on the Fiddler “Do not trust” certificate, then scroll down to remove it
  10. You may need to power cycle your device to get all apps to forget about the Fiddler certificate (e.g., the Chrome browser will continue to try to use it for a while)