SPContext.js ‘UpperCase’ of undefined issue

In a SharePoint MVC hosted app the ‘spcontext.js’ files has a piece of code that will append each hyperlink with the ‘SPHostUrl’ attribute.

Great….

But I ran into the fact that the script was crashing for some reason…
I added this piece

if (authority == null)
return false;

Into the file so it does not do an UpperCase on the next line because of the fact that the object is null

Modified function:

// Appends SPHostUrl as query string to all the links which point to current domain.
function appendSPHostUrlToLinks(spHostUrl, currentAuthority) {
$(“a”)
.filter(function () {
var authority = getAuthorityFromUrl(this.href);

//ADDED PIECE OF CODE
if (authority == null)

return false;

if (!authority && /^#|:/.test(this.href)) {
// Filters out anchors and urls with other unsupported protocols.
return false;
}
return authority.toUpperCase() == currentAuthority;
})
.each(function () {
if (!getSPHostUrlFromQueryString(this.search)) {
if (this.search.length > 0) {
this.search += “&” + SPHostUrlKey + “=” + spHostUrl;
}
else {
this.search = “?” + SPHostUrlKey + “=” + spHostUrl;
}
}
});
}

 

 

JQuery Datatables disables automaticlly sorting

I use this datatable ALL the time.
https://datatables.net

For one of my projects I needed tot disable the automaticlly sorting by default.
If your page loads the datatables will sort the first column automaticlly.
To disable this, but the following in your configuration:

“aaSorting”: []

 

This is my complete configuration:

$(‘#table_timesheets_overview’).DataTable(
{
dom: ‘Bft’,
“bSort”: false,
“aaSorting”: []
});

 

Aside

Add jQuery to EditForm.aspx

Tags

, ,

I needed to remove the content editor dropdown you sometimes see in the form.
The most efficient way to do this is to add een Content Editor WebPart to the form of your list.
This way you could also add any javascript to your edit form, or other things to your page.

Here is the code:

SPList list = web.Lists[LIST_NAME];
string EditFormURL = list.DefaultEditFormUrl;

web.AllowUnsafeUpdates = true;

SPFile file = web.GetFile(EditFormURL);

if (null != file)
{
      using (SPLimitedWebPartManager mgr = file.GetLimitedWebPartManagerPersonalizationScope.Shared))
{
if (null != mgr)
{
//create new webpart object
ContentEditorWebPart contentEditor = new ContentEditorWebPart();
//Add content to CEWP
XmlDocument xmlDoc = new XmlDocument();
XmlElement xmlElement = xmlDoc.CreateElement(“Root”);
xmlElement.InnerText = GetValidationScript().ToString();
contentEditor.Content = xmlElement;contentEditor.Content.InnerText = xmlElement.InnerText;

//Add it to the zone
mgr.AddWebPart(contentEditor, “0”, 0);
web.Update();

}
}
}
}

private StringBuilder GetValidationScript()
{

StringBuilder sbScript = new StringBuilder();
string sJQueryURL = “_layouts/JQuery.js”;//exact path of your js file
sbScript.Append(“<script src='” + sJQueryURL + “‘type=’text/javascript’></script>”);
sbScript.Append(“<script type=’text/javascript’>”);
sbScript.Append(“$(document).ready(function() { “);
sbScript.Append(@” $(‘select[id*=””ContentTypeChoice””]’).parent().parent().hide();”);
sbScript.Append(“}); “);
sbScript.Append(“</script>”);
return sbScript;

}

 

 

 

Add custom action in edit contro block with code …

Actually very straight forward if you are a SharePoint Developer:

 

public class MUACSupplierPerformanceEventReceiver : SPFeatureReceiver
{

public const string ACTION_GENERATE_REPORT = “Generate report”;

// Uncomment the method below to handle the event raised after a feature has been activated.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{

SPSecurity.RunWithElevatedPrivileges(delegate()

{

using (SPWeb spWeb = properties.Feature.Parent as SPWeb)

{

if (spWeb.ListExists(GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION))

{

spWeb.AllowUnsafeUpdates = true;

SPList spList = spWeb.Lists[GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION];

RemoveAction(properties);

SPUserCustomAction useraction = spList.UserCustomActions.Add();

useraction.Name = ACTION_GENERATE_REPORT;

useraction.Location = “EditControlBlock”;

useraction.ImageUrl = “/_layouts/Images/fcoscorecard.png”;

useraction.Url = “{SiteUrl}/_layouts/EuroControl/SupplierPerformanceQuestions.aspx?itemId={ItemId}”;

useraction.Sequence = 106;

useraction.Title = ACTION_GENERATE_REPORT;

useraction.Update();

spList.Update();

spWeb.AllowUnsafeUpdates = false;

}

}

});

}

catch (Exception ex)

{

SPTUlsLog.WriteError(ex, SPTUlsLog.Error);

}

}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

{

RemoveAction(properties);

}

private void RemoveAction(SPFeatureReceiverProperties properties)

{

try

{

SPSecurity.RunWithElevatedPrivileges(delegate()

{

using (SPWeb spWeb = properties.Feature.Parent as SPWeb)

{

if (spWeb.ListExists(GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION))

{

spWeb.AllowUnsafeUpdates = true;

SPList spList = spWeb.Lists[GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION];

foreach (SPUserCustomAction action in spList.UserCustomActions)

{

if (action.Title.Equals(ACTION_GENERATE_REPORT))

{

action.Delete();

spList.Update();

break;

}

}

spList.Update();

spWeb.AllowUnsafeUpdates = false;

}

}

});

}

catch (Exception ex)

{

SPTUlsLog.WriteError(ex, SPTUlsLog.Error);

}

}

}

}

 

 

The image url is ignored in SharePoint 2013, because it does not use icons.

 

Hope it helps

 

SharePoint & jQuery

No doubt that jQuery is one of the best things to use in web development.
But when using it in SharePoint it’s a little bit tricky.
SharePoint always puts this in front of your control id: ‘ctl00$PlaceHolderMain$’ depending on which placeholder you placed your control.
When you need to access a control in jQuery in SharePoint your have to write a jQuery filter which is thinking about performance not always the best way to go.

Filter example:

$(“[id$=control_id]”)This filter will look for al controls were the id ends with your control id.
Using this filter will work perfect although writing it again and again pff…

Using this function will save you some time:

function GetSharePointNotation(control) {
return“[id$='” + control + “‘]”;
}

 

Using the function:

$(GetSharePointNotation(“control id”)).show();

 

Have fun

 

 

 

 

 

Set server for SharePoint

When you install SharePoint on your server you have to add roles and features.
This poershell does it for you:

 

Add-WindowsFeature NET-WCF-HTTP-Activation45,NET-WCF-TCP-Activation45,NET-WCF-Pipe-Activation45
Add-WindowsFeature Net-Framework-Features,Web-Server,Web-WebServer,Web-Common-Http,
Web-Static-Content,Web-Default-Doc,Web-Dir-Browsing,Web-Http-Errors,Web-App-Dev,
Web-Asp-Net,Web-Net-Ext,Web-ISAPI-Ext,Web-ISAPI-Filter,Web-Health,
Web-Http-Logging,Web-Log-Libraries,Web-Request-Monitor,Web-Http-Tracing,Web-Security,
Web-Basic-Auth,Web-Windows-Auth,Web-Filtering,Web-Digest-Auth,Web-Performance,Web-Stat-Compression,
Web-Dyn-Compression,Web-Mgmt-Tools,Web-Mgmt-Console,Web-Mgmt-Compat,Web-Metabase,Application-Server,AS-Web-Support,
AS-TCP-Port-Sharing,AS-WAS-Support, AS-HTTP-Activation,AS-TCP-Activation,AS-Named-Pipes,AS-Net-Framework,WAS,
WAS-Process-Model,WAS-NET-Environment,WAS-Config-APIs,Web-Lgcy-Scripting,Windows-Identity-Foundation,Server-Media-Foundation,Xps-Viewer

 

Have fun

Security trimmed sitemapprovider accross site collections

Tags

, , , ,

For one of my customers I needed a top navigation which was the the same acccross their different
sitecollections, and offcourse security trimmed.
The top navigation will look like this:

Afbeelding

So in this post I’m explaining the following :

  • create a custom security trimmed sitemap provider
  • deploying a masterpage accross all sitecollections
  • modifying the web.config
  • activating the masterpage acccross the whole site collection

The solution will look like this:

Afbeelding

To create a custom sitemapprovider a created a sitemap file.
Just add an xml file and rename it:

<siteMap>
<siteMapNodetitle=Containerurl=“”>
<siteMapNodetitle=Global Portaldescription=“”url=//>
<siteMapNodetitle=Test 002description=“”url=~/Test 002>
<siteMapNodetitle=test 001url=~/sites/test 002/001description=“”/>
<siteMapNodetitle=test 002url=~/sites/test 002/002description=“”/>
<siteMapNodetitle=test 003url=~/sites/test 002/003description=“”/>
</siteMapNode>
<siteMapNodetitle=Teamdescription=“”url=~/team/>
<siteMapNodetitle=Information Technologydescription=“”url=~/it/>
<siteMapNodetitle=Legaldescription=“”url=~/legal/>
<siteMapNodetitle=Regionsdescription=“”url=“”>
<siteMapNodetitle=North Americaurl=~/regions/NAdescription=“”/>
<siteMapNodetitle=Middle Easturl=~/regions/medescription=“”/>
</siteMapNode>
</siteMapNode>
</siteMap>

The next step is to tell SharePoint that this provider exists, therefore we need to change the web.config file.
We could do this manually but if you have a large farm mmmm….
I created a feature with a receiver that does the work form:

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Administration;namespace MUAC.SiteMapProvider.Features.MUAC.SiteMapProvider
{
[Guid(“0b9032da-7c51-48da-8c4b-9635d307a31a”)]
public class MUACEventReceiver : SPFeatureReceiver
{
// Uncomment the method below to handle the event raised after a feature has been activated.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{
SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;
webConfigMod(true, webApp);
}
catch (Exception exception)
{

}
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;
UnInstallMasterPage(webApp);
webConfigMod(false, webApp);
}

private void webConfigMod(bool setting, SPWebApplication webApp)
{
try
{

#region Muac Custom Navigator

SPWebConfigModification setting_CustomNavigator = new SPWebConfigModification();
setting_CustomNavigator.Path = “configuration/system.web/siteMap/providers”;
setting_CustomNavigator.Name = “add[@name=’MUAC_CustomNavigator’]”;
setting_CustomNavigator.Sequence = 0;
setting_CustomNavigator.Owner = “MUAC_CustomNavigator”;
setting_CustomNavigator.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
setting_CustomNavigator.Value = @”<add name=””MUAC_CustomNavigator”” siteMapFile=””_layouts/MUAC.sitemap”” type=””MUAC.SiteMapProvider.CustomNavigator, MUAC.SiteMapProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=02fc9494e4423234″” securityTrimmingEnabled=””true”” />”;

#endregion

if (setting)
webApp.WebConfigModifications.Add(setting_CustomNavigator);
else
RemoveProperty(webApp, setting_CustomNavigator, “MUAC_CustomNavigator”);

webApp.WebService.ApplyWebConfigModifications();
webApp.Update();
}
catch (Exception exception)
{

throw;
}
}

In the code I created a method to add or delete a setting from the web.config.
When you activate your feature your web.config will look like this:

<siteMapdefaultProvider=CurrentNavigationenabled=true>
<providers>

<addname=MUAC_CustomNavigatorsiteMapFile=_layouts/MUAC.sitemaptype=MUAC.SiteMapProvider.CustomNavigator, MUAC.SiteMapProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=02fc9494e4423234securityTrimmingEnabled=true />
</providers>
</siteMap>

Dont mind the type property yet, we will get back on this later.
The last thing to do is to modify your masterpage.
I downloaded a version of the v4.master, renamed it and then did the changes on the copy, which is in my case ‘MUAC.master’

You need to search in the masterpage for the ‘SharePoint:AspMenu’.
Right before this control you need to place the following line:

<asp:SiteMapDataSource ShowStartingNode=”False” SiteMapProvider=”MUACNavigationProvider” id=”MUACNavigationProvider” runat=”server”/>

and then you need to modify the ‘SharePoint:AspMenu’ control itself so it looks like this:

<asp:SiteMapDataSource ShowStartingNode=”False” SiteMapProvider=”MUACNavigationProvider” id=”MUACNavigationProvider” runat=”server”/>
<SharePoint:AspMenu
ID=”TopNavigationMenuV4″
Runat=”server”
EnableViewState=”false”
DataSourceID=”MUACNavigationProvider”
AccessKey=”<%$Resources:wss,navigation_accesskey%>
UseSimpleRendering=”true”
UseSeparateCss=”false”
Orientation=”Horizontal”
StaticDisplayLevels=”1″
MaximumDynamicDisplayLevels=”3″
SkipLinkText=””
CssClass=”s4-tn”/>

You need to change the DataSourceId to the one you just added to your masterpage.
Also change the ‘MaximumDynamicDisplayLevels’ else you will not have submenu’s.
I you now save , publish… this master page in to your SharePoint and deploy this feature you will have your menu.

But you need to manually change your masterpage on all sites … , activate the publishing features on all sites…, deploy your masterpage…
This should be done automaticilly!
So we add a new feature site scoped, this feature will only deploy the masterpage.
To do so we need to add a new module to our solution I called it ‘MasterPage’.

Afbeelding

Remove the sample file and add your masterpage (MUAC.master) to your module.
(Right Click add existing file and then search for your altered masterpage)
Open the the ‘Element.xml’ file and modify it so it looks like this:

<Elementsxmlns=http://schemas.microsoft.com/sharepoint/>
<ModuleName=MasterPagesPath=MasterPageRootWebOnly=TRUEUrl=_catalogs/masterpage>
<FileUrl=MUAC.masterType=GhostableInLibraryIgnoreIfAlreadyExists=TRUE >
</File>
</Module>
</Elements>

This module is just deploying the masterpage.
To do so we add a new feature because this cannot be combined with our first feature because that one is not site scoped but webapplication scoped.
The feature looks like this:
Afbeelding

We have now 2 features:

  • To deploy our custom sitemap
  • To deploy the masterpage

Next we want that the masterpage is automaticilly set so we do not have to change every sitecollection manually.
In ourr first feature I added 2 extra methods:

private void InstallMasterPage(SPWebApplication webApp)
{
foreach (SPSite site in webApp.Sites)
{
SPWeb topLevelSite = site.RootWeb;           // Calculate relative path to site from Web Application root.
string webAppRelativePath = topLevelSite.ServerRelativeUrl;
if (!webAppRelativePath.EndsWith(“/”))
webAppRelativePath += “/”;

// Activate publishing infrastructure
site.Features.Add(new Guid(“f6924d36-2fa8-4f0b-b16d-06b7250180fa”), true);

try
{
site.Features.Add(new Guid(“9e9d8436-3a78-4abd-a9d1-d49707ea2116”), true);
}
catch (Exception ex)
{
throw;
}

// Enumerate through each site and apply branding.
foreach (SPWeb web in site.AllWebs)
{
// Activate the publishing feature for all webs.
web.Features.Add(new Guid(“94c94ca6-b32f-4da9-a9e3-1f3d343d7ecb”), true);
web.MasterUrl = webAppRelativePath + “_catalogs/masterpage/MUAC.master”;
web.CustomMasterUrl = webAppRelativePath + “_catalogs/masterpage/MUAC.master”;

web.Update();
}
}
}

private void UnInstallMasterPage(SPWebApplication webApp)
{
try
{
foreach (SPSite site in webApp.Sites)
{
SPWeb topLevelSite = site.RootWeb;

// Calculate relative path to site from Web Application root.
string webAppRelativePath = topLevelSite.ServerRelativeUrl;
if (!webAppRelativePath.EndsWith(“/”))
webAppRelativePath += “/”;

site.Features.Add(new Guid(“9e9d8436-3a78-4abd-a9d1-d49707ea2116”), true);

// Enumerate through each site and apply branding.
foreach (SPWeb subSite in site.AllWebs)
{
subSite.MasterUrl = webAppRelativePath + “_catalogs/masterpage/v4.master”;
subSite.CustomMasterUrl = webAppRelativePath + “_catalogs/masterpage/v4.master”;
subSite.SiteLogoUrl = string.Empty;
subSite.Update();
}
}
}
catch (Exception ex)
{

throw;
}
}

Add ‘InstallMasterPage ‘ in the feature activated and the ‘UnInstallMasterPage’ in the deactivation method.
So this will activate the feature that contains the masterpage if it is their, also activate the publishing features on every site and subsite.
And next it will set the masterpage, when you deactivate it will reverse the settings again.
It just loops through all sitecollections and changes the propery of the masterpage.

try
{
site.Features.Add(new Guid(“9e9d8436-3a78-4abd-a9d1-d49707ea2116”), true);
}
catch (Exception ex)
{
throw;
}

This piece will acctivate our other feature that holds the masterpage, your guid will properbly be different.
You can find it in the properties of the feature, the one that contains the masterpage.
So everything is almost finished, but to make sure our topnavigation bar is security trimmed we need to create our custom Navigator class,
which has a propery called ‘IsAccessibleToUser’ which we can override:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Navigation;
using System.Web;
using System.Xml;namespace MUAC.SiteMapProvider
{
public class CustomNavigator :XmlSiteMapProvider// SPXmlContentMapProvider
{
public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
{
try
{
if (node == null)
{
throw new ArgumentNullException(“node”);
}
if (context == null)
{
throw new ArgumentNullException(“context”);
}
if (!base.SecurityTrimmingEnabled)
{
return true;
}
if (string.IsNullOrEmpty(node.Url))
{
return this.IsGranted(context, node.ChildNodes);
}
return this.IsGranted(context, node);
}
catch
{
return false;
}
}

private bool IsGranted(HttpContext context, SiteMapNode node)
{
bool isGranted = false;
SPSecurity.RunWithElevatedPrivileges(delegate
{
using (SPWeb web = new SPSite(SPContext.Current.Site.MakeFullUrl(node.Url)).OpenWeb())
{
SPUser user = web.AllUsers[context.User.Identity.Name];
if (user != null)
{
try
{
if ((node.Roles != null) && (node.Roles.Count > 0))
{
foreach (string str in node.Roles)
{
isGranted = (str == “*”) || (user.Groups[str] != null);
if (isGranted)
{
break;
}
}
}

isGranted = web.DoesUserHavePermissions(user.LoginName, SPBasePermissions.EmptyMask | SPBasePermissions.ViewPages);
}
catch
{
}
}
}
});

return isGranted;
}

private bool IsGranted(HttpContext context, SiteMapNodeCollection childNodes)
{
bool flag = false;
foreach (SiteMapNode node in childNodes)
{
flag = this.IsGranted(context, node);
if (flag)
{
return flag;
}
this.IsGranted(context, node.ChildNodes);
}
return false;
}
}
}

The code is not hard to understand so I’m not going to explain eveything.
But let’s look add the code of our first feature again where we added a provider into the web.config.
This provider needs to referr to this class and asssembly because we created our own Navigator.
The type property needs to referr to our class !!!

  <add name="MUAC_CustomNavigator" siteMapFile="_layouts/MUAC.sitemap"
 type="MUAC.SiteMapProvider.CustomNavigator, MUAC.SiteMapProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=02fc9494e4423234" securityTrimmingEnabled="true" />

When you now debug it should your class and checking if the user has access or not.

So that actually it, if you have questions or remarks let me know…