Sunday 20 November 2016

SharePoint 2013 Apps Environment Configuration.

Setting up a SharePoint development environment has always been challenging, and with the new Apps model in SharePoint 2013, there are even more options and requirements.


First, setup and configure DNS on a Windows server. (http://www.tomsitpro.com/articles/configure-dns-windows-server-2012,2-793.html)

The steps to configure the SharePoint 2013 apps environment.

a) Create Apps Forward Lookup Zone

SharePoint 2013 Apps have their own isolated URLs, which are separate from the URLs of the sites where the app is being deployed and where the app is being used. In order to provide isolation, apps should run in their own domain instead of in the same domain name as your farm. Using a different domain name for apps helps prevent cross-site scripting between apps and SharePoint sites.

Microsoft recommends that the new domain name should NOT be a subdomain of the domain that hosts the SharePoint sites.

1. Open DNS Manager and run it as an administrator from the Start screen.

2. In DNS Manager, right-click Forward Lookup Zones, then the New Zone context menu option.



3. Click Next on the New Zone Wizard dialogue box.

4. On the Zone Type step, select the Primary Zone and click the Next button.

5. On the Zone Name step, enter the app domain name (like spappsdeveloper.com), and click the Next button.



6. On the Dynamic Update step, select the type of dynamic updates you want to allow. Here, with the Allow both nonsecure and secure dynamic updates option selected, click the Next button.





b) Link the SharePoint App Domain to the SharePoint Server

Now DNS will forward all the requests from spappsdeveloper.com to the SharePoint server hosting the apps.

1. In DNS Manager, under Forward Lookup Zones, right-click the newly created zone or app domain (spappsdeveloper.com). Click New Alias (CNAME) from the context menu option.

2. On the New Resource Record dialogue, enter * as the Alias name.

3. For a Fully qualified domain name (FQDN) for target host box, select the Browse button.

4. On the Browse dialogue, select server > Forward Lookup Zones > SharePoint sites host domain > select the record that points to the server that hosts the SharePoint site. Ensure Hosts and Aliases (A and CNAME Records) is selected as the Record types, and click the OK button.

5. Click OK to close the dialogue box.


c) Creating the Subscription Settings and App

 Management Service Applications

1. In SharePoint Central Administration, click the Manage service applications option.

2. Click the New button, then the App Management Service menu item.

3. On the New App Management Service Application dialogue, enter “App Management Service” as the Service Application Name. Select the Use existing application pool option, and then select AppManagementService Pool from the drop-down. Ensure the Create App Management Service Application checkbox is checked. Click the OK button.

4. Or you can use the PowerShell command to create an App Management Service Application.

# get the service application pool
$serviceAppPool = Get-SPServiceApplicationPool -Identity "AppManagementService Pool"

# create app management service application and save its response in variable
$appMngmntSvc = New-SPAppManagementServiceApplication -ApplicationPool
$serviceAppPool -Name "App Management Service" -DatabaseName "App Management Service DB"

# create app management service application proxy and mapping it to service application
New-SPAppManagementServiceApplicationProxy -ServiceApplication $appMngmntSvc

5. Once returned to the list of service applications, ensure both App Management Service Applications are started.

6. Open the SharePoint 2013 Management Shell as an administrator and run.

7. Start the SPAdminV4 and SPTimerV4 service applications.

net start SPAdminV4

net start SPTimerV4

8. Set the domain used to host apps to the new zone created above. It can be set by PowerShell as "done" or via central administration. It can also be done later at the Configure App URL step (see below).

# App Domain Name.
Set-SPAppDomain "spappsdeveloper.com"


9. Start the AppManagementServiceInstance and SPSubscriptionSettingsServiceInstance service instances.

# Start the App Management & Subscription SharePoint services.
Get-SPServiceInstance | where{$_.GetType().Name -eq "AppManagementServiceInstance" -or $_.GetType().Name -eq "SPSubscriptionSettingsServiceInstance"} | Start-SPServiceInstance


10. Ensure the AppManagementServiceInstance and SPSubscriptionSettingsServiceInstance service instances are Online.

# Check the "Online" status of the App Management & Subscription SharePoint services.
Get-SPServiceInstance | where{$_.GetType().Name -eq "AppManagementServiceInstance" -or $_.GetType().Name -eq "SPSubscriptionSettingsServiceInstance" }


11. Create the SharePoint Subscription Service.

# get the service application pool
$account = Get-SPManagedAccount "WIN-EODPTE6TSSE\sppool"
$serviceAppPool = New-SPServiceApplicationPool -Name "SettingsServiceAppPool" -Account $account

$serviceAppPool = Get-SPServiceApplicationPool -Identity "AppManagementService Pool"

# create subscription settings service application and save its response in variable
$appSubSvc = New-SPSubscriptionSettingsServiceApplication –ApplicationPool
$appPoolSubSvc -Name "SettingsServiceApp" –DatabaseName "SPSubscriptionSettingsServiceDB"

# create subscription settings service application proxy and mapping it to service application
$proxySubSvc = New-SPSubscriptionSettingsServiceApplicationProxy –ServiceApplication $appSubSvc




d) Configure the App URL

Set the name for the site subscription.

1. Open Central Administration, click Apps, and then click Configure App URLs.

2. In the App Prefix box, type a name to use for the URL prefix for apps.

3. Or it can also be done by PowerShell.

# SharePoint App Subscription name.
Set-SPAppSiteSubscriptionName -Name "apps" -Confirm:$false



Create a new Developer Site site collection for local App deployment.
Now you're ready to deploy your SharePoint apps to your local SharePoint development environment.

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


🚀 "Happy Coding" 🚀

Saturday 5 November 2016

Object doesn't support property or method "attachEvent" in Internet Explorer 11.

I am getting this error on my SharePoint site, which breaks many functionality, like the list AddNew form UI, which is coming out distorted in IE. The error is due to the fact that "attachEvent" is a deprecated function. This error is occurring in the init.js file, which is provided out-of-the-box by SharePoint.

Google Chrome and Mozilla Firefox are handling this error, but IE is unable to do that.

Add the JavaScript code snippet to the JS file used in the master page or used globally.

<script language="javascript">
if (typeof browseris !== 'undefined') {
    browseris.ie = false;
}
</script>


🚀 "Happy Coding" 🚀

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" 🚀