Showing posts with label SharePoint 2013. Show all posts
Showing posts with label SharePoint 2013. Show all posts

Apr 19, 2016

Removing reusable SPD workflows completely

Recently I have been working on a set of different custom SharePoint Designer workflows for different customers. Sometimes the process is not so straightforward that you get things right on the first try and sometimes the errors result in the need to actually remove some trial workflows from the system. 

Reusable workflows are slightly more tricky to remove from the SharePoint UI than the list workflows. In SharePoint Designer, the main way of managing workflows is in the Workflows section. This may well tempt you to remove the workflow simply from that list and be done with it. This, however, will not remove it from the SharePoint UI, and thus an old depracated workflow is still available for users. 



The right place to remove the workflow from is to open the All files section, find the Workflows directory there and delete the whole workflow directory. 

But what if you already deleted the workflow in the Workflows list and now you have no directory for it anymore but it still shows up in the SP UI? This is what I was researching this morning. The place to go in that case is - surprise surprise! - the _catalogs directory. There you will find a folder named wfpub. And in that wfpub folder, you still have the directories for even those workflows that were deleted from the Workflows section listing. Delete these directories with all their contents and you're good.


Mar 18, 2016

SharePoint Branding and the winds of change

Not that long ago, it was quite typical to create a major SharePoint UI solution for a SharePoint intranet. It would contain one or more Master Pages, CSS stylesheets, images, JavaScript, Page Layouts etc. The solution package was deployed to the server, the feature activated and automagically the intranet got its fancy custom UI and functionalities. A slight inconvenience though, that even the smallest change or improvement in any of the shipped files required updating the whole package.
It hasn’t been too long since I last was involved in such a big intranet solution. These days such projects are more of the exception than the rule. So, what’s changed?

One thing, or maybe I should say THE thing is Office 365. Cloud is becoming more and more popular and doesn’t even allow such huge full-trust solutions. Sandbox solutions are still an option, though according to Microsoft they are not the technology of the future – they already once declared them deprecated, but actually had to take it back pretty quickly. However, apps and other alternative ways of doing things (PowerShell, Azure powered functionalities etc.) are the modern and flexible way to build stuff on top of SharePoint, while keeping it separate from SharePoint.

I believe it’s not inaccurate to say that all the rest is just a byproduct of this. While more and more companies are switching to the cloud environments and Microsoft is developing “cloud first”, it is only natural for attitudes and ways to adapt to this change. Lightweight tweaking as opposed to heavy – especially with a heavy update process – custom software development sounds quite enticing.

Years ago, when SharePoint 2010 was launched and companies started to migrate from the old 2007 environment to the shiny new SharePoint in a fast pace, the game of the day was using OOB where ever possible and doing as little customization as possible. Mostly this still meant creating easily retractable solution packages for UI and custom web parts and services, distributed as features. SharePoint wasn’t re-coded anymore as it used to be in many cases with MOSS 2007; the solutions were built on top of SharePoint without touching the OOB stuff.

Then, along came SharePoint 2013. For quite some time the development and customization story was pretty same as with 2010, up until Online made its breakthrough and started to mold the models towards the more lightweight facelifts, apps, external services etc. The process of UI customization, branding, has shrunk from a heavy solution package to creating themes, some custom CSS and maybe a bit of JavaScript.

A color theme is a rather easy and quick way to apply the company branding colors to SharePoint. It already takes you a long way – the company colors are applied to the top suite nav, links, emphasis colors, headings etc. Creating a font scheme adds to this by implementing the company font – even a custom one – everywhere with one setting. What cannot be done by a theme is changing the placement and fancier styling of page elements.

The current default Master Pages seattle and oslo are built using div elements. This means that working with them – the placement, float, etc. – is pretty flexible and easy without needing to change the HTML of the Master Page, i.e. there is no need to create custom Master Pages simply to change how and where the elements are rendered on the page. There are limits to this, but you can do a lot with CSS.



Themes and an alternate CSS stylesheet are easy to implement in the UI of SharePoint, in the site settings, and they are inherited by any subsites with publishing feature activated. Implementing them to sites without publishing feature is a bit more work. Generally, this means pushing the inheritance from the top level each time a new site has been created.

JavaScript adds its own challenge to the equation. It is possible to globally inject JavaScript to all sites in a site collection using PowerShell. However, this should then be done for each new site too, when one is created. In single pages the Script Editor Web Part works nicely, but the global injection is always a bit of an issue. Mainly the options apart from the manual PowerShell injection are adding the script links to the Master Pages or creating an Azure powered service for provisioning the sites. 

The latter option is actually something to consider. Basically the idea is that people don’t create their sites manually but order them by creating a new item into an order list. A timer job polls the list and notices a new site order and launches a PowerShell script that then provisions the site with the exact settings, stylesheets, logos, Web Parts, lists, JavaScript links that are needed for each new site. 

Add responsive design to the equation. Currently SharePoint Online offers two view for mobile devices and the possibility to switch between them. One is the mobile list oriented view, the other one the full desktop view. The list oriented view works fine in team sites where all the content is mainly in lists, but publishing sites, the informative intranet pages, need to be viewed in full desktop experience on mobile devices too – in a responsive way so that it actually is readable.

Unfortunately, responsive SharePoint is still a bit of a everyone for their own – or has been, but I have just discovered that PnP has actually come forth with an awesome fully responsive solution that is purely CSS based! Previously the options have been e.g. bootstrap (heavy version), a responsive SharePoint solution from Codeplex (kinda heavy solution too); both of these require changes to the MasterPage. A bit of a no-no. 

Be as it may, responsive design is always more expensive for a customer than the basic design only. However, as the costs of branding have come down significantly with the lighter approach, it most definitely pays off to invest in responsive design, if not go mobile first. That’s where this world is headed anyway. 

[Originally published in Finnish in our company blog]

Mar 7, 2016

Tip: Fix List Issues After Migration 2010 -> 2013

Once again, faced with issue after issue. There was a SharePoint intranet that was migrated from SP2010 to SP2013. There was a list in the intranet, containing crucial information that should be easily searched. However, there was no list search available. Also, none of the columns were site columns. So I set out to create a search page for the list data. Everything runs cool if you just try to search with data in the title field (or author). However, there were some other fields including data to search wih.

Piece of cake, I thought to myself, disregarding the fact that most of the time the pieces of cake are tough to chew when it comes to SharePoint. I created some site columns and added them to the list, positioning them next to the original ones, with the intention to simply open the list in quick edit mode and copy values from list columns to site columns. Easy enough!

However, the list being a custom list originating in SP2010, the quick view was unavailable. I contemplated on exporting the data to Excel, modifying it there and creating a new list using that sheet. Before attempting that, I decided to google just a bit in hopes of finding a solution. And I did, hurray for Saludeen Rajack!

Since I had already tried most of the tricks in the blog post, in vain, I crossed my fingers and opened the list page for editing, opened list web properties and added clienttemplates.js to the JS Link property of the web part (at the very bottom of the Miscellaneous section).


And lo and behold! First of all, the list search box appeared at the top of the list and secondly, the quick edit button became active. 



Now I could proceed with my plans properly.

Dec 10, 2015

Tip: Targeting a specific SharePoint site with global CSS

Just a little css tip, that came out handy today. Generally, with a global css (say, an alternate style sheet that is deployed throughout the site collection), you don't even attempt targeting some specific site, but go with the global styles instead. Today, however, I was struggling to display the titlerow (logo and navigation) in the enterprise search center without having it become visible in the dialogs as well.

Some Microsoft logic it is, I guess, that the search center by default hides the site collection navigation using the same class as the dialogs, the .ms-dialogHidden. And as anyone working with SharePoint CSS knows, the sites have no IDs in the body tag so that you could e.g. tell your css to display the navigation in search site but not in the dialogs. None, except for the action attribute in the form tag.

I guess it never was as big of an issue before, since I never thought of it before, but indeed, in a site collection already populated with sites, you can target one specific site by using the attribute selector for form tag in your css rule, for the form action contains the relative address of the site in it + in SharePoint, the whole page is always inside of that form tag. So here's the rule for un-hiding the titlerow for this specific search center, just as an example:

form[action*="searchcenter"] #s4-titlerow.ms-dialogHidden {
    display: block !important;
}

And, yey, the dialogs remain untouched and furthermore, there is no need to impelemnt page level css on each and every search center page.

Nov 4, 2015

Choosing between publishing and team sites for your SharePoint intranet

I have noticed, that the tech oriented SharePoint architects tend to prefer team sites over publishing sites for practically any purpose. I cannot remember the reason, and frankly, during my years of building SharePoint intranets I have not come accross any real reason for this - more like on the contrary. An intranet built with team sites causes more problems than not, in most cases. Let me explain.

Team sites have a much more dynamic usage philosophy behind them than publishing sites. Team sites are meant to be virtual team workspaces, where a bunch of people collaborate, manage documents, manage team tasks, chat etc. The content is highly dynamic, based on lists and document libraries rather than text and images on web pages. Thus, the need to edit pages is ususally quite small. The owner creates lists and libraries, sets up the workspace and the content starts to live.

As for the informative intranet? This is not how it works! The content is highly static - text and images on pages, maybe some centrally managed documents that are published on for the large reader audience of the pages - I say pages, for even if the intranet is built with a site hierarchy, like it most commonly is, what users see and use, are pages. 

True, that you can do the same with team sites too. Nobody says that team sites must be collaboration sites. However, using team sites to build traditional intranet sites creates a series of issues for the content managers and designers of the intranet. Issues, that the more technically oriented architects never see, since they rarely step into the boots of the end-user.

Quite many times, I as the UI "guy" and the content management "guy", am called into these situations where someone, some tech person, has built the intranet for a compnay, a Site Collection created as a team site, created a hierarchy of team sites. the customer has started to build their intranet with the blocks they have been given and they run into issues.

1) "I  made changes to this page, but they disappeared". Most of the time the publishing features have been turned on in the site collection of team sites that are meant to act as the intranet. This results in a very strange hybrid situation, where one can edit a page without checking it out, but someone can also check it out and result in loss of data. 




2) "I was trying to edit this web part, but I get this message about checking out." Even when the check out is required for the pages, the whole editing and check out is occasionally quite confusing for the content editor. In some situations, the page allows you to edit it to an extent, until it suddenly hits the editor with the "Hey, you need to check this page out first!". Again, data may get lost in the process. 

3) "Pages or Site pages and what is the welcome page of the site?" When publishing features are activated for the site collection and for the sites in it, each site has two libraries for the site pages. The Site Pages library (originial for the team site) and the Pages library (created by the publishing feature). Now, this creates another funky situation there. 

If indeed pages are created within sites, they may be in each different library, depending on how they were created. And they function a bit differently when editing, not to mention that the whole page architechture is different. More confusion for the content editor. The deafult page of the site can either be the Home.aspx from the Site Pages library or the default.aspx from the Pages library. You need to choose, and set the welcome page in the site settings.


4) "But... this page layout is not consistant and we want it to be something like this [drawing]." When using simply the welcome page of each site in the intranet site collection, the issue that the HR, PR, marketing etc. department raises when getting involved with the intranet is the layout of pages. 

True, not an issue for all companies, but a whole lot of them. There is a need to create a consistent layout for the pages and while text layout offers some options, it rarely truly fullfills the needs here. Publishing pages with the page layouts options are required. All of a sudden you find yourself creating new welcome pages as publishing pages and building them all over again with the content that already was on the home page, the wiki type page of the team site.



So in order to avoit these hybrid versions of sites and the confusion of the people who actually need to work with the pages and manage the content of the intranet, I highly recommend creating the informative intranet hosting site collection using the publishing site template. Seriously, that's why it is there! If you need blogs or wikis or even team sites within, it is all still possible. If you need calendars etc. that are not orginally part of the publishing site, the team collaboration lists feature can be activated. 



This said, if your intranet is not a traditional informative publishing intranet but a collaborative dynamic intranet instead, or if you prefer the wiki type content management, please do use the team sites to get the most out of that scenario! There is nothing to lose and a lot to gain  to actually use the most suitable SharePoint site template for each specific purpose instead of going with the idea that one glove fits all. It doesn't! All it needs is a little bit of planning and understanding the actual use case scenario of the site collection at hand.

Oct 6, 2015

Scoped search results predefined empty

Many are the things you think should be simple in SharePoint, and yet they're not. Another day, another little issue to solve. 

We have made this JavaScript listing of subsites inside a site collection. A site directory. For certain reasons the Content Search Web Part nor the Table of Contents was the thing. That aside, I'm not going into that more deeply here, the other thing that was needed there, was the ability to search the directory.

One would imagine, that it would be simple enough to add a search box to the page, and a search results web part, and then configure it to show only results for search box queries, and show nothing unless a query is made.

Not so much. Not a big deal either, but requires a couple of tricks.

First of all, you need to set the query for the results web part. In this case, there are three things to set up: 
  1. The results need to be restricted to this site (or site collection, depending on where you're setting this up) only
  2. The results need to be site item type only
  3. The results need to be restricted instead of filtered by the search box query (as would be the default, the results web part showing a listing of all sites unless a query is made)
With all this noted, the query would look like this:
{?{searchTerms} path:"https://YourSiteOrSiteCollectionPath"  contentclass:STS_Web}

This can be either typed in, or if you need to modify the result types etc. use whatever query settings are provided. Just be sure to wrap the whole thing inside the {parentheses} with the
?{searchTerms}at the very beginning of the clause.

In the web part properties you might want to tick off some of the settings like eg. choose to not show the adanced search link, preferences link, language dropdown etc. The setting you won't find there, even though the search results display template actually hints that it should be there, is to select "Show nothing when there are no results". The Content Search Web Part does have this setting, so I was kinda surprised.

This just means, that when no query has been made or it does not provide results, you will see all these suggestions and tips in the web part:


In order to be rid of those (or at least the suggestions), depending on what you want - in this case, the web part needed to be completely empty unless there were some search results to show - you need to modify the display template for the search results. I.e. download a copy of the default one, make the necessary changes and upload your modified one to the gallery. 

All the display templates of a site collection are located in the Master Pages gallery, Display templates folder. Search results display template is in the Search folder. Download a copy of the original html file.



Open the downloaded file in an editor (I prefer Notepad...) and search for the div with the id of NoResult.

<div id="NoResult">

Inside that div you can see a bunch of JavaScript and html. If you only wish to get rid of the suggestions, delete only the <ul> element from the page, or replace it with something of your own. 



As it went, I wanted nothing to be shown unless there were some results, so I emptied the NoResult div completely. 

Change the title of your template (third row of the template) and save it locally with a new name. Upload your modified template to the same Search directory of the Display templates as the original one. Don't worry about any .js files; SharePoint will generate it upon upload. 

You most probably will need to publish your template before it can be used in the web part. When this is done, navigate back to the page with the search results web part you are setting up and open the we part properties. (Note, the page needs to be saved and reopened for editing before it can find the new display template)

In the Display Templates settings select your modified template.


Save web part properties, save (and publish) page and the search is ready.


Sep 24, 2015

Modify SharePoint List Alert to Show ID

There is a list in SharePoint, that seems to be quite overlooked. It is, however a pretty ok light weight ticketing system for a project. Yes, I'm talking about the issue list. One advantage the issue list has over a general task list is that it automatically notifies the person if he or she is set as the assignee of the issue. It used to be, that this notification email - alert - contained the issue ID both in the subject and the email body. Not anymore. 

One of my customers uses issue lists for project ticketing and after migrating from an older version of SharePoint they noticed that the emails no longer had this ID in them. It is quite important for them to have the ID easily available. So I set out to modify the alert templates.

There is no specific alert template for issue list. However, there is one for this assigned to notification email. See the MSDN article about alert templates for further reference on all of the different templates. 

In SharePoint 2013 all alert templates are in one XML file located in the C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\TEMPLATE\XML directory on the SharePoint App server. The file you need is alerttemplates.xml.

As with anything located in the SharePoint root, you should not modify the original template file, but make a copy of it in the same directory and rename it, eg. alerttemplatesIssueId.xml. Then open this copy file for editing (notepad will do, VisualStudio is more colorful ;) ).

It is a long winded file. The AlertTemplate elements wrap each different template. Each has the template type set as value for Name attribute, e.g. Name="SPAlertTemplateType.GenericList".

In order to simply make the Issue ID visible in the notification email for changed "assigned to" value, find the SPAlertTemplateType.AssignedtoNotification template. Find the properties element. Remove ID from the ImmediateNotificationExcludedFields.


This will make the ID visible in the email body of this notification email. If you wish to have the ID visible in any alert a user subscribes to regarding the Issue list, you will need to remove the ID from the exclude fields of the properties in the GenericList template, since issue list does not have one of its own. Note, that all the other templates have two properties, the Immediate and Digest notification exclude  fields.


This was not enough for my customer, since they wanted to have the ID also in the email subject. This can be achieved rather easily by adding the ID field variable to the subject element. The trick, especially with the AssignedToNotification template, is to find the correct place. 

The ID field variable itself is added as a single tag element: <GetVar Name="NewValue#ID" />

The place to add it to, is next to the item name or list name, eg.


In the AssignedToNotification template this is crammed between two switch elements, inside the subject element. In the GenericList template, the subject element is far more simple:


This would already display the ID in the subject, but just to make it a bit more readable, you should add some HTML between the variables, eg.



Now, save your alert template. In order to get SharePoint to use this template, you need to run a little stsadm command. The easiest way is to open the SharePoint Management shell, run as admin, and then run the command:

stsadm -o updatealerttemplates -url http://<sitecollectionUrl> -filename "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\XML\alerttemplatesIssueID.xml"

(Note, copy-paste will probably not work for the command because of the quotation marks)

After running this command, you need to do an iisreset, and that's it. Your templates are ready and in use!


Sep 17, 2015

Broken Blog Post Filtering in SharePoint 2013

Some time last spring I created two blog sites to a customer SharePoint 2013 intranet. I set them up quite similarily, the only difference being that I added some custom fields to the other one. This should, in my logic, not affect anything in the different blog views - such as archives and category views - since I didn't touch any of the OOB fields. Whatever the reason, the end result was however, that last week the customer noticed (hmm, not very active blogging then ;) ) that on the other blog site the category and archive pages were broken. They did not filter the posts anymore as they should.

I banged my head against the wall for a while, googled, googled some more and tried this and that. Nothing seemed to help. One workaround I tried was this most commonly mentioned solution of adding a URL filter web part and setting it up as instructed eg. in this post here.

This, however, did not work for me, or this customer site. Be it due to the language pack (Finnish site) or something else, but it did not work. So I needed something else. This is what I did:

1) I created a new blog in the test environment. Really it doesn't matter where, just as long as you can access it in SharePoint Designer.

2) I opened both the new non-broken blog site and the old broken one in SharePoint Designer. I know. I sighed deep when I realized I really needed to go that far. But unfortunately the problem seems to reside in the corrupted query of the posts web part and in order to fix that, yes, SPD is required.

3) The two pages that are not working properly when categories and archive are not filtering posts, are Categories.aspx and Date.aspx. These are not in the Site Pages library of the blog site; you need to dig them out from the All files directory. 
All files > Lists > Categories > Category.aspx
All files > Lists > Posts > Date.aspx

Open both files on both your broken site and the new one created.

4) No, copying all code from one to the other will not work because of the GUIDs. As stated earlier, the broken part is the CAML query of the Posts web part. So the first thing to do is to locate the Posts web part. Since you are working in code view only, look for the main place holder with the blog-MainArea to find the correct web part.


5) Focus on the line starting <View Name="{...

6) Scroll horizontally (or set your page to wrap lines) until you find the <query> tag. This begins the CAML query for the web part. On the borken page it was missing parts. So I copied the query (everything between the <query> and </query> tags) from my new site category and date pages to the broken corresponding ones, replacing the broken queries.

7) I saved my files and tested them. The initial refresh filtered posts nicely, but the blog navigation (the categories list and the blog archive) links were broken, for the Blog tools web part was broken. It seems that any time you open a blog page into SharePoint Designer, this web part breaks.

8) So, I edited both pages in the browser, deleting the broken Blog tools part and adding it back in place and yes, things started functioning again! 

Hope this helps someone else save some time. Boy, there's never a boring day with SharePoint!

Sep 4, 2015

Using SharePoint Client Context JavaScript on Publishing Site

In order to use SharePoint Client Context (SP.ClientContext) in your own custom Javascript on a pafe, you need to ensure that sp.js is loaded. This is an onDemand file, which means it is loaded only when needed, i.e. when the browser requests it. In most cases it gets loaded because of something, most commonly the ribbon, on the page, so it is sufficient to start your custom Javascript with

ExecuteOrDelayUntilScriptLoaded(CustomFunctionUsingContext, "sp.js");

On Publishing sites, however, when the page is published, ribbon is hidden and sp.js does not get loaded unless there is some other code on the page to demand it. Thus, you might need to use a bit of force to get sp.js loaded. That is, you need to demand it yourself. The function for this would be

SP.SOD.executeFunc("sp.js", "SP.ClientContext", CustomFunctionUsingContext);

or

SP.SOD.executeFunc("sp.js", "SP.ClientContext", function(){
 YourCustomCodeHere
}); 

This is exactly what I was trying to use when inserting a code snippet on a Publishing page, using the Client Context to retrieve subwebs, for some odd reason the above did not do it, so I tried something that seemed totally crazy:

SP.SOD.executeFunc("sp.js", "SP.ClientContext", function(){
 ExecuteOrDelayUntilScriptLoaded(CustomFunctionUsingContext, "sp.js");
});

Strangely enough it started working after that. Later on, when none of the above seemed to work on a team site, except in page edit mode, I started to wonder if the problem really was this or something within SharePoint (throttling thresholds, script not loading every time, whatnot). I am not inclined to change it anymore since now it works. Anyway, might be wise to add some code there to avoid being throttled or blocked by SharePoint

Jun 25, 2015

Open documents in Office Web Apps from CSWP

One of my customers has a Content Search Web Part in their intranet home page, listing both documents and pages. They have Office Web Apps installed and are quite happy with the way documents open to OWA instead of the desktop client from the document library and search results. The CSWP, however, does not natively support opening documents in the OWA, so a bit of display template modification is required.

I was already using rather heavily modified item display template for the customer, so I simply needed to modify that one, but you can also copy any item display template (from the Master Pages gallery, Display Templates > Content Web Parts folder) that best fits your purposes and modify the copy. Mind you, you only copy the html template, modify it, save it with another name and upload it back to the library. The .js file will be automatically created.

The things you need to do to your display template in order to enable opening documents in OWA are: 

1) Add the managed property 'ServerRedirectedURL' to the managed properties: 


This is the property that provides the open in OWA url for documents. You could use this as a simple property for the LinkURL, but if you cannot be certain that each and every document that gets listed in the CSWP is in fact an office document, you need to add a bit more code to the template.

2) Right there, where the commented code of the display template begins, below the var encodedId line, insert the following code I intecepted from the common search result item template:

var useWACUrl = !$isEmptyString(ctx.CurrentItem.ServerRedirectedURL);
        if(ctx.ScriptApplicationManager && ctx.ScriptApplicationManager.states){
            useWACUrl = (useWACUrl && !ctx.ScriptApplicationManager.states.openDocumentsInClient);
        }
        var appAttribs = "";
        if(!useWACUrl)
        {
            if (!$isEmptyString(ctx.CurrentItem.csr_OpenApp)) { appAttribs += "openApp=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenApp) + "\"" }; 
            if (!$isEmptyString(ctx.CurrentItem.csr_OpenControl)) { appAttribs += " openControl=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenControl) + "\"" };
   
        }
        var url = ctx.CurrentItem.csr_Path;
          if($isEmptyString(url)){
            if(useWACUrl)
            {
                url = ctx.CurrentItem.ServerRedirectedURL;
    
            } else {
                url = ctx.CurrentItem.Path;
   
            }        
        }

3) Then modify the LinkURL variable to use the url instead of the default Path:
var linkURL = $urlHtmlEncode(url);

4) Finally, remove the line
linkURL.overrideValueRenderer($urlHtmlEncodeValueObject);

And that's it, folks.

Feb 18, 2015

Calculated string field in Content Search Web Part

One of my customers had a need. They have an intranet site collection and another one for team sites. One of the team sites contains a list from which they wanted to show five newest entries on the intranet front page. Since both site collections are indexed in search, no problem, we simply used a Content Search Web Part for it. Re-indexed the list columns and created properties in the search schema for the columns they wanted to show in the CEWP.

One of these columns was a calculated field, that combined the name information into a neat display column of [Lastname, Firstname] string. It was quite a surprise when the CEWP showed this information with an additional string;# resulting in a list such as this:
  • string;#Connor, June
  • string;#Mickowich, Mark
  • string;#Somebody, Else
You get the drift. I started to google, of course, landing several references to this behavior. The workarounds consisted mainly of two propositions: do not use a calculated field but rather a workflow, or, more specificly addressing this CSWP issue, make your own display template and fix the Javascript responsible for this starngeness. I wanted to do neither.

There is a very simple solution, done right there in the client side, on the page. What I did was insert a Script Editor Web part on the page underneath of the CSWP and in that editor, I added this simple piece of script: 

<script type="text/javascript">
$(function(){
$("h2.cbs-picture3LinesLine1").each(function(){$(this).text($(this).text().replace("string;#", ""))});
});
</script>

Element h2.cbs-picture3LinesLine1 was the one containing the name with extra string in front of it. This Javascript insert simply replaces that extra string with nothingness. Mind you, this uses jQuery, so be sure to have the jQuery available, referred to on the page one way or the other; this site already had it in the MasterPage.

[Edit Feb 27th, 2015
If SharePoint has MDS (Minimal Download Strategy) enabled, many JavaScript functions will not fire upon page refresh. To solve this issue, use the ExecuteOrDelayUntilBodyLoaded(function() in order to call your function after page refresh too, eg.

function cleanstring(){
$("h2.cbs-picture3LinesLine1").each(function(){$(this).text($(this).text().replace("string;#", " "))});
}
ExecuteOrDelayUntilBodyLoaded(function() {
   RegisterModuleInit('/mySite/SiteAssets/testModule.js', cleanstring);
   cleanstring();
});

If your script is located in a custom MasterPage or PageLayout, you can use the SharePoint ScriptBlock element which is run on the server, instead of the Script-element. This, however, does not work if your script is added to a page using the Script editor web part or editing page source code.

<SharePoint:ScriptBlock runat="server" >
$(function(){
$("h2.cbs-picture3LinesLine1").each(function(){$(this).text($(this).text().replace("string;#", ""))});
});
</SharePoint:ScriptBlock>

Also, on a publishing page, where MDS was not the issue, the problem was solved by using

$(window).load(function(){
...
}

whereas $(document)ready() did not do the trick.]

Sep 3, 2014

Using a Custom Logo in HTML MasterPages

The Eureka-moment of the day. I admit that I have mostly avoided this whole issue after the intial notion that it just did not work - mostly because I have avoided working with HTML MasterPages alltogether since I don't really like them; I'm much more at home working with the actual .master file (and mind you! it can also be used in a Design Package - that is, creating a Design Package does not mean that you need to use HTML Master Pages; rather, they are an option).

Today I was working with new labs for my Branding trainings and finally decided to take the bull by the horn and try my luck again at figuring out the proper way to actually get your own logo as default logo in the Site Logo snippet. And I did figure it out. It's really nothing new; I just hadn't thought about it this way before. 

If I create a VisualStudio Branding solution, I prefer the 15-hive over placing the files in _catalogs/masterpage. But as it goes, this is not avoidable in all situations, so I, too, have made solutions with all the assets in the MasterPage gallery. So today I gave this logo issue some more thorough thought and got it. The link in the snippet properties needs to be in the same form as it would in my own MasterPage, when the logo is located in _catalogs/masterpage.

In essence: for an HTML MasterPage, store your assets (logos, css stylesheets etc.) in your own folder in _catalogs/masterpage to ensure that they are packaged with the Design Package. When you use the logo in the Site Logo snippet, set the SiteLogoImage url in the following form:
&#60;% $SPUrl:~sitecollection/_catalogs/masterpage/designpackagefiles/dp_logo.gif %&#62;
(or <% $SPUrl:~sitecollection/_catalogs/masterpage/designpackagefiles/dp_logo.gif %>)


As for the CSS file link, that is much more straightforward. The href url can simply be of the form folder-in-catalogs-masterpage/stylesheetname.css, e.g.
<link rel="stylesheet" type="text/css" href="designpackagefiles/DesignPkgStyles.css">

Note, that the CSS link needs to be outside of all the MS-commented sections; the safe place for it is right before the closing </head> tag!

Apr 17, 2014

Tip: Page Layout Content Type Language Resource

Just a short note. I have been using the Page Layout string as the ContentType value for page layouts in the Elements.xml manifest, when creating new page layouts within a UI package Quite many of my customers, however, prefer their sites in Finnish. The Finnish equivalent is "Sivun asettelu" and usually this works fine, but not always. The better practice however (and a must in a multilingual environment) is to use the resource-string as the value for ContentType:

<Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />

Mar 20, 2014

Using SlickNav with SharePoint

Responsive design in the word of the day, and recently full responsiveness has become more and more required in intranets too, and not only in Internet sites. This led me to trying out the SlickNav with a SharePoint site of a customer, and was happily surprised, when everything mostly went in a truly slick manner! Anyone working with SharePoint will, however know, that slick with SharePoint has a whole different meaning than slick in a simple HTML+CSS web site. So a few notes from my journey with SlickNav.
  • For basic implementation it really is enough to download the SlickNav package from the web site, and follow the general instructions of usage.
  • Include the jquery.slicknav.js and slicknav.css files in your project, as instructed.
  • Remember to refer jQuery in your MasterPage along with the SlickNav files.
  • As for SharePoint, if using the OOB menus, there obviously is no ul with the id of "menu"; instead you can use "div.ms-core-listMenu-horizontalBox > ul.root" for the element selector and it works like a dream.
  • Remember to set the prependTo property for the initializing function, e.g.
    prependTo: '#mainnav'
  • I also found it useful to set the allowParentLinks property to 'true', so users can clikc on the parent links in a similar way as the regular SharePoint navigation
  • If you are using dynamic nodes in SharePoint navigation, you need to set the styles for
    .slicknav_open ul.dynamic {
       top:0;
       left:0; }

    in order to show the dynamic navigation; otherwise it is still hidden somewhere at left:9999px etc.
  • The dropdown tends to fall behind the page content, so set the styles for
    .slicknav_nav, .slicknav_nav ul {
        position: relative;
        z-index: 100; }
  • A varying amount of other css tweaking is needed to make it neat in SharePoint.
Still, I wouldn't say it was a really big issue to get the SlickNav up and working and even stylized nicely for the customer.


Mar 19, 2014

Discard AppWebs When Setting Custom MasterPages

I have long used a feature event receiver to set MasterPages throughout the webs (i.e. sites) in a site collection when my branding feature is activated, and also an inheritance event receiver to set the MasterPage for any new web when it has been provisioned. In SharePoint 2010, search sites required a different MasterPage than the rest of the sites, so in the code of the receivers, this was always taken into consideration, as you can see in the article linked above. In 2013, this is unnecessary as the same MasterPage goes for all sites, so I have used the simplified version of the receiver code, with no webtemplateid checks.

Or, so I thought. Turned out, my testing had been lacking, since I do not use apps in my development&test environment, and thus it had not occured to me, that the app webs (which I sort of assumed to be separate, but apparently are not) respond to these event receivers too. As tests proved, my receivers actually "broke the SharePoint" when appwebs were involved. So, a little improvement to both receivers here, checking that a web is not an appweb (APP#0, WebTemplateId == 17) before making any MasterPage changes:

Feature event receiver, activate feature:

SPSite site = properties.Feature.Parent as SPSite;
            if (site != null)
            {
                SPWeb topLevelSite = site.RootWeb;
                string WebAppRelativePath = topLevelSite.ServerRelativeUrl;

                if (!WebAppRelativePath.EndsWith("/"))
                {
                    WebAppRelativePath += "/";
                }

                foreach (SPWeb web in site.AllWebs)
                {
                    if (web.WebTemplateId != 17)
                    {
                        web.MasterUrl = WebAppRelativePath + "_catalogs/masterpage/mycustommaster.2013.masterpage.master";
                        web.CustomMasterUrl = WebAppRelativePath + "_catalogs/masterpage/mycustommaster.2013.masterpage.master";
                        web.Update();
                    }

                }
            }

Event receiver, web provisioned:

SPWeb newWeb = properties.Web;
            SPWeb topSite = newWeb.Site.RootWeb;
            if (newWeb.WebTemplateId != 17)
            {
                newWeb.MasterUrl = topSite.MasterUrl;
                newWeb.CustomMasterUrl = topSite.CustomMasterUrl;
                newWeb.Update();
            }
        }

For the sake of consistency, I also fixed the deactivate feature code to leave the appwebs alone.

Oct 15, 2013

Using Dynamic Navigation to Create a Static Subnavigation with a Little Bit of CSS and no JavaScript

Sometimes I tend to think too complicated. Working with a couple SharePoint navigation customizations involving e.g. a megamenu implementation (btw. I found this blog post highly helpful with that!), I was trying to accomplish something quite simple with a load of jQuery. Most certainly, that would have worked fine too, but when I, for a fleeting moment, wondered why it didn't (eh, I was still missing the jQuery reference), I realized I really did not need it at all.

In this particular case, I needed a two-level navigation, even though there was no real hierarchy, since everything was decidedly in one single team workspace. Yet, there was a logical hierarchy that had to be shown in the left hand side navigation. So, what I did after my futile jQuery excursion, was
  1. to create a hierarchical managed navigation, with the terms pointing to pages that otherwise would have been a flat list in the navigation. 
  2. to use the global navigation in place of the QuickLaunch as the left hand side navigation (in this case, it works much better)
  3. changed the navigation parameters to orientation: horizontal, static display levels: 1, maximum dynamic display levels: 1
  4. picked out the two CSS classes that make the dynamic navigation appear and dispappear:
    li.hover (hover being the class that SharePoint adds to the li.static.dynamic children on hover action)
    and li.hover-off (hover-off being the class that SharePoint adds to the mouseout action)
  5. using the classes above, I set the dynamic navigation to never appear as a dynamic submenu:

    li.hover ul.dynamic, li.hover-off ul.dynamic {
        display:none !important;
        left: -9999px !important;
    }
  6. then I set the dynamic menu of the selected static parent item to be displayed right below the parent item link:
li.selected ul.dynamic, li.selected.hover ul.dynamic, li.selected.hover-off ul.dynamic {
    border: medium none;
    box-shadow: none;
    color: #000000;
    display: block !important;
    left: 0 !important;
    padding: 0;
    position: relative !important;
    top: 0 !important;
}

With a little bit of other CSS modifications to the navigation items (for styling purposes), the menu then looked and behaved like this:


Sep 27, 2013

Inbuilt IE8 hack for SharePoint 2013

Doing yet another UI branding for a customer, and especially the optimzation for each and any Internet Explorer version from 8 on, I stumbled upon something quite interesting and useful too.

I mostly prefer the CSS rule hacks (e.g. using /9 for IE8) over comments and separate stylesheets and/or classes (read more about the options in Web Designer Wall), and try to come up with the most browser-generic solutions as possible. But IE8 is a problem child, that's for sure (fortunately, with SP2013 we don't need to deal with IE7 anymore!). 

The SharePoint team has obviously noted this too. So what I discovered today, when changing the color of the left side of the suite bar (id="suiteBarLeft") is this general class ms-core-needIEFilter that gets inserted to the page by some SharePoint JavaScript when the browser mode is Internet Explorer 8.

For example, this suiteBarLeft just happens to get an IE8 fix for its background, resulting in my custom background color not taking effect. And !important does not do the trick, for the overriding CSS rule is actually a filter rule, not background(-color) rule - which is there too, actually, set to transparent, so the complete fix for the suiteBar backgrouns customization is

.ms-core-needIEFilter #suiteBarLeft {
    filter:none;
    background-color: #92D050;
}



As stated, this class ms-core-needIEFilter exists globally (somewhere - I could not place the class in the DOM tree) on the SharePoint pages when browser is IE8, and thus is quite useful for any IE8 hacks that are needed in any custom UI. I tried out a few, and it works like a dream :)

Sep 18, 2013

Changing Site Logo URL with Client Side Script

In SharePoint, the site logo URL is set in the master page as the NavigateUrl  attribute for the SPLinkButton control, usually with the value of either ~site or ~sitecollection, e.g:
<SharePoint:SPLinkButton runat="server" NavigateUrl="~sitecollection/">

Most of the time this either is what we want, or can be changed in a custom master. 

Lately, though, I have been working on a project where the company intranet is split in two: there is one instance for general intranet, and another one for a special purpose. Both instaces use the same master pages, and the only UI customizations I have been making for the special site collection (in its own web app, not that it matters) are some special page layouts and CSS files - and a bit of JavaScript.

The incentive is to integrate the special site collection to the general intranet as seamlessly as possible, i.e. in a way that the average user wouldn't need to even notice that they actually navigate from one site collection to another and back. One task was to add an intranet root node to the breadcrumb trail, which did not cause too big of a headache, but the other task, changing the URL for the site logo link was not as straight forward as one would have expected.

The problem was created by the SharePoint control adding (invisible) empty text nodes inside the parent div. So while the <a> element seemed to be the firstChild of the div, this was not actually correct. Inspecting the dom tree of the div in Chrome dev console, I found this:



firstChild was trying to change attribute for the text node [0] and childNodes[1] returned empty object for whatever reason, so finally, the problem was solved rather simply by using children[0]:

window.onload = function setLogoUrl() {
    var logolink = document.getElementById('DeltaSiteLogo').children[0];
    logolink.setAttribute('href', 'http://intranet.mothership');
 }

May 14, 2013

Content Query Web Part Breaks Page Layout in SP2013

Content Search Web Part replaces Content Query Web Parts in SharePoint 2013 on many occasions. This probably is the reason why it has taken this long for me to discover this rather annoying SharePoint bug: Content Query Web Part breaks a page layout built with div-elements, because the default web part (or the XSLTs?) seems to miss a </div> closing tag!

Everything is ok in edit mode, for what ever additional wrapper reasons:




but when I click my way out of edit mode, the page breaks:


In FireBug, I added a </div> tag to the page's source code (which is perfectly ok when no CQWP is added to the page) and this fixes the problem:



In real life, this obviously won't do. So as a workaround, I added a table element to the Page Layout code, inside the div-elements as a wrapper for the web part zones: 

<div id="rightcolumn">
  <table width="100%"><tr><td>
    <div class="ms-webpart-zone ms-fullWidth">
    ...
    </div>
  </td></tr></table>
</div>

Not an especially pretty insert, but the easiest fix I came up with for now.

Apr 3, 2013

Accordion "Left Navigation" (Quick Launch) for SharePoint 2013

[Edit 7.3.2016: Since posting this, a lot has changed both in the browser and SharePoint world. This still works, at least in IE, but there have been a whole lot of issues with it. Thus I would urge you to consider the solution by MaxYakovenko instead of implementing this one (I am not attempting to solve the issues of this one anymore).]

One of my customers is working on their new SharePoint 2013 intranet site. They needed the Current Navigation (Foundation: Quick Lauch) to be an accordion. We tried a couple different jQuery code bits, but whereas they used to work in SharePoint 2010, in 2013 they only flashed the subnavigation instead of leaving it open.

Googling for one that would work with SharePoint 2013, I found a code snippet  in http://joao-pinho.blogspot.de/2012/11/sharepoint-2013-accordion-quicklaunch.html, but it did not do everything as intended (s.o. the links did not function anymore as the click was completely captured by jQuery). So, with a couple modifications:

$(function(){
 /*set dynamic css logic*/
 if($('#sideNavBox .menu-item.selected').length){
  //propagates the selected class, up the three.
  $('li.static').removeClass('selected');
  $('#sideNavBox .menu-item.selected').parents('li.static').addClass('selected');

  //collapses top siblings of selected branch
  $('#sideNavBox .menu-item.selected').parents('li.static').last().siblings()
   .find('> ul').hide();
 }
 else $('#sideNavBox .root.static > li.static > ul').hide();

 /*set accordion effect*/
 $('#sideNavBox .root.static > li.static').each(function(){
  if($(this).find('ul').length){
   $(this).addClass('father').click(function(){
    if($(this).children('ul').css('display') != 'none'){
     $(this).removeClass('selected').children('ul').slideUp();
    }
    else {
     /*collapse-siblings*/
     $(this).siblings().removeClass('selected').children('ul').slideUp();

     /*expand*/
     $(this).addClass('selected').children('ul').slideDown();
    }

    /*added: stop event propagation to link nodes*/
    $('a.static').click(function(event) {
        event.stopPropagation();
    });

    /*added*/
    return false;
   });
  }
 });
});

This piece of code assumes that the SharePoint navigation levels in the MasterPage are set to 3 static ones and no dynamic levels, the SiteMapProvider is CurrentNavigation, and the navigation settings in the sites are set to:

- SITE WHOSE CHILDREN FORM THE ACCORDION: Structural Navigation: Display only the navigation items below the current site, Show subsites

- ACCORDION HEADING LEVEL SITES (PARENTS):  Structural Navigation: Display the current site, the navigation items below the current site, and the current site's siblings, Show subsites

- ACCORDION SUB LEVEL SITES (CHILDREN):  Display the same navigation items as the parent site