Sunday 16 October 2016

SharePoint REST API $expand query option.

A few months ago, I got a task that only logged-in users can edit list items created by themselves, which requires the Created By (Author) and Modified By (Editor) field values of that list item.

So, I will share my experience in resolving it. The Created By and Modified By values doesn't appear when a GET request is done; only AuthorId and EditorId are returned, as given below.


The Created By (Author) and Modified By (Editor) fields are of the People type. If you want the Author and Editor Title values, then you have to use the REST API $expand query option. The $expand query option is used to lookup column values, or you can think of it as a JOIN on two lists like in SQL for getting data. The SharePoint REST API supports OData query options. In this scenario, the lookup column points to the User Information List to get the person's details.

The code snippet below defines the proper usage of the $expand query option in the REST API. The code expands the Author and Editor entity fields, and in the $select column name, a forward slash (/) is used.


The success response object is logged using the console API.



The below image shows all the values returned.



🚀 "Happy Coding" 🚀

CSOM PowerShell: Create Term Store Management.

In this blog article, we will create a solution to create "ManagedMetaData Terms" which can be deployed on-premise, SharePoint Online (Office 365), or hybrid platforms.

The artefacts or functionality created using server-side or client-side techniques are not compatible, so they can be used to deploy solutions on any platform.


SharePoint PowerShell is a PowerShell API for SharePoint 2010, 2013, and Online. Very useful for Office 365 and private clouds where you don't have access to the physical server.

The API uses the Managed .NET Client-Side Object Model (CSOM) of SharePoint 2013. It's a library of PowerShell scripts, and in its core, it talks to the CSOM dll's.

The basic steps to implement CSOM PowerShell are:
1) Add Microsoft.SharePoint.Client.dll, Microsoft.SharePoint.Client.Runtime.dll and Microsoft.SharePoint.Client.Taxonomy.dll assemblies (which can be found in the ISAPI folder).
2) Create a ClientContext object.
3) Call the Load() and then ExecuteQuery() method.
4) Call the get and set methods using the ClientContext object.


Prerequisites for easily implementing the CSOM PowerShell script (optional).
Download the PowerGUI Script Editor IDE, which is free from http://en.community.dell.com/techcenter/powergui
It's a very powerful IDE for PowerShell, similar to Visual Studio.
It provides various features, like
1) Script Explorer.
2) Intellisense.
3) Variables and Call Stack.
4) Debugging and many more.


Steps to be used to create Term Store Management are:
1) Add dll's, create clientcontext and load (web and site) objects, and call the executequery() method.
2) Create the ManagedMetaData function and pass the clientcontext object.
3) Which reads the "ManagedMetaData.xml" file content (saved on the local drive).
4) Call the Get-TermStoreInfo function to get all groups and load them.
5) Check whether the group already exists or not, and then create it.
6) Then call the Create-TermSet function and pass the required parameters.
7) Again, check whether TermSet already exists or not, and then create TermSet.
8) Create Terms by calling the Create-Term function.










For more information, download the source from: click here


🚀 "Happy Coding" 🚀

Saturday 9 January 2016

Create a custom WCF REST service hosted in SharePoint to interact with external .NET services.

REST is a software architecture that uses uniform resource identifiers (URI) to specify operations against a remote service.


RESTful web services are REST architecture-based web services. In REST architecture, everything is a resource. RESTful web services are lightweight, highly scalable, and maintainable, and they are very commonly used to create APIs for web-based applications.


REST-based (known more commonly as “RESTful”) solutions use standard HTTP GET, POST, PUTand DELETE verbs to perform CRUD operations against a remote source. Support for the standard HTTP verbs provides easy cross-platform data access.

SharePoint 2013 provides a robust Representational State Transfer (REST) interface that allows any technology that supports standard REST capabilities to interact with SharePoint (sites, libraries, lists, etc).

I have one client requirement to show external web service data in SharePoint search; the search should get records from the SharePoint environment as well as external databases. The external database used is updated frequently.

I have made one pictorial representation of the solution design that we are now going to achieve.




To achieve this, I have tried three approaches as
         a) Directly calling .Net external web service using JQuery (not working due to XML response)
         b) Using the BCS service (which works but requires a method to get all records, and DB sync is not facilitated)
         c) Create custom web service and interact with external service.


Step-by-Step Instructions

1. Open Visual Studio (as Administrator), select the "SharePoint 2013: Empty Project" template, and create a new.

2. Unload the project, edit the csproj file, add TokenReplacementFileExtensions below the SandboxedSolution element, and set its value to svc.


               NoteThe SVC token is not provided by default in the SharePoint template.

3. Download the external web service (asmx) wsdl file.

4. Right-click on the References node and select the "Add Service Reference" option.





5. Follow the above snapshots, put the WSDL path, and click enter or the icon next to it. You can also change the web reference name (optional).

6. Right-click on the project and then Add > SharePoint Mapped Folder.

            NoteCreate a folder under ISAPI, as the service will not run directly under it.




7. There is no SVC template in the SharePoint Project template. So add Text File and change the file extension from txt to svc. Paste the code given below.

<%@ ServiceHost Language="C#" Debug="true" Service="EgSpi.Default.IMRSearch.Services.ImrService, $SharePoint.Project.AssemblyFullName$"
    Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>


8. Add the required assembly references by right-clicking References, and on the Reference Manager dialogue, select the required references.


9. Create a new folder called "Services" (give it any name) and add one class and an interface file.

namespace ABC.Default.IMRSearch.Services
{
    [ServiceContract]
    public interface IImrService
    {
        [OperationContract]
        [WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, Method = "POST", UriTemplate = "GetSpecificDocument", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        List<WebServiceModel> GetSpecificDocument(string sid, string mid, string options, string resultCount, string searchText);
    }
}


namespace ABC.Default.IMRSearch.Services
{
    [BasicHttpBindingServiceMetadataExchangeEndpoint]
    [AspNetCompatibilityRequirementsAttribute(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class ImrService : IImrService
    {
        #region private variables

        private readonly ILogger _logger;

        #endregion private variables

        #region constructor

        public ImrService()
        {
            _logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
        }

        #endregion constructor

        public List<WebServiceModel> GetSpecificDocument(string sid, string mid, string options, string resultCount, string searchText)
        {
            #region trace

            _logger.TraceToDeveloper("Start GetSpecificDocument Method (" + Constants.ASSEMBLY_NAME + " | IMRSearchWP | GetSpecificDocument())",
                LoggerHelper.DefaultEventID, TraceSeverity.Verbose, LoggerHelper.CategoryDefault);

            #endregion trace

            try
            {
                var proxy = new IMRSearchWebReference.EKService
                {
                    Url = "https://kvalitet.imr.no/EKWeb/Login/EKService.asmx",
                    Credentials = CredentialCache.DefaultCredentials
                };

                XmlNode xmlResponse = proxy.GetSearchData(sid, mid, options, searchText);

                List<WebServiceModel> getDocumentList = new List<WebServiceModel>();
                if (!ReferenceEquals(xmlResponse, null))
                {
                    var xmlNodeList = xmlResponse.SelectNodes(Constants.NodeToSelect);
                    if (!ReferenceEquals(xmlNodeList, null))
                    {
                        getDocumentList = (from XmlElement report in xmlNodeList
                                           select new WebServiceModel
                                           {
                                               Id = report.GetAttribute(Constants.Id),
                                               Title = report.GetAttribute(Constants.Title),
                                               Notes = report.GetAttribute(Constants.Notes)
                                           }
                                          ).Take(Convert.ToInt32(resultCount)).ToList();

                        if (getDocumentList.Count < 1)
                        {
                            return null;
                        }
                    }
                }
                return getDocumentList;

                #region trace

                _logger.TraceToDeveloper("Stop GetSpecificDocument Method (" + Constants.ASSEMBLY_NAME + " | IMRSearchWP | GetSpecificDocument())",
                    LoggerHelper.DefaultEventID, TraceSeverity.Verbose, LoggerHelper.CategoryDefault);

                #endregion trace
            }
            catch (Exception ex)
            {
                _logger.LogToOperations(ex, LoggerHelper.DefaultEventID, EventSeverity.Error, LoggerHelper.CategoryDefault);
                return null;
            }
        }
    }
}

10. Now deploy the solution, check it by opening inetmgr, and it should be placed under the site URL mapped to the folder structure created under ISAPI.

            Note: Click and check Package.package file there your svc file should present.



11. Now the service is created and hosted in SharePoint. Create a client to consume the REST SVC web service.

12. Right-click Add > New Item > Visual Web Part. Add a jquery reference, one textbox, a button, and the required hidden fields (see source code).

13. Paste the code on .cs file on the page load method.

protected void Page_Load(object sender, EventArgs e)
        {
            #region trace

            _logger.TraceToDeveloper("Start loading the IMRSearchWP (" + Constants.ASSEMBLY_NAME + " | IMRSearchWP | PageLoad())",
                LoggerHelper.DefaultEventID, TraceSeverity.Verbose, LoggerHelper.CategoryDefault);

            #endregion trace

            try
            {
                string divDisplayVal = Convert.ToString(Display);
                searchDiv.Style.Add(Constants.DisplayProp,
                    divDisplayVal.ToLower().Equals(Constants.DisplayValue.ToLower())
                    ? Constants.DisplayNone
                    : Constants.DisplayBlock);

                hddnSid.Value = SID;
                hddnMid.Value = MID;
                hddnOption.Value = Options;
                hddnResultCount.Value = ResultCount;
                hddnNoResultMessage.Value = NoResultMessage;

                #region "trace"

                _logger.TraceToDeveloper("Finish loading the IMRSearchWP (" + Constants.ASSEMBLY_NAME + " | IMRSearchWP | PageLoad())",
                    LoggerHelper.DefaultEventID, TraceSeverity.Verbose, LoggerHelper.CategoryDefault);

                #endregion "trace"
            }
            catch (Exception ex)
            {
                _logger.LogToOperations(ex, LoggerHelper.DefaultEventID, EventSeverity.Error, LoggerHelper.CategoryDefault);
            }

        }

14. I have also created Web Part properties to pass parameters and customize them dynamically, like
                 a) SID, MID & Options
                 b) Number records to display
                 c) Custom message when no records are present

#region Custom Webpart Properties

        [WebBrowsable(true),
        WebDisplayName("SID"),
        WebDescription("Enter SID value"),
        Personalizable(PersonalizationScope.Shared),
        Category("IMR Search")]
        public string SID { get; set; }

        [WebBrowsable(true),
        WebDisplayName("MID"),
        WebDescription("Enter MID value"),
        Personalizable(PersonalizationScope.Shared),
        Category("IMR Search")]
        public string MID { get; set; }

        [WebBrowsable(true),
        WebDisplayName("Options"),
        WebDescription("Enter option value"),
        Personalizable(PersonalizationScope.Shared),
        Category("IMR Search")]
        public string Options { get; set; }

        [WebBrowsable(true),
        WebDisplayName("Display"),
        WebDescription("Set display value"),
        Personalizable(PersonalizationScope.Shared),
        Category("IMR Search")]
        public bool Display { get; set; }

        [WebBrowsable(true),
        WebDisplayName("Results Count"),
        WebDescription("Set result count to display"),
        Personalizable(PersonalizationScope.Shared),
        Category("IMR Search")]
        public string ResultCount { get; set; }

        [WebBrowsable(true),
        WebDisplayName("No Result Message"),
        WebDescription("Enter message for no results"),
        Personalizable(PersonalizationScope.Shared),
        Category("IMR Search")]
        public string NoResultMessage { get; set; }

 #endregion Custom Webpart Properties


15. Use the JavaScript code below to get data from the custom service.

<script type="text/javascript">
    function callImrWebService(currVal) {
        $("#resultsPanel").empty();
        var sid = $("#hddnSid").val(),
            mid = $("#hddnMid").val(),
            options = $("#hddnOption").val(),
            resultCount = $("#hddnResultCount").val(),
            noResultMessage = $("#hddnNoResultMessage").val(),
            searchText = $("#txtSearchBox").val();


        var serviceUri = _spPageContextInfo.webAbsoluteUrl + "/_vti_bin/EgSpi/Default/Search/ImrSearch.svc/GetSpecificDocument";
        $.ajax({
            url: serviceUri,
            data: '{"sid": "' + sid + '", "mid": "' + mid + '", "options": "' + options + '", "resultCount": "' + resultCount + '", "searchText": "' + searchText + '"}',
            type: "POST",
            contentType: "application/json",
            dataType: "json",
            success: function (data) {
                if (data.GetSpecificDocumentResult == null) {
                    $("#resultsPanel").html(noResultMessage);
                } else {
                    showPresidentsList(data.GetSpecificDocumentResult);
                }
            },
            error: function (err) {
                alert("Error: " + JSON.stringify(err));
            }
        });
    }
function showDocumentsList(documentData) {
        $.each(documentData, function () {
            var aHref = "https://abc.com/EKWeb/GetDoc.aspx?id=" + $(this)[0].Id;
            var string = " <div class='outer'><div class='main'><div ><strong class='ms-srch-item-highlightedText'><a href='" + aHref + "'>" + $(this)[0].Title + "</a></strong></div></div> <div class='notes'><p>" + $(this)[0].Notes + "</p></div><div><a style='color:#338200' href='" + aHref + "'>" + aHref + "</a></div></div>";
            $('#resultsPanel').append(string);
        });
    }
</script>

16. The final output will come like this using CSS (refer to source code).


This can be used simply as a standalone web part to get records from an external web service.

But my objective is to use the above functionality with the out-of-the-box search box provided on the page and on the master page as well.

Add the WP on the page itself and also on the page where the master page search box will redirect you.

So, use the latest source code provided to achieve this. Edit the page and place your custom web part below the SharePoint Search Box and SharePoint Search Results WebPart.

If you face any issue, do write a comment or mail me.

Download source code (click here..)


🚀 "Happy Coding" 🚀

Saturday 19 December 2015

Install and Configure the SharePoint 2013 Workflow Platform.

Steps to install SharePoint 2013 workflow platform type with the new workflow engine.

Let’s begin the installation and configuration-

1. Download the workflow manager from http://go.microsoft.com/fwlink/?LinkID=252092
2. Run workflowmanager.exe as admin.
3. Click on "Install" button.



4. Click "I Accept" on the Prerequisites screen.



5. When the Workflow Manager 1.0 installation process has completed. Click "Continue" on  the configuration screen.


6. You will get "Workflow Manager Configuration Wizard" after this. Choose "Configure Workflow  Manager with Default Settings (Recommended)".



7. On "New Farm Configuration" (not to be confused with SharePoint Farm), enter the SQL server  instance and the credentials as required.

8. In the "Configure Service Account" section, enter the appropriate service account to run the  Workflow Manager.

9. Check the Allow Workflow Management over HTTP on this computer (for development), but you  don’t need to do it on production.

10. In the "Certificate Generation Key" section, enter a unique key and press the right  arrow to continue.


11. In the "Summary" dialogue box, verify all information entered and click OK (tick mark). Also, you  can save it by clicking on the "Copy" link button.



12. Now the "Configuration Process" will start.

13. If all the configuration processes are right, then you will get a screen similar to the one below.

14. Verify that the following services are running:
             a. Workflow Manager Backend.
             b. Windows Fabric Host Service.
             c. Service Bus Gateway.

15. Open IIS and check if "Workflow Management Site" is working or not. We get two ports  configured as:
             a. *:12290 (https)
             b. *:12291 (http)



16. To check the proper configuration, open the following link in your browser.
             a. http://servername:12291
             b. https://servername:12290




17. All the required databases are created as given below.



18. Open "SharePoint Management Shell" and run the following command.
 

19. After successful execution of the command, a new service application will be created and  its administrator will be set by the user provided while doing workflow configuration (optional).



20. If the service application is correct, you will get the screen as given below.


21. After the successful installation of "SharePoint 2013 Workflow Manager" a new platform  called SharePoint 2013 Workflow will come.

It provides the following features, as shown below:
Now take the full advantage of the SharePoint 2013 Workflow engine.


🚀 "Happy Coding" 🚀

Swagger: Web API

Swagger is a tool that allows you to quickly and easily provide API documentation for your Web API project, and it’s free. Its official site is http://swagger.io/

Swagger is basically a framework for describing, consuming, and visualizing RESTful APIs. The nice thing about Swagger is that it really keeps the documentation system, the client, and the server code in sync always, in other words, the documentation of methods, parameters, and models is tightly integrated into the server code.

To implement "Swagger", you need to install "Swashbuckle". Swashbuckle is a combination of ApiExplorer and Swagger/swagger-ui and provides a rich discovery, documentation, and playground experience to your API consumers. Some of the features you get with Swashbuckle are:
  • Auto-generated Swagger 2.0.
  • Seamless integration of swagger-ui.
  • Reflection-based Schema generation for describing API types.
  • Extensibility hooks for customizing the generated Swagger doc.
  • Extensibility hooks for customizing the swagger-ui.
  • Out-of-the-box support for leveraging Xml comments.
  • Support for describing ApiKey, Basic Auth and OAuth2 schemes. Including UI support for the Implicit OAuth2 flow.
Add Swashbuckle using the NuGet package manager console.

You can pass the version as well, but it’s optional; by default, it will pick the latest one.


After the package is installed, you will get a new file named SwagerConfig.cs under the App_Start folder.



To check if swagger is integrated properly or not start a new debugging session (press F5) and navigate to http://localhost:[PORT_NUM]/swagger. You should see Swagger UI help pages for your APIs. Here you will see all your web API action methods.



Expand an API, and clicking the "Try it out!" button will make a call to that specific API and return results.



Enter the parameter in the textbox provided and click "Try it out!", you will get JSON data in the response body of this API. Also, you will get a Response Code and Response Headers. You can try all methods in this way, including all HttpMethod types.




Enable Swagger to use XML comments.
In Solution Explorer, right-click on the Web API project and click Properties. Click the Build tab and navigate to Output. Make sure the XML documentation file is checked. You can leave the default file path. In my case, it's bin\SwaggerDemoApi.XML



Now, add the following line to SwaggerConfig.cs. Make sure to change the file path to the path of your XML documentation file.

c.IncludeXmlComments($@"System.AppDomain.CurrentDomain.BaseDirectory}\bin\ProjectName.Api.XML");

The full SwaggerConfig.cs is given below:
GlobalConfiguration.Configuration 
                .EnableSwagger(c =>
                  {
                     c.SingleApiVersion("v1""Project Name");
                        
                     c.IncludeXmlComments($@"{System.AppDomain.CurrentDomain.BaseDirectory}\bin\ProjectName.XML");
 
                  })
                .EnableSwaggerUi();


Swagger JSON file

To get the API’s complete metadata, navigate to the URL that is in the header of the Swagger UI documentation page.



Copy the above URL and paste it into the browser, and you will get JSON data.


I hope this will help you. Do write a comment below.


🚀 "Happy Coding" 🚀

Friday 18 December 2015

CKS - Dev for Visual Studio 2015.

The CKS - Development Tools Edition is a collection of Visual Studio templates, Server Explorer extensions, and tools providing accelerated SharePoint 2010 and 2013 development. But, unfortunately, it's available only for Visual Studio 2012 and 2013 and not for Visual Studio 2015.

There is a workaround through which you can use CKS - Dev in Visual Studio 2015 as well.
The CKSDev should already be installed on the system in any Visual Studio version; otherwise, you will not get the folder required.

Follow the steps and enjoy the power of CKS - Dev.

  1. Open the window explorer and go to - \Local\Microsoft\VisualStudio\12.0\Extensions.
  2. Find the folder containing CKSDev - on my machine it was called yqxmnzzs.3tk.
  3. Copy the folder.
  4. Open the window explorer and go to - \Local\Microsoft\VisualStudio\14.0\Extensions.
  5. Paste the copied folder here.
  6. Start Visual Studio 2015 (with Office Developer Tools).
  7. Click Tools > Extensions and Updates.
  8. Find CKSDev and click "Enable". It is disabled by default..
  9. Restart Visual Studio 2015.

I hope this will help; do write your comments and suggestions.


🚀 "Happy Coding" 🚀

Saturday 28 November 2015

"Views Likes Comments" in SharePoint 2010.


A month ago, I got a task to design functionality that provides Facebook features such as views, likes, and comments in SharePoint 2010 for our intranet portal.

In SharePoint 2010, there is no way to get such functionality out-of-the-box, but SharePoint 2013 provides it out-of-the-box.

I have created a simple SharePoint webpart to implement the functionality.

Steps to implement are:
1. Create a farm solution and add a web part.



2. Add the feature event receiver, constants, and CustomList file, and add the required images under the mapped image folder. The structure should be similar to that shown below (use standard naming conventions).


3. On the FeatureActivated method, call CustomList methods to create a list and add site columns to it.

4. Add a constants entry in the constants.cs.

5. In the webpart, add child controls for views, comments, and likes. And on page load event query the view count, comment count, comments, and like count from the lists..

6. Deploy the solution and activate the feature.


7. Edit the page and add a webpart to it as given below.


8. The page will look like this with a view count.

9. Click on "Like" to like the page. Here I have used the REST API of SharePoint 2010 using a list.svc you can find it in "like-newsArticle.js". There is a feature of like and unlike, and accordingly, the icon will change.


10. Then an entry will be added to the list - "Record Data List".


11. Click on the comments icon, then a div will slide down to enter, and press the comment button again. A REST query is used to save the data.


12. The comments entry will be saved into the list - "RecordDataCommentsList".


13. The final output is like this:


14. You can also edit the comments.


The views count is done on a per-user, per-page, per-day basis. If you refresh the page, the count will not increase.
The "Approve", "Edit" and "Reply" methods are now not implemented, but you can extend the functionality according to your use and needs.

I have uploaded the latest Visual Studio 2010 solution Likes.Comments.Views.REC.7z


🚀 "Happy Coding" 🚀