Stefan Goßner

Senior Escalation Engineer for SharePoint (WSS, SPS, MOSS, SP2010) and MCMS

Blogs

Automatically cleanup placeholder content after resource gallery items have been deleted

  • Comments 12
  • Likes

A problem I have been confronted serveral times is that authors have problems to save pages because the content of the page is refering to a resource gallery item that has been deleted in the past.

Usually deleting such resource gallery items should not happen as this means that the site contains broken images or invalid attachment links.

But on huge systems with many authors and different department this can happen quite often.

The best solution would be to deny the deletion of a resource gallery item if it is still in use. Unfortunatelly MCMS does not provide such a feature.

Whenever an author tries to update a posting that contains a reference to a deleted resource gallery item he will see the following error message:

Save Placeholder Failed
-----------------------------------------------------------
Error Details:
The current user does not have rights to edit the requested item. If you are seeing this exception when a MCMS template is executing, it means that you have removed the CmsAuthorizationModule from the HttpModules section of your web.config. You can only remove this module if: 1. All requests for every MCMS template in the web application will only be accessed by anonymous users 2. Guest access is enabled in MCMS (via the SCA) 3. The MCMS guest user has access to all postings based on templates in the web application If any of these requirements are not met, you MUST register the CmsAuthorizationModule in your web.config.
-----------------------------------------------------------

This error message is missleading and also does not give the author a hint about which item is causing the problem.

To bypass this problem cleanup code can be inserted in the MCMS workflow to remove all references to resource gallery items which no longer exist in the MCMS repository.

Here is sample code that can be added to the global.asax.cs to achieve this:

private bool DeletedResource(string link)
{
   
// Extract Resource GUID from URL
   
string[] splt = link.Split('/');
   
string GUID = splt[3];

   
// create new context with admin rights to be able to see deleted items
   
CmsApplicationContext appContext = new CmsApplicationContext();
   
appContext.AuthenticateAsUser("WinNT://servername/cmsAdmin", "cmsAdminPassword", PublishingMode.Unpublished);

   
object o = appContext.Searches.GetByGuid("{"+GUID+"}");

   
// Test item for being deleted
    bool isDeleted= false;
   
if (o != null)
   
{
       
isDeleted = ((o
is Resource) & (((Resource)o).Path.StartsWith("/Archive Folder")));
   
}

   
// dispose admin context 
   
appContext.Dispose();
   
appContext =
null;

   
// return result
   
return isDeleted;
}

private static int FindTag(string text, string tag, int start, string recursecompare)
{
   
int pos = text.ToLower().IndexOf(tag.ToLower(),start);
   
int check = text.ToLower().IndexOf(recursecompare.ToLower(),start+1);
   
if ((check >= 0) & (check < pos))
   
{
       
check = FindTag(text,tag,check,recursecompare);
       
pos = text.ToLower().IndexOf(tag.ToLower(), check+1);
   
}
   
return pos;
}

private static string RemoveTagPair(string text, string tag, string endtag, string recursecompare)
{
   
int start = text.ToLower().IndexOf(tag.ToLower());
   
int end = -1;
   
while (start >= 0)
   
{
       
end = FindTag(text,endtag,start,recursecompare);
       
if (end > start)
           
text = text.Remove(end,endtag.Length);
       
text = text.Remove(start,tag.Length);
       
start = text.ToLower().IndexOf(tag.ToLower());
   
}
   
return text;
}

private string HandleImgAndAttPlaceholderTypes(string propValue)
{
   
if (propValue.ToLower().StartsWith("/nr/rdonlyres"))
   
{
       
if (DeletedResource(propValue))
       
{
           
propValue = "";
       
}
   
}
   
return propValue;
}

private string HandleImageTags(string propValue)
{
   
int start=propValue.ToLower().IndexOf("<img ");
   
while (start >= 0)
   
{
       
int end = propValue.IndexOf(">",start);
       
if (end > start)
       
{
           
string tag = propValue.Substring(start,end-start+1);
           
int linkStart = tag.ToLower().IndexOf("\"/nr/rdonlyres");
           
if (linkStart >= 0)
            
{
               
int linkEnd = tag.IndexOf("\"",linkStart+1);
               
if (linkEnd > linkStart)
               
{
                   
string linkStr = tag.Substring(linkStart,linkEnd-linkStart+1);
                   
if (DeletedResource(linkStr))
                   
{
                       
propValue = propValue.Replace(tag,"");
                       
tag = "";
                   
}
               
}
           
}
            
start = propValue.ToLower().IndexOf("<img ",start+tag.Length);
       
}
    
}
   
return propValue;
}

private string HandleAnchorTags(string propValue)
{
   
int start=propValue.ToLower().IndexOf("<a ");
   
while (start >= 0)
   
{
       
int end = propValue.IndexOf(">",start);
       
if (end > start)
       
{
           
string tag = propValue.Substring(start,end-start+1);
           
int linkStart = tag.ToLower().IndexOf("\"/nr/rdonlyres");
           
if (linkStart >= 0)
           
{
               
int linkEnd = tag.IndexOf("\"",linkStart+1);
               
if (linkEnd > linkStart)
               
{
                   
string linkStr = tag.Substring(linkStart,linkEnd-linkStart+1);
                   
if (DeletedResource(linkStr))
                   
{
                       
propValue = RemoveTagPair(propValue,tag,"</a>","<a");
                       
tag = "";
                   
}
               
}
           
}
           
start = propValue.ToLower().IndexOf("<a ",start+tag.Length);
       
}
   
}
   
return propValue;
}

public void CmsPosting_PlaceholderPropertyChanging( Object sender, PropertyChangingEventArgs e )
{
   
string propName = e.PropertyName;
   
string propValue = e.PropertyValue as string;
   
switch (propName) 
   
{
       
case "Html" : // handle HtmlPlaceholder
        {
            propValue = HandleImageTags(propValue);
            propValue = HandleAnchorTags(propValue);
            break;
        
}
        
case "Src" : // handle ImagePlaceholder
        case "Url" : // handle AttachmentPlaceholder
        
{
            
propValue = HandleImgAndAttPlaceholderTypes(propValue);
            
break;
        
}
    
}
    
e.PropertyValue = propValue;
}

 

Comments
  • Hi Stefan,

    I have this problem while adding a posting. The page has an attachment, this attachment is browsed for - a local resource. When saving the page, we get the same error.

    Do you have any ideas?

  • This cannot be the same problem.
    Does the page save if this attachment is not added?

  • Thank you for your quick answer. We have checked, it seems that the user did not have rights on certain templates. An unexpected situation, because last week she had no problems at all and we are the only ones touching the site manager. Regranting rights solved our problem, now everything works fine again.

  • We have expirienced a similar problem:

    If a uses an image from the resource gallery, and the image is deleted from the resource gallery, it is moved to an archived folder outside the normal channel/resource tree.
    Thus only administrators have access rights - the posting (or rather, the image) can not even be viewed by anyone else, because they can not be subscribers.

  • Exactly this is covered with the code above.

  • Hi Stefan,

    When I try to use this code in my global.asax I get errors on the last portion of the code beginning with case "Src" (Invalid expression 'case'). Trying to correct it but I'm having difficulty getting around the errors.

    Also noticed one typo in 'case "Html"' where 'ropValue' might need to be 'propValue'.

    Thanks!

    RickG

  • Hi Rick,

    you are right, there have been two typos: a brace "{" was missing and ropValue needed to be propValue.

    The code above is now correct.

    Cheers,
    Stefan

  • Thanks Stefan!

  • Hi Stefan,

    I have been testing this on my Test Server and have run into an error during a page Save while in Edit mode:

    ***Save Placeholder Failed***
    Exception has been thrown by the target of an invocation.

    I commented out your code and the Save works. I'll let you know if I find the cause. I do have other custom events in my app so it may be a conflict of some sort.

  • I too get the same problem as RickG, in that I get the "Exception has been thrown by the target of an invocation".

    Anyone found out why this is yet, and a solution?

Raw Html Fix