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();
        });
    }
},





Wednesday, March 21, 2018

SharePoint 2013 and TypeScript

The features of any programming language influence on the process of developing, debugging and testing code. Many web developers have faced JavaScript features during developing client code, the most unpleasant of which is the weak dynamic typing. The second feature is that despite the fact that JavaScript is an object-oriented programming language, it uses the OOP model based on prototypes, not classes, as in C #. This greatly complicates the development and reading of code for beginners or developers of server-side code.

Deploying  MS SharePoint 2013  applications (Office 365 applications are a separate topic) is a complex and fairly long process even in the development environment (compiling, assembling a wsp package, deploying  to a SharePoint farm, recycling a pool, re-enabling features). Therefore, any error in the client code not detected at an early stage becomes very expensive for the developer, and the development time can significantly increase. Due to the above features of the JavaScript language, most errors, even typos, cannot be detected at the compilation stage and this grows up to a big problem, and some elementary typos can be detected only at the testing stage.

In this situation, statically typed languages help developers. They have the following advantages:
  • Improving the readability of the code;
  • Promoting early detection of errors;
  • Providing more explicit and reliable refactoring opportunity;
  • Improving language support at the IDE level.
It is noteworthy that JavaScript itself can also be made statically typed, but with the help of auxiliary tools (such as TypeScript or Flow), one of which I will consider in this article. The first one is a programming language, with its own compiler into JavaScript code, the second one is a JavaScript code parser. Due to some advantages of using TypeScript (supported by Visual Studio, supporting classes, etc.), I chose the first one.

The full list of TypeScript features:
  • It is a strongly typed language;
  • Code written in TypeScript is compiled into JavaScript;
  • It has backward compatibility with JavaScript. If you write clean JavaScript code the compiler will spit out your JS and will not throw Exception. You can write mixed code (for example, modules/methods using the TypeScript syntax, and implement methods without typing on pure JS)
  • The language is developed by Microsoft;
  • There is an ability to debug the code in browsers;
  • You can create interfaces, modules, classes;
  • You can inherit interfaces (including multiple inheritance), classes;
  • You can describe your own data types;
  • You can create generic interfaces;
  • You can describe the type of the variable (or properties of the object), or describe witch interface the object must have implement;
  • You can describe the method signature.
To enable the TypeScript in a SharePoint 2013 application project you need to do just a couple of steps:

  1. Download and install the TypeScript compiler for the studio (https://www.microsoft.com/en-us/download/details.aspx?id=48593)
  2. Connect the compiler to the project:
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" />

Then in the project settings there will be accessible an additional tab with the TypeScript compilation settings.

The client code distinction in projects of this type is that the SharePoint platform contains a bunch of its own modules and JavaScript API (JSOM, CSR) and you always have to use one or another of them. Fortunately, most of the kernel files are already described in the TypeScript types that are contained in the file SharePoint.d.ts (https://github.com/gandjustas/sptypescript). You can use the standard SharePoint JavaScript types in your TypeScript code  after including this file into your project.

For most of the common client-side code frameworks (such as JQuery, Angular, etc.) files with TypeScript types are also exist. They can easily be found and included in the project by the NuGet manager.

As I mentioned, common browsers have the ability of debugging the TypeScript code. You must include the .ts files and the .map (source map) files in the package by adding the following code:

<Target Name="AddTsToContent" AfterTargets="CompileTypeScript"Condition="'$(BuildingProject)' != 'false'"<ItemGroup>
    <Content Include="@(TypeScriptCompile)" Condition="'$(TypeScriptSourceMap)' == 'true'" /></ItemGroup>
</Target>

Next, check Generate Source Map checkbox  in the TypeScript tab of the project settings for Debug configuration. The .map and .ts files will be included in the wsp installation package after compilation in Debug mode.

I want to pay special attention to the fact that using of the TypeScript language in SharePoint projects great helped me in the development of client code, even though it took some time to study the fundamental and overcome some difficulties of configuring SharePoint 2013 projects.

Thursday, September 28, 2017

BCS (BDC) metadata caching bug - external list always shows loading

According BCS architecture 
there is a model metadata caching.  Sometimes, after updating model (by Central Administration, or provisioning by farm scope feature there is a bug occurring when external list is being viewed – loading icon always showing.

This happens when the parameters of connection to the database changed in the model because of some problem in the metadata model caching.
To fix this bug - it is necessary with the Sharepoint Designer to change connection settings (for example Authentication Mode), save and then return all back. This manipulation will reset the cache. Don’t forget about one minute:
BDC caches the objects when they first load. A timer running on each server looks for any changes to the metadata objects one time each minute. If it sees a change to a metadata object, it clears the cache and then reloads it. As a result, after you change metadata, you must wait up to a minute for changes to propagate to all the servers in the farm. The changes are immediate on the server where you make the change.

Friday, March 17, 2017

Configuration an environment for apps for SharePoint 2013

There are a lot of articles in internet about it (like this on msdn:  https://technet.microsoft.com/en-us/en-en/library/fp161236.aspx), but in my one I will share my own experience of full cycle which begins with configuration of servers and ends with deploying first app.

1. Configuration of DNS server.

We have two virtual servers in our development environment:  
  •          Domain controller (DC), Domain name - dev.com
  •          SharePoint 2013 server (SP2013)
On DC server in DNS Manager I added

a)       New Host(A or AAAA) on dev.com with the same IP address:


b)       New Alias (CNAME) on dev.com like this:


2. I configured SharePoint 2013 farm by Power Shell script below:

Add-PSSnapin "Microsoft.SharePoint.PowerShell"
$appDomain = "apps.sp2013.dev.com" #your domain
$appPrefix = "app"
$accountName = "dev\spfarm" #login for pools
$accountPassword = "*********" #password

net start spadminv4
net start sptimerv4
Write-Host "Start" -foregroundcolor "green";
$existedAppDomain = Get-SPAppDomain
$existedAppPrefix = Get-SPAppSiteSubscriptionName


if (!$existedAppDomain -or !$existedAppPrefix)
{
        if (!$existedAppDomain)
        {
           Set-SPAppDomain $appDomain -ea:Stop
        }

        Get-SPServiceInstance | where{$_.GetType().Name -eq "AppManagementServiceInstance" -or $_.GetType().Name -eq "SPSubscriptionSettingsServiceInstance"} | Start-SPServiceInstance
        Get-SPServiceInstance | where{$_.GetType().Name -eq "AppManagementServiceInstance" -or $_.GetType().Name -eq "SPSubscriptionSettingsServiceInstance"}
        $User = $accountName
        $account = Get-SPManagedAccount  -Identity $User -ea:Silently
        if(!$account)
        {
           $PWord = ConvertTo-SecureString –String $accountPassword –AsPlainText -Force
           $Credential = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $User, $PWord
           $account = New-SPManagedAccount -Credential $Credential -ea:Stop
        }
       
        $appPoolSubSvc = Get-SPServiceApplicationPool -Identity SettingsServiceAppPool
        if (!$appPoolSubSvc)
        {
           $appPoolSubSvc = New-SPServiceApplicationPool -Name SettingsServiceAppPool -Account $account
        }
        $appPoolAppSvc = Get-SPServiceApplicationPool -Identity AppServiceAppPool
        if (!$appPoolAppSvc)
        {
           $appPoolAppSvc = New-SPServiceApplicationPool -Name AppServiceAppPool -Account $account
        }

        $appSubSvc = New-SPSubscriptionSettingsServiceApplication –ApplicationPool $appPoolSubSvc –Name SettingsServiceApp –DatabaseName SettingsServiceDB
        $proxySubSvc = New-SPSubscriptionSettingsServiceApplicationProxy –ServiceApplication $appSubSvc
        $appAppSvc = New-SPAppManagementServiceApplication -ApplicationPool $appPoolAppSvc -Name AppServiceApp -DatabaseName AppServiceDB
        $proxyAppSvc = New-SPAppManagementServiceApplicationProxy -ServiceApplication $appAppSvc

        Set-SPAppSiteSubscriptionName -Name $appPrefix -Confirm:$false -ea:Stop
             Write-Host "End" -foregroundcolor "green";
}Write-Host "Registration Completed" -foregroundcolor "green";

3. Next, I created a simple SharePoint 2013 add-n app «Hellow word» and deployed it on my dev site collection.
But then I opened my deployed app I was prompted login and password multiple times with a blank page at the end. I modified my registry the way of this article described.   
I tried open my app again, entered login and passport and 'voila' the app was opened! But it was very strange:

4.  To fix it I created an empty root site collection on dev Web Application, that was all! 

5.  I also added the portal to intranet zone in IE: