How to correct a "Save Placeholder Failed" error message that you may receive in Microsoft Content Management Server 2002 (889733)



The information in this article applies to:

  • Microsoft Content Management Server 2002

INTRODUCTION

The authorization model for Microsoft Content Management Server (MCMS) 2002 introduces different behavior than the behavior that is in MCMS 2001. In MCMS 2002, an administrator can control exactly who can reuse resource gallery items from within a posting. This behavior provides a more detailed set of permissions than the permissions that were previously available in MCMS 2001. These permissions give administrators additional flexibility to lock down the digital assets that are stored in the resource gallery.

SYMPTOMS

You are signed in as an Author in MCMS 2002. When you change a posting that was created by another Author, you may receive the following error message when you try to save your changes:
Save Placeholder Failed

CAUSE

Typically, this issue may occur when the following scenario is true:
  • You have an Author who is named Author 1.Author 1 has specific permissions to the CMS resource gallery.
  • You have a second Author who is named Author 2. Author 2 does not have equivalent permissions to the CMS resource gallery.
  • Author 1 creates a posting that contains placeholder references to items that are within the resource gallery.
  • Author 2 edits the posting that Author 1 created. Then, Author 2 tries to save the changes.
Because Author 2 does not have the same permissions on the resource gallery items that are referenced in the placeholders that Author 1 has, Author 2 receives the error message that is mentioned in the "Symptoms" section.

RESOLUTION

To resolve this issue, use the following code example to create a form that uses the URL of a specific posting to iterate all the resources and postings that require resource gallery user rights.

Note To quickly discover the resources that are used in a specific posting, you can write code to do the following:
  • Open the posting.
  • Enumerate the placeholders.
  • Search with a regular expression to find resources and postings that are referenced.
  • Produce an exact list of resources that the administrator can use to verify or set rights on in Site Manager.
Note The following code example uses the MCMS Woodgrove sample site as an example. Use this code example as an example of how you can modify your Web site to resolve this issue.

For additional information about the Woodgrove site, visit the following Microsoft Web site:The following three files can be added to a Microsoft Visual Studio development system project to be used against the Woodgrove sample site:
  • SavePlaceholder.aspx.cs
  • SavePlaceholder.aspx.resx
  • SavePlaceholder.aspx
Microsoft provides programming examples for illustration only, without warranty either expressed or implied. This includes, but is not limited to, the implied warranties of merchantability or fitness for a particular purpose. This article assumes that you are familiar with the programming language that is being demonstrated and with the tools that are used to create and to debug procedures. Microsoft support engineers can help explain the functionality of a particular procedure, but they will not modify these examples to provide added functionality or construct procedures to meet your specific requirements.

SavePlaceholder.aspx.cs

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Microsoft.ContentManagement.WebControls;
using Microsoft.ContentManagement.Publishing;
using Microsoft.ContentManagement.Publishing.Extensions.Placeholders;
using System.Text.RegularExpressions;
using System.Text;

namespace WoodgroveNet
{
  /// <summary>
  /// Summary description for SavePlaceholder.
  /// </summary>
  public class SavePlaceholder : System.Web.UI.Page
  {
    protected System.Web.UI.WebControls.TextBox txtGuid;
    protected System.Web.UI.WebControls.Button btnSubmitGuid;
    protected System.Web.UI.WebControls.TextBox txtPath;
    protected System.Web.UI.WebControls.Label lblReport;
    protected System.Web.UI.WebControls.Button btnSubmitPath;
  
    private void Page_Load( object sender, System.EventArgs e )
    {
      lblReport.Text = "";
    }

    void GetObjectPaths( IEnumerable ieList )
    {
      if( ieList == null )
        return;

      System.Collections.IEnumerator 
         iEnumerator = ieList.GetEnumerator();
      while ( iEnumerator.MoveNext() )
      {
        // Because we are passing in a list of the 
        // GUID, we get the object and output the path.
        WriteInfo(
           CmsHttpContext.Current.Searches.GetByGuid( 
             "{" + iEnumerator.Current+ "}" ).Path, 
             false );
      }
    }

    // This code is to parse the attachment and image 
    // placeholders because they are not fully qualified 
    // HTML but just a string that represents the path 
    // the resource.
    ArrayList DumpHrefs( String inputString, bool bHTML ) 
    {
      Regex r;
      Match m;
      

      if( inputString == null )
        return null;

      ArrayList alURL = new ArrayList();

      
      // This constant will be the first part in the event
      // that we are parsing HTML placeholders. If this is 
      // the case, we will extend the search to parse 
      // out only items that start with an href.
      const string strHTML = "(href|src)\\s*=\\s*\"";

      // Because Image and Attachment placeholders only have 
      // the path and no real HTML, we just have to extract 
      // the GUID.  This will also be appended to the 
      // strHTML later to extract just the GUID.
      //
      // By putting <1> in the expression, we can make sure 
      // that the GUID will be in slot 1 for the match.
      const string strCMSObject =                       "(/NR/RDONLYRES/|/NR/EXERES/)" +
                     "(?<1>[a-f,0-9]{8}-[a-f,0-9]{4}" +
                     "-[a-f,0-9]{4}-[a-f,0-9]{4}" +
                     "-[a-f,0-9]{12})";

      string strRegex = "";

      if(bHTML)
      {
        strRegex = strHTML + strCMSObject;
      }
      else
      {
        strRegex = strCMSObject;
      }

      
      r = new Regex( strRegex, 
                RegexOptions.IgnoreCase | RegexOptions.Compiled );

      for( m = r.Match( inputString ); m.Success; m = m.NextMatch() ) 
      {        
        // We get the first index because we specified <1> 
        // in the earlier expression.
        if( !alURL.Contains( m.Groups[1].Value ) )
          alURL.Add( m.Groups[1].Value );
      }
      return alURL;
    }

    void EnumPlaceholders(Posting pPosting)
    {
      if(pPosting == null)
        return;

      // Because we have a posting, we will then loop through 
      // each of the placeholders and then pass their appropriate 
      // content based on their type.
      foreach(
          Microsoft.ContentManagement.Publishing.Placeholder ph 
          in pPosting.Placeholders )
      {
        ArrayList alURL;

        WriteInfo( 
           string.Format( "PlaceholderName : {0}",ph.Name ), 
           true);

        switch(ph.GetType().ToString())
        {
          case "Microsoft.ContentManagement.Publishing." + 
               "Extensions.Placeholders.HtmlPlaceholder" :
          {
            alURL = DumpHrefs(((HtmlPlaceholder)ph).Html, true );
            GetObjectPaths(alURL);
            break;
          }
          case "Microsoft.ContentManagement.Publishing." + 
               "Extensions.Placeholders.AttachmentPlaceholder" :
          {
            alURL = DumpHrefs( 
                      ( ( AttachmentPlaceholder ) ph ).Url, 
                      false);
            GetObjectPaths( alURL );
            alURL = DumpHrefs( 
                     ( ( AttachmentPlaceholder ) ph ).IconUrl, 
                     False );
            GetObjectPaths( alURL );
            break;
          }
          case "Microsoft.ContentManagement.Publishing." + 
               "Extensions.Placeholders.ImagePlaceholder" :
          {
            alURL = DumpHrefs( ( ( ImagePlaceholder ) ph ).Href, false );
            GetObjectPaths( alURL );
            alURL = DumpHrefs( ( ( ImagePlaceholder) ph ).Src, false );
            GetObjectPaths( alURL );
            break;
          }      
          default:
          {
            WriteInfo("Parse information is not available for " +
                      "this placeholder", false);       
    
            break;
          }
        }
        
        WriteInfo("", false);
      }
    }

    #region Web Form Designer generated code
    override protected void OnInit(EventArgs e)
    {
      InitializeComponent();
      base.OnInit( e );
    }
    
    private void InitializeComponent()
    {    
      this.btnSubmitPath.Click += 
              new System.EventHandler( this.btnSubmitPath_Click );

      this.btnSubmitGuid.Click += 
              new System.EventHandler( this.btnSubmitGuid_Click );

      this.Load += new System.EventHandler( this.Page_Load );

    }
    #endregion

    private void btnSubmitGuid_Click(object sender, System.EventArgs e)
    {
      try
      {
        Posting pPosting = 
          ( Posting ) CmsHttpContext.Current.Searches.GetByGuid(
                        "{" + txtGuid.Text + "}" );
        EnumPlaceholders( pPosting );
      }
      catch(Exception ex)
      {
        string strMessage = 
          string.Format( 
            "There was an error retrieving the Posting for {0}", 
            txtGuid.Text);

        WriteInfo(strMessage, true);
        strMessage = string.Format("Exception: {0}", ex.Message);
        WriteInfo(strMessage, true);
      }
    }

    private void btnSubmitPath_Click(object sender, System.EventArgs e)
    {
      try
      {
        Posting pPosting = 
          ( Posting ) CmsHttpContext.Current.Searches.GetByUrl( 
                        txtPath.Text );

        EnumPlaceholders(pPosting);        
      }
      catch(Exception ex)
      {
        string strMessage = string.Format( 
          "There was an error retrieving the Posting for {0}", 
          txtPath.Text );

        WriteInfo( strMessage, true );                                            
        strMessage = string.Format( "Exception: {0}", ex.Message );
        WriteInfo( strMessage, true );
      }
    }

    void WriteInfo(string strMessage, bool isBold)
    {
      Label label = new Label();
      label.Text = strMessage + "<BR>";
      if(isBold)
        label.Font.Bold = true;
      lblReport.Controls.Add( label );
      
    }
  }
}

SavePlaceholder.aspx.resx

<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>1.3</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="$this.TrayAutoArrange" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <value>True</value>
  </data>
  <data name="$this.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <value>Private</value>
  </data>
  <data name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <value>False</value>
  </data>
</root>

SavePlaceholder.aspx

<%@ Register TagPrefix="uc1" TagName="DefaultConsole" Src="Console/DefaultConsole.ascx" %>
<%@ Page language="c#" Codebehind="SavePlaceholder.aspx.cs" AutoEventWireup="false" Inherits="WoodgroveNet.SavePlaceholder" %>
<%@ Register TagPrefix="cms" Namespace="Microsoft.ContentManagement.WebControls" Assembly="Microsoft.ContentManagement.WebControls, Version=5.0.1200.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
	<HEAD>
		<title>SavePlaceholder</title>
		<meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
		<meta content="C#" name="CODE_LANGUAGE">
		<meta content="JavaScript" name="vs_defaultClientScript">
		<meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
	</HEAD>
	<body MS_POSITIONING="GridLayout">
		<form id="Form1" method="post" runat="server">
			<asp:textbox id="txtGuid" style="Z-INDEX: 101; LEFT: 200px; POSITION: absolute; TOP: 56px" runat="server"
				Width="296px"></asp:textbox><asp:button id="btnSubmitPath" style="Z-INDEX: 106; LEFT: 504px; POSITION: absolute; TOP: 128px"
				runat="server" Text="Submit"></asp:button><asp:label id="Label2" style="Z-INDEX: 105; LEFT: 16px; POSITION: absolute; TOP: 128px" runat="server">Enter Path of Posting</asp:label>
			<asp:TextBox id="txtPath" style="Z-INDEX: 104; LEFT: 200px; POSITION: absolute; TOP: 128px" runat="server"
				Width="296px"></asp:TextBox>
			<asp:Label id="Label1" style="Z-INDEX: 103; LEFT: 16px; POSITION: absolute; TOP: 56px" runat="server">Enter GUID of Posting</asp:Label>
			<asp:Button id="btnSubmitGuid" style="Z-INDEX: 102; LEFT: 504px; POSITION: absolute; TOP: 56px"
				runat="server" Text="Submit"></asp:Button>
			<asp:Label id="lblReport" runat="server" style="Z-INDEX: 107; LEFT: 24px; POSITION: absolute; TOP: 176px"
				Height="16px">Label</asp:Label></form>
	</body>
</HTML>
Note If appropriate, an administrator can then use Site Manager to grant rights on the appropriate resource containers for Author 2. The result that you want is to have Author 1 and Author 2 share the same rights on all referenced resource gallery containers.

MORE INFORMATION

Best practices

To guarantee easier administration and granting of rights as the number of users scales throughout your authoring system, use Active Directory directory service groups instead of individuals when you use MCMS 2002 rights groups.

If you use the Active Directory directory service as you manage the rights in Site Manager, this helps guarantee that you will not mistakenly forget to add the same users to rights groups on each container. When you plan your rights strategy by using MCMS 2002, make sure that you consider rights on all resource gallery items that are used in postings.

Assign the same user rights on resource galleries for all authors who will be editing postings of other authors. This guarantees that all authors that have the rights to change a specific posting have the same rights to use resource gallery items that might be referenced by the posting.

MORE INFORMATION

For more information about MCMS 2002, visit the following Microsoft Web site:

For additional information about this behavior, click the following article number to view the article in the Microsoft Knowledge Base:

873203 You receive a "current user does not have rights to edit the requested item" error message when you save a Web page on Microsoft Content Management Server 2002

STATUS

Microsoft has confirmed that this is the expected behavior in the Microsoft products that are listed in the "Applies to" section.

Modification Type:MajorLast Reviewed:7/19/2005
Keywords:kbprb kbhowto kbinfo KB889733 kbAudDeveloper kbAudEndUser