Monday, February 11, 2019

CrossListQueryInfo - questions of getting cached objects

According msdn CrossListQueryInfo class  is used to getting the cached results or, if there are no cached results available, to cross-list quering to the database and then caching the results for future use. But in practice, there are some questions of using it. Here they are.

1. Does it work without activating features «SharePoint Server Publishing Infrastructure» and «SharePoint Server Publishing»?
- Yes. You can change «Object cache settings» by the direct Url: <sitecollection> /_layouts/15/objectcachesettings.aspx


2. Is setting the option «Check the server for changes every time a cross list query runs» disable caching?
- No. It just checking of any changes in all cached lists before executing query. If there are any changes in any of lists – ALL data getting again from database at first queries(if you using class CrossListQueryInfo to retrieve data from multiple different lists). By the way, if «Use the cached result of a cross list query for this many seconds» option is set it will get data from database for getting information about actual cache before executing query too. The time of executing that queries to database almost the same in both cases.


3. Why my SharePoint Cross-list Query Not Using Cache?
- There may be some reasons. First is described here. You need to set WebUrl property to ServerRelativeUrl (not absolute url). Second and third are described here. You have to call GetSiteData correctly : GetSiteData(SPSite site) not GetSiteData(SPWeb web) and set UseCache property to true.


4. Why in some cases if I did everything right, and set UseCache to true, do I get an «Object reference not set to an instance of an object.» error?
- Probably, you set Query property of CrossListQueryInfo to null. Try set it to String.Empty.


Tuesday, January 22, 2019

Calendar list view SharePoint 2013 customization

As known SharePoint 2013 has a new mechanism for customizing views - Client Side Rendering (CSR). CSR is used for rendering list views, list forms and search results by javascript, but  it has some exclusions. According msdn article  Customize a list view in SharePoint Add-ins using client-side rendering there is no way to load JavaScript files using JSLink  property for Survey or Events lists. Many developers may argue me that JSLink is just mechanism of loading javascript files in SharePoint 2013 and yes, they are right. But if we add our template js file another way (by content editor web part, for example), it will not work! In browser developer console  we can see, that there is no file clienttemplates.js on calendar view page. So, Calendar List View in SharePoint 2013 not support CSR.

In this article, I will show how to make some customizations in calendar list view, such as filter and color events, pass parameters to the new event form (from calendar links), refresh items after new event form closed, change edit form link in calendar.


First of all we have to subscribe to SP.UI.ApplicationPages.Calendar.js file loaded event:
ExecuteOrDelayUntilScriptLoaded(_customCalendar.waitForCalendarToLoad, 'SP.UI.ApplicationPages.Calendar.js');

In waitForCalendarToLoad function we can do some customization:
waitForCalendarToLoad: function () {
    //Pass parameter to new form
    var _$8f_1 = SP.UI.ApplicationPages.CalendarMouseHandler.prototype.$8f_1;
    SP.UI.ApplicationPages.CalendarMouseHandler.prototype.$8f_1 = function ($p0) {
        var $v_0 = $p0.target;
        if (!SP.UI.ApplicationPages.ElementUtility.$M(this.$8_1, $v_0)) {
            return;
        }
        if ($v_0.parentNode.nodeName === 'A') {
            $v_0 = $v_0.parentNode;
        }
        if ($v_0.nodeName === 'A' || $v_0.className.indexOf('ms-acal-vitem') > 0) {
            if (this.$1E_1 && this.$1E_1.$B_1) {
                if (this.$1E_1.$B_1.$4w_1.indexOf('&RoomId') > 0) {
                      this.$1E_1.$B_1.$4w_1 = this.$1E_1.$B_1.$4w_1.substring(0, this.$1E_1.$B_1.$4w_1.indexOf('&RoomId'));
                }
                this.$1E_1.$B_1.$4w_1 = this.$1E_1.$B_1.$4w_1 + '&RoomId=' + _customCalendar.selectedRoomId;
            }
        }
        _$8f_1.call(this, $p0);
    }

    //Refresh items after newitemcreated
    var _$9P_1 = SP.UI.ApplicationPages.CalendarNewFormDialog.prototype.$9P_1;
    SP.UI.ApplicationPages.CalendarNewFormDialog.prototype.$9P_1 = function ($p0, $p1) {
        if ($p0 === 1) {
            var $v_0 = (this.get_events()).getHandler('newitemcreated');
            if ($v_0) {
                  $v_0(this, new Sys.EventArgs());
                  _customCalendar.refreshData();
            }
        }
    }
    //Change edit form
    var _$8g_1 = SP.UI.ApplicationPages.CalendarMouseHandler.prototype.$8g_1;
    SP.UI.ApplicationPages.CalendarMouseHandler.prototype.$8g_1 = function ($p0) {
        if (SP.UI.ApplicationPages.ElementUtility.$M(this.$8_1, $p0.target)) {
             var $v_0 = this.$4U_1($p0);
             var $v_1 = this.$1Z_1;
             if ($v_0.$1j_0 && ($v_0.$1j_0.innerHTML.indexOf("EditForm.aspx") > 0 || $v_0.$1j_0.nextSibling && $v_0.$1j_0.nextSibling.innerHTML && $v_0.$1j_0.nextSibling.innerHTML.indexOf("EditForm.aspx") > 0)) {
                  var div = $v_0.$1j_0 && $v_0.$1j_0;
                  if (div.innerHTML.indexOf("EditForm.aspx") <= 0) {
                        div = div.nextSibling;
                  }
                  id = _customCalendar.getIdFromElement(div);
                  if (id) {
                         var options = {
                            title: "Custom editing",
                            autoSize: true,
                            url: _spPageContextInfo.siteServerRelativeUrl + "/Lists/calendar/EditForm.aspx?ID=" + id,
                            dialogReturnValueCallback: function (dialogResult, returnValue) {
                                       _customCalendar.refreshData();
                            }
                         };
                         SP.UI.ModalDialog.showModalDialog(options);                                               return;
                   }
               }
               while ($v_1 && $v_0.$2E_0) {
                    if ($v_1.$Ai_0($v_0)) {
                           break;
                    }
                    $v_1 = $v_1.$1I_0;
               }
         }
    }
},

To filter and color items we have to override onItemsSucceed handler in SP.UI.ApplicationPages.CalendarStateHandler.prototype:
//Subscribe to the event of loading calendar items
var _onItemsSucceed = SP.UI.ApplicationPages.CalendarStateHandler.prototype.onItemsSucceed;
SP.UI.ApplicationPages.CalendarStateHandler.prototype.onItemsSucceed = function ($p0, $p1) {
     var rootThis = this;
     //filter returned items by comparing with _customCalendar.roomEvents collection
     if (_customCalendar.rommEvents && _customCalendar.rommEvents.length > 0) {
           var new$p0 = $.grep($p0, function (value) {
              var event = _customCalendar.getEventById(value.$11_0);
              if (event) {
                  return true;
              }
              else {
                 return false;
              }
           });
           $.each(new$p0, function (i, el) {
              if (el.$N_0 && el.$N_0.formUrl) {
                 el.$N_0.formUrl = el.$N_0.formUrl.replaceAll('DispForm', 'EditForm');
              }
           });
           _onItemsSucceed.call(this, new$p0, $p1);
           //Customize event template
           _customCalendar.colourCalendar();
     }
     else {
           _onItemsSucceed.call(rootThis, [], $p1);
     }
};

coloreCalendar and refreshData functions example:
colourCalendar: function () {
    var eventsContainers = $('.ms-acal-item');
    if (eventsContainers.length) {
        $.each(eventsContainers, function (i, el) {
            var box = $(el);
            var link = box.find('a');
            if (link.length) {
                 var event = _customCalendar.getEvent(link[0]);
                 if (event) {
                      var color = _mbCore.calendar.getStatusColor(event.Status);
                      if (color) {
                            box.css('background-color', color);
                            box.find('div').css('color', 'black');
                            box.find('a').attr('style', 'color:black !important;');
                       }
                       if (event.Description) {
                            box.attr('title', event.Description);
                       }
                  }
            }
        });
    }
},
refreshData: function () {
    _customCalendar.selectedRoomId = _customCalendar.$filter.val();
    $('#roomH').val(_customCalendar.selectedRoomId);
    var a = $('#AsynchronousViewDefault_CalendarView').find('a[id*="WPQ"]');
    if (a.length > 0) {
        var id = a[0].id;
        var fs = id.indexOf('_');
        if (fs > 0) {
             id = a[0].id.substring(0, fs);
        }
        _customCalendar.loadRoomEvents().then(function (data) {
             _customCalendar.rommEvents = _mbCore.ConverWCFDates(data);
              SP.UI.ApplicationPages.CalendarInstanceRepository.$w[id].refreshItems();
        });
    }
},