torsdag 4 februari 2016

Sätta default value till nyskapat content innan det publicerats

Ett litet tips:

För att sätta default värden på en content typ i EPiServer gör man ju en override på SetDefaultValues() metoden i sin contenttype deklarationsklass.

Men hur sätter  man default värde på ContentReference egenskaper när man vill peka ut den egna sidan? SetDefaultValues anropas ju innan sidan/blocket ens har tilldelats ett ContentReference-värde.

Jo, svaret stavas ContentReferense.SelfReference !!

public override void SetDefaultValues(ContentType contentType)
{
   base.SetDefaultValues(contentType);

   // Default on local property!
   this.MyContentReferenceProperty = ContentReference.SelfReference;
           
   // Also works for properties on local blocks!
   this.MyLocalBlock.ContentReferenceProperty = ContentReference.SelfReference;

}


Lätt som en plätt!

tisdag 2 februari 2016

Sätta behörigheter programmatiskt i EPiServer 8

Tjo!

Long time no see. Råkade skaffa 2 barn så har inte haft tid att skriva nåt på ett tag.

Eftersom jag inte hittade något användbart själv när jag googlade på behörighetsfipplande i EPiServer 8 så tänkte jag att jag drar mitt strå till stacken och lägger upp ett inlägg själv:

Scenariot är att när en egenskap som pekar ut en användare ändras på en EPiServer-sida så ska EPiServer-sidans behörigheter automatiskt ändras. Detta görs genom en initialiseringsmodul som hookar upp på Published-eventet. Men detta är en annan historia...

För att uppnå mina behov behövde jag två metoder, en som tar bort behörigheter från en sida och en metod som lägger till behörigheter.

Detta görs numera bäst med hjälp av servicen IContentSecurityRepository som nås via ”ServiceLocator”-klassen (fick det aldrig att fungera genom att manipulera PageData.ACL...).

private IContentSecurityRepository _contentSecurityRepository
{
  get
  {
    return ServiceLocator.Current.GetInstance<IContentSecurityRepository>();
  }
}

Ta bort behörighet
En metod som kollar om den utpekade sidan har någon entry för den specificerade rollen eller användaren. Om så tas entryn bort. En borttagning sker lite klurigt genom att man lägger till en entry med “NoAccess”-behörigheten och skriver över befintliga regler (mha parametern SecuritySaveType.Replace i save-metoden).
        
/// <summary>
/// Remove ACL entry for specified user and accessright for specified page.
/// </summary>
/// <param name="userOrRoleName"></param>
/// <param name="securityEntityType"></param>
/// <param name="page"></param>
public void RemoveAccessRight(string userOrRoleName, SecurityEntityType securityEntityType, PageData page, bool keepInheritedSecurity)
{
  // Check entry exists
  if (page.ACL.Contains(userOrRoleName))
  {
    IContentSecurityDescriptor securityDescriptor = (IContentSecurityDescriptor)_contentSecurityRepository.Get(page.ContentLink).CreateWritableClone();

    // Remove inherited if set (will throw error otherwise)
    if (securityDescriptor.IsInherited)
    {
      securityDescriptor.IsInherited = false;

      // Keep inherited security?
      if (keepInheritedSecurity)
        AddInheritedACLEntries(ref securityDescriptor, page.ParentLink);
    }

    // Remove by adding an entry with NoAccess and replace existing one (achieved with SecuritySaveType argument to Save method)
    securityDescriptor.AddEntry(new AccessControlEntry(userOrRoleName, AccessLevel.NoAccess, securityEntityType));
    _contentSecurityRepository.Save(page.ContentLink, securityDescriptor, SecuritySaveType.Replace);
  }
}

Manuellt lägga till ärvda behörigheter
Dock så finns risken att sidans behörigheter är inställda på att ärva behörigheter från föräldersidan. Denna flagga ” IsInherited” på IContentSecurityDescriptor-objektet medför att vi får ett exception kastat om vi försöker spara nya behörigheter. Så vi måste först för säkerhets skull sätta “IsInherited” till false. För att då inte tappa alla behörigheter som arvet medförde så har jag lagt till en parameter ”keepInheritedSecurity” som vid true manuellt lägger till de ärvda behörigheterna mha metoden ”AddInheritedACLEntries” nedan.

/// <summary>
/// Adds the inherited ACL entries as new entries on specified security descriptor object
/// </summary>
/// <param name="securityDescriptor"></param>
/// <param name="parentLink"></param>
private void AddInheritedACLEntries(ref IContentSecurityDescriptor securityDescriptor, ContentReference parentLink)
{
  if (!ContentReference.IsNullOrEmpty(parentLink))
  {
    // Read parent ACL entries and add to security descriptor
    PageData parent = _contentLoader.Get<PageData>(parentLink);
    foreach (var aclEntry in parent.ACL.Entries)
    {
      securityDescriptor.AddEntry(aclEntry);
    }
  }
}


Lägga till behörighet
Slutligen en metod för att lägga till en behörighet som är ganska self explanatory. Samma sak här angående flaggan ”IsInherited”.

Tänk också på att AccessLevel enum är en bitmask. Den kan alltså innehålla flera värden samtidigt, t.ex. kan man skriva såhär för att ge en variabel alla värden utom ”Administer”:

AccessLevel accessLevel = AccessLevel.Read | AccessLevel.Create | AccessLevel.Edit | AccessLevel.Delete | AccessLevel.Publish;




Och här är metoden för att lägga till en behörighet:

/// <summary>
/// Add ACL entry for specified user or role with specified level on specified page.
/// </summary>
/// <param name="userOrRoleName"></param>
/// <param name="accessLevel"></param>
/// <param name="securityEntityType"></param>
/// <param name="page"></param>
/// <param name="keepInheritedSecurity"></param>
public void AddAccessRight(string userOrRoleName, AccessLevel accessLevel, SecurityEntityType securityEntityType, PageData page, bool keepInheritedSecurity)
{
  if (page != null && !page.ACL.Contains(userOrRoleName, accessLevel, securityEntityType))
  {
    IContentSecurityDescriptor securityDescriptor = (IContentSecurityDescriptor)_contentSecurityRepository.Get(page.ContentLink).CreateWritableClone();

    // Remove inherited if set (will throw error otherwise)
    if (securityDescriptor.IsInherited)
    {
      securityDescriptor.IsInherited = false;

      // Keep inherited security?
      if (keepInheritedSecurity)
        AddInheritedACLEntries(ref securityDescriptor, page.ParentLink);
    }
                
    securityDescriptor.AddEntry(new AccessControlEntry(userOrRoleName, accessLevel, securityEntityType));
    _contentSecurityRepository.Save(page.ContentLink, securityDescriptor, SecuritySaveType.Replace);
  }

}


Och det var det!
Dessa metoder lade jag sedan i en egen klass för behörighetsfippel.