Experience Sitecore ! | All posts tagged 'API'

Experience Sitecore !

More than 200 articles about the best DXP by Martin Miles

Sitecore PowerShell Extension snippets collection

A collection of Sitecore PowerShell snippets from various sources to have them in one place easy to copy, adjust and execute. Credits to Adam Najmanowicz and Michael West - you guys did a great job!

  1. Write to CSV
  2. Using link fields
  3. Update item presentation final layout
  4. Traverse tree with query
  5. Read from CSV
  6. Output all object properties
  7. Move item
  8. List cms users
  9. Get and edit an item
  10. Generate google sitemap XML
  11. Exclude template
  12. Create users from CSV
  13. Create an item
  14. Convert datetime field value to DateTime object
  15. Change item template
  16. Add base template
  17. Parse a Rich Text to replace elements
  18. Generate a hash of a media file
  19. Exporting the username and custom properties
  20. Show List View with all users' properties
  21. Set workflow for all items
  22. Unlock items of a template recursively
  23. Zip and download sitecore media library folder
  24. Zip and download logs
  25. Updating renderings properties to enable VaryByData
  26. New media item
  27. Update media item
  28. List Roles for User
  29. Remove unused media items
  30. Get items modified in last 7 days

1. Write to csv
More description - normal font
$everythingUnderHome = Get-Item master: -Query "/sitecore/content/Home//*"
$outputFilePath = "C:\temp\my-csv-file.csv"
$results = @();

$everythingUnderHome | ForEach-Object {

  $properties = @{
        Name = $_.Name
        Template = $_.TemplateName
        Path = $_.ItemPath
  }

  $results += New-Object psobject -Property $properties
}

$Results | Select-Object Name,Template,Path | Export-Csv -notypeinformation -Path $outputFilePath

2. Using link fields
More description - normal font
#Get an alias
$alias = = Get-Item -Path "master:/sitecore/system/Aliases/test"

#Get the link field
[Sitecore.Data.Fields.LinkField]$field = $_.Fields["Linked item"]

# the path to the target item
$field.InternalPath

# The guid if internal. Otherwise this is an Empty GUID
$field.TargetID

# 'internal' or 'external'
$field.LinkType

# Other proporties include MediaPath, QueryString and Target

3. Update item presentation final layout
$items = Get-Item master:/content/home//* | Where-Object { $_.Fields["__Final Renderings"] -like "*col-huge*" }

$items | ForEach-Object {    
    Write-Host $_.Fields["__Final Renderings"]
    
    $_.Editing.BeginEdit()
    
    $_["__Final Renderings"] = $_.Fields["__Final Renderings"].toString().replace("col-huge", "col-wide-1");
    
    $_.Editing.EndEdit();
    
    Write-Host $_.Fields["__Final Renderings"]

4. Traverse tree with query
$everythingUnderHome = Get-Item master: -Query "/sitecore/content/Home//*"

$everythingUnderHome | ForEach-Object {

  Write-Host "Item name: " + $_.Name
}

5. Read from CSV
$csv = Import-Csv "C:\temp\my-csv-file.csv"

foreach($row in $csv)
{
    if ($row -eq $csv[0])
    {
        #Skip the first row as it contains the column headings in your CSV
        continue;
    }
    
    #Output value for Column1 and Column2
    Write-Host $row."Column1";
    Write-Host $row."Column2";
}

6. Output all object properties
[Sitecore.Data.Fields.LinkField]$field = $_.Fields["Linked item"]
Write-Host ($field | Format-Table | Out-String)

7. Move item
$item = Get-Item master:/content/home/old/item

#Will move and place this item under the target new
Move-Item -Path $item.ItemPath "master:\sitecore\content\Home\new";

8. List cms users
$allSitecoreUsers = Get-User -Filter "sitecore\*" 

$allSitecoreUsers | ForEach-Object {
    Write-Host $_.Name
}

9. Get and edit an item
$item = Get-Item master:/content/home

$item.Editing.BeginEdit();

$item["Title"] = "New title for the home item!";

$item.Editing.EndEdit();

10. Generate google sitemap XML
$xmlWriter = New-Object System.XMl.XmlTextWriter('c:\temp\sitemap.xml',$Null)
$xmlWriter.Formatting = 'Indented'
$xmlWriter.Indentation = 1
$XmlWriter.IndentChar = "`t"

$xmlWriter.WriteStartDocument()
$xmlWriter.WriteStartElement('urlset')
$XmlWriter.WriteAttributeString('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9')

$everythingUnderHome = Get-Item master: -Query "/sitecore/content/Home//*"
$baseUrl = "https://example.com"

$everythingUnderHome | ForEach-Object {

    $url = $baseUrl + [Sitecore.Links.LinkManager]::GetItemUrl($_)

    $xmlWriter.WriteStartElement('url')
    $xmlWriter.WriteElementString('loc',$url)
    $xmlWriter.WriteEndElement()
}

$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndDocument()
$xmlWriter.Flush()
$xmlWriter.Close()

11. Exclude template
$item = Get-Item -Path master: -Query "/sitecore/content//*[@@templatename !='Sample Item']"

#Or

$item = Get-Item -Path master: -Query "/sitecore/content//*" | Where-Object { $_.TemplateName -ne "Sample Item" -and $_.TemplateName -ne "Local Datasource

12. Create users from CSV
$csv = Import-Csv "C:\temp\my-csv-file.csv"

foreach($row in $csv)
{
    if ($row -eq $csv[0])
    {
        #Skip the first row as it contains the column headings in your CSV
        continue;
    }
    
    #New-User docs: https://doc.sitecorepowershell.com/appendix/commands/New-User.html
    #Include domain in username to specify the target domain (e.g extranet\adam)
    New-User -Identity $row."Username" -Enabled -Password $row."Password" -Email $row."Email" -FullName $row."FullName"
}

13. Create an item
$item = New-Item "master:/content/home/my new sample item" -type "Sample/Sample Item"

14. Convert datetime field value to DateTime object
$dateTime = ([sitecore.dateutil]::IsoDateToDateTime($item["My Date Field"])

Write-Host $dateTime.ToString("dd MMMM yyyy")

#Output 10 September 2016

15. Change item template
$item = Get-Item master:/content/home

$newTemplate = [Sitecore.Configuration.Factory]::GetDatabase("master").Templates["Sample/Sample Item"];

$item.ChangeTemplate($newTemplate)

16. Add base template
$targetTemplate    = Get-item 'master:/sitecore/templates/User Defined/Common/Data';
$templateFilter    = Get-Item "master:/sitecore/templates/System/Templates/Template";
$templateToBeAddAsBaseTemplate     = Get-Item 'master:/sitecore/templates/User Defined/Common/Data/Carousel'
 
 
Get-ChildItem $targetTemplate.FullPath -recurse | Where-Object { $_.TemplateName -eq $templateFilter.Name -and $_.Name -eq "promo"} | ForEach-Object {
    if(-not ($_."__Base template" -match "$($templateToBeAddAsBaseTemplate.Id)")){
           #If not exist then add
         $_."__Base template" = "$( $_."__Base template")|$($templateToBeAddAsBaseTemplate.Id)"
    }
}

17. Parse a Rich Text to replace elements
The easiest solution is probably to find and replace characters rather than parse all of the html:
# Home Item
$rootId = "{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}"

# Get only the immediate children
$items = @((Get-ChildItem -Path "master:" -ID $rootId))
foreach($item in $items) {
    $html = $item.Text
    if([String]::IsNullOrEmpty($html)) { continue }
    $newText = $html.Replace("
    ", "
      ").Replace("
", "") $item.Text = $newText }

18. Generate a hash of a media file
#Generate MD5 Hash of item
function To-Byte-Array
        {
            param (
            [CmdletBinding()]
            [Parameter(Mandatory = $true)]
            [AllowEmptyString()]
            [AllowEmptyCollection()]
            [AllowNull()]
            [Object]
            $InputStream)
             
            $InputStream.Position = 0
            $buffer = New-Object byte[] $InputStream.Length
            $i = 0
            For ($totalBytesCopied=0; $totalBytesCopied -lt $InputStream.Length; $i++) {
                $totalBytesCopied += $InputStream.Read($buffer, $totalBytesCopied, $InputStream.Length - $totalBytesCopied)
            }
             
            $buffer
        }
 
$item = Get-Item master: -ID "{20AA9D26-F1D6-43DB-B8A8-21EC04A5A4CB}" -Language de-DE
    $mediaItem = New-Object Sitecore.Data.Items.MediaItem($item)
    $media = [Sitecore.Resources.Media.MediaManager]::GetMedia($mediaItem)
    $stream = $media.GetStream().Stream
    try {
        $bytes = To-Byte-Array $stream
    }
    finally
    {
        if ($null -ne $stream -and $stream -is [System.IDisposable])
        {
            $stream.Dispose()
        }
    }
     
    $md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
    $hash = [System.BitConverter]::ToString($md5.ComputeHash($bytes))
     
    Write-Host $hash

19. Exporting the username and custom properties
$property = @(
"Name",
@{Name='Email';Expression={ $PSItem.Profile.GetCustomProperty('Email') }},
@{Name='FirstName';Expression={ $PSItem.Profile.GetCustomProperty('FirstName') }},
@{Name='LastName';Expression={ $PSItem.Profile.GetCustomProperty('LastName') }},
@{Name='Title';Expression={ $PSItem.Profile.GetCustomProperty('Title') }},
@{Name='Company';Expression={ $PSItem.Profile.GetCustomProperty('Company') }},
@{Name='Country';Expression={ $PSItem.Profile.GetCustomProperty('Country') }},
@{Name='ZipCode';Expression={ $PSItem.Profile.GetCustomProperty('ZipCode') }},
@{Name='Department';Expression={ $PSItem.Profile.GetCustomProperty('Department') }},
@{Name='Street';Expression={ $PSItem.Profile.GetCustomProperty('Street') }},
@{Name='City';Expression={ $PSItem.Profile.GetCustomProperty('City') }},
@{Name='Phone';Expression={ $PSItem.Profile.GetCustomProperty('Phone') }},
@{Name='Username';Expression={ $PSItem.Profile.GetCustomProperty('Username') }},
)

# Gets not disabled extranet users, next select all customer properties and save all properties to CSV file
Get-User -Filter 'extranet\*'  `
    | Where-Object { $_.Profile.State -ne 'Disabled' } `
        |  Select-Object -Property $property `
            | Export-CSV -Path "$apppath\extranet-enabled-uc.csv" -notype -encoding "unicode"
        
Download-File  "$apppath\extranet-enabled-uc.csv"

20. Show List View with all users' properties
[System.Web.Security.Membership]::GetAllUsers() |

Show-ListView -Property @{Label="User"; Expression={ $_.UserName} },
    @{Label="Is Online"; Expression={ $_.IsOnline} },
    @{Label="Creation Date"; Expression={ $_.CreationDate} },
    @{Label="Last Login Date"; Expression={ $_.LastLoginDate} },
    @{Label="Last Activity Date"; Expression={ $_.LastActivityDate } }

21. Set workflow for all items
## 1. Set default workflow state in template’s standard value ##
## 2. Before running script, must set correct Context Item ##
##################################################################

function SetWorkflow($item)
{
## Update only items assigned __Default workflow
if ($item.”__Default workflow” -eq “{A5BC37E7-ED96-4C1E-8590-A26E64DB55EA}”) {
$item.__Workflow = “{A5BC37E7-ED96-4C1E-8590-A26E64DB55EA}”;
$item.”__Workflow state” = “{190B1C84-F1BE-47ED-AA41-F42193D9C8FC}”;
}
}

## Update correct workflow information.
## Uncomment below two lines if you are ready to go for updating
#get-item . -Language * | foreach-object { SetWorkFlow($_) }
#get-childitem . -recurse -Language * | foreach-object { SetWorkFlow($_) }

## Show Updated Result
get-item . -Language * | Format-Table Id, Name, Language, __Workflow, “__Workflow state”, “__Default workflow”
get-childitem . -recurse -Language * | Format-Table Id, Name, Language, __Workflow, “__Workflow state”, “__Default workflow”

22. Unlock items of a template recursively
Get-ChildItem -Path master:\content\home -Recurse | 
    Where-Object { $_.TemplateId -eq "{ENTER_YOUR_TEMPLATE_GUID}"} | 
    Unlock-Item -PassThru

23. Zip and download sitecore media library folder
  
#
# The ZipFiles function is based on noam's answer
# on the following Stack Overflow's page: http://bit.ly/PsZip
#
function ZipItems( $zipArchive, $sourcedir )
{
  Set-Location $sourcedir
  [System.Reflection.Assembly]::Load("WindowsBase,Version=3.0.0.0, `
      Culture=neutral, PublicKeyToken=31bf3856ad364e35") | Out-Null
  $ZipPackage=[System.IO.Packaging.ZipPackage]::Open($zipArchive, `
      [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite)
  $items = gci -recurse $sourceDir
  [byte[]]$buff = new-object byte[] 40960
  $i = 0;
  ForEach ($item In $items) {
    $i++
    if([Sitecore.Resources.Media.MediaManager]::HasMediaContent($item)){
      $mediaItem = New-Object "Sitecore.Data.Items.MediaItem" $item;
      $mediaStream = $mediaItem.GetMediaStream();
      $fileName = Resolve-Path -Path $item.ProviderPath -Relative
      $fileName = "$fileName.$($item.Extension)" `
        -replace "\\","/" -replace "./", "/"
      # Print out the file - the list will show up once the file is downloaded
      "Added: $fileName"
      # Show progress for the operation
      Write-Progress -Activity "Zipping Files " `
        -CurrentOperation "Adding $fileName" `
        -Status "$i out of $($items.Length)" `
        -PercentComplete ($i *100 / $items.Length)
      $partUri = New-Object System.Uri($fileName, [System.UriKind]::Relative)
      $partUri = [System.IO.Packaging.PackUriHelper]::CreatePartUri($partUri);
      $part = $ZipPackage.CreatePart($partUri, `
        "application/zip",  `
        [System.IO.Packaging.CompressionOption]::Maximum)
      $stream=$part.GetStream();
      do {
        $count = $mediaStream.Read($buff, 0, $buff.Length)
        $stream.Write($buff, 0, $count)
      } while ($count -gt 0)
      $stream.Close()
      $mediaStream.Close()
    }
  }
  $ZipPackage.Close()
}
 
#the location will be set by PowerShell automatically based on which item was clicked
$location = get-location
$dateTime = Get-Date -format "yyyy-MM-d_hhmmss"
$zipName = Split-Path -leaf $location | % { $_ -replace " ", ""}
$dataFolder = [Sitecore.Configuration.Settings]::DataFolder
$zipPath = "$dataFolder\$zipName-$datetime.zip"
 
# Call the Zipping function
ZipItems $zipPath $location
 
#Send user the file, add -NoDialog if you want to skip the download dialogue 
Download-File -FullName $zipPath | Out-Null
 
# Clean up after yourself
Remove-Item $zipPath
 
# Close the results window - we don't really need to see the results
Close-Window

24. Zip and download logs
#
# The ZipFiles function is based on noam's answer
# on the following Stack Overflow's page: http://bit.ly/PsZip
#
function ZipFiles( $zipArchive, $sourcedir )
{
    [System.Reflection.Assembly]::Load("WindowsBase,Version=3.0.0.0, `
        Culture=neutral, PublicKeyToken=31bf3856ad364e35") | Out-Null
    $ZipPackage=[System.IO.Packaging.ZipPackage]::Open($zipArchive, `
        [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite)
    $in = gci $sourceDir | select -expand fullName
    [array]$files = $in -replace "C:","" -replace "\\","/"
    ForEach ($file In $files) {
        $fileName = [System.IO.Path]::GetFileName($file);
            $partName=New-Object System.Uri($file, [System.UriKind]::Relative)
            $part=$ZipPackage.CreatePart("/$fileName", "application/zip", `
                [System.IO.Packaging.CompressionOption]::Maximum)
            Try{
                $bytes=[System.IO.File]::ReadAllBytes($file)
            }Catch{
                $_.Exception.ErrorRecord.Exception
            }
            $stream=$part.GetStream()
            $stream.Write($bytes, 0, $bytes.Length)
            $stream.Close()
    }
    $ZipPackage.Close()
}
 
# Get Sitecore folders and format the zip file name
$dateTime = Get-Date -format "yyyy-MM-d_hhmmss"
$dataFolder = [Sitecore.Configuration.Settings]::DataFolder
$logsFolder = [Sitecore.Configuration.Settings]::LogFolder
$myZipFile = "$dataFolder\logs-$datetime.zip"
 
# Warn that the user log files will fail to zip
Write-Host -f Yellow "Zipping files locked by Sitecore will fail." -n
Write-Host -f Yellow "Files listed below were used."
 
# Zip the log files
ZipFiles $myZipFile $LogsFolder
 
#Download the zipped logs
Get-File -FullName $myZipFile | Out-Null
 
#Delete the zipped logs from t

25. Updating renderings properties to enable VaryByData
$items = Get-ChildItem -Recurse | Where-Object {$_.TemplateID -match “{79A0F7AB-17C8-422C-B927-82A1EC666ABC}”} | ForEach-Object {

$renderingInstance = Get-Rendering -Item $_ -Rendering $rendering
if($renderingInstance){
$renderingInstance.VaryByData = 1
Set-Rendering -Item $_ -Instance $renderingInstance
}
}

26. New media item
function New-MediaItem{
    [CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$filePath,

        [Parameter(Position=1, Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$mediaPath)

$mco = New-Object Sitecore.Resources.Media.MediaCreatorOptions
$mco.Database = [Sitecore.Configuration.Factory]::GetDatabase("master");
$mco.Language = [Sitecore.Globalization.Language]::Parse("en");
$mco.Versioned = [Sitecore.Configuration.Settings+Media]::UploadAsVersionableByDefault;
$mco.Destination = "$($mediaPath)/$([System.IO.Path]::GetFileNameWithoutExtension($filePath))";

$mc = New-Object Sitecore.Resources.Media.MediaCreator
$mc.CreateFromFile($filepath, $mco);
}

# Usage example
New-MediaItem "C:\Users\Adam\Desktop\Accelerators.jpg" "$([Sitecore.Constants]::MediaLibraryPath)/Images"

27. Update media item
function Update-MediaItem{
    [CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$filePath,

        [Parameter(Position=1, Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$mediaPath)

[Sitecore.Data.Items.MediaItem]$item = gi $mediaPath
[Sitecore.Resources.Media.Media] $media = [Sitecore.Resources.Media.MediaManager]::GetMedia($item);
$extension = [System.IO.Path]::GetExtension($filePath);
$stream = New-Object -TypeName System.IO.FileStream -ArgumentList $filePath, "Open", "Read"
$media.SetStream($stream, $extension);
$stream.Close();
}

# Usage example - overwrite the image created with previous cmdlet with new image
Update-MediaItem "$SitecoreDataFolder\Tchotchkeys.jpg" "$([Sitecore.Constants]::MediaLibraryPath)/Images/Accelerators"

28. List Roles for User
function Get-UserRoles{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [string]$Identity
    )
  $user = Get-User $Identity
  return Get-Role -Filter * | Where-Object { $_.IsMember($user,$true,$true) } 
}

Write-Host "Roles for ServicesAPI" -ForegroundColor Green
Get-UserRoles "sitecore\ServicesAPI"

Write-Host "Roles for Admin" -ForegroundColor Green
Get-UserRoles "sitecore\admin"

29. Remove unused media items
#Include all paths you do not want to analyse below 
$protectedPaths = @(
    "/sitecore/media library/System/", 
    "/sitecore/media library/Experience Explorer"
    "/sitecore/media library/Images/Social"
    );
    
#Include all item templates you want to ignore in the array below
$protectedTemplates = @(
    [Sitecore.TemplateIDs]::MediaFolder
    );

$itemsToDelete = 
    Get-ChildItem -Path "master:\sitecore\media library" -Recurse |

        # filter out items of templates you do not want to touch
        Where-Object { $_.TemplateID -notin $protectedTemplates } |

        # do not allow items in the protected paths
        Where-Object { 
            $item = $_; 
            $protected = $protectedPaths | Where-Object { ($item.Paths.Path) -match $_ };
            $protected.Count -lt 1;
        } | 

        # and only items that are not used
        Where-Object { [Sitecore.Globals]::LinkDatabase.GetReferrerCount($_) -eq 0 }

#List the items
$itemsToDelete | ft ProviderPath

#If the list above looks like what you want to delete you can uncomment the following line
#$itemsToDelete | Remove-Item

30. Get items modified in last 7 days
$days = 7;
Read-Variable -Parameters @{ Name="days"; Title="Number of days"} -Title "Pointy haired report" | Out-Null

Get-ChildItem master:\content\home -Recurse | 
    ? { $_."__updated" -gt (Get-Date).AddDays(-$days) } | 
    Show-ListView -Property ID, Name, "__updated", "__updated by"

Few SQL tricks helping to work with Sitecore

1. Delete all the databases having a specific prefix, in the example below it will drop all the databases starting with habitat.dev.local_ - this may be helpful when Sitecore 9 installation broke half way down and you'd like to return everything to an initial point, also removing (partly) installed databases:

USE MASTER
GO

DECLARE @dbnames NVARCHAR(MAX)
DECLARE @statement NVARCHAR(MAX)
SET @dbnames = ''
SET @statement = ''
SELECT @dbnames = @dbnames + ',[' + name + ']' FROM sys.databases WHERE NAME LIKE 'habitat.dev.local_%'
IF LEN(@dbnames) = 0
    BEGIN
    PRINT 'no databases to drop'
    END
ELSE
    BEGIN
    SET @statement = 'drop database ' + SUBSTRING(@dbnames, 2, LEN(@dbnames))
    PRINT @statement
    EXEC sp_executesql @statement
    END


2. Backup all SQL database having some prefix (in this case, habitat.dev.local_) to a folder (like C:\DatabaseBackups\):

DECLARE @name VARCHAR(50) -- database name  
DECLARE @path VARCHAR(256) -- path for backup files  
DECLARE @fileName VARCHAR(256) -- filename for backup  
DECLARE @fileDate VARCHAR(20) -- used for file name
DECLARE @scPrefix VARCHAR (20) -- Sitecore database prefix

-- specify database backup directory
SET @path = 'C:\DatabaseBackups\' 

-- specify filename format
SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112) 

-- specify the Sitecore db prefix
SET @scPrefix = 'habitat.dev.local_'
DECLARE db_cursor CURSOR FOR SELECT name FROM master.dbo.sysdatabases WHERE CharIndex(@scPrefix, name) = 1 OPEN db_cursor FETCH NEXT FROM db_cursor INTO @name WHILE @@FETCH_STATUS = 0 BEGIN SET @fileName = @path + @name + '_' + @fileDate + '.BAK' BACKUP DATABASE @name TO DISK = @fileName FETCH NEXT FROM db_cursor INTO @name END CLOSE db_cursor DEALLOCATE db_cursor

3. Shrink all databases larger than a certain number of megabytes (stored in @megabytesMoreToShrink variable):

DECLARE @megabytesMoreToShrink INT SET @megabytesMoreToShrink = 100

DECLARE @name NVARCHAR(MAX) 
DECLARE @logical_name NVARCHAR(MAX) 

DECLARE db_cursor CURSOR FOR 
SELECT db.name AS DBName, mf.Name AS Logical_Name
FROM sys.master_files mf INNER JOIN sys.databases db ON db.database_id = mf.database_id Where type_desc = 'LOG' AND (size*8)/1024 > @megabytesMoreToShrink

OPEN db_cursor  
FETCH NEXT FROM db_cursor INTO @name, @logical_name  

WHILE @@FETCH_STATUS = 0  
BEGIN  

              exec('USE ' + @name + ' ;ALTER DATABASE ' + @name + ' SET RECOVERY SIMPLE;DBCC SHRINKFILE ('''+@logical_name +''', 1);ALTER DATABASE '+@name +' SET RECOVERY FULL')

              FETCH NEXT FROM db_cursor INTO @name, @logical_name  
END  

CLOSE db_cursor  
DEALLOCATE db_cursor

4. Change sa password for SQL instance, you do not need to know the previous password at all, but need to have an administrative access, for sure. Run osql tool, it's path should be added to the environment variable, but if it's not there, please find it here: "%ProgramFiles%\Microsoft SQL Server\130\Tools\Binn\OSQL.exe":

osql -S SITECOREDEV -E
1> sp_password NULL, 'SA_PASSWORD','sa'
2> GO

5. Some basic item's operations, such as retrieving by ID, by parent and retrieving fields and versions:

-- Get item by Sitecore ID
SELECT TOP 1000 *
  FROM [habitat_Master].[dbo].[Items]
  WHERE ID = '{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}'

-- Get item by ID of parent in Sitecore
SELECT TOP 1000 *
  FROM [habitat_Master].[dbo].[Items]
  WHERE ID = '{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}'

-- Enumerate all the versioned fieds for an item in Sitecore by item ID
SELECT TOP 1000 
  i.Name, v.[Id], [ItemId], [LANGUAGE], [FieldId], [Value], v.[Created], v.[Updated]
  FROM [habitat_Master].[dbo].[versionedFields] v
  JOIN [habitat_Master].[dbo].Items i 
    ON v.FieldId = i.ID
  WHERE ItemId = '{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}'
  ORDER BY [Name]

5. Some basic item's operations, such as retrieving by ID, by parent and retrieving fields and versions:

-- Get item by Sitecore ID
SELECT TOP 1000 *
  FROM [habitat_Master].[dbo].[Items]
  WHERE ID = '{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}'

-- Get item by ID of parent in Sitecore
SELECT TOP 1000 *
  FROM [habitat_Master].[dbo].[Items]
  WHERE ID = '{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}'

-- Enumerate all the versioned fieds for an item in Sitecore by item ID
SELECT TOP 1000 
  i.Name, v.[Id], [ItemId], [LANGUAGE], [FieldId], [Value], v.[Created], v.[Updated]
  FROM [habitat_Master].[dbo].[versionedFields] v
  JOIN [habitat_Master].[dbo].Items i 
    ON v.FieldId = i.ID
  WHERE ItemId = '{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}'
  ORDER BY [Name]

6. One of the most popular snippets, to restore default admin / b password. Please keep in mind running that against core database:

-- use core database
UPDATE dbo.aspnet_Membership
SET
    Password='qOvF8m8F2IcWMvfOBjJYHmfLABc=',
    PasswordSalt='OM5gu45RQuJ76itRvkSPFw=='
WHERE
    UserId = (SELECT UserId FROM dbo.aspnet_Users WHERE UserName = 'sitecore\Admin')

7. Copy users and all their data including passwords from core database of one instance to another:

-- Copy all non-existing users and their data from OLD core database to NEW core database
INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_Applications SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_Applications 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_Applications WHERE [Sitecore.NEW.Core].dbo.aspnet_Applications .ApplicationId = [Sitecore.OLD.Core].dbo.aspnet_Applications.ApplicationId)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_Membership SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_Membership 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_Membership WHERE [Sitecore.NEW.Core].dbo.aspnet_Membership .UserId = [Sitecore.OLD.Core].dbo.aspnet_Membership.UserId)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_Paths SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_Paths 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_Paths WHERE [Sitecore.NEW.Core].dbo.aspnet_Paths .PathId = [Sitecore.OLD.Core].dbo.aspnet_Paths.PathId)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_PersonalizationAllUsers SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_PersonalizationAllUsers 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_PersonalizationAllUsers WHERE [Sitecore.NEW.Core].dbo.aspnet_PersonalizationAllUsers.PathId = [Sitecore.OLD.Core].dbo.aspnet_PersonalizationAllUsers .PathId)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_PersonalizationPerUser SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_PersonalizationPerUser 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_PersonalizationPerUser WHERE [Sitecore.NEW.Core].dbo.aspnet_PersonalizationPerUser.PathId = [Sitecore.OLD.Core].dbo.aspnet_PersonalizationPerUser .PathId)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_Profile SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_Profile 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_Profile WHERE [Sitecore.NEW.Core].dbo.aspnet_Profile.UserId = [Sitecore.OLD.Core].dbo.aspnet_Profile.UserId)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_Roles SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_Roles 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_Roles WHERE [Sitecore.NEW.Core].dbo.aspnet_Roles.RoleName = [Sitecore.OLD.Core].dbo.aspnet_Roles.RoleName)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_SchemaVersions SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_SchemaVersions 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_SchemaVersions WHERE [Sitecore.NEW.Core].dbo.aspnet_SchemaVersions.Feature = [Sitecore.OLD.Core].dbo.aspnet_SchemaVersions.Feature)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_Users SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_Users 
WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_Users WHERE [Sitecore.NEW.Core].dbo.aspnet_Users.UserName = [Sitecore.OLD.Core].dbo.aspnet_Users.UserName)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_UsersInRoles SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_UsersInRoles 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_UsersInRoles WHERE [Sitecore.NEW.Core].dbo.aspnet_UsersInRoles.UserId = [Sitecore.OLD.Core].dbo.aspnet_UsersInRoles.UserId)

INSERT INTO [Sitecore.NEW.Core].dbo.aspnet_WebEvent_Events SELECT * FROM [Sitecore.OLD.Core].dbo.aspnet_WebEvent_Events 
    WHERE NOT EXISTS (SELECT * FROM [Sitecore.NEW.Core].dbo.aspnet_WebEvent_Events WHERE [Sitecore.NEW.Core].dbo.aspnet_WebEvent_Events.EventId = [Sitecore.OLD.Core].dbo.aspnet_WebEvent_Events .EventId)

8. Delete children item of an item with a given ID directly from SQL (beware item cache). Please make sure you execute that in a context of a Sitecore database (ie. call use Sitecore_master before running this):

DECLARE @parentId AS UNIQUEIDENTIFIER; SET @parentId = '{29267838-AAB2-415F-B07A-D006719CD088}'
DECLARE @RowsToProcess  INT
DECLARE @CurrentRow     INT
DECLARE @SelectCol1     UNIQUEIDENTIFIER
DECLARE @Items2Delete TABLE (RowID INT NOT NULL PRIMARY KEY IDENTITY(1,1),ID UNIQUEIDENTIFIER)

SET @parentId = '{29267838-AAB2-415F-B07A-D006719CD088}'

INSERT INTO @Items2Delete (ID)
SELECT [Descendant] AS ID
  FROM [dbo].[Descendants]
  WHERE Ancestor = @parentId
SET @RowsToProcess=@@ROWCOUNT
PRINT  @RowsToProcess

INSERT INTO @Items2Delete (ID)
SELECT ID FROM [dbo].[Items] WHERE ParentID = @parentId
SET @RowsToProcess=@@ROWCOUNT
PRINT  @RowsToProcess

SET @CurrentRow=0
WHILE @CurrentRow<@RowsToProcess
BEGIN
    SET @CurrentRow=@CurrentRow+1
    SELECT
        @SelectCol1=ID
        FROM @Items2Delete
        WHERE RowID=@CurrentRow

  DECLARE @id AS UNIQUEIDENTIFIER;

  SET @id = @SelectCol1
  PRINT @id

  EXEC sp_executesql N'DELETE FROM [Items]
        WHERE [ID] = @itemId

        DELETE FROM [SharedFields]
        WHERE [ItemId] = @itemId

        DELETE FROM [UnversionedFields]
        WHERE [ItemId] = @itemId

        DELETE FROM [VersionedFields]
        WHERE [ItemId] = @itemId',N'@itemId UNIQUEIDENTIFIER',@itemId=@id

  EXEC sp_executesql N'DELETE FROM [Descendants] WHERE [Descendant] = @itemId',N'@itemId uniqueidentifier',@itemId=@id
  EXEC sp_executesql N' DELETE FROM [Links] WHERE [SourceItemID] = @itemID AND [SourceDatabase] = @database',N'@itemID uniqueidentifier,@database nvarchar(6)',@itemID=@id,@database=N'master'
  EXEC sp_executesql N'DELETE FROM [Tasks] WHERE [ItemID] = @itemID AND [Database] = @database',N'@itemID uniqueidentifier,@database nvarchar(6)',@itemID=@id,@database=N'master'
END

Hope that helps you!

Mass update of field value for all existing items of certain template - two ways of achieving result

Given: I have multiple already existing items of a specific template across my content tree. Template has some field that I want to update with a new value for all existing items, within some node I can specify.

Use cases for that scenario are really universal - you may use that for various purposes. What comes into my head, at a first glance, is the situation when you would like to assign some template to a workflow but already have multiple existing items of that template (just to remind - setting a field value at Standard Values will apply for all new, but not existing items of that particular template). In that case you need assign a workflow to each existing item individually.

Another good example is when you have a folder with multiple items, growing in amount with time flow. Then you may decide to make a folder bucketable, but those existing items require to set a Bucketable checkbox for each existing item individually. I have described that case in one of my previous blog posts - "Understanding Buckets: adding new items to buckets correctly". So let's pick up second case and try to solve it.

Solution: So, what are possible ways of achieving that goal? Not so much of them, there are just two options.

1. Straightforward option - use C# code. With C# task may be achieved by a recursive method, that accepts an item, verifies if the one is of desired template and if yes - updates the field and does the same for all item's children if any. Here is an example of such method and example of call :

private void UpdateAllFieldsRecursively(Item parentItem, string templateName, string fieldName, string newValue)
{
    if (parentItem != null)
    {
        using (new SecurityDisabler())
        {
            foreach (Item childItem in parentItem.Children)
            {
                if (childItem.Fields[fieldName] != null && childItem.TemplateName == templateName)
                {
                    using (new EditContext(childItem))
                    {
                        childItem[fieldName] = newValue;
                    }
                }

                if (childItem.HasChildren)
                {
                    UpdateAllFieldsRecursively(childItem, templateName, fieldName, newValue);
                }
            }
        }
    }
}

// and below there is the call to recursive mehod
const string parentNode = "/sitecore/content";
var database = Sitecore.Context.Database;
var parentItem = database.GetItem(parentNode);

UpdateAllFieldsRecursively(parentItem, "Article", "Bucketable", "1");

The code above works out perfectly and does exactly what we want from it, however there are certain disadvantages: first of all you need to run the code in some context - call if from some existing class, for example. Second - as that is C# code - it must be re-compiled and re-deployed. And if you may need to amend small change into the code - will have to re-compile and re-deploy again. But, luckily, there is a much better and faster alternative:

2. PowerShell Module. That module integrates into the platform and offers unprecedentedly ultimate possibilities and power over Sitecore instance and databases. After installation there is PowerShell console and more advanced PowerShell ISE, where you may create and test scripts. They are executed immediately without any of prerequisites like rebuilt DLL with a code at your bin folder. So, here is a script that does exactly the same as previous C# code:

cd 'master:/sitecore/content'
Get-ChildItem -Recurse . | Where-Object { $_.TemplateName -match "Article" -and $_.Fields["Bucketable"] -ne $null } | ForEach-Object {
   
    $_.Editing.BeginEdit()
    $_.Fields["Bucketable"].Value = "1";
    $_.Editing.EndEdit()
    ""
}

PowerShell script above does exactly the same - iterates Sitecore tree for "Article" templates, starting recursively from /sitecore/content node and if anything found - updates the filed value.

Hope this code helps!

API - Useful IDisposable context switchers

Sitecore API provide several IDisposable context switchers, so I thought it's worth to review them in one place. Here they are:
  1. EditContext
  2. EventDisabler
  3. SecurityDisabler
  4. UserSwitcher
  5. LanguageSwitcher
  6. DatabaseSwitcher
  7. SiteContextSwitcher
  8. DatabaseCacheDisabler
  9. LockingDisabler
  10. LinkDisabler
1. EditContext
This is a very elegant way of editing an item in sitecore. What it does is automatically does item.Editing.BeginEdit(), item.Editing.EndEdit() as well as wraps that into try / catch block with item.Editing.CancelEdit() on Exception. You may map additional parameters into constructor to skip updating statistics (ie. last modified item's system field), suppress raising events (like item:save event) and also you may SecurityDisabler as a constructor parameter. 
// default usage
using (new EditContext(item))
{
    item.Fields["fieldname"] = "Some value to set";
}
// example of using with supressing events and avoiding statistics update
bool updateStatistics = false;  
bool silent = true;  
using (new EditContext(item, updateStatistics, silent))  
{  
    item.Fields["fieldname"] = "Some value to set";
}
// using EditContext and disable SecurityCheck together
using (new EditContext(iem, SecurityCheck.Disable))  
{  
    item.Fields["fieldname"] = "Some value to set";
}

2. EventDisabler
It gives exactly the same effect as if you were using Item.Editing.EndEdit(bool silent), passing true within silent. No events will be raised.
using (new EventDisabler())
{
    item.Editing.BeginEdit();
    // do edits
    item.Editing.EndEdit();
}

3. SecurityDisabler
Turns of permission check for operations performed within using block, regardless of if the user from the context can or cannot create / modify / move / rename /delete items. Beware as it may be insecure especially on CD instances. There is an alternative for this method - UserSwitcher (see below at 4). Here is an example of  SecurityDisabler usage:
using (new SecurityDisabler())
{
    subNode = homeItem.Add("SubNode", templateId);
    using (new EditContext(subNode))
    {
        subNode[fieldIdA] = "test";
        subNode[fieldIdB] = "testBBB";
    }
}

4. UserSwitcher
That is recommended practice (by Sitecore) to be used instead of SecurityDisabler. Everything you do is done in the context of a specific user. The user can be an Administrator and can do everything. Maybe you want to create a "service user" with only specific permissions or whatever, you can use this user to do what you want.
If you use the SecurityDisabler - Sitecore will not do any permission checks at all. In fact, the result will be the same as if you use the UserSwitcher() with an administrator, but you have no control over the context.
//User which is already created in Sitecore User Manager
 string testUser = @"sitecore\testuser"; 
if (Sitecore.Security.Accounts.User.Exists(testUser))
{
    //Getting Sitecore User Object with UserName
    Sitecore.Security.Accounts.User scUser = Sitecore.Security.Accounts.User.FromName(testUser, false); 
    //Switching Context User
     using (new Sitecore.Security.Accounts.UserSwitcher(scUser))
    {
        //Using EditContext to edit an Item
        using (new Sitecore.Data.Items.EditContext(home))
        {
            home["Text"] = "Modified Text from Code";
        }
    }
}

5. LanguageSwitcher
When working on multi-lingual website, you may have a need in retrieving the values in the language different than one you're having currently in the Context. Please consider the following code to achieve that:
var item = Sitecore.Context.Item;
using (new Sitecore.Globalization.LanguageSwitcher("fr"))
{
    item = myItem.Database.GetItem(item.ID);
}


6. DatabaseSwitcher
Sometimes you may need to do some database operations from the database
another than you currently have in Context, for instance you may have a specific task to look-up master database, or read some setting from core. Of course, such type of activities is fairly an exclusion from normal practice, so think well ahead. In any case, the following using switcher will be handy for that type of situations:
var master = Sitecore.Configuration.Factory.GetDatabase("master"))
using (new DatabaseSwitcher(master))
{
// now within this scope Sitecore.Context.Database is returning master database
}

7. SiteContextSwitcher
That one is use to substitute current site context. Usage is pretty easy:
var item = Sitecore.Context.Item;
using (new SiteContextSwitcher(SiteContextFactory.GetSiteContext("site_name_as_in_config")))
{
    // all the code here would run in the context of "site_name_as_in_config" site
}

8. DatabaseCacheDisabler
This one has become one of my favorites. When working with many items in background tasks etc., it will use the regular Items cache for fetching items, but on cache misses, it won’t put items loaded from the database into the items cache. This avoids the items cache from being polluted by for example a background task traversing multiple items. As the number of items grows in a solution, it’s important that the items being accessed by users are in the cache and aren’t outcompeted by one-time operations etc.
var item = Sitecore.Context.Item;
using(new DatabaseCacheDisabler())
{
  // your code here...
}
9. LockingDisabler
The __lock field isn’t updated also the locking events aren’t fired, so that switch effectively d
isables the item locking mechanism.

10. LinkDisabler
This is one is handy but use it with care as it disables updates of the Links Database. This may speed up item saves when doing changes to items from code that you know won’t change any links.

Hope you find this article helpful.

[Updatable] API snippets

This blog post is [Updatable] - I use it to store snippets at the same place just for a case.

I know for most of experienced Sitecore geeks my post may seem sort of obvious, however even you may find something interesting below or suggest me to add anything you consider important to append.

While working with Sitecore for years, I have met so many attempts to re-invent the wheel in sense of ugly implementation of functions that are part of Sitecore API, so I decided to write this post, placing most frequent but sometimes less known usages here within one post. So, here we go:

  1. Standard API - create, read, edit, save, rename,
  2. Media library
  3. Publishing
  4. Presentation / placeholders
  5. Indexing
  6. DMS / xDB
  7. Other


1. Standard API - create, read, edit, save, rename, move etc.


1.1. To start with the basics - how to get the item. 

Item item = Sitecore.Context.Database.GetItem("/sitecore/content/home/products/tv");

if (item != null)             
{                 
    // allways check for null before going forward
}
Avoid using explicit database by name like below, get database out of Context, rather than by name explicitly.

// incorrect, except when you need explicitly to get CM database.
var database = Database().GetDatabase("master")); 


1.2. Accessing the fields - multiple ways:

// Indexer on Item supports using a field's name, index, or ID
string value1 = someItem[fieldName];
// Get the value from the field
string value2 = someField.Value;
// Method 3: 
bool allowStandardValue = false;
string value3 = someField.GetValue(allowStandardValue);
bool allowStandardValue = false;
bool allowDefaultValue = false;
string value5 = someField.GetValue(allowStandardValue, allowDefaultValue);

The order usually follows: if actual value exists, it is returned, otherwise if the item is clone - it returns clone's value, if not - returns standard value, and in case standard value not set - the default value (empty string, otherwise - null).


1.3. Read all fields.

For the sake of performance, Sitecore will not give you all fields in the FieldCollection in the following code, only fields with explicit values on item level, including empty string.

Sitecore.Context.Item.Fields.ReadAll();


1.4. Checking if a Sitecore field has a value

Sitecore.Context.Item.Fields["fieldName"].HasValue
or
Sitecore.Context.Item.Fields["fieldName"].Value != ""


1.5. When rendering fields from within the code, always use FieldRenderer class, in order to ensure that resulting output HTML  is Page Editor friendly.

FieldRenderer.Render(item, "field name")


1.6. Editing the item - straightforward way.

item.Editing.BeginEdit();
item.Fields["Title"].Value = "My New Title";
item.Editing.EndEdit();


1.7. More elegant way of editing the item, using IDisposable EditContext

using (new EditContext(item)) 
{ 
    item["Title"] = "My New Title"; 
}

More about EditContext and its options please read in API - IDisposable Usings article.


1.8. Miltilist field. I have evidenced several attempts of manipulation multi-list items by physically stored pipe-separated GUIDs, all clumsy and not best practice code with bad smell. Don't do that - use Sitecore API instead:

Sitecore.Data.Fields.MultilistField multilistField = Sitecore.Context.Item.Fields["myMultilistField"];
Sitecore.Data.Items.Item[] items = multilistField.GetItems();


1.10. Create item programmatically

Minimum code:
Item newItem = parent.Add(itemName, template); 
newItem.Editing.BeginEdit(); 
newItem.Fields["fieldName"].Value = "fieldValue"; 
newItem.Editing.EndEdit();
Full ready-to-use-code:
       public static void CreateItem()
        {
            // The SecurityDisabler overrides the current security model, allowing you
            // to access the item without any security. It's like the user being an administrator
            using ( new Sitecore.SecurityModel. SecurityDisabler())
            {
                // Get the master database
                Sitecore.Data. Database master = Sitecore.Data.Database.GetDatabase("master" );
                // Get the template to base the new item on
                TemplateItem template = master.GetItem("/sitecore/templates/Ecosystem/Concrete/MenuPage" );

                // Get the place in the site tree where the new item must be inserted
                Item parentItem = master.GetItem( "/sitecore/content/Ecosystem");

                // Add the item to the site tree
                Item newItem = parentItem.Add( "My New Page", template);

                // Set the new item in editing mode
                // Fields can only be updated when in editing mode
                // (It's like the begin tarnsaction on a database)
                newItem.Editing.BeginEdit();
                try
                {
                    // Assign values to the fields of the new item
                    newItem.Fields[ "Title"].Value = "My New Page - Title" ;
                    //newItem.Fields["Text"].Value = "Value2";

                    // End editing will write the new values back to the Sitecore
                    // database (It's like commit transaction of a database)
                    newItem.Editing.EndEdit();
                }
                catch (System. Exception ex)
                {
                    // The update failed, write a message to the log
                    //Sitecore.Diagnostics.Log.Error("Could not update item " + newItem.Paths.FullPath + ": " + ex.Message, this);

                    // Cancel the edit (not really needed, as Sitecore automatically aborts
                    // the transaction on exceptions, but it wont hurt your code)
                    newItem.Editing.CancelEdit();
                }
            }           
        }


1.11. Item propose name - will suggest valid item name according with current settings

ItemUtil.ProposeValidItemName("some-name");


1.12. Item has plenty of helpful methods, like CopyTo() and MoveTo() and others you may see in the context menu.

item.MoveTo (parentItem);



2. Media Library


2.1. Determining item type.

// usual content data item
Sitecore.Context.Item.Paths.IsContentItem;
// media item
Sitecore.Context.Item.Paths.IsMediaItem

2.2. The example below is anti-pattern. At the first glance it seems to be an elegant way of inline resolving media path right in MVC Razor view. So what is wrong with that?

 <img src="@MediaManager.GetMediaUrl(
Sitecore.Context.Database.GetItem(Html.Sitecore().CurrentRendering.DataSource))" />

First and most important - if datasource is not set (or set wrongly) then GetItem will return null being passed into GetMedaiUrl as the parameter, so it will fire NullReferenceException and the whole page won't load. You must validate parameters and do null-checks, so the best way out is to extract this logic into helper static (or extensions) class.

But the best option for sure is to pass this resolved URL into a view as a public property of strongly-typed viewmodel object.



3. Publishing


3.1. Publish item programmatically

private void PublishItem(Sitecore.Data.Items.Item item)
{
  // The publishOptions determine the source and target database,
  // the publish mode and language, and the publish date
  Sitecore.Publishing.PublishOptions publishOptions =
    new Sitecore.Publishing.PublishOptions(item.Database,
                                           Database.GetDatabase("web"),
                                           Sitecore.Publishing.PublishMode.SingleItem,
                                           item.Language,
                                           System.DateTime.Now);  // Create a publisher with the publishoptions
  Sitecore.Publishing.Publisher publisher = new Sitecore.Publishing.Publisher(publishOptions);

  // Choose where to publish from
  publisher.Options.RootItem = item;

  // Publish children as well?
  publisher.Options.Deep = true;

  // Do the publish!
  publisher.Publish();
}



4. Presentation / placeholders


4.1. Get count of renderings in a particular placeholder:

public static int GetRenderingsCount(string placeholderKey)
{
  return Sitecore.Context.Item.Visualization.GetRenderings(Sitecore.Context.Device, false).Count(p => p.Placeholder.Equals(placeholderKey));
}

4.2. Get position index of a rendering within a particular placeholder:

public static string GetComponentPositionOnPlaceHolder(System.Web.UI.Control webControl)
{
 if (webControl == null) return "0";
 var split = webControl.ID.Split('_');
 if (split.Length > 1)
 {
  int index = 0;
  if (int.TryParse(split.Last(), out index))
  {
   return (index + 1).ToString(CultureInfo.InvariantCulture);
  }
 }
 return "0";
}



5. Indexing


5.1. Simple Linq query

//Create a Search Context to the "index name" Index (could be SOLR/Lucene/something else)
using (var context = new ContentSearchManager.GetIndex("indexname").CreateSearchContext()) 
{
    //LINQ Query
    var query = context.GetQueryable.Where(i => i.Name.StartsWith("Something"));
}


5.2. Add single item to index

public static void AddItemToIndex(Item item, string indexName)
{
    var tempItem = (SitecoreIndexableItem)item;
    ContentSearchManager.GetIndex(indexName).Refresh(tempItem);
}
 
public static void UpdateItemInIndex(Item item, string indexName)
{
    var tempItem = (SitecoreIndexableItem)item;
    ContentSearchManager.GetIndex(indexName).Update(tempItem.UniqueId);
}
 
public static void DeleteItemInIndex(Item item, string indexName)
{
    var tempItem = (SitecoreIndexableItem)item;
    ContentSearchManager.GetIndex(indexName).Delete(tempItem.UniqueId);
 }


5.3. Queryable extensions

public static class QueryableExtensions
{
  public static IQueryable IsContentItem(this IQueryable query) where T : SearchResultItem
  {
    return query.IsDescendantOf(ItemIDs.ContentRoot);
  }
 
  public static IQueryable IsContextLanguage(this IQueryable query) where T : SearchResultItem
  {
    return query.Where(searchResultItem => searchResultItem.Language.Equals(Context.Language.Name));
  }
 
  public static IQueryable IsLatestVersion(this IQueryable query) where T : SearchResultItem
  {
    return query.Where(searchResultItem => searchResultItem["_latestversion"].Equals("1"));
  }
 
  public static IQueryable HasBaseTemplate(this IQueryable query, ID templateId) where T : SearchResultItem
  {
    var id = templateId.ToShortID().ToString().ToLowerInvariant();
    return query.Where(searchResultItem => searchResultItem["_basetemplates"].Contains(id));
  }
 
  public static IQueryable IsDescendantOf(this IQueryable query, ID parentId) where T : SearchResultItem
  {
    return query.Where(searchResultItem => searchResultItem.Paths.Any(ancestorId => ancestorId == parentId));
  }
}



6. DMS / xDB


6.1. DMS - hit the goal programmatically.

//Check that analytics are on...
if (Sitecore.Analytics.Tracker.IsActive &amp;&amp; Sitecore.Analytics.Tracker.CurrentPage != null)
{
  //Get the item goal.
  Item goalItem = Sitecore.Context.Database.GetItem(&amp;quot;{xxxxx-xxx-xxx-xxxxx}&amp;quot;);
  //Page event wrapper
  PageEventItem goal = new PageEventItem(goalItem);
  //Create the record that needs to  store the goal
   VisitorDataSet.PageEventsRow pageEventsRw = Sitecore.Analytics.Tracker.CurrentPage.Register(goal);
  //this is not mandatory
  pageEventsRw.Data = "custom text";
  Sitecore.Analytics.Tracker.Submit();
}


7. Other

7.1. Jumping between the sites of current Sitecore instance

Sitecore.Context.SetActiveSite(currentSiteName);