Showing posts with label SharePoint 2019. Show all posts
Showing posts with label SharePoint 2019. Show all posts

May 27, 2021

SharePoint 2019 FBA Setup - "fullName" Error

Introduction

Recently we have set up Form based authentication (FBA) in SharePoint 2019 environment for a washing company based at Milan, Italy. We have configured the FBA with best practice but when we try to login using windows user or FBA user,  we were getting error – “Fullname”.

Issue

We faced below error when we try to login using windows user or FBA user:


Solution:

We have checked multiple blogs in google and verified that the set up was done using best practice. So, our doubt goes to configuration in web application, and we found out that “Client Integration” settings is responsible for this.

To disable the “Client Integration” settings follow below steps:

  • Go to Central Admin.
  • Click “Manage web application” under “Application Management”.



  • Select your web application.


  • Click on “Authentication provider” from Ribbon.


  • It will show default Zone. Click on that.


  • Scroll bottom of the authentication provider settings dialog.
  • Click “No” for “Client Integration” Settings.


  • Click Save.
  • It will take some time and provide below dialog. Click close.


  • Now try to login in web application. You will see the user is able to login successfully.

If you have any questions you can reach out our SharePoint Consulting team here.

April 1, 2021

Explained: Behavior of Date and Time column value in SharePoint List/Library

Introduction:

We implemented Intranet Portal on SharePoint Online for a Real Estate Agency based out of Washington, United States. We had to tackle several questions regarding Date and Time in SharePoint from different business users after go-live. We conducted training sessions with the business users and explained the behavior of Date and Time in SharePoint. In this article, we will see how SharePoint shows the value of the Date and Time column in lists/libraries.

Many times, we observe, value of the Date and Time column is different for different users and when we try to get the value of the same Date and Time column using Rest API, it will return a different value.

So, in this blog, we will understand why the value of the Date and Time column is different at different places.


Which settings SharePoint considers to show the value of the Date and Time column?

The date-time value that SharePoint shows does not depend on the time zone of the user's computer.

SharePoint considers the below time zones to show the value of the Date and Time column:

  1. Time zone selected by currently logged-in user in their profile.
  2. Time zone selected in Regional settings of the site settings.

How SharePoint shows the value of Date Time column?

Now we will understand how SharePoint shows the value of the date time column.

  • If the user has selected a time zone in his/her profile, then SharePoint will display date time in that particular time zone.
  • If the user has not selected a time zone in his/her profile, then SharePoint will display date time in the time zone which is selected in regional settings of the specific SharePoint site.


Let's understand this with an example:

Here, we have created one list and added a Date and Time column to the list. In site settings of this site collection, we have selected (UTC-08:00) Pacific Time (US and Canada) time zone.

We will check with two different users, which has different regional settings in their user profile.

  • User 1: This user has selected (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi time zone in his user profile.
  • User 2: This user has not selected any time zone in his user profile.

Now, we are creating one item in the list as "User 1" and add below values:


  • Now for "User 1", it will display the date-time value which was selected while creating the item. So, for User 1 it will display as below:


  • As the item created by "User 1" and time zone selected by User 1 is (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi, SharePoint will display date time in selected (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi time zone.
  • But for "User 2", it will display a different date time because "User 2" has not selected time zone in his user profile. So, it will display date time in the time zone selected in the regional settings of the site collection. It means it will display date time in (UTC-08:00) Pacific Time (US and Canada) time zone.


How SharePoint returns the value of Date and Time column in REST API?

  • If we try to get the Date and Time column value of the above item using REST API, it will return a different value.
  • Here we have hit the REST API in the browser it returns the value as shown below screenshot:

REST API: /_api/web/lists/getbytitle('Sample')/items?$select=Title,Sample_x0020_Date_x0020_Field

  • So the value returned is not in the time zone of site collection or time zone of the current user's user profile's time zone.
  • It is in UTC time because SharePoint stores all the DateTime values in UTC time (REST API returns Date and Time column value in UTC).

Conclusion:

This is how SharePoint shows the value of the Date and Time column. Hope this blog will be helpful for you!

If you have any questions you can reach out to our SharePoint Consulting team here.

March 25, 2021

[Nintex Form]: How to setup auto populate value in dropdown

INTRODUCTION

We need to implement autocomplete dropdown feature using Nintex form in SharePoint 2019 custom list for hospitality company based out Saudi Arabia. Client has already acquired Nintex forms license for customizing SharePoint forms and wanted to set autocomplete dropdown feature for department field in Nintex form.

PROBLEM

Nintex form does not provide OOTB autocomplete dropdown control which can be utilized to meet client requirement. So, we thought of an alternate approach by setting "Department" field as lookup column in SharePoint custom list and set that column as dropdown in Nintex form, but the issue is lookup column also does not have auto suggest functionality.

SOLUTION

Nintex form provides an option to inject custom jQuery in Nintex forms. So, finally, we have decided to use custom jQuery Auto-complete combo box in Nintex form to achieve autocomplete dropdown functionality.

In the Nintex form, we need to set label control and inside that rendering the jQuery auto-complete combo box component programmatically. Also, we've implemented Ajax call to get list of department values from SharePoint list and binds to jQuery combo box component. To store selected values from combo box to SharePoint list, we've used other single line of text field in the Nintex form and set the selected values from the control to this field on save button click. We've hide the control using jQuery when form loads.

PROCESS

Step 1: Open Nintex Forms

Go to the list where you want to integrate this Nintex form, click on the list and select Nintex Forms.

Here, we have SharePoint custom list having column named Department with single line of text type.



Step 2: Setup Default Department.

You can see the default form already setup here; and we need to hide the department column that was already set with single line of text. It can be implemented using custom jQuery.

To open setting of this control, Select and open control setting from the top left side of the page.


It will open the control setting page, expand the formatting tab, Here, we need to add "formDepartmentField" class for both Control CSS class and CSS class.


Step 3: Setup Dropdown.

Now, We need to set our dropdown control in the label field. And to do that, we need to first add a label control in the Nintex form from left side panel.


Double click on the label OR click on Control setting, will open Control Settings - Label window.



Expand the Formatting tab, In CSS class, add “autoChoiceBox” class for custom dropdown and 

nf-form-input nf-section” class to apply CSS. Click on Save button.


Step 4: Setup Submit button.

Open submit button control setting and expand the Advance tab.


Now, on Client click, we have to write code for validation and store value from department field to SharePoint list. It can be achieved using custom jQuery.

Add below code in the Client click field.


 NWF$(document).ready(function () {  
 if (NWF$('.custom-combobox-input').val() == "") {  
 NWF$('.custom-combobox-input').addClass('nf-error-highlight');  
 } else {  
 NWF$('.custom-combobox-input').removeClass('nf-error-highlight');  
 }  
 NWF$(NWF$(".formDepartmentField").find('input')[0]).val($('.custom-combobox-input').val());  
 });  

The code must be written only on submit button client click event.

Step 5: Apply jQuery and Give code reference path.

Click on the form setting to apply CSS and custom jQuery code.


Expand Advanced tab, in Custom JavaScript Include section, set reference URLs of JS files containing our custom code, Here, we have stored these files in a Style library.

For Autocomplete dropdown, we need to add one jQuery plug-in called jQuery UI (1-12-1)

Reference URL for jQuery UI:- http://jqueryui.com/resources/download/jquery-ui-1.12.1.zip

We need to download it from the jQuery site and uploaded it to Style library. The path of this file must be added prior to our custom JS file link added in "Custom JavaScript Includes section as shown above.

Add below code for autocomplete dropdown in the script file - INF_Autocomplete.js and Save the changes.

 NWF$(document).ready(function () {  
   NWF$("#s4-ribbonrow").attr("style", "display:none !important;");  
 });  
 var userDep = "";  
 NWF.FormFiller.Events.RegisterAfterReady(function () {  
   var isNewMode = document.location.pathname.indexOf("/NewForm.aspx") >-1;  
   var isDisplayMode = document.location.pathname.indexOf("/DisplayForm.aspx") >-1;  
   NWF$('.nameOfUser').change(function () {  
     var userName = NWF$("[data-val]").attr("data-val");  
     if (userName) {  
       var userName2 = userName.slice(7);  
       var userName1 = userName2.replace("\\", "\\\\");  
       var valToSet = onUserNameChange(userName2);  
 setValToDropdown(valToSet);  
       NWF$('.custom-combobox-input').val(valToSet);  
     }  
   });  
 userDep = getUserDepartment();  
   var htmlForCombo = '<div class="ui-widget"><select id="combobox"><option value>Select one...</option></select></div>';  
   NWF$('.autoChoiceBox').html(htmlForCombo);  
   var allDep = getDepartmentData();  
   var departmentArray = [];  
 allDep.forEach(function (item, index) {  
 departmentArray.push(item.DepartmentName);  
     NWF$('#combobox').append(NWF$('<option>', {  
       value: item.DepartmentName,  
       text: item.DepartmentName  
     }));  
   });  
   if (isNewMode) {  
 setValToDropdown(userDep);  
   } else {  
     var valToSet1 = NWF$(NWF$(".formDepartmentField").find('input')[0]).val();  
 setValToDropdown(valToSet1);  
   }  
   if (isDisplayMode) {  
 setTimeout(function () {  
       NWF$('.ui-widget').attr("disabled", "disabled");  
       NWF$('a.ui-button').unbind('click').removeAttr('title');  
     }, 100);  
   }  
   NWF$(function () {  
 NWF$.widget("custom.combobox", {  
       _create: function () {  
 this.wrapper = NWF$("<span>").addClass("custom-combobox").insertAfter(this.element);  
 this.element.hide();  
         this._createAutocomplete();  
         this._createShowAllButton();  
       },  
       _createAutocomplete: function () {  
         var selected = this.element.children(":selected"),  
           value = selected.val() ? selected.text() : "";  
 this.input = NWF$("<input>").appendTo(this.wrapper).val(value).attr("title", "").addClass("custom-combobox-input ui-widget ui-widget-content ui-state-default ui-corner-left").autocomplete({  
           delay: 0,  
 minLength: 0,  
           source: NWF$.proxy(this, "_source")  
         }).tooltip({  
           classes: {  
             "ui-tooltip": "ui-state-highlight"  
           }  
         });  
 this._on(this.input, {  
 autocompleteselect: function (event, ui) {  
 ui.item.option.selected = true;  
 this._trigger("select", event, {  
               item: ui.item.option  
             });  
           },  
 autocompletechange: "_removeIfInvalid"  
         });  
       },  
       _createShowAllButton: function () {  
         var input = this.input,  
 wasOpen = false;  
         NWF$("<a>").attr("tabIndex", -1).attr("title", "Show All Items").tooltip().appendTo(this.wrapper).button({  
           icons: {  
             primary: "ui-icon-triangle-1-s"  
           },  
           text: false  
         }).removeClass("ui-corner-all").addClass("custom-combobox-toggle ui-corner-right").on("mousedown", function () {  
 wasOpen = input.autocomplete("widget").is(":visible");  
         }).on("click", function () {  
 input.trigger("focus"); /*Close if already visible*/  
           if (wasOpen) {  
 return;  
           } /*Pass empty string as value to search for, displaying all results*/  
 input.autocomplete("search", "");  
         });  
       },  
       _source: function (request, response) {  
         var matcher = new RegExp(NWF$.ui.autocomplete.escapeRegex(request.term), "i");  
         response(this.element.children("option").map(function () {  
           var text = NWF$(this).text();  
           if (this.value&& (!request.term || matcher.test(text))) return {  
             label: text,  
             value: text,  
             option: this  
           };  
         }));  
       },  
       _removeIfInvalid: function (event, ui) {  
         /*Selected an item, nothing to do*/  
         if (ui.item) {  
 return;  
         } /*Search for a match (case-insensitive)*/  
         var value = this.input.val(),  
 valueLowerCase = value.toLowerCase(),  
           valid = false;  
 this.element.children("option").each(function () {  
           if (NWF$(this).text().toLowerCase() === valueLowerCase) {  
 this.selected = valid = true;  
             return false;  
           }  
         }); /*Found a match, nothing to do*/  
         if (valid) {  
 return;  
         } /*Remove invalid value*/  
 this.input.val("");  
 this.element.val("");  
 this.input.autocomplete("instance").term = "";  
       },  
       _destroy: function () {  
 this.wrapper.remove();  
 this.element.show();  
       }  
     });  
     NWF$("#combobox").combobox();  
   });  
   NWF$('.formDepartmentField').hide();  
 });  
 function getUserDepartment() {  
   var dataToReturn = "";  
   var reqUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/SP.UserProfiles.PeopleManager/GetMyProperties";  
   $.ajax({  
     url: reqUrl,  
     type: "GET",  
     headers: {  
       "accept": "application/json;odata=verbose"  
     },  
     async: false,  
     success: function (data) {  
       if (data.d&&data.d.UserProfileProperties&&data.d.UserProfileProperties.results) {  
 dataToReturn = data.d.UserProfileProperties.results[11].Value;  
       }  
     }  
   });  
   return dataToReturn;  
 }  
 function getDepartmentData() {  
   var dataToReturnDep = [];  
   var reqUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/GetByTitle('Lists%20Of%20Departments')/items?$select=DepartmentName&$top=1000";  
   $.ajax({  
     url: reqUrl,  
     type: "GET",  
     headers: {  
       "accept": "application/json;odata=verbose",  
       "content-type": "application/json;odata=verbose"  
     },  
     async: false,  
     success: function (data) {  
       if (data &&data.d&&data.d.results) {  
 dataToReturnDep = data.d.results  
       }  
     }  
   });  
   return dataToReturnDep;  
 }  
 function onUserNameChange(dataFun) {  
   var dataToReturn = "";  
   var reqUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor(accountName=@v)?@v='" + dataFun + "'";  
   $.ajax({  
     url: reqUrl,  
     type: "GET",  
     headers: {  
       "accept": "application/json;odata=verbose"  
     },  
     async: false,  
     success: function (data) {  
       if (data.d&&data.d.UserProfileProperties&&data.d.UserProfileProperties.results) {  
 dataToReturn = data.d.UserProfileProperties.results[11].Value;  
       }  
     }  
   });  
   return dataToReturn;  
 };  
 function setValToDropdown(valueToSet) {  
   NWF$('#combobox').val(valueToSet);  
   NWF$('#combobox').change();  
 };  

Step 6: Apply CSS.

On form setting, expand the Custom CSS tab.



You can add below code for designing the dropdown as per business need.

 .nf-form-input {       
   border: 1px solid #d4dbe0;  
 }  
 .nf-form-label .nf-filler-control-inner{    
   top: 1px;  bottom: 1px; line-height: 1.2;  
 }  
 .custom - combobox - input {  
   width: 90 %  
 }  
 .custom - combobox {  
   padding: 10 px;  
 }.custom - combobox {  
   height: 34 px; margin: 10 px; padding: 0 px; display: block; border: 1 px solid #ababab;  
 }.custom - combobox> input {  
   height: 29 px; background: #ffffff; border: 0 px; margin - top: 2 px!important; width: 93 % ;  
 }       

Conclusion

This is how Autocomplete dropdown cab be setup in Nintex Form.

 If you have any questions you can reach out our SharePoint Consulting team here.

February 4, 2021

[Issue Resolved] : Error occurred in deployment step 'Recycle IIS Application Pool': Invalid namespace SharePoint

Problem/Issue:
Recently, while developing a custom visual web part for SharePoint 2019 On-Premise version, I was facing below error on deployment of the solution:

"Error occurred in deployment step 'Recycle IIS Application Pool': Invalid namespace"

Analysis & findings:
In Past, we'd already faced such issue and it was related to .NET Framework. So, I tried with reducing current version to 4.5 framework but it didn't help much.
Then, I did bit googling, and spent more time to analyze the the issue further. And finally, I was able to find the root cause of the issue. It was related to changes in configuration of development environment.

Solution:
We need to add "IIS 6 WMI Compatibility" feature in our development machine to fix the issue. To add it, we need to follow below steps:
  • If you are using windows server, go to server manager. 
  • Click on “Add Roles and Feature”.
  • It will open “add feature” dialog. Click next till you reach “Server Roles”.

  • Expand “Web Server (IIS)”
  • Expand “Management Tools”
  • Expand “IIS 6 Management Compatibility”
  • Now, check “IIS 6 WMI Compatibility”, “IIS 6 Metabase Compatibility” & “IIS 6 Management Console” and click Next.
Note - In my case, the first two features were already installed. If it is not installed in your machine, please select it as well.
  • Click Next and move to feature tab.
  • Click Install in confirmation installation screen.
  • It will start installing the feature. Wait for the completion.
  • Click close.
  • Restart the visual studio and try to deploy the project and it will deploy successfully. 

If you have any questions you can reach out our SharePoint Consulting team here.

March 30, 2020

Manage more than 100 API calls with Batch Request in SharePoint Framework (SPFx) using React

Requirement:
We have IDs of 1000 list items and want to fetch list items for only those particular IDs using Rest API with OR condition in the filter.

Problem Statement:
  1. If we append all IDs in the filter parameter with OR condition, it will give REST API length limitation (255 characters).
  2. If we use API with OR condition for all IDs and use this API in a batch request, batch API will return a blank array.
  3. If we create API for each ID and create an array of APIs & use this array in a batch request, it will work for maximum up to 100 APIs only. As we have 1000 list items to be retrieved, this will also not work due to the 100 APIs limit with a single batch request.

Solution:
We can create API for 100 Item IDs with OR condition in the filter API and create a bunch of 100 APIs in an array and then use this array in a batch request.

You can find batch utility code here on GitHub. Here is the code snippet for this solution:
 //Here we have imported batchutility.ts  
 import { BatchUtils } from "../../BatchUtils";  
 public componentDidMount() {  
   var listItemIds = [1, 2, 3, 4, 5, 6, ..............................., 1000];  
   var url = this.props.SPUrl + "/_api/web/lists/getbytitle('" + this.props.projectPhaseListName + "')/items?$filter=";  
   this.getDashboardBatchData(listItemIds, 0, listItemIds.length, url);  
 }  
 //Below function will create bunch of filters with max of 100 id in filter with or condition  
 public getDashboardBatchData(listItemIds, Index, totalCount, url) {  
   var loopLen = Index + 100;  
   if (Index <= totalCount) {  
     var filterString = "";  
     var tempApi = '';  
     var callNext = true;  
     if (totalCount > Index && totalCount < loopLen) {  
       loopLen = totalCount;  
       callNext = false;  
     }  
     for (var i = Index; i < loopLen; i++) {  
       if (filterString == '') {  
         filterString = "ID eq " + listItemIds[i];  
       }  
       else {  
         filterString += " or ID eq " + listItemIds[i];  
       }  
     }  
     tempApi = url + filterString;  
     dashboardBatchArray.push(tempApi);  
     if (callNext) {  
       this.getDashboardBatchData(listItemIds, loopLen, totalCount, url);  
     }  
     else {  
       //dashboardBatchArray will have all APIs with max of 100 filters for ID  
       this.processBatch(dashboardBatchArray);  
     }  
   }  
   else {  
     //dashboardBatchArray will have all APIs with max of 100 filters for ID  
     this.processBatch(dashboardBatchArray);  
   }  
 }  
 //Below funcion will create bunch of 100 APIs and will be used in batch request  
 public processBatch(dashboardBatchArray){  
   var index = 0;  
   var arrayLength = dashboardBatchArray.length;  
   var tempArray = [];  
   var chunk_size = 100;  
   //Below code will create array of 100 APIs in single bunch  
   for (index = 0; index < arrayLength; index += chunk_size) {  
     var myChunk = dashboardBatchArray.slice(index, index + chunk_size);  
     tempArray.push(myChunk);  
   }  
   //Below code will execute all apis of tempArray  
   let listItemsArray = [];  
   for (var i = 0; i < tempArray.length; i++) {  
     await BatchUtils.GetBatchAll({ rootUrl: this.props.SPUrl, FormDigestValue: '', batchUrls: tempArray[i] }).then((batchResult) => {  
       if (batchResult.length > 0) {  
         for (var i = 0; i < batchResult.length; i++) {  
           for (var j = 0; j < batchResult[i].d.results.length; j++) {  
             listItemsArray.push(batchResult[i].d.results[j]);  
           }  
         }  
       }  
     }  
   }  
   //You will see all 1000 items in listItemsArray variable  
   console.log(listItemsArray);  
 }  

If you have any questions you can reach out our SharePoint Consulting team here.