How to drag and drop a file into a dropzone in HTML5

We can find plenty of demo and tutorials about it on the web, but it took me a while to understand how to setup something easy and quick.

The purpose is to have a zone where we can drop a file from our computer.

You need:

  1. A zone where the file will be dropped (for example a <div></div>)
  2. A minimum of three events:
    • drop
    • dragover
    • dragleave

The three events attached to our dropzone must all call event.preventDefault() otherwise it won’t work as expected.

Then, you can apply a style to the dropzone based on dragover and dragleave, and read the property event.dataTransfer.files from drop to get the file.

See here the super simple example: https://codepen.io/Aymkdn/pen/oovXNY

Overwrite the Created field for a Sharepoint list item

Sharepoint provides automatic fields, like « Created » that contains the creation date of a list item. It’s a readonly field.

You could use a webservice to change the ReadOnly property in order to overwrite its value.

Using SharepointPlus the code is:

// don't change the below information
// the details about this field are found using $SP().list().info
var updateSystemFields = "" +
    '' +
    '' +
    "" +
    "";
// send the request to the server
$SP().webService({
  webURL: "https://website.url.com/",
  service: "Lists",
  operation:"UpdateList",
  properties:{
    listName: "Name of the list",
    listProperties: "",
    updateFields: updateSystemFields,
    newFields: "",
    deleteFields: "",
    listVersion: "",
  }
})

Classic ASP to get a remote page with NTLM authenticate

It’s very frustrating to have to work with ASP again, after so many years…

I wanted to use an ASP page to get a remote page. However I needed to pass some NTLM authenticate to it.

After a few searches I found this solution:

<%
' I want this page to be accessible for Cross Domain, especially from http://my.company.com (using JavaScript)
Response.AddHeader "Access-Control-Allow-Origin", "http://my.company.com"
Response.AddHeader "Access-Control-Allow-Credentials", "true"

' get "url" from the parameters
set REMOTE_FILE_URL=Request.QueryString("url")

If REMOTE_FILE_URL <> "" Then
  ' You could use MSXML2.ServerXMLHTTP but you would have to hardcoded the login/password
  ' Example:
  '    Set http = Server.CreateObject("MSXML2.ServerXMLHTTP.6.0")
  '    http.open "GET", REMOTE_FILE_URL, False, "domain\user_name", "Password"
  ' So here we'll use "WinHttp.WinHttpRequest.5.1" that will pass the IIS' credentials
  set http = CreateObject("WinHttp.WinHttpRequest.5.1")
  http.SetAutoLogonPolicy 0

  ' we can define timeouts (http://msdn.microsoft.com/en-us/library/windows/desktop/aa384061(v=vs.85).aspx)
  ' http.SetTimeouts(resolveTimeout, ConnectTimeout, SendTimeout, ReceiveTimeout)
  ' example: http.SetTimeouts 60000, 60000, 60000, 60000

  ' we can add some headers to the request that will be done by the server
  ' http.SetRequestHeader "Content-type", "application/json"

  ' multiple options can be defined
  ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa384108(v=vs.85).aspx
  ' examples:
  ' to define the user agent: http.Option(0) = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"
  ' to ignore ssl errors: http.Option(4) = 13056

  ' if you use a proxy
  http.SetProxy 2, "proxy:80"

  ' method: open(http method, absolute uri to request, async (true: async, false: sync)
  http.open "GET", REMOTE_FILE_URL, False
  http.send

  'Response.AddHeader http.GetAllResponseHeaders
  Response.write http.responseText
Else
  Response.write "Parameter 'url' not provided."
End If
%>

Here I use the URL parameter url to call that specific page.

Sharepoint WopiFrame allow framing

In Sharepoint you can use <WebPartPages:AllowFraming runat="server" /> to disable the SAMEORIGIN for X-FRAME-OPTION giving you the opportunity to call your page from an iframe (see this other article).

However you cannot use the same trick to preview a file into the browser with WopiFrame.aspx. But there is a way to do it, using the parameters into the URL.

Let’s say the url to preview your document is:
https://my.company.com/sharepoint/_layouts/15/WopiFrame.aspx?sourcedoc=/files/Demo.docx&action=default

You need to replace action=default with action=embedview, AND you need to call WopiFrame2.aspx instead of WopiFrame.aspx (which will lead to a Something Went Wrong - Sorry, you don't have access to this page).

So the URL to use for your iframe will be:
https://my.company.com/sharepoint/_layouts/15/WopiFrame2.aspx?sourcedoc=/files/Demo.docx&action=embedview

Change a « Choice with Fill-In » field into a magic dropdown [Sharepoint]

Sometimes you want to modify the options for a dropdown box by removing some existing options, however you want to be able to keep these old values for reporting or whatever reasons.

Below is a way to do it by using jQuery and SharepointPlus. It will hide the « Specify your own value » stuff, and add the old value (if any) into the dropdown selection.

For example if you have a Choice field with options « A », « B », « C ». Your item ID #1 has value « B ».
After a while you decide to delete « B » and add « D », but you want to be able to find items with the « B » value.
So you choose « Choice with Fill-In » and apply the below code:

// For flexibility reasons we create them as a Choice with Fill-In option,
//    however we hide the free text field and we show only the dropdown
//    in case of the fill-in has a value, then we add it into the list of options
$SP().formfields(["My Fist Dropdown", "Another One"]).each(function() {
  var $e = this.elem();
  // hide all except the dropdown
  //$e.not('select').hide().filter(':radio:last').closest('tr').hide(); // SP2010
  $e.eq(0).closest('table').find('tr:gt(0)').hide(); // SP2013
  // if we have a value into the fill-in box, then:
  //   - add it into the options
  //   - when another value is selected we check the first checkbox
  var fillValue = $e.last().val();
  if (fillValue) {
    $e.filter('select').append('<option data-fillin="true">'+fillValue+'</option>').each(function() {
      var $this=$(this);
      $this.find('option:last').prop("selected", true);
      $this.removeAttr("onclick").on('change', function(event) {
        var $opt = $(this).find(':selected');
        if ($opt.data("fillin")) {
          $e.filter(':radio:last').prop("checked", true);
        } else {
          $e.filter(':radio:first').prop("checked", true);
        }
      });
    });
  }
})

So now, if you create a new item you’ll see a dropdown with « A », « C », and « D » only.
But if you edit your item #1, you’ll have a dropdown with « A », « C », « D » and « B ».

Trigger an event when a file is uploaded on Sharepoint 2013 by drag and drop

If you want to trigger an event after a drag&drop a file for an upload operation on Sharepoint, then you’ll have to add some JavaScript into your page.

// we need to make sure sp.core.js is loaded
SP.SOD.executeOrDelayUntilScriptLoaded(function() {
  window.UploadFinishFunc=function(b, a) {
    typeof g_currentControl.postUploadFunc == "function" && a.status != UploadStatus.CANCELLED && g_currentControl.postUploadFunc(a.files);
    a.status != UploadStatus.CANCELLED && myPostUploadFunc(a);
    g_currentControl.status = ControlStatus.UPLOADED;
    UpdateProgressBar(ProgressMessage.UPLOADED, a);
    RefreshResult(a);
    g_currentControl.status = ControlStatus.IDLE
  }
}, 'sp.core.js');
SP.SOD.executeFunc('sp.core.js')

// below is the function that will be called
function myPostUploadFunc(data) {
  console.log("myPostUploadFunc => ",data)
}

Adding a custom action to a callout in SharePoint 2013

This article has been VERY useful. But I wanted something lightly different: I wanted to add a custom action, but also have the « EDIT » button (but not the « SHARE »), and to use the current item info for my custom action.

Here is the result:

// source: https://www.eliostruyf.com/adding-a-custom-action-to-a-callout-in-sharepoint-2013/
// add a special callout action for our library
SP.SOD.executeFunc("callout.js", "Callout", function () {
  var itemCtx = {};
  itemCtx.Templates = {};
  itemCtx.BaseViewID = 'Callout';
  // Define the list template type
  itemCtx.ListTemplateType = 101;
  itemCtx.Templates.Footer = function (itemCtx) {
    // context, custom action function, show the ECB menu (boolean)
    return CalloutRenderFooterTemplate(itemCtx, AddCustomAction, true);
  };
  SPClientTemplates.TemplateManager.RegisterTemplateOverrides(itemCtx);
});

function AddCustomAction (renderCtx, calloutActionMenu) {
  var itemIndex = renderCtx.CurrentItemIdx
  // Add your custom action
  calloutActionMenu.addAction(new CalloutAction ({
    text: "Custom Action",
    tooltip: 'This is your custom action',
    onClickCallback: function() {
      // all the data related to your item are into `renderCtx.ListData.Row[itemIndex]`
      console.log('Callback from custom action');
    }
  }));
  // re-add EDIT action
  calloutActionMenu.addAction(new CalloutAction ({
    text: "Edit",
    onClickCallback: function(event) {
      // use the default action we have when clicking on the filename into the library
      // or call the EditForm if it's a list item and not a library
      DispEx(this, event,'TRUE','FALSE','FALSE','SharePoint.OpenDocuments.3','1','SharePoint.OpenDocuments','','1https://your.sharepoint.com/_layouts/15/WopiFrame.aspx?sourcedoc='+renderCtx.ListData.Row[itemIndex].FileRef+'&action=default','','1','0','0','0x7fffffffffffffff')
    }
  }));
}

How to hide the left navigation bar in Sharepoint without CSS

We can easily find some workarounds to hide the left navigation bar on Sharepoint using CSS…. But I wanted to remove it on a specific page, without using any CSS.

It’s actually pretty simple. You need to add the below tag into your .aspx page:

<asp:Content ContentPlaceHolderID="PlaceHolderLeftNavBar" runat="Server"></asp:Content>

Tested with Sharepoint 2013 On-Promise.

How to cache the CSS Fonts with Sharepoint

If you use your own CSS file with your own CSS fonts, then we’ll notice that Sharepoint doesn’t send cache headers if you use the below CSS code:

@font-face {
  font-family: 'Roboto';
  src: url('/_catalogs/masterpage/css/fonts/Roboto/Regular/Roboto-Regular.woff2') format('woff2'),
       url('/_catalogs/masterpage/css/fonts/Roboto/Regular/Roboto-Regular.woff') format('woff'),
       url('/_catalogs/masterpage/css/fonts/Roboto/Regular/Roboto-Regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
}

To have this resource sent back from the Sharepoint server with cache headers, you just need to add ?ctag=0 at the end:

@font-face {
  font-family: 'Roboto';
  src: url('/_catalogs/masterpage/css/fonts/Roboto/Regular/Roboto-Regular.woff2?ctag=0') format('woff2'),
       url('/_catalogs/masterpage/css/fonts/Roboto/Regular/Roboto-Regular.woff?ctag=0') format('woff'),
       url('/_catalogs/masterpage/css/fonts/Roboto/Regular/Roboto-Regular.ttf?ctag=0') format('truetype');
  font-weight: 400;
  font-style: normal;
}