El monstruito no soy yo, es el SharePoint

El Blog de Luis Mañez, dedicado a tecnologías MS, principalmente SharePoint y Office 365
[Office 365] Enable / Disable Feature from Power Shell using CSOM

Here we have 2 PowerShell scripts to Enable-Disable a feature using CSOM

Enable Feature
   1: ##########################################################################################
   2: #    Name:            Enable-SPOFeature
   3: #    Description:    This script enables a feature using CSOM
   4: #    Usage:            .\Enable-SPOFeature -User "name@server.onmicrosoft.com" -Password "Password" -Url "https://sposite.sharepoint.com/" -Feature "4aec7207-0d02-4f4f-aa07-b370199cd0c7" -Scope Site -Sandbox $true -Force $true
   5: #    Creator:        Luis Manez
   6: ##########################################################################################
   8: param([string]$user, [string]$password, [string]$url, [string]$feature, [ValidateSet("Site","Web")][string]$scope, [bool]$sandbox, [bool]$force)
  10: Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll" 
  12: if ($user -eq "") {
  13:     Write-Host "User parameter not provided, exiting" -ForegroundColor Red
  14:     exit 1;
  15: }
  16: if ($password -eq "") {
  17:     Write-Host "Password parameter not provided, exiting" -ForegroundColor Red
  18:     exit 1;
  19: }
  20: if ($url -eq "") {
  21:     Write-Host "Url parameter not provided, exiting" -ForegroundColor Red
  22:     exit 1;
  23: }
  24: if ($feature -eq "") {
  25:     Write-Host "Feature parameter not provided, exiting" -ForegroundColor Red
  26:     exit 1;
  27: }
  28: if ($scope -eq "") {
  29:     Write-Host "Scope parameter not provided, exiting" -ForegroundColor Red
  30:     exit 1;
  31: }
  32: if ($sandbox -eq $null) {
  33:     Write-Host "Sanbox parameter not provided, exiting" -ForegroundColor Red
  34:     exit 1;
  35: }
  36: if ($force -eq $null) {
  37:     $force = $false;
  38: }
  40: function GetClientContext($url, $user, $password) {
  42:     $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
  44:     $context = New-Object Microsoft.SharePoint.Client.ClientContext($url) 
  45:     $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($user, $securePassword) 
  46:     $context.Credentials = $credentials
  48:     return $context
  49: }
  51: # MAIN CODE
  52: #
  54: $featureId = [GUID]($feature)
  55: $clientContext = GetClientContext $url $user $password
  56: write-host "Conected to SharePoint OK"
  58: if ($scope.ToLower() -eq "web") {
  59:         $features = $clientContext.Web.Features
  60: }
  61: else {
  62:     $features = $clientContext.Site.Features
  63: }
  65: $featureDefinitionScope = [Microsoft.SharePoint.Client.FeatureDefinitionScope]::Farm
  66: if ($sandbox) {
  67:     $featureDefinitionScope = [Microsoft.SharePoint.Client.FeatureDefinitionScope]::Site
  68: }
  70: $clientContext.Load($features)
  71: $clientContext.ExecuteQuery()
  74: $features.Add($featureId, $force, $featureDefinitionScope)
  75: try {
  76:     $clientContext.ExecuteQuery()
  77:     write-host "Feature activated"
  78: }
  79: catch {
  80:     write-host "An error ocurred activating Feature. Error detail: $($_)"
  81: }
  • User: user in the Office 365 tenant
  • Password: user password
  • Url: Full URL to the site or web
  • Scope: Site / Web. Scope of the feature that we want to activate.
  • Feature: Feature Id.
  • Force: Re-enable feature if is already activated
  • Sandbox: true/false. Basically this param indicates if the solution where the feature lives, is a Farm solution, or a Sandbox solution.

About the Sandbox param. This is a little confuse if we take a look to the MSDN documentation

If we look at the FeatureDefinitionScope enum, we have:

   1: public enum FeatureDefinitionScope
   2: {
   3:     None,
   4:     Farm,
   5:     Site,
   6:     Web
   7: }

However, the documentation in the Add method says that you can only use 2 values: Farm and Site. This param can make you be wrong, because you can think that is the Scope of the feature, so, if you want to activate a Web scope feature, you’d try with the Web value in the enum type. However, this param indicates the scope of the Solution that contains the feature: Farm or Sandbox solution, so, the possible values are only Farm and Site.

Microsoft, Why are you using an Enum with 4 values in a function that only accept 2 possible values from the Enum ??? OK… that’s another question…

Let’s see now how we can use the same approach to Disable a Feature.

Disable Feature
   1: ##########################################################################################
   2: #    Name:            Disable-SPOFeature
   3: #    Description:    This script disables a feature using CSOM
   4: #    Usage:            .\Disable-SPOFeature -User "name@server.onmicrosoft.com" -Password "Password" -Url "https://sposite.sharepoint.com/" -Feature "4aec7207-0d02-4f4f-aa07-b370199cd0c7" -Scope Site
   5: #    Creator:        Luis Manez
   6: ##########################################################################################
   8: param([string]$user, [string]$password, [string]$url, [string]$feature, [ValidateSet("Site","Web")][string]$scope)
  10: Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll" 
  12: if ($user -eq "") {
  13:     Write-Host "User parameter not provided, exiting" -ForegroundColor Red
  14:     exit 1;
  15: }
  16: if ($password -eq "") {
  17:     Write-Host "Password parameter not provided, exiting" -ForegroundColor Red
  18:     exit 1;
  19: }
  20: if ($url -eq "") {
  21:     Write-Host "Url parameter not provided, exiting" -ForegroundColor Red
  22:     exit 1;
  23: }
  24: if ($feature -eq "") {
  25:     Write-Host "Feature parameter not provided, exiting" -ForegroundColor Red
  26:     exit 1;
  27: }
  28: if ($scope -eq "") {
  29:     Write-Host "Scope parameter not provided, exiting" -ForegroundColor Red
  30:     exit 1;
  31: }
  33: function GetClientContext($url, $user, $password) {
  35:     $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
  37:     $context = New-Object Microsoft.SharePoint.Client.ClientContext($url) 
  38:     $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($user, $securePassword) 
  39:     $context.Credentials = $credentials
  41:     return $context
  42: }
  44: # MAIN CODE
  45: #
  47: $featureId = [GUID]($feature)
  48: $clientContext = GetClientContext $url $user $password
  49: write-host "Conected to SharePoint OK"
  51: if ($scope.ToLower() -eq "web") {
  52:         $features = $clientContext.Web.Features
  53: }
  54: else {
  55:     $features = $clientContext.Site.Features
  56: }
  58: $featureDefinitionScope = [Microsoft.SharePoint.Client.FeatureDefinitionScope]::Farm
  59: if ($sandbox) {
  60:     $featureDefinitionScope = [Microsoft.SharePoint.Client.FeatureDefinitionScope]::Site
  61: }
  63: $clientContext.Load($features)
  64: $clientContext.ExecuteQuery()
  67: $features.Remove($featureId, $false)
  68: try {
  69:     $clientContext.ExecuteQuery()
  70:     write-host "Feature deactivated"
  71: }
  72: catch {
  73:     write-host "An error ocurred deactivating Feature. Error detail: $($_)"
  74: }

That’s all, hope it helps!!

Luis Manez

[Office 365] Using image renditions from Display Templates in SharePoint Online 2013

In this post I will show you how to use image renditions from a custom Display template. If you are new with Display temlates, I suggest you to read the following article from Chris O’Brien, and if you are starting with image renditions, this article from Waldek Mastykarz is perfect.

In this sample, I’m using a Search results webpart (not a CBSWP, that is not available on SharePoint Online), with a custom display template. The display template is configured with this Managed properties:

   1: ctx['DisplayTemplateData']['ManagedPropertyMapping'] = { 'Title': ['Title'], 
   2:     'Path': ['Path'], 'Description': ['Description'], 
   3:     'EditorOWSUSER': ['EditorOWSUSER'], 
   4:     'LastModifiedTime': ['LastModifiedTime'], 
   5:     'CollapsingStatus': ['CollapsingStatus'], 
   6:     'DocId': ['DocId'], 'HitHighlightedSummary': ['HitHighlightedSummary'], 
   7:     'HitHighlightedProperties': ['HitHighlightedProperties'], 
   8:     'FileExtension': ['FileExtension'], 
   9:     'ViewsLifeTime': ['ViewsLifeTime'], 'ParentLink': ['ParentLink'], 
  10:     'DisplayAuthor': ['DisplayAuthor'], 
  11:     'Picture URL': ['PublishingImage', 'PublishingRollupImage', 'Rollup Image'], 
  12:     'ArticleByLineOWSTEXT': ['ArticleByLineOWSTEXT'] };

If we want to get the value of a Managed property on the display template, we can do something like that (of course, the property has to be set on the display template first):

   1: ctx.CurrentItem.Path

or even something like that:

   1: ctx.CurrentItem["Path"]

but it’s a better option to use the following function:

   1: $getItemValue(ctx, "Picture URL")

with this function, we are getting a Srch.valueInfo object, that contains some interesting properties, as we can see in the next image:


  • value: the value of the property
  • isEmpty and isNull: quite useful to check if the property has a value
  • propertylookupName: this is the name of the slot that we are using to store the Managed property.
  • propertyMappings: and array with all the managed properties that we are including in the slot. As we can on the ManagedPropertyMapping array defined on the display template, the slot “Picture URL”, is mapped with 3 managed properties.
  • managedPropertyName: this is the managed property source of the value. In this case, we are mapping the slot with 3 properties, and the value is coming from the “PublishingImage” property.

with this object, we can use a SharePoint JS function to get the image URL with the rendition that we want to apply:

   1: var image = $getItemValue(ctx, "Picture URL");
   2: var imageUrlWithRendition = Srch.U.getImageSourceWithRendition(image, 190, 120)

This code will return the image URL with the width and height parameters:


Unfortunately, the SharePoint JSOM is almost undocumented on the MSDN, so, to find out this function and much more, I suggest you to take a look to all the OOB display templates, and use the Developer tools in Chrome to debug the client code and test the results of some functions.

With the value returned for the function, we can compose other IMG tag, and the result will be an image using the rendition that we want: 190x120.

Also, when you are working with display template, you will find useful to use the Diagnostic display template, which shows you all the managed properties used in the template, with its value and other information. This template is only available for the Content By Search webpart, but with a couple of changes, you can use the template in the Search Resutls webpart if you are working with SharePoint Online. This is the diagnostic template code for the Search Results WP

   1: function ULScqk(){var o=new Object;o.ULSTeamName="Search Server";o.ULSFileName="Item_Diagnostic.js";return o;}
   2: function DisplayTemplate_6be7c3604a7e4519a210e962b4ed867c(ctx) {ULScqk:;
   3:   var ms_outHtml=[];
   4:   var cachePreviousTemplateData = ctx['DisplayTemplateData'];
   5:   ctx['DisplayTemplateData'] = new Object();
   6:   DisplayTemplate_6be7c3604a7e4519a210e962b4ed867c.DisplayTemplateData = ctx['DisplayTemplateData'];
   8:   ctx['DisplayTemplateData']['TemplateUrl']='~sitecollection\u002f_catalogs\u002fmasterpage\u002fDisplay Templates\u002fSearch\u002fItem_Diagnostic.js';
   9:   ctx['DisplayTemplateData']['TemplateType']='Item';
  10:   ctx['DisplayTemplateData']['TargetControlType']=['Content Web Parts'];
  11:   this.DisplayTemplateData = ctx['DisplayTemplateData'];
  13:   ctx['DisplayTemplateData']['ManagedPropertyMapping']={'Picture URL':['PublishingImage', 'PictureURL', 'PictureThumbnailURL'], 'Path':null, 'SecondaryFileExtension':null, 'ContentTypeId':null, 'Line 1':['Title'], 'Line 2':['Description'], 'Line 3':['ArticleByLineOWSTEXT'], 'Line 4':[], 'Line 5':[], 'Line 6':[], 'Line 7':[], 'Line 8':[], 'Line 9':[], 'Line 10':[]};
  14:   var cachePreviousItemValuesFunction = ctx['ItemValues'];
  15:   ctx['ItemValues'] = function(slotOrPropName) {ULScqk:;
  16:     return Srch.ValueInfo.getCachedCtxItemValue(ctx, slotOrPropName)
  17: };
  19: ms_outHtml.push('',''
  20: );
  21: var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_Diagnostic_");
  23: var linkURL = $getItemValue(ctx, "Path");
  24: linkURL.overrideValueRenderer($urlHtmlEncode);
  26: var line1 = $getItemValue(ctx, "Line 1");
  27: var pictureURL = $getItemValue(ctx, "Picture URL");
  28: pictureURL.overrideValueRenderer($urlHtmlEncode);
  29: var pictureId = encodedId + "picture";
  30: var pictureMarkup = pictureURL //Srch.ContentBySearch.getPictureMarkup(pictureURL, 100, 100, ctx.CurrentItem, "cbs-picture3LinesImg", line1, pictureId);
  32: window.cbsDiagnostic_RenderPropertyMappings = function(valueInfoObj)
  33: {ULScqk:;
  34:     var combinedManagedPropertiesMapping = "";
  35:     if(!$isNull(valueInfoObj) && !$isNull(valueInfoObj.propertyMappings) && !$isNull(valueInfoObj.propertyMappings.length))
  36:     {
  37:         for (var i = 0; i < valueInfoObj.propertyMappings.length; i++)
  38:         {
  39:             var managedPropertyName = valueInfoObj.propertyMappings[i];
  40:             combinedManagedPropertiesMapping += i == 0 ? managedPropertyName : String.format(Srch.Res.edisc_MultiValueFormat, managedPropertyName);
  41:         }
  42:     }
  43:     return $htmlEncode(combinedManagedPropertiesMapping);
  44: }
  46: var itemContainerTitle = null;
  47: var canBuildItemContainerTitle = !$isNull(ctx.CurrentItemIdx) && !$isNull(ctx.CurrentGroup) && 
  48:     !$isNull(ctx.CurrentGroup.ResultRows) && !$isNull(ctx.CurrentGroup.ResultRows.length) &&
  49:     !isNaN(ctx.CurrentGroup.ResultRows.length) && !isNaN(ctx.CurrentItemIdx);
  50: if(canBuildItemContainerTitle)
  51: {
  52:     itemContainerTitle = String.format($resource("item_Diagnostic_ItemTitleFormat"), ctx.CurrentItemIdx + 1, ctx.CurrentGroup.ResultRows.length);
  53: }
  55: var containerId = encodedId + "container";
  56: var pictureSlotContainerId = encodedId + "pictureSlotContainer";
  57: var pictureContainerId = encodedId + "pictureContainer";
  58: var pictureLinkId = encodedId + "pictureLink";
  59: var pathContainerId = encodedId + "pathContainer";
  60: ms_outHtml.push(''
  61: ,'        <ul class="cbs-diagnostic-Container" id="', containerId ,'" data-displaytemplate="ItemDiagnostic">'
  62: ,'            <li>'
  63: );
  64: if(!$isEmptyString(itemContainerTitle))
  65: {
  66: ms_outHtml.push(''
  67: ,'                <h3 class="cbs-diagnostic-ContainerTitle">', $htmlEncode(itemContainerTitle) ,'</h3>'
  68: );
  69: }
  70: ms_outHtml.push(''
  71: ,'                <ul id="', pictureSlotContainerId ,'" class="cbs-diagnosticSlot">'
  72: ,'                    <li class="cbs-diagnosticSlotName">', $htmlEncode($resource("item_Diagnostic_PictureSlot")) ,'</li>'
  73: ,'                    <li class="cbs-diagnosticItemProperty">'
  74: ,'                        <ul class="cbs-diagnosticItemContainer">'
  75: ,'                            <li class="cbs-diagnosticItemName">', $htmlEncode($resource("item_Diagnostic_Value")) ,'</li>'
  76: );
  77: if(pictureURL.isEmpty)
  78: {
  79: ms_outHtml.push(''
  80: ,'                            <li class="cbs-diagnosticItemValue">&#160;</li>'
  81: );
  82: }
  83: else
  84: {
  85: ms_outHtml.push(''
  86: ,'                            <li class="cbs-diagnosticItemValue">', $htmlEncode(pictureURL.value) ,'</li>'
  87: );
  88: }
  89: ms_outHtml.push(''
  90: ,'                        </ul>'
  91: ,'                    </li>'
  92: ,'                    <li class="cbs-diagnosticItemProperty">'
  93: ,'                        <ul class="cbs-diagnosticItemContainer">'
  94: ,'                            <li class="cbs-diagnosticItemName">', $htmlEncode($resource("item_Diagnostic_Preview")) ,'</li>'
  95: ,'                            <li class="cbs-diagnosticItemValue">'
  96: ,'                                <div class="cbs-picture3LinesImageContainer" id="', pictureContainerId ,'">'
  97: ,'                                    <a class="cbs-pictureImgLink" href="', linkURL ,'" title="', line1 ,'" id="', pictureLinkId ,'">'
  98: ,'                                        ', pictureMarkup ,''
  99: ,'                                    </a>'
 100: ,'                                </div>'
 101: ,'                            </li>'
 102: ,'                        </ul>'
 103: ,'                    </li>'
 104: ,'                    <li class="cbs-diagnosticItemProperty">'
 105: ,'                        <ul class="cbs-diagnosticItemContainer">'
 106: ,'                            <li class="cbs-diagnosticItemName">', $htmlEncode($resource("item_Diagnostic_MappedManagedProperty")) ,'</li>'
 107: ,'                            <li class="cbs-diagnosticItemValue">', $htmlEncode(pictureURL.managedPropertyName) ,'</li>'
 108: ,'                        </ul>'
 109: ,'                    </li>'
 110: ,'                    <li class="cbs-diagnosticItemProperty">'
 111: ,'                        <ul class="cbs-diagnosticItemContainer">'
 112: ,'                            <li class="cbs-diagnosticItemName">', $htmlEncode($resource("item_Diagnostic_PropertyMappings")) ,'</li>'
 113: ,'                            <li class="cbs-diagnosticItemValue">', cbsDiagnostic_RenderPropertyMappings(pictureURL)  ,'</li>'
 114: ,'                        </ul>'
 115: ,'                    </li>'
 116: ,'                </ul>'
 117: ,'            </li>'
 118: ,'            <li>'
 119: ,'                <ul id="', pathContainerId ,'" class="cbs-diagnosticSlot">'
 120: ,'                    <li class="cbs-diagnosticSlotName">', $htmlEncode($resource("item_Diagnostic_PathSlot")) ,'</li>'
 121: ,'                    <li class="cbs-diagnosticItemProperty">'
 122: ,'                        <ul class="cbs-diagnosticItemContainer">'
 123: ,'                            <li class="cbs-diagnosticItemName">', $htmlEncode($resource("item_Diagnostic_Value")) ,'</li>'
 124: ,'                            <li class="cbs-diagnosticItemValue">', $htmlEncode(linkURL.value) ,'</li>'
 125: ,'                        </ul>'
 126: ,'                    </li>'
 127: ,'                </ul>'
 128: ,'            </li>'
 129: );
 130: for(var lineNum = 1; lineNum <= 10; lineNum++)
 131: {
 132:     var lineValueInfo = $getItemValue(ctx, String.format("Line {0}", lineNum));
 133:     if(!$isNull(lineValueInfo) && !$isEmptyString(cbsDiagnostic_RenderPropertyMappings(lineValueInfo)))
 134:     {
 135:         var lineId = String.format("{0}line{1}", encodedId, lineNum);
 136:         var slotName = String.format($resource("item_Diagnostic_SlotNameFormat"), lineNum);
 137: ms_outHtml.push(''
 138: ,'            <li>'
 139: ,'                <ul id="', lineId ,'" class="cbs-diagnosticSlot">'
 140: ,'                    <li class="cbs-diagnosticSlotName">', $htmlEncode(slotName) ,'</li>'
 141: ,'                    <li class="cbs-diagnosticItemProperty">'
 142: ,'                        <ul class="cbs-diagnosticItemContainer">'
 143: ,'                            <li class="cbs-diagnosticItemName">', $htmlEncode($resource("item_Diagnostic_Value")) ,'</li>'
 144: );
 145:         if(lineValueInfo.isEmpty)
 146:         {
 147: ms_outHtml.push(''
 148: ,'                            <li class="cbs-diagnosticItemValue">&#160;</li>'
 149: );
 150:         }
 151:         else
 152:         {
 153: ms_outHtml.push(''
 154: ,'                            <li class="cbs-diagnosticItemValue">', lineValueInfo ,'</li>'
 155: );
 156:         }
 157: ms_outHtml.push(''
 158: ,'                        </ul>'
 159: ,'                    </li>'
 160: ,'                    <li class="cbs-diagnosticItemProperty">'
 161: ,'                        <ul class="cbs-diagnosticItemContainer">'
 162: ,'                            <li class="cbs-diagnosticItemName">', $htmlEncode($resource("item_Diagnostic_MappedManagedProperty")) ,'</li>'
 163: ,'                            <li class="cbs-diagnosticItemValue">', $htmlEncode(lineValueInfo.managedPropertyName) ,'</li>'
 164: ,'                        </ul>'
 165: ,'                    </li>'
 166: ,'                    <li class="cbs-diagnosticItemProperty">'
 167: ,'                        <ul class="cbs-diagnosticItemContainer">'
 168: ,'                            <li class="cbs-diagnosticItemName">', $htmlEncode($resource("item_Diagnostic_PropertyMappings")) ,'</li>'
 169: ,'                            <li class="cbs-diagnosticItemValue">', cbsDiagnostic_RenderPropertyMappings(lineValueInfo)  ,'</li>'
 170: ,'                        </ul>'
 171: ,'                    </li>'
 172: ,'                </ul>'
 173: ,'            </li>'
 174: );
 175:     }
 176: }
 177: ms_outHtml.push(''
 178: ,'        </ul>'
 179: ,'    '
 180: );
 182:   ctx['ItemValues'] = cachePreviousItemValuesFunction;
 183:   ctx['DisplayTemplateData'] = cachePreviousTemplateData;
 184:   return ms_outHtml.join('');
 185: }
 186: function RegisterTemplate_6be7c3604a7e4519a210e962b4ed867c() {ULScqk:;
 188: if ("undefined" != typeof (Srch) &&"undefined" != typeof (Srch.U) &&typeof(Srch.U.registerRenderTemplateByName) == "function") {
 189:   Srch.U.registerRenderTemplateByName("Item_Diagnostic", DisplayTemplate_6be7c3604a7e4519a210e962b4ed867c);
 190: }
 192: if ("undefined" != typeof (Srch) &&"undefined" != typeof (Srch.U) &&typeof(Srch.U.registerRenderTemplateByName) == "function") {
 193:   Srch.U.registerRenderTemplateByName("~sitecollection\u002f_catalogs\u002fmasterpage\u002fDisplay Templates\u002fSearch\u002fItem_Diagnostic.js", DisplayTemplate_6be7c3604a7e4519a210e962b4ed867c);
 194: }
 196:         $includeLanguageScript("~sitecollection\u002f_catalogs\u002fmasterpage\u002fDisplay Templates\u002fSearch\u002fItem_Diagnostic.js", "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
 198: }
 199: RegisterTemplate_6be7c3604a7e4519a210e962b4ed867c();
 200: if (typeof(RegisterModuleInit) == "function" && typeof(Srch.U.replaceUrlTokens) == "function") {
 201:   RegisterModuleInit(Srch.U.replaceUrlTokens("~sitecollection\u002f_catalogs\u002fmasterpage\u002fDisplay Templates\u002fSearch\u002fItem_Diagnostic.js"), RegisterTemplate_6be7c3604a7e4519a210e962b4ed867c);
 202: }

And finally, if you are working with image renditions, you can provisioning your renditions using server side code, like Waldek shows in his article, and even tough I have not try it, it should work on sandbox, but also, you can do it declaratively, deploying the file “PublishingImageRenditions.xml” to the Master page gallery using a Module.

The “PublishingImageRenditions.xml” looks like that:

   1: <SiteImageRenditions xmlns="http://schemas.datacontract.org/2004/07/Microsoft.SharePoint.Publishing" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
   2:   <NextId>6</NextId>
   3:   <Renditions>
   4:    <ImageRendition>
   5:     <Height>120</Height>
   6:     <Id>5</Id>
   7:     <Name>My custom rendition</Name>
   8:     <Version>1</Version>
   9:     <Width>190</Width>
  10:    </ImageRendition>
  11:     <ImageRendition>
  12:       <Height>100</Height>
  13:       <Id>1</Id>
  14:       <Name>Display Template Picture 3 Lines</Name>
  15:       <Version>1</Version>
  16:       <Width>100</Width>
  17:     </ImageRendition>
  18:     <ImageRendition>
  19:       <Height>100</Height>
  20:       <Id>2</Id>
  21:       <Name>Display Template Picture On Top</Name>
  22:       <Version>1</Version>
  23:       <Width>304</Width>
  24:     </ImageRendition>
  25:     <ImageRendition>
  26:       <Height>220</Height>
  27:       <Id>3</Id>
  28:       <Name>Display Template Large Picture</Name>
  29:       <Version>1</Version>
  30:       <Width>468</Width>
  31:     </ImageRendition>
  32:     <ImageRendition>
  33:       <Height>68</Height>
  34:       <Id>4</Id>
  35:       <Name>Display Template Video</Name>
  36:       <Version>1</Version>
  37:       <Width>120</Width>
  38:     </ImageRendition>
  39:   </Renditions>
  40: </SiteImageRenditions>

My advice is that you create your image renditions using the User Interface, and then download the file and add it to your Visual Studio Module. If you edit the file manually, take care with the nodeNextId” !!

The module for the xml file would be:

   1: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   2:   <Module Name="MO_ImageRenditions" Url="_catalogs/masterpage" RootWebOnly="TRUE">
   3:     <File Path="MO_ImageRenditions\PublishingImageRenditions.xml" Url="PublishingImageRenditions.xml" Type="GhostableInLibrary" Level="Published" ReplaceContent="true">
   4:       <Property Name="ContentTypeId" Value="0x01010012BCF119622FF14793A8A38D5831F25C" />
   5:       <Property Name="ContentType" Value="Document" />
   6:     </File>
   7:   </Module>
   8: </Elements>

And that’s all, with this, your display template will show an image using the desired rendition.

Hope it helps, and let me know any comment.

Luis Manez

Deploying Managed Metadata Fields declaratively in SharePoint 2013 Online (Office 365)

In this article, we will see how we can deploy Managed metadata fields in a declarative way in SharePoint online, showing a new and different approach than in Farm solutions (the old way).

Basically this approach tries to cover the needed of deploying Managed metadata fields from a custom web template using a Sandbox solution. As you probably know, if you want to do this in a farm solution, you will find yourself following this post from Wictor Wilen (http://www.wictorwilen.se/Post/How-to-provision-SharePoint-2010-Managed-Metadata-columns.aspx). This is perfect for a Farm solution, but is not a valid approach for Sandbox solutions and Office 365 because the Microsoft.Sharepoint.Taxonomy.dll is not allowed. Apparently, the only option that we have is to create the Managed Metadata fields using the UI (or maybe some others imaginative solutions with remote events and client code), but if our solution is a custom web template that will be used to create new site collections, that means that the user has to create all the Managed metadata fields for every site collection... sounds not good!

However, Wictor shows us some interesting things about the Schema of a Managed metadata field, that we also see using SharePoint Manager from a Managed metadata field created from the SharePoint UI.

Here is the complete Schema XML of a Managed metadata field created manually:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <Field Type="TaxonomyFieldType" 
   3:        DisplayName="LMLRoleSingleValue" 
   4:        List="{81bbf7e0-763f-4e61-98bc-5fd2c7e9c69c}" 
   5:        WebId="4a38c107-bf70-48fc-87b0-ae101d14c970" 
   6:        ShowField="Term1033" 
   7:        Required="FALSE" 
   8:        EnforceUniqueValues="FALSE" 
   9:        Group="LML" 
  10:        ID="{7baffc59-449d-4743-883b-0b92c5bdd4d6}" 
  11:        SourceID="{4a38c107-bf70-48fc-87b0-ae101d14c970}" 
  12:        StaticName="LMLRoleSingleValue" 
  13:        Name="LMLRoleSingleValue" 
  14:        Version="1">
  15:   <Default></Default>
  16:   <Customization>
  17:     <ArrayOfProperty>
  18:       <Property>
  19:         <Name>SspId</Name>
  20:         <Value xmlns:q1="http://www.w3.org/2001/XMLSchema" p4:type="q1:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">2567ccb0-68f2-46f6-8f4f-36b0bb507422</Value>
  21:       </Property>
  22:       <Property>
  23:         <Name>GroupId</Name>
  24:       </Property>
  25:       <Property>
  26:         <Name>TermSetId</Name>
  27:         <Value xmlns:q2="http://www.w3.org/2001/XMLSchema" p4:type="q2:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">2cd1c0fb-88af-4066-a4fc-4c9734160eb5</Value>
  28:       </Property>
  29:       <Property>
  30:         <Name>AnchorId</Name>
  31:         <Value xmlns:q3="http://www.w3.org/2001/XMLSchema" p4:type="q3:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">00000000-0000-0000-0000-000000000000</Value>
  32:       </Property>
  33:       <Property>
  34:         <Name>UserCreated</Name>
  35:         <Value xmlns:q4="http://www.w3.org/2001/XMLSchema" p4:type="q4:boolean" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">false</Value>
  36:       </Property>
  37:       <Property>
  38:         <Name>Open</Name>
  39:         <Value xmlns:q5="http://www.w3.org/2001/XMLSchema" p4:type="q5:boolean" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">false</Value>
  40:       </Property>
  41:       <Property>
  42:         <Name>TextField</Name>
  43:         <Value xmlns:q6="http://www.w3.org/2001/XMLSchema" p4:type="q6:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">{0ea51395-dcf0-4740-aa9f-2ab8e93bb798}</Value>
  44:       </Property>
  45:       <Property>
  46:         <Name>IsPathRendered</Name>
  47:         <Value xmlns:q7="http://www.w3.org/2001/XMLSchema" p4:type="q7:boolean" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">false</Value>
  48:       </Property>
  49:       <Property>
  50:         <Name>IsKeyword</Name>
  51:         <Value xmlns:q8="http://www.w3.org/2001/XMLSchema" p4:type="q8:boolean" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">false</Value>
  52:       </Property>
  53:       <Property>
  54:         <Name>TargetTemplate</Name>
  55:       </Property>
  56:       <Property>
  57:         <Name>CreateValuesInEditForm</Name>
  58:         <Value xmlns:q9="http://www.w3.org/2001/XMLSchema" p4:type="q9:boolean" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">false</Value>
  59:       </Property>
  60:       <Property>
  61:         <Name>FilterAssemblyStrongName</Name>
  62:         <Value xmlns:q10="http://www.w3.org/2001/XMLSchema" p4:type="q10:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">Microsoft.SharePoint.Taxonomy, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Value>
  63:       </Property>
  64:       <Property>
  65:         <Name>FilterClassName</Name>
  66:         <Value xmlns:q11="http://www.w3.org/2001/XMLSchema" p4:type="q11:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">Microsoft.SharePoint.Taxonomy.TaxonomyField</Value>
  67:       </Property>
  68:       <Property>
  69:         <Name>FilterMethodName</Name>
  70:         <Value xmlns:q12="http://www.w3.org/2001/XMLSchema" p4:type="q12:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">GetFilteringHtml</Value>
  71:       </Property>
  72:       <Property>
  73:         <Name>FilterJavascriptProperty</Name>
  74:         <Value xmlns:q13="http://www.w3.org/2001/XMLSchema" p4:type="q13:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">FilteringJavascript</Value>
  75:       </Property>
  76:     </ArrayOfProperty>
  77:   </Customization>
  78: </Field>

Let’s see the values that we need to focus on:






TaxonomyFieldType (field with a single value)/ TaxonomyFieldTypeMulti (field with multiple terms)


Name of the field


Static name (usually the same as name)


Display name that will show in the UI


Value setted by SP when deploys the field. We can skip this attribute


Value setted by SP when deploys the field. We can skip this attribute


Term1033 (Term + LCID that we are using)


Mandatory field


Only one list item with this value in the field?


Group to show the field in the UI


Value setted by SP when deploys the field. We can skip this attribute


Value setted by SP when deploys the field. We can skip this attribute


GUID of the Managed Metadata Service Application


GUID of the TermSet you want to bind the field


true/false (user can insert new values, only if the TermSet is defined as open)


GUID of the Hidden Note field

There are 3 values that we have no control over them:

· SspId: when you create an Office 365 tenant (you buy it), the Managed metadata service application is created with a name like: “Taxonomy_Zz2Z7zzzZzZzzzzZZzz2zz==” and a GUID. Obviously this value is different from each tenant y we don’t have any control on it.
We can see the GUID from the UI:


· TermSetId: This GUID is assigned when you create a new TermSet. If we create the TermSet manually, we can’t set this GUID. However, as we will see later, we can deploy our Term Store including TermSets and Term, specifying the GUID. So, let’s say that this value will be well-known for us.

· TextField: When SharePoint creates a MM field, actually is creating 2 fields, one is the MM field with the Schema showed above, but it creates a second Field of type Note, and a Hidden field. Again, using SharePoint Manager we can see a field, that usually has the same name as the MM field, but adding a “_0”


The Schema of the Hidden Note field is:

   1: <Field Type="Note"
   2:     DisplayName="LMLRole_0"
   3:     StaticName="d005416fced9454aaf7937757ad56c02"
   4:     Name="d005416fced9454aaf7937757ad56c02"
   5:     ID="{775a2c68-7660-42e5-890c-3cab91dbd88a}"
   6:     ShowInViewForms="FALSE"
   7:     Required="FALSE"
   8:     Hidden="TRUE"
   9:     CanToggleHidden="TRUE" />

So we can deploy this hidden Note field in our sandbox solution, and that way, the TextField value will be a well-known value as well.

Note: When SharePoint deploys the Elements.xml file with our 2 fields: MM and Note field, it follows a sequential order, for this reason, is a requirement that the Note field is before in the Elements.xml than the Taxonomy field. If not, when SP create the Taxonomy field, he won’t find the TextField ID because is not provisioned yet. You won’t get an error, but SP will create its own Note field, and the Taxonomy field will not be mapped correctly.

The only field that we can’t assign by ourselves and will change from one Office 365 tenant to other is the SspId, but later will talk a little more about it.

Now, we are going to see how we can deploy our Taxonomy, and set fixed GUIDs for TermSets and Terms. To achieve this, I think the best approach is to write some CSOM code in a power shell script, which we will run as a part of the deployment. I usually work with a Taxonomy xml file created using the Power Shell commands from Gary Lapointe (http://blog.falchionconsulting.com/index.php/downloads/). That way, you can create all your taxonomy in your local environment, and later, export it to an XML file, that will be deployed in the Office 365 tenant using CSOM.

The next code is not complete but shows the path that you can follow to create TermSets and Terms using CSOM inside PowerShell:

# Get the password as a SecureString and load the client libraries

$securePassword = ConvertTo-SecureString $Password -AsPlainText -Force

Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll"

Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Taxonomy.dll"

# Get the client context

$context = New-Object Microsoft.SharePoint.Client.ClientContext($url)

$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($user, $securePassword)

$context.Credentials = $credentials

# Get the Taxonomy Session and first Term store

$taxonomySession = [Microsoft.SharePoint.Client.Taxonomy.TaxonomySession]::GetTaxonomySession($context);




$termStores = $taxonomySession.TermStores



$termStore = $termStores[0]


# Create a Group

$group = $termStore.CreateGroup($name, $groupGuid);



# Create a Term Set

$termSet = $group.CreateTermSet($name, $id, $termStore.DefaultLanguage);

# Create a Term

$term = $termSet.CreateTerm($name, $lcid, $id);

Well, as I said before, we still have a disadvantage. If we have different environments and Office 365 tenants (test, production…) we have to change manually the SspId before creating the WSP file in every Managed metadata field, which can be hard in some scenarios, but remember, in my opinion is still better than force the user to create all the Taxonomy fields manually on each site collection. Anyway, I’m so lucky for working with Chris O’brien who has solved this inconvenient using MSBuild, and I’m sure will blog about it very short. I will update this article linking Chris’ blog.

Hope it helps, and let me know any comment.

Luis Manez

I’m coming back …

Hola a todos,

No me enrollo mucho. La cosa es que se que he estado muy callado desde hace bastante, y voy a ver si hago algo mas de ruido por aqui. Eso si, os ruego me disculpes, pero los nuevos posts seran en Ingles (mas o menos ingles), ya que estoy trabajando en Londres (http://www.contentandcode.com/) y me viene mejor asi (de hecho no tengo letra “enye” ni acentos, y asi, no se puede…Smile). En cualquier caso, si algo no os queda claro, dejad un comentario y lo hablamos.



Posted: 27/8/2013 17:55 por Luis Mañez | con no comments
Archivado en:
SharePoint 2013 preview: Primeros libros disponibles en pre-order

Se me ha ocurrido lanzar una búsqueda rápida en Amazon sobre SharePoint 2013 y la verdad es que me ha sorprendido que ya aparecen los primeros 6 resultados con libros disponibles para pre-order. Curioso también las fechas previstas de publicación.

El primero que llegará, lo hará relativamente pronto, el 22 de Noviembre de este mismo año, y es de 2 de los grandes de Critical Path Training (nada de extrañar, teniendo en cuenta que han sido los encargados de hacer los videos de training de SP 2013).


La editorial Wrox, aparece también con sus clásicos Professional y Beggining SharePoint para finales de Febrero de 2013, que, de ser cierto, sería señal de que la versión final de SP 2013 andará por esas fechas.


El último que aparece de momento, también se espera para Marzo 2013, y está escrito por otro SharePoint MVP.


Y digo yo, ¿tendremos la suerte de contar con otro “SharePoint 2013 de Principio a Fin”? … ahí lo dejo Sonrisa


Posted: 15/9/2012 19:08 por Luis Mañez | con 1 comment(s)
Archivado en:
SharePoint 2010: Propiedades de Lista ReadSecurity y WriteSecurity

En más de una ocasión he visto el requisito de que los items de una lista, o documentos de una biblioteca de documentos, sólo puedan ser editados por el mismo usuario que los creó, o por usuarios administradores, pero no puedan ser editados por otros usuarios contributors

Antes este requisito, más de una vez he visto soluciones que incluyen manejadores de eventos o workflows, que asignan permisos exclusivos a esos elementos.

Sin embargo, la mayor parte de las ocasiones, se puede solucionar con las propiedades del SPList ReadSecurity y WriteSecurity

Si ReadSecurity es igual a 2, los contributors, sólo pueden acceder a los items que han creado. Con valor 1, todos los usuarios tienen acceso de lectura.

Con WriteSecurity, podemos establecer el valor a 1, 2 ó 4. Dónde:

  • WriteSecurity = 1 : Todos los usuarios pueden modificar cualquier item
  • WriteSecurity = 2 : Los usuarios sólo pueden modificar los ítems que han creado
  • WriteSecurity = 4 : Los usuarios NO pueden modificar ningún item ya creado, sean ellos el creador, o no. Este valor suele tener sentido para listas que representan encuestas o similares.

Recordar que estas 2 propiedades no tienen efecto sobre usuarios administradores o propietario de la lista.


Posted: 8/8/2012 17:29 por Luis Mañez | con no comments |
Archivado en:
SharePoint 2013 preview: publicados varios diagramas técnicos

Recientemente se ha ampliado la información disponible sobre SharePoint 2013 preview, con algunos diagramas técnicos.

Estos diagramas se agrupan en varias categorías:

  • Ejemplos de diseño
  • Arquitectura
  • Upgrade
  • Búsqueda
  • Apps
  • Backup y Recovery
  • Base de datos

Una documentación muy recomendable:


Corporate Portal Path-based Sites SharePoint 2013     Thumbnail of design sample
Posted: 29/7/2012 12:32 por Luis Mañez | con no comments
Archivado en:
SharePoint 2013 preview: Mejoras en Listas externas (Business Connectivity Services)

SharePoint 2013 introduce interesantes mejoras en el servicio de Conectividad de datos empresariales. Algunas de estas mejoras, relativa a listas externas son:

Mejoras a nivel de rendimiento

Se ha mejorado el rendimiento de listas externas gracias a un sistema externo, que se encarga de hacer el paginado, filtrado y ordenado de los datos de la listas externa, antes de que se envíen a SharePoint.

Límite en el número de registros devuelto por el sistema externo

Si se define un filtro de límite en el modelo del BDC, los usuarios pueden especificar el número de registros a retornar por página.

Filtros en datos

Los usuarios podemos filtrar datos externos a través de desplegables. Y los desarrolladores, podemos lanzar queries CAML para hacer filtrados sobre datos externos. Si el filtro de datos está definido en el modelo BDC, éste filtro se realiza en el sistema externo, con la consiguiente mejora de rendimiento que comentaba en el primer punto.

Exportar listas externas a Excel

Podemos exportar datos de listas externas e Excel 2010 y 2013 como si fuera cualquier lista nativa de SharePoint. Se exportan las columnas de la vista actual, y se hace en una única dirección, es decir, el usuario puede usar Excel para jugar con los datos, pero los cambios que haga, no se trasladan al sistema externo.

Si queremos saber más sobre estas mejoras en listas externas, y otras mejoras introducidas por el BCS, os recomiendo este artículo:

What's new for Business Connectivity Services in SharePoint 2013 Preview


Posted: 28/7/2012 21:08 por Luis Mañez | con 1 comment(s)
Archivado en:
SharePoint 2013 preview: Image renditions

Esta nueva característica de SharePoint 2013 nos permite poder mostrar una misma imagen, en diferentes tamaños, según en qué página se muestra.

En este artículo vamos a ver cómo podemos configurarlo, y cuál es su cometido.

Para acceder a la configuración, lo haremos a través de las “Site settings”:


Una vez dentro, lo primero que nos llamará la atención es el mensaje de alerta que nos informa que debemos activar la Blob cache, antes de poder utilizar las Image renditions.


Aquí tenemos las instrucciones detalladas para activar la Blob Cache. Básicamente:

  1. Editar el web.config de nuestra aplicación web
  2. Localizar el nodo <BlobCache
  3. Edita el atributo Location a una ruta válida. Se recomienda que no esté en el mismo disco que el sistema operativo.
  4. Edita el atributo enabled a true.

Por ejemplo:

enabled="true" />

Ojo con las extensiones, aseguraros que la extensión de vuestras imágenes está incluida, o incluirla vosotros.

Con esto, si volvemos a la pantalla de configuracion de Image Renditions, veremos que ha desaparecido el mensaje de alerta.

Vamos a crear un nuevo tamaño, por ejemplo, para las imágenes de "listados”, en modo thumbnail


Ahora vamos a crear una nueva página, e insertamos una nueva imagen en un campo de tipo imagen. Fijaros en que el formulario nos permite elegir la Image Renditions que hemos creado anteriormente


Si por el contrario, estamos insertando una imagen en un campo de texto enriquecido, en la Ribbon tenemos un botón para selecionar también el Image Rendition que queremos usar:


Si seleccionamos nuestro Thumbnail Image Rendition, y guardamos la página, tenemos:


Y si vemos las propiedades de la imagen desde el navegador, vemos como la anchura y altura es de 100px


Pero además, fijaros en la URL de la imagen. SharePoint le ha añadido la Query String RenditionID=5

Esta es otra forma de especificar el RenditionID, donde el ID es el ID que SharePoint le ha asignado cuando hemos creado la Image rendition, y que podemos ver en la página de configuración.

OJO, este parámetro sólo hace efecto si la imagen está dentro de SharePoint. No podemos pasar ese parámetro a una imagen de flikr, por ejemplo.

El atributo RenditionID también se puede utilizar en el makup de un “image field control” de nuestro PageLayout.

Sin duda, esta novedad nos facilita la vida a la hora de redimensionar nuestras imágenes, y nos ayuda a ahorrar disco, ya que no es necesario subir la misma imagen en n tamaños distintos.

Espero que os sirva.


Posted: 24/7/2012 16:47 por Luis Mañez | con no comments
Archivado en: ,
SharePoint 2013 preview: HTML Field Security

Os traigo otra pequeña, pero interesante novedad en SharePoint 2013. Se trata de la opción de configuración del Site “HTML Field Security”


Desde esta opción, podemos configurar si queremos permitir que se inserte IFrames dentro de un campo de texto enriquecido. Podemos decir que SI, que NO, o que sí, pero solo cuando el iFrame apunte a un dominio dentro de una lista. Fijaros:


Espero que os sirva.


Posted: 23/7/2012 19:00 por Luis Mañez | con no comments
Archivado en:
SharePoint 2013 preview: Nueva plantilla “Community site”

En SharePoint 2013 tenemos disponible una nueva plantilla de sitio, llamada “Community site”. Esta plantilla está destinada a la creación de sitios de discusión sobre diferentes temas.


Toda la funcionalidad del site, viene a través de la Feature “Community site Feature”


Disponemos de los siguientes webparts específicos:


Y esta es la pinta que tiene una vez creado el site:


Como veis, el principal elemento de este site, es la lista de Discusiones, que contiene los “topics” con información sobre su estado, el autor, etc.


Además, tenemos una barra superior donde podemos filtrar las discusiones. A pesar de que para ese filtrado, se utilizan vistas sobre la lista, yo no he conseguido añadir una vista personalizada a esa barra. Lo que sí podemos hacer, es establecer nuestra vista personalizada, como la vista por defecto de la lista (desde la configuración del webpart de la lista).

De esta lista, podemos crear nuevos topics, o dar respuesta/comentar los topics existentes. También podemos usar el famoso “like” de las redes sociales o marcar una respuesta cómo “respuesta válida”


Los topics se asocian a una categoría. Dicha categoría se define en otra lista. Sólo se puede asociar a una categoría, sin embargo, al tratarse de una lista, podemos activar la característica de “Managed metadata and Enterprise Keywords”, y poder asignar múltiples “tags” a un topic.

Además de la lista de Discusiones, la plantilla incluye un webpart con accesos directos a las opciones de configuración más típicas del site:


También tenemos un resumen de la actividad del Site, y los usuarios que más contribuyen:


Desde la configuración, podemos definir, entre otras cosas, cuántos puntos otorgamos a un usuario, por realizar determinada acción:


Como vemos, con esta nueva plantilla y toda su funcionalidad, es bastante sencillo conseguir un típico portal de preguntas y respuestas estilo “2.0”. Sin embargo, sigo echando de menos algunas funcionalidades muy interesantes, como por ejemplo, que se puedan dar votos positvos/negativos a los topics, más facilidad para trabajar con metadatos y poder filtrar según esos metadatos, o que antes de crear un nuevo topic, se use el servicio de búsqueda para encontrar otros topics similares y evitar preguntas redundantes.

Para SharePoint 2010, tenemos un producto como RIVER, que sí que tiene todas estas características.

Espero que os sirva.


Posted: 22/7/2012 9:35 por Luis Mañez | con no comments
Archivado en:
SharePoint 2013 preview: Insertando código embebido en nuestra página

Otra pequeña novedad que, pese al tamaño, para mi es bastante de agradecer, es la facilidad a lo hora de insertar código emebebido en el Page Content de nuestras páginas.

Si recordáis, en 2010, había que insertar un CEWP y hacer varios clicks sobre la Ribbon hasta que finalmente podías escribir tu código embebido.

Pues bien, ahora tenemos un nuevo botón en la ribbon, para ello:




Otra mejora Smile


Posted: 21/7/2012 12:56 por Luis Mañez | con no comments
Archivado en:
SharePoint 2013 preview: “Term Property” Nuevo webpart

En el post anterior

SharePoint 2013 Preview: Novedades en el almacén de términos

vimos algunas novedades relacionadas con los Metadatos administrados. En este post, vamos a ver un nuevo webpart relacionado con esos metadatos. Se trata del Term Property Webpart, que tenemos dentro de la categoría Content Rollup


Como su propio nombre indica, este webpart, nos permite sacar información de un Term concreto.

Aquí vemos las opciones que tenemos:


Primero, podemos elegir entre un Term concreto, o el term que se está usando en el contexto de la página.

Luego tenemos las diferentes propiedades que podemos mostrar. Fijaros como tenemos la opción de elegir una Custom Property, que como os contaba en el post anterior, ahora podemos dar de alta y administrar de forma fácil, desde la propia herramienta del Almacén de términos.

Como veis, no están todas las propiedades del Term disponibles, y en concreto, hay una que me escuece que no salga. Se trata de la Category Image, que según vemos en la pantalla de administración del Term, sí debería poderse mostrar con el Term Property webpart:


No sé si me falta hacer algo más (que podría ser), o se trata de una funcionalidad no incluida en esta Preview.

Aún así, este webpart me parece de gran utilidad.

Espero que os sirva.


SharePoint 2013 Preview: Novedades en el almacén de términos

Note: this article applies to SharePoint 2013 Preview (esto lo he visto hacer a los Masters de SP, así que me lo copio Smile)

Tal y como nos han contado ya Andrew Connell y Waldek Mastykarz, SharePoint 2013 nos trae bastantes novedades en la parte de WCM.

Algunas de esas novedades, tienen que ver con los Metadatos administrados. Os cuento algunas de las cosas que he visto hasta el momento.

Desde la herramienta de administración de términos, ahora podemos ver el GUID asignado a un item (TermGroup, TermSet, Term…)


Editor para las Custom Properties. En 2010, cada item del TermStore, tenía un objeto de tipo PropertyBag, que podíamos usar para propiedades personalizadas. En 2013, podemos editar esa bolsa desde la misma herramienta


Intended Use: Desde esta pestaña podemos hacer que los Términos aparezcan como items de navegación:


Al marcarlo para usar en la navegación, tendremos un nuevo menú de navegación en nuestro site:


Desde la configuración de la Navegación del site, podemos deefinir si queremos que se aplica a la Global Navigation y/o Current Navigation


Existen otras novedades que iremos viendo en otros artículos, de momento ya hemos hecho boca Smile

Y por poner un pero, echo de menos que sigamos sin poder Exportar nuestro Almacén de términos a un fichero con el mismo formato que luego usa la herramienta de Importación.

Espero que os sirva.


Office 2013: Primeros post sobre la preview !!

Este post a la carrera es una recopilación inicial de los primeros posts escritos sobre Office 2013. Hace unos minutos que ha terminado la presentación de Steve Ballmer, y la red ya echa humo sobre la nueva versión de Office, que tiene muy buena pinta.

Algunos enlaces de interés:

Office Preview acaba de llegar…y no viene solo!

SharePoint 2013: Pre-requisitos de SW para su instalación!

Descargas de Office 2013

SharePoint 2013 - What you need to know

Understanding SharePoint 2013 Apps (aka: Apps 101)

SP2013 WCM: Forget Everything You Knew About SharePoint WCM!

Try it!!!

A disfrutar!!


SharePoint 2010: descripción y visio de las BDs de SP por versión

Si queréis tener claro qué BDs instala SharePoint, según la versión, además de qué recomendaciones seguir con cada BD (requerimientos de espacio, Read/Write, Backup, Recovery…), os recomiendo el siguiente enlace de Technet

Database types and descriptions (SharePoint Server 2010)

Ahí tendremos las BDs de SharePoint Foundation, Server Standar, Server Enterprise y Project Server.

De cada BD tenemos una tabla con las siguientes características:


Para completar la información, en este enlace tenemos un dibujo visio con todas las BDs, donde de forma gráfica podemos ver qué BDs componen cada versión, y una idea del tamaño de la misma.

Databases That Support SharePoint 2010 Products


La imagen es simplemente para que veáis si os interesa el recurso.

Espero que os sirva.


SharePoint 2010: Mejorando el atributo Title en un sitio de publicación

Si has trabajado con SharePoint 2010 en sitios de publicación, sabrás que el atributo Title de cada página de publicación, saca su valor del propio título de la página:


Bueno, no está mal, pero si estamos en el sitio de Ferrari que se ha debido gastar algunos euros en su sitio web, qué menos que aparezca Ferrari en el title de cada página.

Lo suyo sería montar un title del estilo:

Nombre del sitio raiz :: subsitio :: sub-subsitio…::Página

Si queremos hacer algo de ese estilo “jerárquico”, tenemos un post del gran Waldek Mastykarz donde explica cómo hacerlo, con algo de código.

Si no queremos escribir código y a nivel funcional nos conformamos con que aparezca el nombre del sitio y el título de la página, podemos tirar de SharePoint designer y los controles OOTB de SharePoint.

El atributo title, se define a nivel de Master Page de la siguiente forma:

   1: <title id="onetidTitle"><asp:ContentPlaceHolder id="PlaceHolderPageTitle" runat="server"/></title>

Como vemos, se trata de un ContentPlaceHolder, que será sobrescrito desde el Page Layout.

Editamos el Page Layout que nos interese, y sobrescribimos el PlaceHolder con lo siguiente:

   1: <asp:Content ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
   2:   Titulo gordooo - 
   3:   <SharePoint:ProjectProperty runat="server" id="WebTitle" Property="Title" /> -
   4:   <SharePoint:FieldValue id="PageTitle" FieldName="Title" runat="server"/>
   5: </asp:Content>

Como veis, primero hay un texto fijo, que es algo así como el título principal del sitio web.

Lo siguiente es el control ProjectProperty, que nos permite sacar una propiedad del SPWeb (en este caso, el título del SPWeb donde está la página). Por desgracia este control no se centra a nivel de SiteCollection, de ahí que esté usando un valor fijo antes de este control.

Para acabar, hacemos uso del control FieldValue, que permite sacar un campo de la página, en este caso, el título.

No es una solución tan perfecta como la de Waldek, pero al menos mejora lo que hace SharePoint OOTB con poco esfuerzo.

Espero que os sirva.


Posted: 10/7/2012 20:27 por Luis Mañez | con no comments
Archivado en: ,
SharePoint 2010: ¿La BD de SharePoint log te está comiendo el disco duro?

Hoy un pequeño tip para los developers que no nos preocupamos mucho de nuestro entorno.

Hace unos días observé que me estaba quedando sin espacio en mi disco virtual del entorno de SharePoint. Utilizando la herramienta WinDirStat observé que la BD de SharePoint log, se comía más de 6 GB de espacio en disco!! ahí es ná!

Por suerte, es fácil de solucionarlo, basta con hacer un “Srink” de la BD.


Seleccionamos el FileType a Log:


y aceptamos! y a recuperar disco duro.


Posted: 6/7/2012 15:55 por Luis Mañez | con no comments
Archivado en:
SharePoint 2010: Configurar app web con 2 zonas, una con acceso anónimo mediante script Power-Shell

El siguiente post pretende solucionar una caso de uso típico cuando se construyen sitios públicos en SharePoint.

Si tenemos un sitio público, obviamente tendremos que habilitar el acceso al usuario anónimo. Si lo hacemos, seguramente querremos ocultar la Ribbon de alguna forma, para que el usuario anónimo no vea ningún enlace de “Sign in”.

Pero entonces, ¿Cómo hacemos para que los usuario editores del portal, puedan acceder a la configuración del sitio, crear páginas, etc?

Pues bien, probablemente lo más rápido y sencillo sea decirle al usuario que acceda a una URL específica, protegida, que solicite autenticación al usuario, por ejm:


Pero la verdad es que no queda muy elegante… y SharePoint tiene mucha clase!! Sonrisa

Otra solución algo más elegante, es hacer uso de las Zonas de SharePoint y sus Authentication Providers.

La idea es que la zona Default, esté configurada para que permita el acceso anónimo. Esta zona responderá al host hearder http://www.contoso.com. A esta url accederá el usuario anónimo.

A continuación, extendemos la aplicación web, creamos la zona “Intranet” y hacemos que responda al host header: http://authoring.contoso.com

Los usuarios administradores y editores, accederán por la parte de Authoring, donde el sistema les solicitará credenciales, y, si hemos ocultado la Ribbon usando la técnica que nos cuenta el gran Santi en este post, tendremos la Ribbon para el usuario logado.

Hacer esto es muy sencillo desde la interfaz de la administración central. Yo aquí os dejo un script Power-Shell para conseguir esta configuración. Como este script lo reutilizo en otros escenarios, este script NO crea la aplicación web con el usuario anónimo habilitado. En este link tenéis muy bien explicado cómo hacerlo desde la interfaz. Si queréis también podéis modificar el script para que el comando New-SPWebApplication especifique el parámetro AllowAnonymousAccess

Os dejo el script:

Primero definimos las variables según nuestro entorno

   1: # ******** Set variables for WebApp
   2: $WebAppName = "SharePoint - dpa.heroes.lab80"
   3: $WebAppHostHeader = "dpa.heroes.lab"
   4: $WebAppPort = 80
   5: $WebAppAppPool = "SharePoint - dpa AppPool"
   6: $WebAppAppPoolAccount = "HEROES\sp_apppool" # This User has to be a Sharepoint Manager Account
   7: $WebAppDatabaseName = "WSS_Content_DPA_LAB"
   8: $WebAppDatabaseServer = "Spiderman"
   9: # ******** Set variables for Site Collection
  10: $SiteCollectionName = "DPA desa"
  11: $SiteCollectionURL = ("http://" + $WebAppHostHeader)
  12: $SiteCollectionTemplate = "BLANKINTERNET#2" #see templates: https://www.nothingbutsharepoint.com/sites/devwiki/SP2010Dev/Pages/Site%20Templates%20in%20SharePoint%202010.aspx
  13: $SiteCollectionOwner = "HEROES\lmanez"
  14: #******** Set variables to Extend to Authoring site
  15: $authoringSiteName = "Sharepoint DPA Authoring"
  16: $authoringHostHeader = "authoringdpa.heroes.lab"
  17: $authoringPort = 80

A continuación un par de funciones para crear la aplicación web y la colección de sitios. OJO, el script detecta si ya existe la app web, y pregunta si se quiere re-crear.

   1: function CreateWebApplication([string]$WebAppName, [string]$WebAppHostHeader, $WebAppPort, [string]$WebAppAppPool, [string]$WebAppAppPoolAccount, 
   2:                                 [string]$WebAppDatabaseName, [string]$WebAppDatabaseServer)
   3: {
   4:     Write-Host "Checking SPWebApplication... $WebAppName"
   5:     $target = Get-SPWebApplication | Where {$_.Url -eq ("http://" + $WebAppHostHeader + "/")}
   6:     if ($target -ne $null)
   7:     {
   8:         Write-Host "Removing SPWebApplication..."
   9:         Remove-SPWebApplication ("http://" + $WebAppHostHeader) -DeleteIISSite 
  10:         #-RemoveContentDatabases
  11:         Start-Sleep 5
  12:         iisreset /noforce
  13:         Start-Sleep 5
  14:         Write-Host "SPWebApplication Removed"
  15:     }
  17:     Write-Host "Creating SPWebApplication..."
  18:     New-SPWebApplication -Name $WebAppName -Port $WebAppPort -HostHeader $WebAppHostHeader -URL ("http://" + $WebAppHostHeader) -ApplicationPool $WebAppAppPool -ApplicationPoolAccount (Get-SPManagedAccount $WebAppAppPoolAccount) -DatabaseName $WebAppDatabaseName -DatabaseServer $WebAppDatabaseServer
  19:     Write-Host "SPWebApplication Created"
  20: }
  22: function CreateSiteCollection([string]$SiteCollectionName, [string]$SiteCollectionURL, [string]$SiteCollectionTemplate, [string]$SiteCollectionOwner)
  23: {
  24:     # +++++++++++++ Create a new Sharepoint Site Collection
  25:     Write-Host "Creating SPSite..."
  26:     New-SPSite -URL $SiteCollectionURL -OwnerAlias $SiteCollectionOwner -Template $SiteCollectionTemplate -Name $SiteCollectionName
  27:     Write-Host "SPSite created"
  28: }

Finalmente, llamamos a las funciones y otros comandos Power-Shell:

   1: CreateWebApplication $WebAppName $WebAppHostHeader $WebAppPort $WebAppAppPool $WebAppAppPoolAccount $WebAppDatabaseName $WebAppDatabaseServer
   2: Write-Host "Web Application created succesfully" -ForegroundColor Green
   3: iisreset /noforce
   4: Start-Sleep 30
   5: Write-Host "IIS Restarted succesfully" -ForegroundColor Green
   6: CreateSiteCollection $SiteCollectionName $SiteCollectionURL $SiteCollectionTemplate $SiteCollectionOwner
   7: Start-Sleep 10
   8: iisreset /noforce
   9: Write-Host "Site collection created succesfully" -ForegroundColor Green
  10: Write-Host "Extending authoring..."
  11: Get-SPWebApplication -Identity ("http://" + $WebAppHostHeader) | New-SPWebApplicationExtension –Name $authoringSiteName -HostHeader $authoringHostHeader -Port $authoringPort -Zone Intranet -URL ("http://" + $authoringHostHeader)
  12: Write-Host "Authoring site extended" -ForegroundColor Green

Espero que os sirva.


SharePoint 2010: Localized resource for token 'Direction' could not be found for file with path

Si al activar vuestra Feature, os encontráis con un error similar a este (el token puede ser cualquier Key de los ficheros de Resources de SharePoint):

Localized resource for token 'Direction' could not be found for file with path: "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\Template\Features\My_Feature\…\schema.xml

Se debe al siguiente motivo:

Lo voy a explicar centrado en mi caso. Yo he partido de una definición de lista de SharePoint, creada con la plantilla de Visual Studio 2010. En esa definición, en el schema.xml, sale algo como esto:

   1: <List xmlns:ows="Microsoft SharePoint" 
   2:       Title="My List Definition" 
   3:       FolderCreation="FALSE" 
   4:       Direction="$Resources:Direction;" 
   5:       Url="Lists/My custom List" 
   6:       BaseType="0" 
   7:       EnableContentTypes="True">

Fijaros en el parámetro Direction, que por defecto, la plantilla lo crea con el valor:


Si  te preguntas para qué sirve ese parámetro, lo tienes aquí

El problema de ese valor, es que SharePoint va a buscar el valor de esa Key, en el fichero de Resources por defecto de la feature. Lo buscará en este path:

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\Template\Features\My_Feature\…\

Si ahí no tenemos un fichero llamado Resources.resx, la activación de la feature fallará.

Para solucionarlo, tenemos 3 opciones:

  1. Incluir el fichero Resources.resx (con sus variantes de idiomas que queramos), en esa ruta, y que tenga definida la Key que queremos.
  2. Indicar el parámetro de la Feaure “DefaultResourceFile”, con el nombre del fichero de Resources que queremos utilizar. Dicho fichero debe estar en el path: C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\Resources
  3. Indicar en el mismo valor del parámetro, el fichero de Resources en el que está la clave deseada. En el ejemplo que estamos viendo, sería: Direction="$Resources:core,Direction;"
    De esta forma, indicamos que la key Direction, está en el fichero de Resources Core.resx (dentro del 14\Resources)

Con culquiera de estas 3 soluciones, funcionará.

CURIOSO!! (por llamarlo de alguna manera):

Os puedo asegurar que tengo más de un entorno, donde no me hizo falta hacer ninguna de las 3 soluciones, porque nunca me falló. Este error me lo he encontrado en un entorno nuevo, del que no tengo mucha información, salvo que se ha corregido aplicando la opción 3.

Y por qué ha aparecido de repente??? Pues parece que tiene que ver con algún update de SharePoint 2010 o con los language packs instalados. En este enlace hay algo de información, aunque no lo deja claro.

Espero que os sirva.


Posted: 28/6/2012 14:50 por Luis Mañez | con no comments
Archivado en:
Más artículos Página siguiente >