Extend the Newsfeed Functionality of SharePoint 2013

Requirement: Provide ability to “one-click” post content to newsfeed or site feeds that users have access to.

postbutton

Solution:

  • SharePoint Provider-Hosted App
    • default.aspx – a custom page to allow users entering content and post to their newsfeed or site feed.
    • postnewsfeed
    • Custom Action – add “Post” to ECB menu of target lists
      <?xml version="1.0" encoding="utf-8"?>
      <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
       <CustomAction Id="c72a6c2f-b61e-41bc-90ec-4d7000aa4a64.PostToNewsfeed"
                     RegistrationType="List"
                     RegistrationId="100"
                     Location="EditControlBlock"
                     Sequence="20"
                     Title="$Resources:profilebrowserscriptres,PostCommandText"
                     HostWebDialog="true"
                     HostWebDialogHeight="420"
                     HostWebDialogWidth="500">
      
          <UrlAction Url="~remoteAppUrl/Pages/Default.aspx?{StandardTokens}&amp;SPListItemId={ItemId}&amp;SPListId={ListId}" />
       </CustomAction>
  • JQuery UI AutoComplete
    • Generating mention and hashtags via JSOMmention
/// <summary>
/// WebMethod to search people for metion
/// </summary>
/// <param name="spHostUrl"></param>
/// <param name="term"></param>
/// <returns>People Search result in an array contains AccountName and PreferredName</returns>
[WebMethod]
[System.Web.Script.Services.ScriptMethod]
public static string[] SearchPeople(string spHostUrl, string term)
{

   List<string> searchResults = new List<string>();

   try
   {  
     using (var ctx = new ClientContext(spHostUrl))
     {
       if (ctx != null && term != null)
       {
         KeywordQuery query = new KeywordQuery(ctx);
         query.QueryText = "(PreferredName:" + term + "* AND LastName:" + term + "*)";
         query.SourceId = new Guid("B09A7990-05EA-4AF9-81EF-EDFAB16C4E31"); // this is the People result source
         query.RowLimit = 10;
         query.TrimDuplicates = true;
         query.EnableSorting = true;
         query.SelectProperties.Add("AccountName");
         query.SelectProperties.Add("PreferredName");
         query.SortList.Add("LastName", Microsoft.SharePoint.Client.Search.Query.SortDirection.Ascending);
 
         SearchExecutor searchExecutor = new SearchExecutor(ctx);

         ClientResult<ResultTableCollection> results = searchExecutor.ExecuteQuery(query);
 
         ctx.ExecuteQuery();

         #region loop through results
         if (results.Value.Count > 0)
         { 
           for (int i = 0; i < results.Value[0].ResultRows.Count(); i++)
           {
             var resultRow = results.Value[0].ResultRows.ElementAt(i);
             string name = resultRow["PreferredName"].ToString();
             string accountName = resultRow["AccountName"].ToString();
             searchResults.Add(name + "|" + accountName);
           }
         }
         #endregion
       }
     }
   }
   catch (Exception ex)
   {
     Logger.Log(ex);
   }

   return searchResults.ToArray();
}
/// <summary>
/// WCF Service method to retrieve Hashtags
/// </summary>
/// <param name="spHostUrl"></param>
/// <param name="term"></param>
/// <returns></returns>

public string[] SearchHashTag(string spHostUrl, string term)
{
   List<string> searchResults = new List<string>();
   try
   {
     using (var ctx = TokenHelper.GetS2SClientContextWithWindowsIdentity(new Uri(spHostUrl), null))
     {
       if (ctx != null && term != null)
       {
         TaxonomySession taxonomySession = TaxonomySession.GetTaxonomySession(ctx);
         TermStore termStore = taxonomySession.GetDefaultSiteCollectionTermStore();
         TermSetCollection termSetCollection = termStore.GetTermSetsByName("Hashtags", 1033);
         TermSet termSet = termSetCollection.GetByName("Hashtags");

         TermCollection termCollection = termSet.GetAllTerms();
         ctx.Load(termCollection);
         ctx.ExecuteQuery();

         #region loop through results
         if (termCollection.Count > 0)
         {
           var terms = termCollection.GetEnumerator();
           while (terms.MoveNext())
           {
             searchResults.Add(terms.Current.Name);
           }
         }
         #endregion
       }
     }

   }
   catch(Exception ex)
   {
     Logger.Log(ex);
   }

   return searchResults.ToArray();
}
Advertisements

Add Site Setting and Site Action from CSOM

Cited: FTC to CAM – Custom actions and property bag entries from SP App

// Add site settings link
UserCustomAction siteSettingLink = clientContext.Web.UserCustomActions.Add();
siteSettingLink.Group = "SiteTasks";
siteSettingLink.Location = "Microsoft.SharePoint.SiteSettings";
siteSettingLink.Name = "Sample_CustomSiteSetting";
siteSettingLink.Sequence = 1000;
siteSettingLink.Url = string.Format(DeployManager.appUrl, clientContext.Url);
siteSettingLink.Title = "Modify Site Metadata";
siteSettingLink.Update();
clientContext.ExecuteQuery();


// Add site actions link
UserCustomAction siteAction = clientContext.Web.UserCustomActions.Add();
siteAction.Group = "SiteActions";
siteAction.Location = "Microsoft.SharePoint.StandardMenu";
siteAction.Name = "Sample_CustomAction";
siteAction.Sequence = 1000;
siteAction.Url = string.Format(DeployManager.appUrl, clientContext.Url); ;
siteAction.Title = "Modify Site Metadata";
siteAction.Update();
clientContext.ExecuteQuery();

Make Property Bags Searchable in SharePoint 2013

A new cool thing in SharePoint 2013 is adding properties to a property bag and getting them indexed and searchable. SharePoint 2013 makes this possible, not only for webs, but even all the way down to an SPList.

Here is the PowerShell you can use to create an “Searchable” property in an SPWeb property bag:

$web = Get-SPWeb http://mytest.com
$web.AllProperties["Searchable"] = "Yes"
$web.IndexedPropertyKeys.Add("Searchable")
$web.Update()

Here is another exmample for adding an indexable property to a list:

$list = $web.Lists["Announcements"]
$folder = $list.RootFolder
$folder.Properties["ListSearchable"] = "Cool"
$folder.Update()
$list.IndexedRootFolderPropertyKeys.Add("ListSearchable")
$list.Update()

Then, you just need a Full or incremental crawl.  Make sure the crawled property is created in the search service application. Create Managed Property that maps to the crawled property.

Note:  The IndexedPropertyKey value is also saved in AllProperties using key (vti_indexedpropertykeys).  The value is base 64 encoded so if you want to update it, you’ll need to decode from and encode to base 64 string. However, this SPWeb.IndexedPropertyKeys property is not available for CSOM. Vesa provides an alternative solution in his post FTC to CAM – Setting indexed property bag keys using CSOM

// Used to convert the list of property keys is required format for listing keys to be index
public static string GetEncodedValueForSearchIndexProperty(List keys)
{
    StringBuilder stringBuilder = new StringBuilder();
    foreach (string current in keys)
    {
        stringBuilder.Append(Convert.ToBase64String(Encoding.Unicode.GetBytes(current)));
        stringBuilder.Append('|');
    }
    return stringBuilder.ToString();
}
// Decode the IndexedPropertyKeys value so it's readable
public static List GetDecodeValueForSearchIndexProperty(string encodedValue)
{
     List decodedKeys = new List();
     string[] keys = encodedValue.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            
     foreach (string current in keys)
     {
          decodedKeys.Add(Encoding.Unicode.GetString(Convert.FromBase64String(current)));
     }

     return decodedKeys;
}

Update Property Bag – Add / Remove

string indexedPropertyKeys = web.AllProperties["vti_indexedpropertykeys"].ToString();

List decodedKeys = GetDecodeValueForSearchIndexProperty(indexedPropertyKeys);
decodedKeys.Add(key);     //decodedKeys.Remove(key);

web.AllProperties["vti_indexedpropertykeys"] = GetEncodedValueForSearchIndexProperty(decodedKeys);
web.Update();
clientContext.ExecuteQuery();

SharePoint 2013 App Details Page Error

Cited: http://www.sharepointfire.com/MyBlog/2014/06/error-when-opening-details-view-for-sharepoint-2013-apps.

use the following Powershell command to create new database for Usage and Health Logging Application Service

Get-SPUsageApplication | Set-SPUsageApplication -DatabaseServer “********” -DatabaseName “WSS_UsageApplication2″

Configure continuous crawl interval using Powershell in SharePoint 2013

A continuous crawl crawls content that was added, changed, or deleted since the last crawl. Unlike an incremental crawl, which starts at a particular time and repeats regularly at specified times after that, a continuous crawl automatically starts at predefined time intervals. The default interval for continuous crawls is every 15 minutes.
Update: Continuous Crawl doesn’t work for People search (users update their profile values). The workaround is to create a separate content source for People and set up Incremental Crawl every 15 – 30 minutes.
To Enable Continuous Crawls:
$ssa = Get-SPEnterpriseSearchServiceApplication
$contentsource = Get-SPEnterpriseSearchCrawlContentSource -SearchApplication $ssa -Identity "Local SharePoint sites"
Set-SPEnterpriseSearchCrawlContentSource -Identity $contentsource -EnableContinuousCrawls $True
To Disable Continuous Crawls:
$ssa = Get-SPEnterpriseSearchServiceApplication
$contentsource = Get-SPEnterpriseSearchCrawlContentSource -SearchApplication $ssa -Identity "Local SharePoint sites"
Set-SPEnterpriseSearchCrawlContentSource -Identity $contentsource -EnableContinuousCrawls $False
To get the continuous crawl interval:
############# Get the Continuous Crawl Interval in SharePoint 2013 ############# 
$ssa = Get-SPEnterpriseSearchServiceApplication
$ssa.GetProperty("ContinuousCrawlInterval")

To set the continuous crawl interval:
############# Set the Continuous Crawl Interval in SharePoint 2013 ############# 
$ssa = Get-SPEnterpriseSearchServiceApplication
$ssa.GetProperty("ContinuousCrawlInterval",20)

SharePoint 2013 Presence Indicator not working

Currently, we encountered an issue, which the presence indicator doesn’t work as expected.

Resolution:

  1. Verify if “Person Name Actions and Presence Settings” has been enabled
    • Go to Central Admin -> Manage Web Applications -> Select your web app -> General Settings
  2. Verify if the users have SIP Address imported from AD
  3. Verify if you have install Office in your testing computer, and the name.dll is in place.

It might take a while to reflect the changes. Also, if you are using Office 2003, you might have to reinstall the Office Web Components. Furthermore, disable / enable the “Person Name Actions and Presence Settings” may be required.

Appendix

Cited: SharePoint 2013 – Office Communicator 2007 R2 Presence Indicator

How SharePoint Presence works:

SharePoint presence status is displayed through a client-side setting by using a dynamic link library called name.dll. This file is installed with Microsoft Office 2010, Office 2007 and Office 2003 and is located within the Office installation directory for e.g., (C:\Program Files\Microsoft Office\Office 14). The name.dll file is an ActiveX® control which gets loaded within the Internet Explorer that calls the Lync \ Office Communicator API directly to request and display presence status within SharePoint site collections.

Presence is enabled in SharePoint by default; there are no configuration steps for the SharePoint administrator to perform. Each SharePoint page includes Microsoft JScript® code, which enables presence for that site. JScript uses name.dll to call the Lync \ Office Communicator API and pull presence for users names who appear on the site. JScript uses the users’ SIP URI to pull presence for names that are listed on the site.