Posts Tagged ‘SharePoint’

Problem:

You need check permissions in the xsl transformation of custom field. But the permissions are not evaluated correctly, because they are compared with site level permissions instead of the list item permission mask.

Solution:

You can use build-in xslt template “IfHasRights”, which is used fe. by “Edit” column and is placed in the file fldtypes.xsl. Or you can create and customize own template according to needed permission level check.

  
  <!-- IfHasRightsAddListItems - 2 -->
  <xsl:template name="IfHasRightsAddListItems" ddwrt:ghost="always">
    <xsl:param name="thisNode" select="."/>
    <xsl:variable name="mask" select="$thisNode/@PermMask"/>
    <xsl:variable name="bit" select="substring($mask, string-length($mask))"/>
    <xsl:choose>
      <xsl:when test="$bit = '2' or $bit = '3' or $bit = '6' or $bit = '7' or 
                      $bit = 'A' or $bit = 'a' or $bit = 'B' or $bit = 'b' or $bit = 'E' or $bit = 'e' or $bit = 'F' or $bit = 'f'">1</xsl:when>
      <xsl:otherwise>0</xsl:otherwise>
    </xsl:choose>
  </xsl:template>

… you can change permission mask compare according to table https://vintentou.wordpress.com/2010/06/30/limit-view-from-xslt-according-to-rights/

    
<xsl:variable name="hasRight">
  <xsl:call-template name="IfHasRightsAddListItems">
    <xsl:with-param name="thisNode" select ="$thisNode"/>
  </xsl:call-template>
</xsl:variable>
<xsl:choose>
  <xsl:when test="$hasRight = '1'">
    ... <!-- has AddListItems rights -->
  <xsl:when>
  <xsl:otherwise>
    ... <!-- has not AddListItems rights -->
  <xsl:otherwise>
<xsl:choose>

Problem:
You try add sender name to email address, send email via SPUtility.SendEmail. Sender name in email client contains bad characters starting with =?utf-8?B?.
Sample code causing the problem:

...
string senderAddress = Web.Site.WebApplication.OutboundMailSenderAddress;
string senderName = "Some name with special chars"; // you can use fe. localized web title Web.TitleResource.GetValueForUICulture(receiverCulture)

// add rows here

string sender = senderName + " <" + senderAddress + ">"; 

var messageHeaders = new StringDictionary();
messageHeaders.Add("to", receiver);
messageHeaders.Add("from", sender);
messageHeaders.Add("subject", subject);
messageHeaders.Add("content-type", "text/html");
messageHeaders.Add("charset", "utf-8");

result = SPUtility.SendEmail(Web, messageHeaders, emailBody);
...

Solution:
I think you can use SmtpClient to send email and define encoding but I want to use Sharepoint library :-).
So try to encode senderName to base64 encoding and add rows…

var utf8BytesSenderName = Encoding.UTF8.GetBytes(senderName);
senderName = "=?utf-8?B?" + Convert.ToBase64String(utf8BytesSenderName) + "?=";

You are trying to remove the sharepoint solution using powershell (remove-spsolution ) and you get an error message:
The solution cannot be removed when a job is scheduled or running

Solution:

  • stsadm -o enumdeployments
  • stsadm -o canceldeployment -id
  • remove-spsolution

Problem:

You want to delete site column, but the field has still occurences in some content type(s) and you get the error message:

Site columns which are included in content types or on lists cannot be deleted. Please remove all instances of this site column prior to deleting it.

Solution:

Use powershell to find all occurences of field in content types recursively in all subwebs.

function CheckFieldOccurences($webUrl, $fieldInternalName) 
{  
  $rootweb = Get-SPWeb $webUrl
  $fld = $rootweb.Fields.GetFieldByInternalName($fieldInternalName)
  CheckFieldOccurencesRecursive $rootweb $fld
}

function CheckFieldOccurencesRecursive(($web, $fld)
{  
  $webUrl = $web.Url
  Write-Host "- WEB >> $webUrl" -ForegroundColor Green  
  foreach($list in $web.Lists)  
  {   
    $listTitle = $list.Title
    if ($list.ContentTypesEnabled)   
    {    
      Write-Host "-- LIST >> $listTitle" -ForegroundColor Green    
      $cts = $list.ContentTypes    
      foreach($ct in $cts)    
      {     
        $ctname = $ct.Name     
        Write-Host "--- CONTENT TYPE >> $ctname" -ForegroundColor Green     
        foreach($fl in $ct.FieldLinks)     
        {      
          if ($fl.Id -eq $fld.Id)      
          {              
            write-host "!!! Found occurence in Content Type >> $ctname" -ForegroundColor Red      
          }     
        }    
      }   
    }
    foreach($listFld in $list.Fields)
    {
      if ($listFld.InternalName -eq $fld.InternalName)
      {
        write-host "!!! Found occurence in List >> $listTitle" -ForegroundColor Red
      }
    }
  }  
  foreach($subWeb in $web.Webs)  
  {   
    CheckFieldOccurencesRecursive $subWeb $fld
  } 
}

cls

$webUrl = "http://sharepoint_portal/"

$fieldInternalName = "FieldInternalName"

CheckFieldOccurences $webUrl $fieldInternalName

Delete occurences in content types and try to delete field.

        /// 
        /// Delete all items in list.
        /// Default batch size is 2000 items and order items by id descending.
        /// 
        /// SharePoint list
        public static void ProcessBatchDelete(SPList list)
        {
            ProcessBatchDelete(list, string.Empty, 2000);
        }

        /// 
        /// Delete all items in list. 
        /// 
        /// SharePoint list
        /// Custom query to filter items. Default query order items by id descending.
        /// Batch size
        public static void ProcessBatchDelete(SPList list, string query, uint batchSize)
        {
            if (list == null)
                throw new ArgumentNullException("list");

            if (string.IsNullOrEmpty(query))
                query = "";

            string CMDDELETEITEMTEMPLATE = "" + list.ID + "{0}Delete";
            string BATCHCMDTEMPLATE = "{0}";

            SPQuery spQuery = new SPQuery();
            spQuery.Query = query;
            spQuery.ViewFields = "";
            spQuery.ViewFieldsOnly = true;
            spQuery.ViewAttributes = "Scope=\"RecursiveAll\"";
            spQuery.RowLimit = batchSize;

            SPListItemCollection items;

            while (true)
            {
                // get ids of items to be deleted
                items = list.GetItems(query);
                if (items.Count <= 0)
                    break;

                // make batch command
                StringBuilder itemsBatchCmd = new StringBuilder();
                foreach (SPListItem item in items)
                {
                    itemsBatchCmd.Append(string.Format(CMDDELETEITEMTEMPLATE, item.ID));
                }
                string batchCommand = string.Format(BATCHCMDTEMPLATE, itemsBatchCmd.ToString());

                // process batch command
                bool unsafeUpdate = list.ParentWeb.AllowUnsafeUpdates;
                try
                {
                    list.ParentWeb.AllowUnsafeUpdates = true;
                    list.ParentWeb.ProcessBatchData(batchCommand);
                }
                finally
                {
                    list.ParentWeb.AllowUnsafeUpdates = unsafeUpdate;
                }
            }
        }

This script can be useful when you need find occurences of features in some content database.
For example when you have some “Missing server side dependencies” in Central Administration – Monitoring – Review problems and solutions.

You can see message like this…

[MissingFeature] Database [SharePoint_...] has reference(s) to a missing feature: Id = [...], Name = [...], Description = [...], Install Location = [...]. The feature with Id ... is referenced in the database [SharePoint_...], but is not installed on the current farm. The missing feature may cause upgrade to fail. Please install any solution which contains the feature and restart upgrade if necessary. 

This script is limited only for site collection and site features.

Only change parameters: $featureID and $contentDB.

#  Check-SPFeature

Add-PSSnapin Microsoft.SharePoint.Powershell;

#region params

[string] $featureID = "4a3962d4-8fba-4f51-638b-14b3bd5ea521";
[string] $contentDB = "SharePoint_..."; 

[System.Collections.Generic.List[string]] $occurences = New-Object 'System.Collections.Generic.List[string]';

#endregion

#region functions

function Check-SPFeature([string] $featID, [string] $DBname)
{
	[Microsoft.SharePoint.Administration.SPContentDatabase] $DB = $null;
	$DB = Get-SPDatabase | where { $_.Name -eq $DBname };
	if ($DB -eq $null)
	{
		Write-Host "Cannot find db '$DBName'." -ForegroundColor Red;
	}
	else
	{
		$DB.Sites | ForEach-Object {
			Check-SPObjectRecursive $_ $featID;
		}
		if ($occurences -ne $null -and $occurences.Count -gt 0)
		{
			foreach($item in $occurences)
			{
				Write-Host $item -ForegroundColor Green;
			}
		}
		else
		{
			Write-Host "Feature '$featID' does not exist on '$DBname'." -ForegroundColor Blue;
		}
	}
}

function Check-SPObjectRecursive ($SPObj, [string] $featID)
{
	if ($SPObj -eq $null)
	{
		return;
	}
	
	[Microsoft.SharePoint.SPFeature] $feat = $null;
	[string] $levelName = [String].Empty; 
	[bool] $checkChild = $false;
	[Microsoft.SharePoint.SPWebCollection] $webs = $null;
	
	if ($SPObj -is [Microsoft.SharePoint.SPSite])
	{
		$levelName = "site collection";
		$checkChild = $true;
		$webs = $SPObj.AllWebs;
	}
	elseif ($SPObj -is [Microsoft.SharePoint.SPWeb])
	{
		$levelName = "site";
		$checkChild = $SPObj.Webs.Count -gt 0;
		$webs = $SPObj.Webs;
	}
	else
	{
		return;
	}
	$feat = $SPObj.Features[$featID];
	if ($feat -ne $null) 
	{
		[string] $msg = "Found on " + $levelName + " " + $SPObj.Url + ".";
		if (!$occurences.Contains($msg))
		{
			$occurences.Add($msg);
		}
	}
	if ($webs -ne $null -and $checkChild -eq $true)
	{
		foreach($web in $webs)
		{
		   Check-SPObjectRecursive $web $featID;
	    }
	}
}

#endregion

#region process

cls; 

Check-SPFeature $featureID $contentDB;

#endregion


You can set new homepage for example in web feature on feature activated event…

public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            // set new homepage
            using (SPWeb web = properties.Feature.Parent as SPWeb)
            {
                SPFolder root = web.RootFolder;
                root.WelcomePage = "SitePages/HomePage.aspx"; // relative path to root folder
                try
                {
                    root.ParentWeb.AllowUnsafeUpdates = true;
                    root.Update();
                }
                finally
                {
                    root.ParentWeb.AllowUnsafeUpdates = false;
                }
            }
        }

You can change anything in web.config…

  1. programmatically using SPWebConfigModification: http://msdn.microsoft.com/en-us/library/bb861909.aspx
  2. declarative – create a supplemental .config file: http://msdn.microsoft.com/en-us/library/ms439965(v=office.14).aspx