Oct 17
Object's names for the taxonomy usage in JSOM

If you look at the taxonomy examples for JSOM, you will see that the object's GUIDs are used for the data retrieval. These examples work perfect on specific environment, but in common case you don't have access to this environment or you know only metadata structure. Therefore I was afraid that I should use a chain of the requests to get needed data and I was surprised that this stuff can be done much more easier. Check yourself!

var ctx = SP.ClientContext.get_current();
var taxonomySession = SP.Taxonomy.TaxonomySession.getTaxonomySession(ctx);

var store = taxonomySession.getDefaultKeywordsTermStore();
var groups = store.get_groups();
var group = groups.getByName('GroupName');
var termSets = group.get_termSets();
var termSet = termSets.getByName('TermSetName');
var terms = termSet.getAllTerms();
var term = terms.getByName('TermName');
var childTerms = term.get_terms();

ctx.load(childTerms);
ctx.executeQueryAsync(function () {
    var termsenumerator = childTerms.getEnumerator();
    while (termsenumerator.moveNext()) {
        var currentTerm = termsenumerator.get_current();
        alert(currentTerm.get_id() + ' ' + currentTerm.get_name());
    }
}, function() {
    SP.UI.Notify.addNotification('Request failed.');
});


Aug 07
Build Knowledge Base on SharePoint online

​Finally I've got the next my project - Knowledge Base. The goal of this project is to create a system for collecting and ordering articles which have helped me to solve tasks. I find it useful, as repetition of tasks may cause a long research on the Internet. To build this system I've decided to use my SharePoint Online and have set itself the conditions:

  • Minimal effort of work
  • Easy of use
  • Categorization


On the basis of the requirements I've chosen a simple SharePoint list and have customized its forms to auto populate title, description and url fields. One of the main part of the customization is a Bookmarklet which is an entry point for the adding content (see the picture to understand the whole process).

07.08.png

The user opens a list view of the Knowledge Base and adds bookmarklet link to the bookmark panel of a browser (1,2). Then the user read a page. If he decides to remember the page he can select a text (3) and use the bookmark (4). After this the newform.aspx page is opened with the prepopulated fields (5). The distinctive feature of this process is that the previously selected text is displayed in the description field.

It looks complicated, but the development takes half an hour. First of all, I've added two JavaScript files to the SharePoint Online.

  • The script which is loaded to the page after the bookmark has been clicked
(function(){
    var desc = document.getSelection();
    var url = "https://sitename/Lists/Knowledge%20Base/NewForm.aspx?title="
+ encodeURI(encodeURI(document.title)) + "&description=" + encodeURI(encodeURI(document.getSelection()))
+ "&url=" + encodeURIComponent(encodeURIComponent(document.location.href));
    window.open(url);
})()
  • The script which is added to the newform.aspx
var shnt_clr = shnt_clr || {};

(function(){
    var self = this;
    this.getQueryStringParameter = function (param) {
        var params = document.URL.split("?")[1].split("&");
        var strParams = "";
        for (var i = 0; i < params.length; i = i + 1) {
            var singleParam = params[i].split("=");
            if (singleParam[0] == param) {
                return singleParam[1];
            }
        }
    }
    
    this.init = function(ctx){
        if (ctx.ListSchema.Field[0].Name == 'Title'){
            var titleElement = document.querySelector('[id^="Title"][id$="TextField"]');
            titleElement.value = decodeURI(decodeURI(self.getQueryStringParameter('title')));
        }
        if (ctx.ListSchema.Field[0].Name == 'Description'){
            var descElement = document.querySelector('[id^="Description"][id$="TextField_inplacerte"]');
            descElement.innerHTML = decodeURI(decodeURI(self.getQueryStringParameter('description')));
        }
        if (ctx.ListSchema.Field[0].Name == 'URL'){
            var urlElement = document.querySelector('[id^="URL"][id$="UrlFieldUrl"]');
            urlElement.value = decodeURIComponent(decodeURIComponent(self.getQueryStringParameter('url')))
            
            var urlDescElement = document.querySelector('[id^="URL"][id$="UrlFieldDescription"]');
            urlDescElement.value = decodeURI(decodeURI(self.getQueryStringParameter('title')));
        }
    }
}).apply(shnt_clr);

(function () {
    var overrideCtx = {};
    overrideCtx.OnPostRender = shnt_clr.init;

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
})();

As can you see by the code the first script forms an URL which contains the page title, description and link as parameters and opens this URL in a new tab of the browser. For the script loading the user should use bookmaklet link which has been added to the list view of the knowledge base.

<a href="javascript:(function(){instacalc_script=document.createElement('SCRIPT');
instacalc_script.type='text/javascript';
instacalc_script.src='http://sitename/SiteAssets/KnowledgeBaseBookmarklet.js?x='+(Math.random());
document.getElementsByTagName('head')[0].appendChild(instacalc_script);})();">bookmarklet</a>

The second script uses CSR for the field filling and have been added as JSLink of ListFormWebPart

<JSLink xmlns="http://schemas.microsoft.com/WebPart/v2/ListForm">~site/SiteAssets/KnowledgeBaseForm.js</JSLink>

This simple code facilitates adding of the items to SharePoint Online and I must admit, I use it with enjoyment.

Jul 30
SharePoint fields in the front-end engines

One of the best new features for SharePoint 2013 is Client Side Rendering. It allows us to style SharePoint elements using JavaScript that provides several advantages for us:

  • Performance - rendering is performed on the client side
  • Cross-platform language - web developers are familiar with JavaScript
  • Flexibility - we can apply changes to a single field, entire view or form


These points tell that the field's rendering functions are completely moved to the front-end and we can use them to build complex interfaces based on usage of any JavaScript Temlate Engines. In troth, if you are doing research, you will find a lot of examples which describe how to use JavaScript to initialize OOTB controls. I brought this knowledge together and prepared the presentation with a demo for Belarus SharePoint User Group. This presentation contains a common approach to the use of knockout.js and no special interest, but it is necessary to understand the demo which explains how to initialize and work with the most complex fields:

  • Date time picker
  • People picker
  • Taxonomy picker
  • Custom picker


The sample code for the demo will help you to start your own front-end implementation quickly, so be free to find it on CodePlex.

Jun 16
Syntax Highlighter for SharePoint

I've decided to make a blog on SharePoint online and have faced with code formating issue. OOTB SharePoint controls provide possibility to insert the formatted text, but the function is not supported by all the browsers and the text formatting depends on a source which may lead to ugly look of the blog post. To bring the code to the general form you can use Ribbon. In this case you have to select each word and choose the color for it on the palette. It takes a lot of time, doesn't it? The real solution for this issue can be a third party plugin. I have searched on the Internet and have found SyntaxHighlighter. It is easy for deployment and usage - you should add script links to the master page.

<link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushVb.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js' type='text/javascript'></script>
<script language='javascript'>
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf';
SyntaxHighlighter.all();
</script>

Then you should wrap your code into one of two possible structures

<script type=&quot;syntaxhighlighter&quot; class=&quot;brush: csharp&quot;>
<![CDATA[
 
// Comment
public class Test {
public Test() {
}
}
]]></script>

or

<pre class="brush: csharp">
// Comment
public class Test {
public Test() {
}
}
</pre>

After these two steps you can see the result on the page

// Comment
public class Test {
    public Test() {
    }
}

That is really cool! But every day is not Sunday - first of all, to wrap your code you should use Edit Source button, find the text in the popup window and write structure. It can be difficult when there is a lot of text. Moreover, SharePoint uses <p> tag to shift the text to the next line. This tag is not ignored by SyntaxHighlighter.

13.05.png

I have not found the better plugin for SharePoint thus I had to pull my socks up and make my own plugin under SyntaxHighlighter. This plugin is a sandbox solution which solves the issues described above and can be found on CodePlex as Syntax Highlighter For SharePoint.


Apr 21
SharePoint Apps and custom domain

​If you develop provider or autohosted SharePoint Apps for the public site you deal with access tokens. They work perfectly until you decide to change the site domain to you own. In this case the access denied axception is thrown.

I have been searching solution for a long time and several days ago I was listening Artur Kukharevich's lecture at Belarus SharePoint User Group meetup. He told that SharePoint uses two sets of URLs for the public site:

  1. Internal
    • https://about-public.sharepoint.com (host web)
    • https://about-0d1bfd940113f4.sharepoint.com/AppForAnonWithPublicDomain (app web)
  2. External (public)
    • http://www.sharenotes.pro (host web)
    • http://about-425002940113f4.sharepoint.com/AppForAnonWithPublicDomain (app web)

Artur explained that while the SharePoint Apps required internal URLs for the access token, in anonymous context they had external URLs only. Therefore developer should make a hack to retrieve correct params and one of the possibilities is to use authentication form. How does it work? We make an http request to the authentication form and use query string to retrieve replay address.

private static string ExtractOriginalUrl(Uri urlFromRequest, bool isHostWeb)
{
var url = urlFromRequest + (isHostWeb ? "/_layouts/15/Authenticate.aspx" : "");
    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
    
EmulateBrowserWebRequest(webRequest);
    
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

    var originalHostUrl = HttpUtility.ParseQueryString(response.ResponseUri.Query)["wreply"];
    if (string.IsNullOrEmpty(originalHostUrl))
    {
     return string.Empty;
    }
    
var index = originalHostUrl.IndexOf("_form", StringComparison.InvariantCultureIgnoreCase);
    if (index > 0)
    {
     originalHostUrl = originalHostUrl.Substring(0, index - 1);
    }

    var relativePathFromRequest = urlFromRequest.GetComponents(UriComponents.PathAndQuery, UriFormat.SafeUnescaped)
.TrimEnd('/');
    var originalUrlServerPath = new Uri(originalHostUrl).GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped);

    return string.Format("{0}{1}", originalUrlServerPath, relativePathFromRequest);
}

private static void EmulateBrowserWebRequest(HttpWebRequest request)
{
request.Method = "HEAD";
    request.AllowAutoRedirect = true;

    request.Accept = "text/html, application/xhtml+xml, */*";
    request.UserAgent = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)";

    request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip");
    request.AutomaticDecompression = DecompressionMethods.GZip;
    request.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-US");

    request.Headers.Add(HttpRequestHeader.CacheControl, "max-age=0");
}

Using these methods we can retrieve internal URLs and correct access tokens.

_internalSPHostUrl = ExtractOriginalUrl(new Uri(SPHostUrl), true);

var lastIndex = SPAppWebUrl.LastIndexOf('/');
var appWebRelativeUrl = SPAppWebUrl.Substring(lastIndex);
_internalSPAppWebUrl = ExtractOriginalUrl(new Uri(_internalSPHostUrl + appWebRelativeUrl), false);

var uri = new Uri(_internalSPAppWebUrl);
var token=TokenHelper.GetAppOnlyAccessToken(TargetPrincipalName,uri.Authority,TokenHelper.GetRealmFromTargetUrl(uri));
using (ClientContext ctx = TokenHelper.GetClientContextWithAccessToken(_internalSPHostUrl, token.AccessToken))

I was trying to find another solution but I wasn't lucky. So if you have thoughts about this issue I will be glad to discuss them.

Special thanks to Artur, he helped me to write this post and provided the example which you can find here.
Mar 11
Posts with OOTB layouts on the home page
I decided to create my own developer blog on SharePoint online with minimal amount of efforts and one of the first thing that I wanted was to display my posts on home page. I added XSLTListViewWebPart to the page but it displayed list view which was not suitable for the main page of the site.

I found a web part that was called Blog Tools. It provided quick links for the blog management and selector for the OOTB layouts. It was nice, because I could use this web part for post's styling. One thing was bad. I could not hide Blog Tools from the user that had permissions to manage blog (the hidden property does not work) and it made the page ugly for them.

The only way that I saw was implementing of a custom page layout which displayed web parts in edit mode only. For this I coppied default layout and added hidden web part zone to the body
<div class="hidden-content">
<WebPartPages:WebPartZone runat="server" AllowPersonalization="false" FrameType="None" ID="HiddenZone"
Title="Hidden" Orientation="Vertical" QuickAdd-GroupNames="Default" QuickAdd-ShowListsAndLibraries="false"/>
</div>
Then I added needed styles to PlaceHolderAdditionalPageHead control
<style type="text/css">
    .hidden-content{
        display:none;
    }
</style>
<PublishingWebControls:EditModePanel runat="server">
    <style type="text/css">
        .hidden-content{
            display:block;
        }
    </style>
</PublishingWebControls:EditModePanel>
I configured Blog Tools as I wanted and the next step was to make it work with post's list view. All I need was to set summary view in the web part settings of XSLTListViewWebPart.
Oct 07
SharePoint 2013 workflows in SharePoint-hosted app
When you develop a workflow for the SharePoint-hosted app, you should keep in mind a few things. First of all, If you use OOTB or your own web template for an app, in most cases you will see this exception

@"Error 1
CorrelationId: 9b943d19-b575-4c83-8b53-027ae89241b7
ErrorDetail: There was an error during the operation.
ErrorType: Configuration
ErrorTypeName: Configuration
ExceptionMessage: Operation is not valid due to the current state of the object.
Source: Common
SourceName: Common App Deployment

The MSDN tells:

Do not use the WebTemplate element in the app manifest to designate any of the built-in SharePoint site definition configurations as the app web's site type. We do not support using any of the built-in site definition configurations, other than APP#0, for app webs.

Thus, if you would like to include your workflow to SharePoint-hosted app, you have to use APP#0 template.

secondly, if you are not a citizen of USA, you may localize your app. It's simple while you do not work with the host web elements (like web parts). You can find the solution which uses the AddRelsToAppPackage application, but this application breaks an app package in case the app contains a workflow. You will see this exception:

Error occurred in deployment step 'Install app for SharePoint': File contains corrupted data.

Artur Kukharevich found out the problem. The AddRelsToAppPackage applicationuses ZipArchive class of .net framework which removes empty files from a zip file, but leaves a reference to the files in the zip definition.We used dotPeek to decompile AddRelsToAppPackage application, copied Main method to our new application and add a new line to empty files.

foreach (ZipArchiveEntry source in zipArchive.Entries)
{
     if (source.FullName.StartsWith("_rels/feature", StringComparison.OrdinalIgnoreCase) &&
source.FullName.EndsWith(".xml.rels", StringComparison.OrdinalIgnoreCase))
     {
          ...
     }

     //fix.
     else if (source.Length == 0)
     {
           using (StreamWriter streamWriter = new StreamWriter(source.Open()))
                 streamWriter.Write(Environment.NewLine);
     }
     //end of fix.
}
This solution works fine for us, and, I hope, will work for you. Good luck!
May 22
Duplicate validation messages of a rich text box

​For some reason the SharePoint shows duplicate validation messages for a rich text box.

22.05.png

I haven't found solution on the Internet and had to create own fix. I found out that these validation messages have different markup.

<span class="ms-formvalidation">
    <span role="alert">You mus specify a value for this required field.</span>
</span>

<span class="ms-formvalidation" role="alert">You mus specify a value for this required field.</span>
After this I made a research and desided to hide second span
.ms-formvalidation[role="alert"] { display: none; }
Apr 01
Change default text of SmallSearchInputBox

Sometimes you have the requirements to change default text of a search box for one site collection in your farm. The good news for you - it can be done with delegate control. There is a declaration in the SharePoint:

   
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control Id="SmallSearchInputBox" Sequence="50" ControlClass="Microsoft.SharePoint.Portal.WebControls.SearchBoxEx"
ControlAssembly="Microsoft.Office.Server.Search, Version=14.0.0.0, Culture=neutral,PublicKeyToken=71e9bce111e9429c">
<Property Name="GoImageUrl">/_layouts/images/gosearch15.png</Property>
<Property Name="GoImageUrlRTL">/_layouts/images/gosearchrtl15.png</Property>
<Property Name="GoImageActiveUrl">/_layouts/images/gosearchhover15.png</Property>
<Property Name="GoImageActiveUrlRTL">/_layouts/images/gosearchrtlhover15.png</Property>
<Property Name="DropDownMode">ShowDD</Property>
<Property Name="SearchResultPageURL">/_layouts/osssearchresults.aspx</Property>
<Property Name="ScopeDisplayGroupName"></Property>
<Property Name="FrameType">None</Property>
</Control>
</Elements>
All you need is to create a feature and add a new delegate control declaration, copy the default declaration and add this line of code:
<Property Name="QueryPromptString">New default text</Property>


If you need to localize this string you can use Resources. For this you should use these steps:
  1. Add resources mapped folder to you project
  2. Add resources files for each culture
  3. Use $Resources taken in the control declaration
<Property Name="QueryPromptString">$Resources:filename,key</Property>
Now the search box displays text in the language that is default for the current web. But this solution does not work if the user can change UI culture and this text should be changed dynamicaly.

K2ScI[1].png

In this case my solution is to add a class to the project, inherit it from SearchBoxEx class
and override QueryPromptString property.
public classSearchBox : SearchBoxEx
{
[Browsable(false)]
public new string QueryPromptString
{
get
{
return base.QueryPromptString;
}
set
{
string fullKey = string.Format("$Resources:{0}", value);
base.QueryPromptString = SPUtility.GetLocalizedString(fullKey, null,
(uint)Thread.CurrentThread.CurrentUICulture.LCID);
}
}
}
Then add these changes in the declaration of delegate control:
1. <Control ControlClass="LocalizedSearch.SearchBox"                        
ControlAssembly="LocalizedSearch, Version=1.0.0.0, Culture=neutral, PublicKeyToken=71df3f77c17b7c69">
</Control>

2. <Property Name="QueryPromptString">filename,key</Property>

I hope this topic will be helpful.
Dec 12
SharePoint 2013 Preview. Field templates issue.
I faced with issue on custom new form of a list. This form had controls going page postbacks. After each postback the fields of boolean and note types are cleared. My first thought was the ViewState of contols is disabled, I thought so because one of the recommendation of the best practices is to disable ViewState if it is possible. After a while I have found out that the ViewState is enabled in all needed controls, but I have also found that the user controls for the fields do not have any value. My research has led me to the article that has good explanation how to create your own field and how new field templates work. In this article I saw that fields use JSLink property to store information about js file for rendering templates. This file is clientforms.js which is located in C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\TEMPLATE\LAYOUTS.

Template for boolean field retrieves data from client context by GetFormContextForCurrentField method of SPClientTemplates.Utility object and then use this data to set value of a field.
var _myData = SPClientTemplates.Utility.GetFormContextForCurrentField(rCtx);
...
var _value = _myData.fieldValue != null ? _myData.fieldValue : '0';
var _checked = Boolean(Number(_value)) ? 'checked="checked"' : '';
The _myData.fieldValue was 0 each times as the templates called, thus I was suspecting that the problem in context initialization. But was wrong. I have found that there is a method to update control state on value changed event.
function OnValueChanged() {
if (_inputElt != null)
    _myData.updateControlValue(_myData.fieldName, GetCheckBoxValue());
}
This method is never invoked, because event binding is not correct.
AddEvtHandler(_inputElt, "onchange", GetCheckBoxValue());
It should be
AddEvtHandler(_inputElt, "onchange", OnValueChanged());
I modified AddEvtHandler, restarted IIS and refreshed the page. It solved a issue with boolean field.

After this I knew what should I looking for in template for note field. I opened template and didn't find event binding. I think that it doesn't exist because control that stores current text for note field is a div element. It's difficult to handle changes in it, thus I decided to write my ouwn and use on lose focus event for this purpose.

AddEvtHandler(_textAreaRte, "onblur", OnEnhancedRichTextValueChanged);

function OnEnhancedRichTextValueChanged() {
if (_textAreaRte != null)
    _myData.updateControlValue(_myData.fieldName, GetEnhancedRichTextControlValue());
}
You can look at my changes here, but I don't recommend you to use this approach because this file is OOTB file of SharePoint and it can be rewrited by SharePoint updates. Instead of it I recommend to use REST api or CSOM and jQuery to performe data on New/Edit form of a list without postbacks.
1 - 10Next