Enable Likes or Rating on a SharePoint List programmatically using reflection

For some reasons there is no public property to enable likes or ratings on a list in SharePoint. Using reflection, this is still possible to do. Feel free to use this codesnippet for SharePoint 2013 on-premises.

private static void EnableLikes(SPList list)
{
    Assembly theAssembly = Assembly.Load("Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
    Type reputationHelperType = theAssembly.GetType("Microsoft.SharePoint.Portal.ReputationHelper", false, true);
    MethodInfo milf = reputationHelperType.GetMethod("EnableReputation", BindingFlags.Static | BindingFlags.NonPublic);
    milf.Invoke(reputationHelperType, new object[] {list, "Likes", false});
    list.Update();
}

You get the error “Method not found: ‘Void Microsoft.SharePoint.Client.Conten tTypeCreationInformation.set_Id(System.String)’”

Problem

You get the error message “Method not found: ‘Void Microsoft.SharePoint.Client.ContentTypeCreationInformation.set_Id(System.String)’” when trying to create content types using CSOM. You are specifying the ID of the content type(s) you are creating.

Reason

SharePoint 2013 CSOM API did not have support for specifying the ID of content types on creation when using the Microsoft.SharePoint.Client.ContentTypeCreationInformation method of creating content types before Service Pack 1.

SP1 for SharePoint 2013 includes various fixes as outlined in the post CHANGE: Service Pack 1 for Office 2013 and SharePoint 2013.

One of the fixes includes:

SharePoint 2817429​ Developers are unable to specify a Content Type ID when creating Content Types in the client object model.

Which I’m pretty sure refers to the changes in the API added with SP1.

Note: SharePoint 2010 did not have the possiblity to specify ID of content types either.

Solution

Most likely the problem is that the environment does not have SP1. Update your SharePoint environments to Service Pack 1, or find another means of creating content types.

A less likely solution: The code is picking up the dlls from SharePoint 2010 which doesn’t have the function either. In this case it probably means that you’re installing on an environment with the wrong version of SharePoint.

Adding link icons to your anchor tags in SharePoint

ToolsAndResources

This is a simple trick that gives a bit of context to the end users of a SharePoint site. This technique is not new, and has been used by various web pages for a long time. It can be easily utilized in SharePoint context as well. In rich text like wiki pages and article pages you will often find a few links to different types of content. By default you don’t really know where the links will take you, unless you take a closer look at the browser’s preview of the URL on mouseover. It would be much easier to know where the links lead you (astray) if the links are being prepended by an icon, letting you recognize which content the links are pointing to.

Types of link icons

In my recent project we created this functionality for a few types of content, as you see from the example above. The types of content are internal link, external link, link to document, link to image, link to a user’s mysite profile and a mailto-link. We’ve also created a link icon for “the magnificent tool”. This is a selection of pages living in SharePoint within a specific site. Even though the links are to internal pages, we want them to have their own style, as “the magnificent tool” should be recognized beyond a normal internal link. Of course you could add as many or few types of link icons as you’d like.

How does it work?

I have defined a method Pzl.Core.AddLinkIconsToAnchorTags which accepts a jQuery selector for the anchor tags that should get link icons. This way the method can be reused on different kind of pages, for example wiki pages and article pages. The method is called when the DOM is ready, and it should also just be called when the page is in display mode, not in edit mode (or else it will insert into the markup of your content, which you really don’t want).

Pzl.Core.AddLinkIconsToAnchorTags('.pzl-wiki-page .page-content a, .pzl-wiki-page .right-wp-zone-col .ms-rtestate-field a');

The method itself looks like the following piece of javascript. As is apparent from the comments, a check is made for making sure the page is not in edit mode. A check is also made for each anchor tag in the selector, checking if it’s not a anchor tag in a SharePoint web part. This is because we don’t want to  apply link icons to out of the box webparts, because this will make things look really weird.

Pzl.Core.AddLinkIconsToAnchorTags = function (selector) { 
    //Don't apply custom link icons in edit mode. Will get messy 
    if (!Pzl.Core.IsEditMode()) { 
        //Filter out anchor tags that are not supposed to have links and anchor tags in webparts 
        jQuery(selector).not('.noUrlIcons,.ms-listlink').filter(':noparents(.ms-wpContentDivSpace,.ms-webpart-titleText)').each(function () { Pzl.Core.CheckLinkAndInsertIcon(this); }); 
    } 
};

Moving closer to where the magic happens, let’s take a look at the Pzl.Core.CheckLinkAndInsertIcon function, which is actually adding the icon to the link.

Pzl.Core.CheckLinkAndInsertIcon = function (anchorElement) {
    var anchor = jQuery(anchorElement);
    //Apply link icon if the anchor tag isn't already an image and doesn't contain an image
    if (!anchor.hasClass('pzl-icon-container') && anchor.has('img').length <= 0) {
        var url = anchor.attr('href');
        if (url != undefined && !url.startsWith('javascript')) {
            var linkObject = Pzl.Core.GetIconClassForLinkUrl(url);

            var linkIcon = "<span class='pzl-icon pzl-icon-" + linkObject.className + "' title='" + linkObject.toolTip + "'></span>";

            if (linkObject.external) anchor.attr('target', '_blank');

            anchor.addClass('pzl-icon-container inline-icon');
            anchor.prepend(linkIcon);
        }
    }
};

There are a few things to note here. First, pzl-icon-container is the css-class we’re adding to the anchor tag. We’re checking, just in case, that this class is not already present for the anchor element (we don’t want to add icons twice). We are also checking that the anchor doesn’t include an image, as we don’t want to add icons if we have image links – that would look bad. Moving on, we’re grabbing the actual href (url) of the anchor tag and checking that the url isn’t undefined or a javascript link.

Get to the point already!

Pzl.Core.GetIconClassForLinkUrl(url) is getting the link icon for that url, based on a set of rules. Basically it returns us an object with a className-property and a toolTip-property, as well as a property saying whether the link is external or not. The className-property is used for styling the icon, i.e. setting the right icon, and the toolTip-property is used as a tooltip for when the user is hovering the link icon.

We generate the span-markup for the link icon and add target=_blank to the anchor if it’s an external link. Finally, we’re adding the .pzl-icon-container and .inline-icon CSS-classes to the anchor tag, and prepending it with the link icon markup.

Deciding on the type of link

The logic for deciding on the type of link is a mix of regular expressions tests and indexOf-checks. I guess i could make it all regex tests. The checks are quite simple, and it’s easy to add new ones. As you see, the if’s higher up will “overrule” the if’s further down, meaning that e.g. a profile page link icon will take effect over an external url link icon and an external link icon will take effect over a document link icon.

Pzl.Core.GetIconClassForLinkUrl = function (url) {
    var toolTip = "Internal link";
    var className = "internal";
    var external = false;

    url = url.toLowerCase();
    var currentHost = location.protocol + '//' + location.hostname;
    if (url.indexOf('/person.aspx?accountname=') >= 0) {
        className = "profile";
        toolTip = "Profile page";
    }
    else if (/^(f|ht)tps?:\/\//i.test(url) && url.substring(0, currentHost.length) != currentHost) {
        className = "external";
        toolTip = "External link";
        external = true;
    }
    else if (/\.(gif|jpg|jpeg|tiff|png|bmp)$/i.test(url)) {
        className = "image";
        toolTip = "Image";
    }
    else if (/\.(doc|docx|xls|xlsx|pdf|odt|rtf|txt|ppt|pptx)$/i.test(url)) {
        className = "document";
        toolTip = "Document";
    }
    else if (/^(mailto:)/i.test(url)) {
        className = "mailto";
        toolTip = "E-mail";
    }
    else if (url.indexOf('/magnificent/') >= 0) {
        className = "magnificent";
        toolTip = "Magnificent tool";
    }
    var linkObject = {
        className: className,
        toolTip: toolTip,
        external: external
    };
    return linkObject;
};

CSS and making it look good

It would be pointless to post all the CSS for all the types of link icons we use here, but I’m going to share an example. We are using a sprite for icons, and hence all icons share background-image, and just use different positions. Here you’ll also see the reason why we’re using the .pzl-icon-container, to get a nice hover effect on the icons when the link is being hovered.

.pzl-icon {
    background-image: url('/_layouts/15/Pzl.Core/img/GFX-Sprite.png?rev=20140526');
    display: inline-block;
    width: 12px;
    height: 12px;
}
.pzl-icon-container.inline-icon .pzl-icon{
    padding-left: 3px;
    margin-bottom: -1px;
}
.pzl-icon-internal {
    background-position: 90px -90px;
    height: 13px;
}

    .pzl-icon-internal:hover {
        background-position: 60px -90px;
        height: 13px;
    }

.pzl-icon-container:hover .pzl-icon-internal {
    background-position: 60px -90px;
}

The boring part if you want to roll your own

You’ll have to create your own graphics sprite or handle the icons in your own way for this to work. It would be smoother if this worked without dependency on creating custom icons. The benefit of doing that though, is that you can make the icons with style and form consistent with the rest of your SharePoint site.

The final result

An example of how this is in play is seen in the following picture, here the user is hovering over the magnificent tool-link.

ToolsAndResourcesHover

Improving contextual Search in SharePoint 2013

Contextual search in SharePoint should be easy and intuitive. Apparently it is not, and I’ve investigated this a bit in order to come up with a configurable solution using out of the box functionality.

The challenge is simple: I want to search in the context of the site I’m browsing. At the same time I want to use a specific search center I have branded and configured. Easier said than done.

My research have found that I cannot get the contextual search to work using the standard search settings without being sent to the out of the box / _layouts/15/osssearchresults.aspx.  This is achieved by selecting the “Turn on the drop-down menu inside the search box and use the first Search Navigation node at the destination results page”. The dropdown, however, doesn’t show up anyway.

In this post I’m going to explain how I solved this.

The u-parameter and the {contexturl}-token

The wanted result is acheived by setting the search result page to the search results page of your search center. The u-parameter lets the search know that you want to search only beneath the url which is the value of u. The {contexturl} is actually translated to the current site’s url when you perform a search, yielding the result you are after.

ContextualSearch

Searching within the site’s context, now what?

Performing a search from the search field in the masterpage will now search only in content of the site you were standing at. But the only way of seeing that is by investigating the URL, where you’ll see the the u-parameter with the value of your site’s url.url

Not very user friendly, huh? What if the user doesn’t know he’s just searching within the context of the site he searched from? What if the user wants to search everything again?

Notification and a way out

The way I’ve solved this is through javascript on the search results page which checks for the u-parameter and adds a message to the page if it is present. This could be added in a script snippet web part or added to the masterpage directly. In my case I had several search result pages I wanted this to happen on, and I found it easier to just add the javascript to the masterpage instead of adding it to each results page. The result looks like the following.

searchcontextresults

From the masterpage I’m calling Pzl.Search.AddContextualMessage() which does the work.

Pzl.Search.AddContextualMessage = function () {
    //Check if the current page is the results page and the u-parameter is present
    var url = decodeURIComponent(window.location.href.toLowerCase());
    if (url.indexOf('results.aspx') &gt; -1 &amp;&amp; (url.indexOf('?u=') &gt; -1 || url.indexOf('&amp;u=') &gt; -1)) {
        //Get the site the search initiated from
        var contextualUrl = getParameterByName('u').toLowerCase();

        //Create the 'search in everything' URL but removing unnecessary parameters
        var searchAllUrl = url.replace('&amp;u=' + contextualUrl, '').replace('?u=' + contextualUrl + '&amp;', '?').replace('?u=' + contextualUrl, '?');
        //Create the contextual search html message
        var contextMessage = '&lt;div class=&quot;contextualMsg ms-textLarge ms-srch-result-noResultsTitle&quot;&gt;Du s&#248;ker n&#229; innenfor omr&#229;det &lt;a href=&quot;' + contextualUrl + '&quot;&gt;' + contextualUrl + '&lt;/a&gt;. Du kan &lt;a href=&quot;' + searchAllUrl + '&quot;&gt;utvide s&#248;ket&lt;/a&gt; for &#229; s&#248;ke i alt.&lt;/div&gt;';
        
        //Wait until the search results are returned as they are loaded async (this could be done in a smoother way)
        setTimeout(function () {
            if (jQuery('.ms-srch-result .contextualMsg').length &lt;= 0) {
                jQuery('.ms-srch-result').prepend(contextMessage);
            }
        }, 500);
    }
};

The javascript should explain itself (especially with the inline comments, lol), but basically it checks if it’s a contextual search, grabs the contextual URL, creates the “search all”-URL, creates the contextual message-HTML and finally adds it to the top of the search results.

I thought I was ready to wrap up this functionality, but then I discovered an interesting feature/bug. On successive searches, the message is not displayed even though we’re still searching with the u-parameter. What’s up with that?

Adding the message after successive searches

It was apparent that my javascript did not run on successive searches. The reason is simple: SharePoint loads new search results asynchronously, meaning that my javascript method would not be called when the user searches for something else. I investigated the search input field and found that onkeypress the following javascript is called.

if (Srch.U.isEnterKey(String.fromCharCode(event.keyCode))) { $getClientControl(this).search(this.value);return Srch.U.cancelEvent(event); }

As you see, the enter keypressevent is canceled because SharePoint wants to load the results its own way – fair enough and we all like fewer page loads, right?

To work around this issue I hooked into the Srch.U.cancelEvent function and called my Pzl.Search.AddContextualMessage function from there after the event is canceled. I do this by overwriting the cancelEvent-function in the method Pzl.Search.QueueAddContextualMessageOnNewSearches(). This method looks like the following

Pzl.Search.QueueAddContextualMessageOnNewSearches = function() {
    var orginalCancelSearchEvent = Srch.U.cancelEvent;

    Srch.U.cancelEvent = function (event) {
        orginalCancelSearchEvent(event);
        Pzl.Search.AddContextualMessage();
    };
};

That sealed the deal. Contextual search in SharePoint is now a little bit better in my opinion.

PS: My colleague Trond Øivind Eriksen suggested that the logic for adding the contextual message could also be added as part of the Control display template of the search results webparts, which I think is a good idea. Then you shouldn’t have to worry about how SharePoint load new searches, and can kill the QueueAddContextualMessageOnNewSearches function.

Client-side auto-generated table of contents for text-heavy pages in SharePoint

toc

In the project I’m currently working on we are creating a solution in SharePoint 2013 for content that previously has been in word documents. The content is very text-focused, and consists almost entirely of headings and text blocks. We are using enterprise wiki as the new home for the content in SharePoint.

One of the “key features” of the documents (and Word-documents in general) are the table of contents, where you get quick links to the sections you are interested in. This was a feature we wanted to have in SharePoint as well, and what’s better than having the table of contents auto-generated to avoid ToC’s that needs to be manually updated and often go stale.

The solution is pretty simple. In the page layout we’re using, I’ve added a header and an empty unordered list.

<h1 class="pzl-toc-header">Table of Contents</h1>
<ul class="pzl-toc-content"></ul>

The Javascript

When the DOM is ready, we’re calling a nice little function that iterates over all the h1 and h2 tags in the document, adds anchor bookmarks to the headings and adds the headings to the table of contents. To make it somewhat more reusable, the function takes the selector of the table of contents list element and the selector of the section we are looking for headers in.

var Pzl = Pzl || {};
Pzl.Wiki = Pzl.Wiki || {};

Pzl.Wiki.GenerateTableOfContents = function (jQueryTocListSelector, jQueryContentSectionSelector) {
    var tocId = 0;
    jQuery(jQueryContentSectionSelector + ' h1,' + jQueryContentSectionSelector + ' h2').each(function () {
        var heading = jQuery(this);
        var headingText = heading.text();
        //Replace any non-alphanumeric characters
        var headingId = headingText.replace(/\W/g, '') + '-' + tocId++;
        var cssLevelClass = heading.is('h1') ? "level1" : "level2";
        
        //Add the id to the header-element
        heading.attr('id', headingId);
        //Add the list item to the ToC
        jQuery(jQueryTocListSelector).append('<li class="' + cssLevelClass + '"><a href="#' + headingId + '">' + headingText + '</a></li>');
    });
};

The code should be pretty self-explanatory, but there’s a couple of things to note.

  • I’m removing all non-alphanumeric characters (line 7) because the element’s id’s needs to be on a valid format.
  • I’m also adding a level1 or level2 css class (line 8) in order to style the ToC afterwards (I obviously want the level 2 items to have more left-padding than level  1 items).

In my page layout where I want the table of contents to be generated I’m calling this function in the following way:

<script type="text/javascript">
    jQuery(document).ready(function () {
        Pzl.Wiki.GenerateTableOfContents('ul.pzl-toc-content', '.pzl-radviser .page-content .ms-rtestate-field');
    });
</script>

The looks of it

The CSS for the table of contents is super-simple and looks like this

ul.pzl-toc-content {
    list-style: none;
    font-size: 16px;
    padding: 0;
    margin: 0 0 20px 0;
}

Which creates a table of contents that looks similar to the image in the top of this post (there are some other css styles defined which are not relevant).

Back to the top

When the user gets to the bottom of the page, he most likely would love to go the top again. Hence I’m adding a simple bookmark-link to go to the top of the page after the content section.

<a href="#top">To the top</a>

That’s basically it! The solution would work fine as it is now.

Bonus: Smoothscrolling

Wouldn’t it be great if the browser scrolled smoothly down to the sections we cared about instead of skipping down instantly? This way it’s more clear to the user what happens at the click of the button.

Smoothscrolling in SharePoint apparently wasn’t that easy . I’m going to spare you the details, but the final solution I ended up with was the one discussed and improved over at the technet forums. I did some minor modifications to the script discussed there, and came up with the following function which I’m calling after generating the Table of Contents.

Pzl.Wiki.ApplySmoothScrollingOfTOC = function() {
    jQuery('#s4-workspace .page-content a[href^="#"]').on('click', function (e) {
        var elemEvt, elemDst, hashVal;
        e.preventDefault();

        elemEvt = jQuery(this);
        hashVal = elemEvt.attr('href').match('^#+(.*)')[1];

        // Look for id, if not found then a[name]
        elemDst = jQuery('#' + hashVal).eq(0);

        // Still not found, either implicit 'top' or don't scroll at all
        if (elemDst.size() == 0) {
            elemDst = hashVal == 'top' ? 0 : null;
        }

        if (elemDst !== null) {
            jQuery("#s4-workspace").scrollTo(elemDst, {
                axis: 'y',
                duration: 300,
                onAfter: function () {
                    window.location.hash = hashVal;
                }
            });
        }
    });
};

There are a couple of things to note here

  • I’m using the scrollTo jQuery plugin which needs to be references somewhere on the page.
  • After the scrolling has happened I still want the #bookmark in the URL, in order to allow directly linking to sections as well as back and forth functionality in the browser. That’s why I’m setting window.location.hash = hashVal.

Good luck!