This is a collection of questions and answers about Microsoft® Content Management Server (MCMS) gathered from the MCMS newsgroup. This content is provided "AS IS" with no warranties, and confers no rights. If you have feedback about one of these topics or would like to add information, please post your comments to the MCMS newsgroup. Thank you, Stefan Goßner Escalation Engineer Steve Cawood Program Manager
I have heard that it is not a good idea to use the DataSource.RawContent property to access the content of a placeholder. What is the reason for this?
Using the DataSource.RawContent property completly bypasses the internal processing of the derived placeholder classes. This means that the content received relies on the current implementation of the placeholder.
Only when the correct properties and methods exposed by the actual placeholder definition are used, can expect to retrieve identical content. This is true even after upgrading the placeholder version with service packs or hotfixes.
So what does bad code and good code look like?
I have seen recommendations to access the Url of an ImagePlaceholder in the current posting using this statement:
bad: string Url = CmsHttpContext.Current.Posting.Placeholders["Image"].Datasource.RawContent.ToString;
The correct method for an ImagePlaceholder is as follows:
good: string Url = ((ImagePlaceholder)(CmsHttpContext.Current.Posting.Placeholders["Image"])).Src;
The same needs to be done for Attachment, Html and XmlPlaceholders:
string Url = ((AttachmentPlaceholder)(CmsHttpContext.Current.Posting.Placeholders["Attachment"])).Url;string Html = ((HtmlPlaceholder)(CmsHttpContext.Current.Posting.Placeholders["HtmlContent"])).Html;string Xml = ((XmlPlaceholder)(CmsHttpContext.Current.Posting.Placeholders["XmlContent"])).XmlAsString;
If you are unsure which placeholder type you have, then use the following method:
if (CmsHttpContext.Current.Posting.Placeholders["ph"] is ImagePlaceholder){ ...}if (CmsHttpContext.Current.Posting.Placeholders["ph"] is AttachmentPlaceholder){ ...}if (CmsHttpContext.Current.Posting.Placeholders["ph"] is HtmlPlaceholder){ ...}if (CmsHttpContext.Current.Posting.Placeholders["ph"] is XmlPlaceholder){ ...}
If you have any custom placeholder definitions add them to the list above.
Is it possible to administer User Roles with the Publishing API (assign new users, create new user roles, etc.)?
The current version of MCMS does not expose this functionality. You will have to use the Site Manager application.
However, if you add AD user groups to the MCMS roles, you will be able to administer the members of these groups using ADSI.
Is it possible to create Custom Properties for a Channel with the Publishing API?
The MCMS 2002 Publishing API does not support this. You can create Custom Properties for a Channel in the Site Manager application.
Using MCMS 2002, I was able to create custom placeholder control. How can I create custom placeholder definitions? If I open the Placeholder Definition Collection Editor, all the placeholder definitions come from the out of the box placeholders:
- HtmlPlaceholderDefinition- XmlPlaceholderDefinition- AttachmentPlaceholderDefinition- ImagePlaceholderDefinition- OfficeAttachmentPlaceholderDefinition- OfficeHtmlPlaceholderDefinition
I want to create something called "MyPlaceholderDefinition" and want it to be listed into the drop-down list below the 'Add' button of this editor. How can I do this?
Placeholder Definitions are listed and defined in the following config file:
<drive>:\program files\microsoft content management server\server\config\Microsoft.ContentManagement.Publishing.config
Here you need to add a definition for your custom placeholder. You need a DLL that contains the class for the definition and the custom placeholder. In other words, write two c# classes to extend PlaceholderDefinition and Placeholder, and build them into one assembly and make it shared.
The format of the XML is as follows:
<PlaceholderDefinitionType Assembly="MyPlaceholder, Version=1.0.991.20032, Culture=neutral, PublicKeyToken=bcb3b9103afe8777" ClassName="MyPlaceholder.MyPlaceholderDefinition" />
Note: the new definition will be listed in MCMS Template Explorer only if there is no error in this line. This means that the assembly must exist, the version must match, the 'PublicKeyToken' must match and the 'ClassName' must match.
If there is an error, there will be no error message displayed from within the Microsoft Visual Studio® .NET UI. However, there will be an event logged in the application event log. The error describes the problem which prevented the type from being included in the list.
How can I display the last modified date of a posting in the local time of the browser ?
Use the following sample code:
Response.Write("<script language='JavaScript'>");Response.Write(" d = new Date('"+posting.LastModifiedDate+" GMT');");Response.Write(" document.write(d.toLocaleString());");Response.Write("</script>");
Response.Write("<script language='JavaScript'>");
Response.Write(" d = new Date('"+posting.LastModifiedDate+" GMT');");
Response.Write(" document.write(d.toLocaleString());");
Response.Write("</script>");
I keep receiving the following error message when browsing my site: "COM object that has been separated from its underlying RCW can not be used". What can be the problem?
You need to check your code for COM Interop calls. It seems that a COM component is instantiated by one thread and then used by another. One thread may have unloaded the COM component so the other thread finds that the component is gone.
One common explanation is storing an object reference in a static variable. When multiple instances can be retrieved in parallel (which is common for a web page) this means that the second reference will override the first one. This leaves it orphaned and causes this error.
So don't use static variables to store references to COM objects. Channel., Posting, Resource objects all are implemented in COM so don't store MCMS objects in static variables.
Each time I set the Channel.StartDate property (or any of the channel's properties for that matter), it sends back a "Server ODBC error. Please contact the administrator" message. No further details were given.
What is going on here?
This can happen if different users are updating this property at the same time. In addition it can also be caused if the update is done using CmsApplicationContext and if this context is not disposed before creating a new one. In this situation the lock created in the not disposed CmsApplicationContext can still be active.
Take a look at the documentation for the StartDate property which says:---This property can only be set if the CanSetProperties property has a value of true for the current User and the PublishingMode is Update. However, even if both of these conditions are satisfied, an attempt to set this property can still fail such as when, for example, the object is being edited concurrently by another user. Therefore, setting this property should be enclosed in appropriate try...catch blocks.---
Calling Dispose() is a mandatory when working with different CmsApplicationContext objects.
During upload of a resource gallery item using the CreateResource method an exception occurs. What can be the reason for this?
The following reasons can cause such an exception:
How can I find out whether the current posting is in Authoring mode or Live mode?
Microsoft.ContentManagement.WebControls.WebAuthorContextMode is meant to be used for this purpose.
Sample code:
WebAuthorContextMode mode = WebAuthorContext.Current.Mode; if (mode == WebAuthorContextMode.AuthoringNew || mode == WebAuthorContextMode.AuthoringPreview || mode == WebAuthorContextMode.AuthoringReedit){ // in authoring mode } else if (mode == WebAuthorContextMode.PresentationPublished || mode == WebAuthorContextMode.PresentationUnpublished) { // in presentation mode }
I need to know the size of an attachment attached to an AttachmentPlaceholder. How can I achieve this?
We need to differentiate between local attachments and shared attachments to answer this.
Is it possible to access MCMS objects in ASPX template files directly, or do I have to code this logic in the codebehind file?
This code demonstrates how you can use MCMS code directly in an ASPX page. Although most of your code should be in codebehind pages, it is possible that you may want to use inline code in some cases.
CMSApplicationContext.AutheticateAsUser contains an overloaded method that has a parameter called remoteMachineAddress. Is it possible to connect to a remote machine using this method?
This overload should not be used. It is the IP address of the machine that the user is using to login. However, since the API is has no remote interface, this must always be the local IP address.
In EAP/Beta builds it was possible to request the CmsAuthenticationTicket from the CmsApplicationContext instance. In order to create a ticket, MCMS needs an IP address for the requesting machine (which is encrypted within the ticket). The capability of requesting the ticket was removed but this overloaded method was not removed.
Is it possible to create a Resource Gallery with the Publishing API?
The MCMS 2002 Publishing API does not support this. You can create a Resource Gallery in the Site Manager application.
Since I'm using the .Text property of the HtmlPlaceholder object I experience performance problems. Is this a known issue?
Yes. The implementation of the .Text property is done in a Single-Threaded Apartment (STA) COM object that suffers from serialization problems.
A workaround (or better way) to get the Text content is to use the following code:
public string StripHtmlTags(string source){ string strRegExp = "<(.|\n)+?>"; Regex r = new Regex( strRegExp, RegexOptions.IgnoreCase | RegexOptions.Compiled ); string strNoTags = r.Replace( source, "" ); return System.Web.HttpUtility.HtmlDecode( strNoTags );}
My customer wants the ability to share the content of a placeholder in many pages of his web site. He wants some feature similar to the resource gallery, but with a block of text/html (so he can use the replace feature of the resource gallery).
You could store the content that you want to share in placeholders within a separate posting. In the pages where you want to show the content of that placeholder, you can just include a web control that retrieves the content of that placeholder and displays it. In addition, you could create a specific channel for this type of shared placeholder. This is a very common scenario and very easy to implement with MCMS 2002. Also note that in 2002 you can access the content of a resource gallery item as a .NET file stream with Resource.OpenReadStream.
Why does SortByName() not work in the following code:
String ResourcePath = ...; ResourceGallery gallery = CmsHttpContext.Current.Searches.GetByPath(ResourcePath) as ResourceGallery; gallery.Resources.SortByName(); foreach (Resource rs in gallery.Resources) { // here the resources are unsorted :-( }
The behavior you are seeing is by design. All collection properties in the API return copies of the collection. The primary reason for this is so you can write code like the following:
Channel c = … // Some channel. foreach( Posting p in c.Postings ) { if( p.Name.StartsWith( "a" ) ) { p.Delete(); } }
If the Postings collection was not a copy of the original then this code could fail since the collection that you are iterating over would be changing while you are iterating over it. In your example you are retrieving a copy of the original collection and sorting it then dropping your reference to it.
Another reason why "gallery.Resources.SortByName();" does not effect the original collection is that it might be misinterpreted as actually permanently sorting the collection. When you sort the collection the new sort order is not made permanent because different templates etc. might want to show the postings in a different order. There really is no one 'correct' sort order.
The correct coding is as follows:
String ResourcePath = ...; ResourceGallery gallery = CmsHttpContext.Current.Searches.GetByPath(ResourcePath) as ResourceGallery; ResourceCollection MyCollection = gallery.Resources; MyCollection.SortByName(); foreach (Resource rs in MyCollection) { // here the resources are sorted :-) }
I'm using CmsApplicationContext.AuthenticateAsCurrentUser within my ASP.NET application but it does not work. What could be wrong?
CmsApplicationContext is not ASP.NET aware. It is meant to be used primarily from stand alone Win32 applications. You can still do what you want, but you cannot use AuthenticateAsCurrentUser since this will take the identity that the thread is running under. This will be the ASPNET user by default. This is true unless you enable impersonation but this will cause a performance hit, so you probably don’t want to do it.
Instead, simply use the AuthenticateUsingUserHandle method and pass in the WindowsIdentity.Token which you get from casting HttpContext.Current.User.Identity to a WindowsIdentity. This will give you the identity of the currently authenticated Windows user:
CmsApplicationContext cmsContext = new CmsApplicationContext(); WindowsIdentity ident = HttpContext.Current.User.Identity as WindowsIdentity; cmsContext.AuthenticateUsingUserHandle(ident.Token,PublishingMode.Update);
Note: This method does not work with Forms authentication. The only works with Windows Authentication because HttpContext.Current.User.Identity is a WindowsIdentity. If you are doing forms authentication, then HttpContext.Current.User.Identity is a FormsIdentity. The only way to authenticate as a Windows user when using ASP.NET forms authentication are:
I'd like to automate a script to programmatically modify placeholder content. But I receive an error "Not in Edit Mode" when attempting to do this.
My questions is, how can I programmatically force the posting into Edit mode so I can copy the placeholder contents and then programmatically Approve or Submit the posting?
There are two ways that you could do this in the Microsoft® Content Management Server API.
1. Use the CmsHttpContext property "ChannelItem.QueryStringModeUpdate".
From the MCMS documentation:
This property will generally be used for the principal objects that are based on classes that are derived from ChannelItem. These classes are: Channel and Posting. The value returned by this property can be used to communicate session information across HTTP requests. The information in the query string is necessary when you want to call MCMS Publishing API objects in an ASP file stored outside the database. The Autosession object in the external ASP file uses the posting the query string was obtained from as its This and ThisPosting properties. The external file's ThisChannel property is the parent channel of the posting that created the query string. This property is similar to the QueryString property, except that it guarantees that the ASP script receiving the query parameters will use the Autosession 'Update' mode.
This property will generally be used for the principal objects that are based on classes that are derived from ChannelItem. These classes are: Channel and Posting.
The value returned by this property can be used to communicate session information across HTTP requests.
The information in the query string is necessary when you want to call MCMS Publishing API objects in an ASP file stored outside the database. The Autosession object in the external ASP file uses the posting the query string was obtained from as its This and ThisPosting properties. The external file's ThisChannel property is the parent channel of the posting that created the query string.
This property is similar to the QueryString property, except that it guarantees that the ASP script receiving the query parameters will use the Autosession 'Update' mode.
2. Use the CmsApplicationContext.
You could create a new application context in update mode.
Note: you cannot use object references from the standard HttpContext and update them in the new application context. You need to retrieve these objects again. The best way would be to locate them via the GUID.
Here is some sample code to do this:
AppContext = new CmsApplicationContext();AppContext.AuthenticateAsCurrentUser( PublishingMode.Update ); // Use GUID from CmsHttpContextpPosting = context.Searches.GetByGuid( strGuid ) as Posting; // Your update code - including the Submit and approve actions // Destroy the application contextAppContext.Dispose();
AppContext = new CmsApplicationContext();AppContext.AuthenticateAsCurrentUser( PublishingMode.Update );
// Use GUID from CmsHttpContextpPosting = context.Searches.GetByGuid( strGuid ) as Posting;
// Your update code - including the Submit and approve actions
// Destroy the application contextAppContext.Dispose();
I have a channel GUID or URL, and I also have a user account id, how I can check whether the user has right to access the channel?
I tried CmsHttpContext.Current.UserHasRightToBrowse(channel.guid) because when the user currently in the child channel, uses this method to check the access right of parent channel, it always returns true independent of the assigned user rights.
The behaviour you are seeing is by design. It was implemented to allow navigation controls to work in "islands of unreachability" (i.e. postings which a user has rights to but the user does not have rights to all of the parent channels in between the posting and the root channel).
With this feature, the navigation will display the parent channels even though the user does not have rights to them. If the user does click on one of these channels in the navigation, then MCMS issues a 401 because the user does not actually have rights to it.
When you ask UserHasRightToBrowse for a specific channel, it takes into account the current context (i.e. current posting). So if you have:
Channel A | Channel B | Posting B | Channel C | Posting C
If you are currently viewing Posting C and you ask UserHasRightToBrowse(Channel B), it will always say yes since you must have rights to Posting C and therefore you are granted read rights to all the parent channels in this context. However, if you try to browse Posting B, you will be denied access because you really don't have rights to Channel B.
Are you using this for navigation purposes? Is there a good reason why you have created these islands of unreachability in your channel hierarchy? Can these islands of unreachability be removed?
If not you might want to try using a CmsApplicationContext instead. Since this does not have the concept of a current posting, UserHasRightToBrowse should return the desired result (i.e. in the above scenario, it will always return false for Channel B).
Can you use Visual Basic .NET and C# in the same project? I have tried using Visual Basic .NET in a C# project and I get errors (Visual Studio cannot open the ASPX in design view). I tried it the other way and the same thing happened.
Microsoft® Visual Studio® .NET currently has a limitation that only one language can be used in a web application project at a time. In the common language runtime this limitation does not exist.
This White Paper is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS DOCUMENT.
Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation.
Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.
© 2004 Microsoft Corporation. All rights reserved.
Microsoft, ActiveX, Visual Studio, Windows, and Windows Server are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.
The names of actual companies and products mentioned herein may be the trademarks of their respective owners.