MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Wednesday, August 29, 2007

Fix Publishing Pages Page Layout URL

This command was created to fix an issue with our upgraded sites as well as issues I discovered with sites that had been imported to new farms. What happened was that after the upgrade (or an import) the Page Layout URL (page.ListItem[FieldId.PageLayout]) for various publishing pages was pointing to the wrong place. This didn't prevent the page from loading as the Layout property of the page was referencing the correct PageLayout object - what failed was the editing of the page settings (so edit a page and click Page->Page Settings).

When I attempted to edit the page settings I'd get an error due to this url being incorrect (pointed to the old location). So rather than create something one off I figured I'd make this a command that I could use later as I know I'll need it any time I decide to move a site to a new site collection or farm. The command I created, gl-fixpublishingpagespagelayouturl, is detailed below (apologies for the verbose name).

The code is pretty simple - I'm just resetting the URL based on what I know it should be (I'm using the existing page layout filename and just fixing the host and site collection path). You can also pass in a known page layout url or a regular expression search and replace string to use. I suggest you test this in a virtual environment or at least by using the test parameter to see what will change before running on your entire intranet. The core code is shown below (I left out the details about how I'm looping through the sites - download the code if you'd like to see that):

   1: public static void FixPages(PublishingWeb publishingWeb, string pageName, string pageLayoutUrl, Regex searchRegex, string replaceString, bool verbose, bool fixContact, bool test)
   2: {
   3:     if (!PublishingWeb.IsPublishingWeb(publishingWeb.Web))
   4:         return;
   5:  
   6:     PublishingPageCollection pages;
   7:     int tryCount = 0;
   8:     while (true)
   9:     {
  10:         try
  11:         {
  12:             tryCount++;
  13:             pages = publishingWeb.GetPublishingPages();
  14:             break;
  15:         }
  16:         catch (InvalidPublishingWebException)
  17:         {
  18:             // The following is meant to deal with a timing issue when using this method in conjuction with other commands.  When
  19:             // used independently this should be unnecessary.
  20:             if (tryCount > 4)
  21:                 throw;
  22:             Thread.Sleep(10000);
  23:             SPWeb web = publishingWeb.Web;
  24:             SPSite site = web.Site;
  25:             string url = site.MakeFullUrl(web.ServerRelativeUrl);
  26:             site.Close();
  27:             site.Dispose();
  28:             web.Close();
  29:             web.Dispose();
  30:             publishingWeb.Close();
  31:             site = new SPSite(url);
  32:             web = site.OpenWeb(Utilities.GetServerRelUrlFromFullUrl(url));
  33:             publishingWeb = PublishingWeb.GetPublishingWeb(web);
  34:         }
  35:     }
  36:  
  37:     foreach (PublishingPage page in pages)
  38:     {
  39:         if (!(string.IsNullOrEmpty(pageName) || page.Name.ToLower() == pageName.ToLower()))
  40:             continue;
  41:  
  42:         if (verbose)
  43:         {
  44:             Log(string.Format("Begin processing {0}.", page.Url));
  45:             Log(string.Format("Current layout set to {0}.", page.ListItem[FieldId.PageLayout]));
  46:         }
  47:  
  48:         // Can't edit items that are checked out.
  49:         if (Utilities.IsCheckedOut(page.ListItem))
  50:         {
  51:             if (verbose)
  52:                 Log("Page is already checked out - skipping.");
  53:             continue;
  54:         }
  55:  
  56:         SPFieldUrlValue url;
  57:         if (string.IsNullOrEmpty(pageLayoutUrl))
  58:         {
  59:             if (searchRegex == null)
  60:             {
  61:                 if (page.ListItem[FieldId.PageLayout] == null || string.IsNullOrEmpty(page.ListItem[FieldId.PageLayout].ToString().Trim()))
  62:                 {
  63:                     if (verbose)
  64:                         Log(string.Format("Current page layout is empty - skipping.  Use the 'pagelayout' parameter to set a page layout."));
  65:  
  66:                     continue;
  67:                 }
  68:  
  69:                 // We didn't get a layout url passed in or a regular expression so try and fix the existing url
  70:                 url = new SPFieldUrlValue(page.ListItem[FieldId.PageLayout].ToString());
  71:                 if (string.IsNullOrEmpty(url.Url) ||
  72:                     url.Url.IndexOf("/_catalogs/") < 0)
  73:                 {
  74:                     if (verbose)
  75:                         Log(string.Format("Current page layout does not point to a _catalogs folder or is empty - skipping.  Use the 'pagelayout' parameter to set a page layout  Layout Url: {0}",
  76:                                 url));
  77:                     continue;
  78:                 }
  79:  
  80:  
  81:                 string newUrl = publishingWeb.Web.Site.ServerRelativeUrl.TrimEnd('/') +
  82:                               url.Url.Substring(url.Url.IndexOf("/_catalogs/"));
  83:  
  84:                 string newDesc = publishingWeb.Web.Site.MakeFullUrl(newUrl);
  85:  
  86:                 if (url.Url.ToLowerInvariant() == newUrl.ToLowerInvariant())
  87:                 {
  88:                     if (verbose)
  89:                         Log("Current layout matches new evaluated layout - skipping.");
  90:                     continue;
  91:                 }
  92:                 url.Url = newUrl;
  93:                 url.Description = newDesc;
  94:             }
  95:             else
  96:             {
  97:                 if (page.ListItem[FieldId.PageLayout] == null || string.IsNullOrEmpty(page.ListItem[FieldId.PageLayout].ToString().Trim()))
  98:                     if (verbose)
  99:                         Log(string.Format("Current page layout is empty - skipping.  Use the pagelayout parameter to set a page layout."));
 100:  
 101:                 // A regular expression was passed in so use it to fix the page layout url if we find a match.
 102:                 if (searchRegex.IsMatch((string)page.ListItem[FieldId.PageLayout]))
 103:                 {
 104:                     url = new SPFieldUrlValue(page.ListItem[FieldId.PageLayout].ToString());
 105:                     string newUrl = searchRegex.Replace((string)page.ListItem[FieldId.PageLayout], replaceString);
 106:                     if (url.ToString().ToLowerInvariant() == newUrl.ToLowerInvariant())
 107:                     {
 108:                         if (verbose)
 109:                             Log("Current layout matches new evaluated layout - skipping.");
 110:                         continue;
 111:                     }
 112:                     url = new SPFieldUrlValue(newUrl);
 113:                 }
 114:                 else
 115:                 {
 116:                     if (verbose)
 117:                         Log("Existing page layout url does not match provided regular expression - skipping.");
 118:                     continue;
 119:                 }
 120:             }
 121:         }
 122:         else
 123:         {
 124:             // The user passed in an url string so use it.
 125:             if (pageLayoutUrl.ToLowerInvariant() == (string)page.ListItem[FieldId.PageLayout])
 126:             {
 127:                 if (verbose)
 128:                     Log("Current layout matches provided layout - skipping.");
 129:                 continue;
 130:             }
 131:  
 132:             url = new SPFieldUrlValue(pageLayoutUrl);
 133:         }
 134:  
 135:         string fileName = url.Url.Substring(url.Url.LastIndexOf('/'));
 136:         // Make sure that the URLs are server relative instead of absolute.
 137:         if (url.Description.ToLowerInvariant().StartsWith("http"))
 138:             url.Description = Utilities.GetServerRelUrlFromFullUrl(url.Description) + fileName;
 139:         if (url.Url.ToLowerInvariant().StartsWith("http"))
 140:             url.Url = Utilities.GetServerRelUrlFromFullUrl(url.Url) + fileName;
 141:  
 142:         if (page.ListItem[FieldId.PageLayout] != null && url.ToString().ToLowerInvariant() == page.ListItem[FieldId.PageLayout].ToString().ToLowerInvariant())
 143:             continue; // No difference detected so move on.
 144:  
 145:         if (verbose)
 146:             Log(string.Format("Changing layout url from \"{0}\" to \"{1}\"", page.ListItem[FieldId.PageLayout], url));
 147:  
 148:  
 149:         if (fixContact)
 150:         {
 151:             SPUser contact = null;
 152:             try
 153:             {
 154:                 contact = page.Contact;
 155:             }
 156:             catch (SPException)
 157:             {
 158:             }
 159:             if (contact == null)
 160:             {
 161:                 if (verbose)
 162:                     Log(string.Format("Page contact ('{0}') does not exist - assigning current user as contact.", page.ListItem[FieldId.Contact]));
 163:                 page.Contact = publishingWeb.Web.CurrentUser;
 164:  
 165:                 if (!test)
 166:                     page.ListItem.SystemUpdate();
 167:             }
 168:         }
 169:  
 170:         if (test)
 171:             continue;
 172:  
 173:         page.CheckOut();
 174:         page.ListItem[FieldId.PageLayout] = url;
 175:         page.ListItem.UpdateOverwriteVersion();
 176:         PublishItems.Settings settings = new PublishItems.Settings();
 177:         settings.Test = test;
 178:         settings.Quiet = !verbose;
 179:         settings.LogFile = null;
 180:  
 181:         PublishItems.PublishListItem(page.ListItem, page.ListItem.ParentList, settings, "stsadm -o fixpublishingpagespagelayouturl");
 182:         //page.ListItem.File.CheckIn("Fixed URL to page layout.", SPCheckinType.MajorCheckIn);
 183:         //if (page.ListItem.ModerationInformation != null)
 184:         //    page.ListItem.File.Approve("Publishing changes to page layout.");
 185:     }
 186: }

The syntax of the command can be seen below:

C:\>stsadm -help gl-fixpublishingpagespagelayouturl

stsadm -o gl-fixpublishingpagespagelayouturl


Fixes the Page Layout URL property of publishing pages which can get messed up during an upgrade or from importing into a new farm.

Parameters:
        -url <url>
        -scope <WebApplication | Site | Web | Page>
        [-pagename <if scope is Page, the name of the page to update>]
        {[-pagelayout <url of page layout to retarget page(s) to (format: "url, desc")>] /
         [-regexsearchstring <search pattern to use for a regular expression replacement of the page layout url>]
         [-regexreplacestring <replace pattern to use for a regular expression replacement of the page layout url>]}
        [-verbose]
        [-test]

To fix all the pages on a given web application you would use the following syntax:

stsadm –o gl-fixpublishingpagespagelayouturl –url "http://intranet/" -scope webapplication

Update 2/17/2008: I've made quite a few changes to this command. Note that if you have a previous version the syntax of the command has changed a lot (content above has been updated). You can now pass in a single url parameter along with a scope parameter. Also - you can now pass in a test parameter to simulate what changes would occur. The verbose switch will tell you what it's doing. If you know you want to set a specific page to you can pass in the pagename parameter. Similarly if you want to set a specific page layout url use the pagelayout parameter or you can use the regex parameters for doing a search and replace. Note that if you pass in the pagelayout parameter you want to use the format "[url], [desc]" - for example:

stsadm -o gl-fixpublishingpagespagelayouturl -url "http://intranet" -scope site -pagelayout "http://intranet/_catalogs/masterpage/WelcomeLinks.aspx, /_catalogs/masterpage/WelcomeLinks.aspx".

91 comments:

Ryan Lee said...

Hi Gary,

I just want to let you know that you've saved our site!! We ran into the page layouts issue and after running this command our site is rid of that error.

Thank you very much and keep up the good work!!

Anonymous said...

Hi

i've tried your command and i've this message:
startIndex cannot be less than zero
Parameter name: startIndex.

What does it mean ?
Regards
Manuelito

Gary Lapointe said...

Manualito - I've updated this command quite a bit so you may want to pull down the latest and try again. If it still fails pass in the verbose parameter and send me the output and I'll see if I can help you out.

Anonymous said...

Hi Gary,

Just to let you know this worked brilliantly in our environment and really saved my bacon!

Just one thing, if a page doesn't have an associated page layout, the process stops with a 'object not set...' error. Not a problem as associating a temporary page layout allows it to carry on, but just thought you should know

Gary Lapointe said...

I actually have a fix for that - I just haven't had a chance to publish the latest build as I've got a ton of new stuff that I need to wrap up and stabilize first.

Sriram said...

Hey Gary... the posting was very helpful to me.

I also found another solution from MS. If we open the publishing page in Sharepoint Designer and detach the master page check in and attach the master page back. Then open the page in the browser in Edit Mode and then going to Page->Page Settings and selecting a new page layout and publish the page is also fixing the problem.

Just thought I would share... might be helpful to make a quick fix...

Rich Finn said...

Gary,

Thanks a TON for this one. Looks like there is also a problem in Alternate Access Mappings when changing the public URL for a zone of a site. The page layouts retain their original URL, so I couldn't create a new site with a default page using one of my layouts, or edit a page layout's settings.

Next Starera reunion, I owe you a beer!

-Rich Finn

Matthew McDermott, MVP said...

Gary,

Great Job! We use the following query to find the offending pages. Just create a new item in "Content and Structure Reports". (Dang...can't post the XML...)
Here's the query. THANKS!

Ian said...

Hi Gary,

This looks like exactly what I need... I have manually updated the offending PageLayout in some pages to confirm that this is the problem.

I have no idea how to get stsadm to use your custom commands. Do you have some doco on how its done.

Many thanks!

Ian said...

Hi Gary,

Dont worry, found it. Just needed to stsadm -o deploysolution -local -allowgacdeployment -name "Lapointe.SharePoint.STSADM.Commands.wsp

Anonymous said...

Hi,

Thanks a million for all the great commands. I have a problem that was mentioned earlier about the page without a page layout. I'm using reghostfile after an upgrade and then this command which both are 'successful' however the page still has the error directing you to reconnect the layout. Is my command wrong, the -verbose does not output anything?

stsadm -o gl-fixpublishingpagespagelayouturl -url "http://intranet" -scope page -pagename "http://intranet/News/Pages/default.aspx" -pagelayout "http://intranet/_catalogs/masterpage/DefaultLayout.aspx, /_catalogs/masterpage/DefaultLayout.aspx" -verbose

Thanks!

Anonymous said...

First off - thanks for your wonderful solution - but I have a question.

Can you explain to me the reason for the absolute links? They make no sense to my mind - why not ref /_catalogs/masterpage/BlankWebPartPage.aspx
rather than http://mnwebsite.com/_catalogs/masterpage/BlankWebPartPage.aspx?

It seems to me that would resolve the issue across the board.

I have manually editing the files and made the relative and they appear to work fine. I tried having it be a relative link w/in the stsadm command, but it still put it as an absolute.

Can you explain why this is?

thanks,
matt

Gary Lapointe said...

Try this: stsadm -o gl-fixpublishingpagespagelayouturl http://intranet/news/pages/forms/allitems.aspx -pagename "default.aspx" -scope page -pagelayout "/_catalogs/masterpage/defaultlayout.aspx, /_catalogs/masterpage/defaultlayout.aspx" -verbose

Gary Lapointe said...

The link doesn't need to be absolute - I honestly can't remember if I had a reason for making it absolute but a server relative url will do just fine. I'll try to change it if I have time.

Anonymous said...

Thanks for getting back to me so quickly. A couple of things - I could not run off of the allitems.aspx, but I could using the -url switch (by removing the /pages/forms/allitems.aspx).

However, that did not solve the issue. gl-fix.... prefixes the PageLayoutTemplate with the absolute path (ie even when I put in "/_catalogs..." in the cmd line it puts in "http://myserver/_catalogs..." in the file.

I had tested it before my prev post and that was why I asked re the relative vs absolute links.

Currently I am forced to manually download the file and edit them. The main reason being is that we have multiple access points (ie through our network and also remotely through FBA) and as such we need it to reference not the server name but the relative link.

Any thoughts?

thanks,
m

Benjamin said...

Hi Gary,

Thanks a bunch for your work and contribution. We discovered this issue just a few days before launch of the our client's site and thanks to you we were able to cut save ourselves from stressful late nights.

Thank you!

Jose Otavio said...

I love you mate. Thank you. This command saved my life.
Jose.

Gary Lapointe said...

Matt - regarding your comment about the links being absolute - I've just pushed a new build out which sets the URL to be relative instead. Let me know if you have any issues with it.

Gary Lapointe said...

One thing that I forgot to mention in my previous comment about the code using the relative url now - it actually doesn't matter that I'm setting the URL to be relative - the API is "fixing" the URL on its own so that even though I set it to be relative it changes it back to absolute - there's actually nothing I can do about this as its done via internal code. I only fixed my code in case Microsoft decides to change how the URL is handled internally with any upcoming updates.

Col said...

Hi Gary
Thanks for these commands. Helped me out numerous times now.
I moved a Site from one area to another (within the same site collection) and for some reason the page layout was not retained on the default pages. If I view in "content and structure" the page layout field is blank.
Tried to run your command but I get the following results:

Progress: Begin processing Pages/default.aspx.
Progress: Current layout set to .
Object reference not set to an instance of an object.

Any chance you know what this is?
Thanks
Col

Gary Lapointe said...

To be honest I'm not sure what is causing the error - I skimmed through my code and didn't see any obvious spots that would give a null reference exception. Are you able to step through the code or perhaps add a try/catch and output the stack trace? You may try adding the pagelayout parameter to set the layout explicitly.

Col said...

Thanks Gary. Its a strange one indeed.
I did specify the -pagelayout parameter but still got an error unfortunately.
I'm not sure why the pages lost their page layout when I moved the subsites around, but the only way I was able to fix them was to recreate each and every one.
Unfortunately I'm not able to step into the code at this time. As soon as I find more info on it, I'll post it here.
Thanks for your prompt assistance!

Anonymous said...

Hi Gary,

your stsadm extensions is exactly what I was looking for!
But I have a problem with using it.
I ran Deploy.bat, then Update.bat.
Then I copypasted your sample command with changed website name:
stsadm –o gl-fixpublishingpagespagelayouturl –url "http://NewNameOfMySite/" -scope webapplication

When I run this command it doesn't work. Looks like syntax error. If I try
stsadm -help gl-fixpublishingpagespagelayouturl
I get the message window:
"Windows SharePoint Services administration tool has encounted a problem and nneds to close"

Could you please comment on this?
Thank you in advance!

Gary Lapointe said...

It would appear that something failed during the install. It shouldn't matter but you don't need to run the upgrade.bat file. Try running the deploy.bat file again or try manually installing the wsp without using the bat file. If that fails then simple put the dll in the GAC and copy the xml file to the 12 hive's config folder.

jul-bearn said...

Thank you for your responce, Gary!
I've tried to rerun deploy.bat, manually run commands from this file, copied dll file to GAC and 3 xml files to bin and command derictories. Now I don't have a message error with -help command, but command still don't work. Though I recieve a list of your extensions by stsadm -help. Could it be because I use non english Sharepoint version? I can't read error vessages in console - all symbols turns into "?". Maybe there's some calls to functions in your code which have another names in non english versions of SP?

Ry said...

Hi Gary,

I think there's a bug on line 61. Isn't a continue statement missing there?

Thank you!

- Ricardo

Anonymous said...

I'm pretty new to this stuff and it seems to be a bit over my head. I downloaded and installed the source files. Now when I run
stsadm –o gl-fixpublishingpagespagelayouturl –url "http://intranet/" -scope webapplication
I get a command line error. I am trying to export a subsite and then import it into a new site collection, I then get a File not found error on the site collection after the import.

Any help as to what I'm doing wrong would be great.

Thanks,

Gary Lapointe said...

Ricardo - yes, there is supposed to be a continue there - if you look at the code in the download it is there but for some reason there's a few things missing from the code in my post - I'll update the post with the latest version of the code - thanks for pointing out the discrepency.

Gary Lapointe said...

Can you provide what the specific command line error is? Do any of the other gl-* commands work for you? If not then the extensions are probably just not installed correctly - use the addsolution and deploysolution commands to install the .wsp. The File Not Found error occurs in several situations - if you run the gl-repairsitecollectionimportedfromsubsite command it will attempt to fix most of the known issues (typically it's due to either the master page not existing or the page layout not existing - don't worry about the name of the command - I created to address some specific issues in converting sub-sites to site collections but the fixes are relevant to most import scenarios).

Anonymous said...

It just says Command Line Error. I have tried the deploysolution it deployed but the other command still didn't work. Does it matter which server this is done on?

Gary Lapointe said...

As long as you're running on one of your WFEs you should be okay. When you say theother command still didn't work do you mean the built-in addsolution (which you have to run before deploysolution) or some of my other custom commands?

Anonymous said...

I meant the stsadm –o gl-fixpublishingpagespagelayouturl –url "http://intranet/" -scope webapplication.

disturbed said...

Hi,

This is the first time I've ever commented on your blog, but I've been following it closely for quite some time now. I would like to start off by thanking you for your excellent work with these commands, they have given me lots of ideas in my own work.

I do have a question about this command though, as I've seen the page layout problem appear on quite a few of my Farms lately.

Do we have any information regarding
a) the source of the problem. Is it a known bug, has Microsoft published any information abou tit ?
b) Is there anything we can do to PREVENT this from happening, instead of running scripts to fix the problems that occur afterwards.

I realize that these questions aren't easily answered, but maybe someone reading this blog can shed some light over this issue.

Gary Lapointe said...

Disturbed (nice name :)) - thanks for the feedback! - The source of the problem can be a variety of things but typically it's due to content migration issues. MSFT released a content deployment rollup a few weeks ago which may resolve most if not all of the issues but I've not had a chance to do any testing of it. One of the core problems though is that even if you store the link as a relative link it converts it to an absolute link so if you're moving the content to another web app it will typically break (it's odd though because I've seen situations where it didn't break when I expected it would so it's almost like there is some processing going on to account for the issue but that processing is somehow flawed as it's not addressing all scenarios and I can't figure out what the deliniator is). As far as preventing the issue - don't use MSFT content deployment stuff :) (of course the rollup may change that). Oh, and my import*/export* commands all use the content deployment API with the exception of gl-exportlistitem2 and gl-addlistitem which are completely custom and do address the issue.

Disturbed said...

Thanks for responding.

We've had this happen to us on quite a few installations where we've moved content databases between farms. So I suppose it cannot be DIRECTLY related to the content deployment mechanisms.

This problem also recently occurred on a farm which is patched beyond belief (:>) with all the rollups and hotfixes you can think of, so I actually doubt that MS has released a solution yet.

Oh well, I guess I'd better try to get in touch with MS Support and see if they have any comments on this issue.

Anonymous said...

Gary,

I too am running into the issue of the command
stsadm –o gl-fixpublishingpagespagelayouturl –url "https://URL/" -scope webapplication

not running. merely indicates a command error...I have ran on both the WFE and the CA...neither appear to execute.

Gary Lapointe said...

Make sure that the help for the command is working. Also, make sure you are typing the command in and not pasting it (looks like the hyphens for the parameters are not the standard hyphen).

Flor said...

Hi, and thank you for your great extensions !
I have test it and we have this error :

>"C:\Program Files\Common Files\Microsoft Shared\
web server extensions\12\BIN\STSADM.EXE" -o gl-fixpublishingpagespagelayouturl -
url "http://pvmd.vmd.ca/" -scope webapplication

User cannot be found.

I don't understand what happen. We have tried a basic command and there is no problem. An idea ?

Gary Lapointe said...

Try passing in the verbose parameter so you can see where it's failing. Also - try passing in the fixcontact parameter. Most likely it's failing on the save because of an invalid contact.

Yahav said...

Hi Gary and thanks for the wonderful fix!

One question: how can we rollback what you're doing?

You know, in case the layout is changed and somehow something goes wrong - is there any easy roll back?

Gary Lapointe said...

Sure - just make sure versioning is enabled on your library (it is by default for the Pages library) and then simply go back to a previous version. Another thing is to use the -test parameter to see what it will do before it does it.

Darren said...

Hi Gary,

I LOVE YOU!!

This problem occurred on our development environment today and was causing us a great deal of stress. Thank goodness google directed me to this blog entry! I ran your extension and it completely and utterly fixed my problem.

Thanks so much!!

Darren

Melanie said...

Hi Gary,

Thank you very much for your great extensions.

I have a problem using them. We have MOSS server, I run the deploy.bat without any problem.
Then when I run
stsadm -help gl-fixpublishingpagespagelayouturl

command, I get the help of your command properly but still, I cannot run the following command:

stsadm –o gl-fixpublishingpagespagelayouturl –url "http://MySite/" -scope webapplication

It seems it doesn't know this command and brings the general help of stsadm.

Do you have any idea?

Thank you very much.

Gary Lapointe said...

Melanie - try rekeying the command manually - it looks like you have a special character for the -o part.

Melanie said...

Gary thank you for your reply. I don't have any special character there, it's minus. Do you have any other idea?

Gary Lapointe said...

Sorry - not a clue - if you manually retyped the whole command and it's still giving you the error then I'm at a loss - especially if the help is working. The only time I've ever seen this happen is when someone had a special character in the command string.

Melanie said...

Gary, I retyped the command and it works now. It was really stupid, sorry :D

Now, I have other problems and questions:

1) In one of applications I get this error:

"The site is not valid. The 'Pages' document library is missing. "

The publishing feature is activated in the application.

2) After running the extension in other applications, when I try to edit the settings of involved pages, I get this error:

User cannot be found. at Microsoft.SharePoint.SPUserCollection.GetByID(Int32 id)

3) I have noticed this problem too late. We have some applications on 3 servers: test server, pre-production server and production server.
Some months ago, we deployed from test to pre-production and from pre-production to production and now we perform just partial deployments.

If I correct this problem in test server using the extension and then perform a partial deployment to second and third servers, the changes would be deployed or should I run your extension in every server?

4) If I delete "http://oldserver" expression in all fields in all tables of content database, does it have the same effect as this extension? I ask this, because it may solve "user not found" problem as well.

Thank you again very much for your help.

Gary Lapointe said...

Melanie - here's my best guesses to your questions (sorry I'm a little late with this - been busy):
1. Not really sure what the deal is with this - sounds like someone deleted the pages library - if it's there make sure that it wasn't deleted and then manually recreated. You might also want to try reactivating the publishing feature.

2. Try running the gl-fixpagecontact command - basically what's happened is that the page is referencing a user ID that doesn't exist - a common problem if the user was deleted from the site collection or the page was migrated from another site.

3. If your target environments didn't start out using the blank site template (or no site template) and you are using the content deployment then you will run into the same issues whenever you do a migration so you'll need to rerun the commands in each environment - I see this all the time which is why I created these two commands.

4. You definitely don't want to edit the content database - you'll lose support from Microsoft and could encounter any number of issues. The gl-fixpublishingpagespagelayouturl and gl-fixpagecontact commands in conjuction with the gl-replacefieldvalues, gl-replacewebpartcontent, and gl-replacenavigationurls (released but not yet documented) commands were built to address the many issues encountered when migrating from one environment to another, without the need to edit the database thus maintaining your support with Microsoft.

Melanie said...

Hi Gary,

Thank you very much for your reply and your cool extensions, you saved our websites.

Mark Wilson said...

First of all congratulations on a fantastic set of extensions.

I do have a quick question, in looking at the code it would seem that each change gets checked in as a major version regardless of whether the last version modified was a minor one or not - is this intentional? I guess i will need to make my own version, we have lots of in progress pages which we would not want to publish immediately but which do require the page layout url fixing...what would be great is if the last version was minor then it checked in as minor otherwise publish if you see what I mean.

Anonymous said...

Thank you Gary!!! Your tool is Magic!!!



Bye bye
Rodrigo

Dhaval Patel said...

Hi Gary,

I am getting error "Value does not fall within the expected range" after creating one page. I have checked there is no syntax error in the page.

I have copied .aspx code using Microsoft Sharepoint Designer in same server page. I have edited page settings and tried to open that page but it gives error.

Please help me to solve this.

Gary Lapointe said...

Can you confirm that you have the latest version of the extensions deployed?

Froggy.NET said...

Hi Gary... I tried this tool, for a single layout page it destroyed the whole site (Other pages took the new fixed layout). Is this scenario logical, or did I misstype my command?

Gary Lapointe said...

What scope did you use?

Ryan Edgerley said...

Gary,

This code is a great help, thank you for putting it up for all to use and learn from.

I'm trying to determine if it is possible to fix older file versions in the event a user reverts to a previous version that uses the wrong PublishingPageLayout URL.

Anyone know if there is any way to fix the PublishingPageLayout in the SPFileVersionCollection?

So far I have not found a way to update version history short of completely recreating an article & history using correct values.

Gary Lapointe said...

I haven't looked into modifying the version history but another option would be to use an event receiver and just adjust the layout as items are restored (so on every update check that the page layout is valid - not pretty but it would work).

Sunil said...

This is magic! You saved my life !!

Steven Mumby said...

I was also having an issue "script error" and, as suggested by Gary, it was related to non standard hyphens. If you're doing a copy/paste, delete and re-type the "-"s.

Gary Lapointe said...

Yeah - Auto Correct kept biting me in the a$$ with those hyphens - I finally got Live Writer configured to not do that so newer posts should be okay to copy and paste.

Guangming said...

Hi Gary,

this is a very nice comman.

After I applied (last night) it seems fixed the problems. However this morning I checked the sites again and the problem came back.

More details on my case:

I have two site collections in one web application; The non-root site collection is a backup of the root site collection. There are two sites are with publishing features enabled and not created with publishing templates.

One of the two site is based on Search Center Template.

Someone told me that Publishing feature has some timer jobs to sync the content. Probably those jobs change something back.

However, I reran your command and it showed that these two sites are using the right pagelayout. The only thing is that the pages are using the absolute path. The command will change the absolute path to relatives.

Any thoughts?

Guangming

Gary Lapointe said...

Guangming - I'm not actually sure what's going on. The only situation in which I know a timer job updates publishing pages is when Variations are involved. You might want to see if SP2 fixes this for you (there were some fixes related to the publishing page layout URL).

kcs02s said...

Gary - Thank you for the great solution!

BoogieMan said...

I exported a subsite that uses the publisher template and when I imported I get page not found error of course. I am trying to run your command fixpublisghinpages but I do not understand how to work the command I just don't have enough knowledge in this area. basically the site address is http://sharepointmain2:4000/bst and then I import that into http://sharepointmain2/bst into a new site collection but how should I setup the Paramaeters. I just have no clue at all everything I try does not work at all. If there is an example somewhere that would help I do better by example

Gary Lapointe said...

Try just running the command with only the required parameters:

stsadm -o gl-fixpublishingpagespagelayouturl -url http://sharepointmain2/bst -scope site

Adrian said...

Is there a way to update the Page Layout URL so that resultant Url changes from this:
"http://badrooturl/_catalogs/Masterpage/ContentTypeName.aspx, ContentTypeName"

and looks like this:
"/_catalogs/Masterpage/ContentTypeName.aspx, ContentTypeName"

I like this better than the standard "/relativeURL, /relativeURL" format.

I've tried using
-regexsearchstring "http://badrooturl/"
-regexreplacestring "/"

Which seems to do the trick when using the -verbose -test. However when we arrive at a page without a PageLayout assigned the following error message displays and the processing terminates.

Value cannot be null.
Parameter name: input


This error only occurs when I use the Regex pattern matching. Deleting the offending aspx pages running the utility and then restoring from the Recycle bin seems to do the trick.

Full syntax below:
stsadm -o gl-fixpublishingpagespagelayouturl -url "http://site" -scope site -verbose -test -regexsearchstring "http://badrooturl" -regexreplacestring "/"

Makelele said...

Every time I use stsadm to export and import a site between two site colelctions, the page layouts url is broken!

Is that right? Can I fix that URL forever?

Thanks,
Makelele

Gary Lapointe said...

It's an unfortunate side affect of the content deployment API.

Ari said...

Gary,

thank you very much for this! Although my problem is not entirely solved.

The page layout that goes wrong is connected to a custom publishing pages Content Type. Some custom columns are defined in this content type, which are then inherited (though the page layout) to the page.

After migrating the pages to another site the page layout is broken (it contains the old url). Running your script updates the page layout to the correct url indeed, but erases all custom content type columns from the page. The publishing pages content type is present in the new site as well.

Can you please advise?

Thank you,
Ari.

Ari said...

Gary,

following my previous post,

I think that I figured out what happened. The Publishing Page Content Type was present with the same name in the destination site, but with a different guid. So, when pages were migrated to destination, Sharepoint could not link them with the same content type.

I then proceeded to create a small console program to batch update the destination site pages with the correct content type.

Your code about fixing the pages layout is - I believe - irrelevant to this particular happening.

Thank you again,
Ari.

Anonymous said...

I found this site using [url=http://google.com]google.com[/url] And i want to thank you for your work. You have done really very good site. Great work, great site! Thank you!

Sorry for offtopic

Anonymous said...

Everytime I try to use this command, I get an error of
Value cannot be null.
Parameter name:type

I am entering the following:

stsadm -o gl-fixpublishingpagespagelayouturl -url "http://server/depts/is" -scope site -pagelayout "http://server/depts/_catalogs/masterpage/WebPartPage.aspx,WebPartPage"

I have tried every scope on multiple sites, and always receive the same error. What am I missing?

Gary Lapointe said...

Sounds like the extensions are not installed correctly. Can you run any of the other commands?

Anonymous said...

Able to run other commands. I did a successful gl-copylist as a test.

Gary Lapointe said...

Did you install the WSS or the MOSS WSP file?

Anonymous said...

Hi Gary,
Thank you so much for developing this excellent tool. It got our sharepoint web site back in working order again. You are the dude!!

Regards,
Jim

Anonymous said...

Hi Gary,

Love your work.

When executing the following command I am recieving a "server out of memory error" and a "cannot complete this action" error.

The server has 8gb ram and the stsadm process is only using about 2gb when running. So I don't think it is actually a memory problem. I have executed the same command on others server and recieve the same errors. It also doesn't error at the same aspx page in the pages list, so I don't think it is an issue with aspx files. It doesn't matter if I use the -verbose switch or not, the same error occurs.
It does complete fine when I use the -test switch.
Could it be a limitation with list size, there are 1210 aspx files to process in the pages list
Any ideas?

STSADM.EXE -o gl-fixpublishingpagespagelayouturl -url http://web2.domainA.name/ -scope web -regexsearchstring "http:\/\/web1.domainB.name\/site1\/_catalogs\/masterpage\/ArticleLayout\.aspx" -regexreplacestring "/_catalogs/masterpage/ArticleLayout.aspx" -verbose

Thanks in advance !!

Gary Lapointe said...

Odd - I might not be disposing of something somewhere which would explain the leak (sure I checked everything but possible I missed something). Unfortunately I don't have a 2007 dev environment at the moment so I can fix anything in that project right now - what I'd recommend you do is wrap the call to the command in a PowerShell script - it will run slower but it should avoid any leak issues.

Anonymous said...

Thanks a lot Gary, been using this tool on my MOSS 2007 implementations, works like a charm!

I would just like to point out that if a page is checked out, it's page layout will not be fixed. In my case, I tried to fix the page layouts of the whole site collection and it so happened that I have a page checked out when i used your tool, all pages were fixed except for that one. I tried it again with the page now checked in, page fixed!

Thanks again.

Julius Serdone
jmserdone@gmail.com

Gary Lapointe said...

Thanks Julius - that's actually by design - I didn't want to force a check-in of changes that may not be ready to be published.

Anonymous said...

Hey
Your could is suppose to save my work, because i need to fix the layout url. But i do not know how to deploy the code nor how to use it could you tell me how? i found a link but i did not understand how i am supposed to use it.
Many Thanks
Khalil

Gary Lapointe said...

Go to the downloads page and follow the instructions to download and deploy the WSP appropriate to your environment.

Anonymous said...

Hi Gary,

I am trying to use the gl-fixpublishingpagespagelayouturl command, but it does not seem to be doing anything. The Default.aspx page on our test server is linked to the Pagelayout on prod instead of dev. I must not be doing the syntax correctly, and I don't see any examples for just changing a single page (all the other pages in the Pages directory are fine). Could you give me an example? It keeps telling me the operation completed successfully, but nothing happens!

Thanks,
Joe

Gary Lapointe said...

Use the pagename property with a scope of page to change the value for a single page.

Anonymous said...

Hi Gary,

I tried your command its saying operation completed successfully but its not doing anything.

Can you tell me how can I fix only 1 hyperlink before running it to the entire web.

when I check in SPdesigner I have
test/mytest hyperlink as broken and mt.aspx as in page

what should i write in -pagename?

hypelink to be fixed or page in which this hyperlink reside.

I am writing this command

stsadm gl-fixpub -url"http://test" - pagename "hyperlink" or -pagename "page" -scope page

Gary Lapointe said...

Specify a scope of Page and for the page name use the actual name of the file (ie "default.aspx").

Anonymous said...

Thanks for reply Gary,

but how about hyperlink. in url I have to specify path of hyperlink?

It is not taking whole path. So if I specify only server name how can I check for only 1 hyperlink?

I am sorry I am new to this..

Gary Lapointe said...

The -url parameter just points to the web containing the pages library that you wish to update.

Anonymous said...

Thanks for reply.

I did what you said.

It changing layout url and Operation completed successfully

But when I go to SPdesigner and verify broken link its still broke. So its not fixing.

Is there anything I am missing?

Sorry if I am giving you hard time!!

Anonymous said...

I am getting a command line error when I attempt to use this to update a web application. Here is the command I am using:

stsadm -o gl-fixpublishingpagespagelayouturl -
url http://test.myurl.org/ -scope webapplication

What am I doing wrong?

Gary Lapointe said...

Make sure you enter it all on one line (not sure if the line break after the "-" is just a result of how you entered in the comment or not). Also, make sure you're not pasting in and that you are typing it out so as to avoid any unicode characters. I'm also assuming that the extensions are installed and that you can bring up the help for the extensions.