Tuesday 26 October 2021

SharePoint 2013 / 2016 : Associate a Workflow with a List or Library

SharePoint 2013  / 2016 : Associate a Workflow with a List or Library 

Scenario/Problem: You want to assign a workflow to a list or library so that users can select it from the list of available workflows, or you might want to define a workflow that starts automatically when a document or list item is added or modified in the library or list.

Solution: To attach different workflows to a list or library, you need the Manage List or Manage Library permission on that list or library. If you have those permissions, switch to the Library ribbon or List ribbon and click on the Workflows button (see Figure 1).


FIGURE 1 The Workflows button in the Library ribbon.

Clicking the Workflows button opens the Workflow Settings page for the list or library. This page enables you to associate a workflow with the list or library (see Figure 2).


FIGURE 2 The Workflow Settings page for adding a workflow or managing the existing ones.

The list of workflows that were already added to the list or library appears at the top of the page (under the Workflows title). The option to add a workflow is shown as a link at the bottom of the page. Click this link to open the Add a Workflow page, shown in Figure 3.


FIGURE 3 The Add a Workflow page.

In the Add a Workflow page, you can define what workflow you want to create on the list or library by selecting from the workflow box that lists the available workflows. By default, the only workflows available in SharePoint Server are the five built-in workflows:


Finally, the last section of the page, Start Options, enables you to specify when the workflow starts (see Figure 4).









Monday 20 September 2021

How to Add Remote Event Receivers to the Host Web List SharePoint

 In the previous post, we looked at the process of adding and debugging of Remote Event Receiver. The receiver was added to the list, which is set in our Web application.

In this post we will take a look at how to add event receivers to the lists that are in the Host Web.

As an example, we will create a Provider-hosted application (app). This application will allow us to connect the remote event receiver to any list in the Host Web.

First step: create a list (Custom List) and name it Test.



Then create Provider-hosted app in Visual Studio (as in the previous post). So we created a solution that consists of two projects (app + website).






Now add the Remote Event Receiver. Right click on the project name RERHostDemo -> Add -> New Item … select Remote Event Receiver.

Let’s name it RERHostReceiver. Then click Add.



Next step: select List Item Events, as a source of Custom List, as well as select the event, on which should trigger our receiver – An item was added. Click Finish



Both of our project will be modified. The project RERHostDemo reveal element RERHostReceiver, then click on Elements.xml to view its contents.


We see that this receiver will be connected to all lists with a template Custom List (ListTemplateId = 100). Now open the second draft RERHostDemoWeb, code file receiver – RERHostReceiver.svc.cs. Now we set up the breakpoint in ProcessOneWayEvent method and run the application in debug mode – press F5 (Do not forget to set the service bus and endpoint for RERHostDemo project. How to do this, see the previous post).

After launching the application, appears the window asking for permission. Click Trust It.


Then we are redirected to the page of our web application. Now this is a normal MVC application without any changes.



Without closing the tab (or Visual Studio will be out of debug mode), open another tab and go to your portal. Open the test list and use it to create the element. Our breakpoint will not work.
Let’s take a look at the Test list of remote receiver.
To do this, open the Powershell ISE and run the following code:


function listEventReceivers([Microsoft.SharePoint.Client.ClientContext]$context, [string]$listName)   
 {  
   $list = $context.Web.Lists.GetByTitle($listName);   
   $context.Load($list)  
   $eventReceivers = $list.EventReceivers  
   $context.Load($eventReceivers)  
   $context.ExecuteQuery()  
   foreach ($er in $eventReceivers)  
   {  
     Write-Host ("Found ER: " + $er.ReceiverName)  
     Write-Host ("ReceiverClass: " + $er.ReceiverClass)  
     Write-Host ("ReceiverAssembly: " + $er.ReceiverAssembly)  
     Write-Host ("EventType: " + $er.EventType)  
     Write-Host ("ReceiverUrl: " + $er.ReceiverUrl)  
     Write-Host ("Synchronization: " + $er.Synchronization)  
   }  
 }  
 $username = "test"   
 $password = "test"   
 $url = "https://test.sharepoint.com"  
 $securePassword = ConvertTo-SecureString $password -AsPlainText -Force  
 $listName = "Test"  
 Add-Type -Path "c:Program FilesCommon Filesmicrosoft sharedWeb Server Extensions15ISAPIMicrosoft.SharePoint.Client.dll"   
 Add-Type -Path "c:Program FilesCommon Filesmicrosoft sharedWeb Server Extensions15ISAPIMicrosoft.SharePoint.Client.Runtime.dll"   
 $clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($url)   
 $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePassword)   
 $clientContext.Credentials = $credentials  
 if (!$clientContext.ServerObjectIsNull.Value)   
 {   
   listEventReceivers $clientContext $listName  
 }   


As the result of the executed code, the screen will be empty, since in the list of Test does not have event receivers (for detailed info about the above code – here).

The point is that the markup in RERHostReceiver (Elements.xml) file connects only to our receiver lists in the app web. Declaratively, connection of remote event receivers to lists on Host Web is not possible. But we can do it programmatically.
First, open AppManifest.xml file (in RERHostDemo project), then go to the tab Permissions. In order to enable us to add event receivers from the code, it is necessary for user to have the right to Manage Host Web. In the Scope column set Web, and Permissions column Manage value.


Now remove RERHostReceiver element from the RERHostDemo project, and leave the code of the receiver in the project RERHostDemoWeb (we will use it later).

In RERHostDemoWeb project open the file Index.cshtml and replace its contents with the following code:

 @{  
   ViewBag.Title = "Home Page";  
 }  
 <div class="row">  
   @using (Html.BeginForm("Subscribe", "Home", new { SPHostUrl = Request.QueryString["SPHostUrl"] }))  
   {  
   @Html.AntiForgeryToken()  
   <div class="col-md-10">  
     @Html.DropDownList("listTitle", (IEnumerable<SelectListItem>)ViewBag.HostLists)  
     <input type="submit" value="Subscribe" />  
   </div>  
   }  
 </div> 


Now open HomeController.cs file, change the code of the Index method to the following:

[SharePointContextFilter]  
 public ActionResult Index()  
 {  
   var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);  
   using (var clientContext = spContext.CreateUserClientContextForSPHost())  
   {  
     if (clientContext != null)  
     {  
       var spWeb = clientContext.Web;  
       var hostListColl = spWeb.Lists;  
       clientContext.Load(spWeb, w => w.Id);  
       clientContext.Load(hostListColl);  
       clientContext.ExecuteQuery();  
       ViewBag.HostLists = hostListColl.Select(l => new SelectListItem() { Text = l.Title, Value = l.Title });  
     }  
   }  
   return View();  
 }  

After launching the application, we will see a drop-down list filled with lists of names from Host Web.


By clicking on the Subscribe button, Subscribe action of the Home controller will be triggered. We have not yet written code for it, so click on the button does not make sense.

Let’s go back to Visual Studio. Add new class RERUtility to RERHostDemoWeb project with the following code:

 using Microsoft.SharePoint.Client;  
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Web;  
 namespace RERHostDemoWeb  
 {  
   public class RERUtility  
   {  
     public static void AddListItemRemoteEventReceiver(ClientContext context, string listName, EventReceiverType eventType,  
       EventReceiverSynchronization synchronization, string receiverName,  
       string receiverUrl, int sequence, string receiverAssemblyName = "", string receiverClassName = "")  
     {  
       var list = context.Web.Lists.GetByTitle(listName);  
       context.Load(list);  
       var eventReceivers = list.EventReceivers;  
       context.Load(eventReceivers);  
       context.ExecuteQuery();  
       var newRER = new EventReceiverDefinitionCreationInformation();  
       newRER.EventType = eventType;  
       newRER.ReceiverName = receiverName;  
       newRER.ReceiverClass = receiverClassName;  
       newRER.ReceiverAssembly = receiverAssemblyName;  
       newRER.ReceiverUrl = receiverUrl;  
       newRER.Synchronization = synchronization;  
       newRER.SequenceNumber = sequence;  
       list.EventReceivers.Add(newRER);  
       list.Update();  
       context.ExecuteQuery();  
     }  
   }  
 }  

In this class, we created a method that gets the list by name and adds to it the remote event receiver.
Re-open HomeController.cs file and add the following code:

[SharePointContextFilter]  
     [HttpPost]  
     public ActionResult Subscribe(string listTitle)  
     {  
       var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);  
       using (var clientContext = spContext.CreateUserClientContextForSPHost())  
       {  
         if (!string.IsNullOrEmpty(listTitle))  
         {  
           RERUtility.AddListItemRemoteEventReceiver(  
             clientContext,  
             listTitle,  
             EventReceiverType.ItemAdded,  
             EventReceiverSynchronization.Asynchronous,  
             "RERHostReceiver",  
             "",  
                 10);  
         }  
       }  
       return RedirectToAction("Index", new { SPHostUrl = spContext.SPHostUrl.ToString() });  
     } 


Pay attention to (“”) value in the penultimate parameter in AddListItemRemoteEventReceiver method. In this setting, we need to pass a reference to our receiver, namely, on the service responsible for events processing. If our service was published online, then we would know its address. But what if we want to debug receiver code without publishing (to localhost)?
The answer – we need to register the service address in the service bus.
Select RERHostDemo project and press F4, in the appeared properties window, set True in Handle App Installed.


Visual Studio automatically creates a handler class in the folder Services, which will be triggered when you install our application. Open AppEventReceiver.svc (project RERHostDemoWeb).
Replace the code of the ProcessEvent method to the following:

 public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)  
 {  
    SPRemoteEventResult result = new SPRemoteEventResult();  
    var serviceUrl = System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri.ToString();  
    return result;  
 }  

Set the breakpoint on line on the last line. Press F5 to run the solution in debug mode. This will open a browser window with information on the requested rights; click Trust It. Wait until our receiver launches (it may take a few seconds). Once the code execution stops, look at the contents of serviceUrl variable. It contains service address, which is responsible for receiving the event when the application is installing. Copy and save it in notepad.


Exit debug mode, open HomeController.cs file, go to Subscribe method. Replace the value of the (“”) penultimate parameter to the service address that you saved in Notepad. At the end of the address, replace AppEventReceiver.svc to the name of your service – RERHostReceiver.svc.
You can safely remove AppEventReceiver service. Also, do not forget to set false value in the property of the RERHostDemo project for Handle App Installed.

The following example creates a receiver that will be triggered asynchronously to the «Item was added» event. Now open RERHostReceiver.svc.cs file and set breakpoint in ProcessOneWayEvent method. Press F5. Select list, to which we want to add our receiver, click Subscribe.

Open another tab in your browser and go to the list, to which we added the receiver. Create a new element. Hooray, our breakpoint load.



ps: Unfortunately, at the moment it is impossible to remove the event receiver that are added «through service bus». An error «Access Denied» occurs. The only way  is to delete the list. Receivers, written by published service (in Azure) can be removed properly. Here you can see examples of code for removing/ adding/etc. event receivers.










Get the List of Event Receivers for a Custom List in SharePoint 2016 and SharePoint 2013

 To check the list of Event Receivers attached to a custom List in SharePoint 2016 and 2013

 

Power Shell command to get the list of Event Receivers are as follows:


$spWeb = Get-SPWeb Identity http://mySite

$spList = $spWeb.Lists["Custom List Name"]

$spList.EventReceivers | Select Name, Assembly, Type


Sunday 22 August 2021

SharePoint Online : Comments on List Formatting JSON

 SharePoint Online : Show comments on List Formatting JSON. 

Following source code will display List item comments count and button to open comments with edit item. 




Click on comments button will following screen to add and delete comments. 






Source Code : 


{

  "$schema": "https://developer.microsoft.com/json-schemas/sp/view-formatting.schema.json",

  "hideSelection": true,

  "hideColumnHeader": true,

  "rowFormatter": {

"elmType": "div",

"style": {

  "display": "flex",

  "flex-direction": "row",

  "height": "50px",

  "width": "285px"

},

"children": [

  {

"elmType": "div",

"style": {

  "display": "flex",

  "flex-direction": "column",

  "align-items": "center",

  "justify-content": "center",

  "height": "100%",

  "width": "15%"

},

"children": [

  {

"elmType": "div",

"style": {

  "border-width": "2px",

  "border-style": "solid",

  "height": "60px"

},

"attributes": {

  "class": "ms-borderColor-neutralSecondary"

}

  },

  {

"elmType": "div",

"style": {

  "height": "60px",

  "width": "20px",

  "border-radius": "50%",

  "cursor": "pointer",

  "outline": "none",

  "background-color": "black"

},

"attributes": {

  "class": "ms-bgColor-themePrimary"

}

  },

  {

"elmType": "div",

"style": {

  "border-width": "2px",

  "border-style": "solid",

  "height": "60px"

},

"attributes": {

  "class": "ms-borderColor-neutralSecondary"

}

  }

]

  },

  {

"elmType": "div",

"style": {

  "font-size": "20px",

  "width": "50%",

  "text-align": "left",

  "cursor": "pointer",

  "color": "#34A4E3"

},

"txtContent": "[$Title]",

"customCardProps": {

  "directionalHint": "rightCenter",

  "isBeakVisible": true,

  "openOnEvent": "click",

  "formatter": {

"elmType": "div",

"style": {

  "display": "flex",

  "flex-direction": "column",

  "height": "700px",

  "width": "850px",

  "position": "static",

  "top": "auto"

},

"children": [

  {

"elmType": "div",

"txtContent": "[$Title]",

"style": {

  "height": "50px",

  "width": "100%",

  "color": "white",

  "font-size": "20px",

  "display": "flex",

  "align-items": "center",

  "padding-left": "40px",

  "background-color": "#34A4E3"

},

"attributes": {

  "class": "ms-bgColor-themePrimary"

}

  },

  {

"elmType": "a",

"attributes": {

  "href": "[$pict]",

  "target": "_blank"

},

"children": [

  {

"elmType": "img",

"style": {

  "height": "95%",

  "width": "95%",

  "margin-top": "10px"

},

"attributes": {

  "src": "[$pict]",

  "title": "[$Title]"

}

  }

]

  },

  {

    "elmType": "button",  

  "customRowAction": {  

    "action": "defaultClick"  

  },  

  "attributes": {  

    "class": "ms-fontColor-themePrimary ms-fontColor-themeDark--hover",  

    "title": "Open Comment"  

  },  

  "style": {  

    "border": "none",  

    "background-color": "transparent",  

    "cursor": "pointer"  

  },  

  "children": [  

    {  

      "elmType": "span",  

      "attributes": {  

        "iconName": "OpenPane",  

        "class": "ms-font-xxl"  

      }  

    }  ,

{

  "$schema": "https://developer.microsoft.com/json-schemas/sp/column-formatting.schema.json",

  "elmType": "div",

 "style": {    "float": "left",

    "padding": "10px"

},

  "attributes": {

    "class": "ms-fontColor-themePrimary ms-fontSize-m"

  },

  "children": [

    {

      "elmType": "span",

      "style": {

        "padding-right": "10px",

"float": "left"

      },

      "attributes": {

        "iconName": "Comment"

      }

    },

    {

      "elmType": "span",

      "txtContent": "=if([$_CommentCount] == '' , 0 ,[$_CommentCount]) "

    }

  ]

}  

  ]},

  {

"elmType": "div",

"txtContent": "[$Description]",

"style": {

  "height": "80%",

  "width": "85%",

  "padding-top": "20px"

}

  }



  


]

  


  }

}

  }

]

  }

}

SharePoint Office 365 List Tile View

SharePoint Office 365 List formatting with Tile view 


Products can be shown in following nice way. 







Step 1: Create List with following 4 columns 

Title : Single Line Text
Location: Single Line Text
ProductPhoto : HyperLink 
ProjectURL : HyperLink ,


Step 2 : Upload Proper data 

Step 3 :  GO to All Items in List and click on Format current view as shown in following 





Step 4:  select Advance mode  as shown in following picture 




Step 5:  Copy following code in Text Editor and save it. 





Step 6:  Select Tile and save as view as showing in following picture. 








Source Code : 


{
"$schema": "https://developer.microsoft.com/json-schemas/sp/v2/tile-formatting.schema.json",
"height": "450",
"width": "300",
"formatter": {
"elmType": "div",
"style": {
"display": "flex",
"align-items": "stretch",
"margin-bottom": "16px",
"min-width": "150px",
"flex-grow": "1",
"justify-content": "space-around",
"padding": "8px",
"color": "#333333"
},
"children": [
{
"elmType": "div",
"style": {
"width": "95%",
"height": "98%",
"box-shadow": "0px 1.6px 3.6px 0 #00000024, 0px 0.3px 0.9px 0 #00000024",
"overflow": "hidden",
"border-radius": "2px"
},
"attributes": {
"class": "ms-bgColor-neutralLighterAlt"
},
"children": [
{
"elmType": "div",
"style": {
"display": "inline-block",
"min-width": "250px",
"text-align": "center",
"margin-top": "50px"
},
"children": [
{
"elmType": "img",
"attributes": {
"src": "=if([$ProductPhoto] == '', @currentWeb + '/_layouts/15/userphoto.aspx?size=M', [$ProductPhoto])",
"title": "=if([$ProductPhoto] == '', 'No picture available', [$Picture.desc])"
},
"style": {
"width": "85%",
"height": "200px"
}
}
]
},
{
"elmType": "div",
"style": {
"display": "inline-block",
"min-width": "250px",
"vertical-align": "top",
"padding-top": "16px",
"text-align": "center"
},
"children": [
{
"elmType": "div",
"style": {
"margin-bottom": "15px",
"font-size": "20px",
"font-weight": "600"
},
"txtContent": "[$Title]"
},
{
"elmType": "div",
"style": {
"font-size": "14px",
"margin-bottom": "20px",
"text-align": "center"
},
"txtContent": {
"operator": "+",
"operands": [
"",
"[$Location]"
]
}
},
{
"elmType": "a",
"style": {
"text-decoration": "none"
},
"attributes": {
"href": "[$ProjectURL]",
"target": "_blank"
},
"children": [
{
"elmType": "div",
"style": {
"font-size": "14px",
"margin-top": "15px",
"height": "30px",
"text-align": "center",
"width": "144px",
"vertical-align": "middle",
"display": "inline-block",
"color": "#444",
"border": "2px solid #CCC",
"background": "#DDD",
"box-shadow": "0 0 5px -1px rgba(0,0,0,0.2)",
"cursor": "pointer",
"max-width": "100px",
"padding": "6px 10px 0px 10px"
},
"attributes": {
"target": "_blank",
"class": "ms-bgColor-white--hover"
},
"txtContent": "View Project"
}
]
}
]
}
]
}
]
}
}



Output 





SharePoint Office 365 List formatting with Tile view

SharePoint Office 365 List formatting with Tile view 

Products can be shown in following nice way. 





Step 1: Create List with following 4 columns 

Title : Singe Line Text
Location: Singe Line Text
ProductPhoto : HyperLink ,
Features: Multilines Lines Text

Step 2 : Upload Proper data 

Step 3 :  GO to All Items in List and click on Format current view as shown in following 





Step 4:  select Advance mode  as shown in following picture 




Step 5:  Copy following code in Text Editor and save it. 





Step 6:  Select Tile and save as view as showing in following picture. 








Source Code


{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/view-formatting.schema.json",
  "tileProps": {
    "height": "450",
    "width": "300",
    "formatter": {
      "elmType": "div",
      "style": {
        "display": "flex",
        "align-items": "stretch",
        "margin-bottom": "16px",
        "min-width": "150px",
        "flex-grow": "1",
        "justify-content": "space-around",
        "padding": "8px",
        "color": "#333333"
      },
      "children": [
        {
          "elmType": "div",
          "style": {
            "width": "95%",
            "height": "98%",
            "box-shadow": "0px 1.6px 3.6px 0 #00000024, 0px 0.3px 0.9px 0 #00000024",
            "overflow": "hidden",
            "border-radius": "2px"
          },
          "attributes": {
            "class": "ms-bgColor-neutralLighterAlt"
          },
          "children": [
            {
              "elmType": "div",
              "style": {
                "display": "inline-block",
                "min-width": "300px"
              },
              "children": [
                {
                  "elmType": "img",
                  "attributes": {
                    "src": "=if([$ProductPhoto] == '', @currentWeb + '/_layouts/15/userphoto.aspx?size=M', [$ProductPhoto])",
                    "title": "=if([$ProductPhoto] == '', 'No picture available', [$Picture.desc])"
                  },
                  "style": {
                    "width": "100%",
                    "height": "200px"
                  }
                }
              ]
            },
            {
              "elmType": "div",
              "style": {
                "display": "inline-block",
                "min-width": "300px",
                "vertical-align": "top",
                "padding-left": "16px",
                "padding-top": "16px"
              },
              "children": [
                
                {
                  "elmType": "div",
                  "style": {
                    "margin-bottom": "12px",
                    "font-size": "16px",
                    "font-weight": "600"
                  },
                  "txtContent": "[$Title]"
                },
                {
                  "elmType": "div",
                  "txtContent": "Location",
                  "style": {
                    "color": "#767676",
                    "font-size": "12px",
                    "margin-bottom": "2px"
                  }
                },
                {
                  "elmType": "div",
                  "style": {
                    "font-size": "14px",
                    "margin-bottom": "12px"
                  },
                  "txtContent": {
                    "operator": "+",
                    "operands": [
                     
                      "",
                      "[$Location]"
                    ]
                  }
                },
                {
                  "elmType": "div",
                  "txtContent": "Features",
                  "style": {
                    "color": "#767676",
                    "font-size": "12px",
                    "margin-bottom": "2px"
                  }
                },
                {
                  "elmType": "div",
                  "style": {
                    "font-size": "14px",
                    "margin-bottom": "10px",
                    "width": "235px",
                    "height": "70px"
                  },
                  "txtContent": "[$Features]"
                }
              ]
            }
          ]
        }
      ]
    }
  }
}



Output 

Friday 26 March 2021

Office 365: View your Google Calendar in Outlook

Office 365: Integrate Google Calendar to Outlook 

Step 1: Login your Google account and click on settings option and then click on Calendar 




Step 2: Open your Google Calendar in a browser.  Select "My Calendars" on the left, then select the "…" next to the calendar name.  Click Settings:



Step 3Look for "Integrate Calendar", then copy the "Secret address in iCal format"





Step 4
Now, we’ll move to Outlook–select File, then “Account Settings”, then “Account Settings” again:




Step 5:
 Choose the “Internet Calendars” tab, then select “New”:



Step 6

Paste the URL you previously copied here, then click “Add”:


 
Step 7:

 Name the calendar (this will display in the Outlook calendar list), and enable any options as needed:




Step 8:

Your Google Calendar will appear in your “Other Calendars” list and will update periodically, automatically: