I wasn’t planning on creating this command but then I ran my gl-replacefieldvalues command and forgot to pass the “-publish” switch in so now I was stuck with all these list items that needed to be published and/or approved. When this happened I thought, no problem – Andrew Connell has a PublishAllItems command so I’ll just download his stuff and use that rather than create my own. Unfortunately however, Andrew’s command didn’t take all scenarios into account and thus I was left with the task of creating a new command to do what Andrew’s attempted.

For those that are using Andrew’s command without issue I decided to name mine gl-publishitems so as to not step on his command thus allowing both to be installed. The main problem I had with Andrew’s command was that it didn’t take into account items that needed approval but which did not have an SPFile object associated with it. There were also issues where he was calling SPListItem.Update() after a checkin which would throw an error because the update call requires the file to be checked out.

Andrew’s code also didn’t take into account workflows that would need to be canceled as a result of approving an item. And finally his code didn’t allow all the scoping options that I needed (Farm, Web Application, Site Collection, Web, or List). The large bulk of the code is just a series of methods with different loops in them to handle the various scoping capabilities (similar to what I did with gl-replacefieldvalues).

The core code itself is considering all the cases in which an item may need to be either checked in, published, and/or approved. I’ve also added the ability to dump all changes to a log file as well as to run the command in a “test” mode where it will show you what it would publish but not actually make any changes – I’d strongly recommend you use this first to verify all the changes that will be made. The core code is shown below:

  1/// <summary>
  2/// Publishes the list item.
  3/// </summary>
  4/// <param name="item">The item.</param>
  5/// <param name="list">The list.</param>
  6/// <param name="settings">The settings.</param>
  7/// <param name="source">The source.</param>
  8internal static void PublishListItem(SPListItem item, SPList list, Settings settings, string source)
  9{
 10    try
 11    {
 12        if (item.File != null)
 13        {
 14            // We first need to handle the case in which we have a file which means that
 15            // we have to deal with the possibility that the file may be checked out.
 16            if (item.Level == SPFileLevel.Checkout)
 17            {
 18                // The file is checked out so we now need to check it in - we'll do a major
 19                // checkin which will result in it being published.
 20                if (!settings.Test)
 21                {
 22                    item.File.CheckIn("Checked in by " + source, SPCheckinType.MajorCheckIn);
 23                    // We need to get the File's version of the SPListItem so that we get the changes.
 24                    // Calling item.Update() will fail because the file is no longer checked out.
 25                    item = item.File.Item; // If workflow is supported this should now be in a pending state.
 26                }
 27                TaskCounts.Checkin++;
 28                TaskCounts.Publish++; // The major checkin causes it to be published so we'll track that as well.
 29                Log(settings, string.Format("Checked in item: {0} ({1})", item.Title, item.Url));
 30            }
 31            else if (item.Level == SPFileLevel.Draft && item.ModerationInformation == null)
 32            {
 33                // The file isn't checked out but it is in a draft state so we need to publish it.
 34                if (!settings.Test)
 35                {
 36                    item.File.Publish("Published by " + source);
 37                    // We need to get the File's version of the SPListItem so that we get the changes.
 38                    // Calling item.Update() will fail because the file is no longer checked out.
 39                    item = item.File.Item; // If workflow is supported this should now be in a pending state.
 40                }
 41                TaskCounts.Publish++;
 42                Log(settings, string.Format("Published item: {0} ({1})", item.Title, item.Url));
 43            }
 44        }
 45    }
 46    catch (Exception ex)
 47    {
 48        TaskCounts.Errors++;
 49        Log(settings, string.Format("An error occured checking in an item:\r\n{0}", ex.Message));
 50    }
 51
 52    if (item.ModerationInformation != null)
 53    {
 54        // If ModerationInformation is not null then the item supports content approval.
 55        if (item.File == null &&
 56            (item.ModerationInformation.Status == SPModerationStatusType.Draft ||
 57            item.ModerationInformation.Status == SPModerationStatusType.Pending))
 58        {
 59            // If content approval is supported but no file is associated with the item then we have
 60            // to treat it differently.  We simply set the status information directly.
 61            try
 62            {
 63                if (!settings.Test)
 64                {
 65                    // Because the SPListItem object has no direct approval method we have to 
 66                    // set the information directly (there's no SPFile object to use).
 67                    CancelWorkflows(settings, list, item);
 68                    item.ModerationInformation.Status = SPModerationStatusType.Approved;
 69                    item.ModerationInformation.Comment = "Approved by " + source;
 70                    item.Update(); // Because there's no SPFile object we don't have to worry about the item being checkedout for this to succeed as you can't check it out.
 71                }
 72                TaskCounts.Approve++;
 73                Log(settings, string.Format("Approved item: {0} ({1})", item.Title, item.Url));
 74            }
 75            catch (Exception ex)
 76            {
 77                TaskCounts.Errors++;
 78                Log(settings, string.Format("An error occured approving an item:\r\n{0}", ex.Message));
 79            }
 80        }
 81        else
 82        {
 83            // The item supports content approval and we have an SPFile object to work with.
 84            try
 85            {
 86                if (item.ModerationInformation.Status == SPModerationStatusType.Pending)
 87                {
 88                    // The item is pending so it's already been published - we just need to approve.
 89                    if (!settings.Test)
 90                    {
 91                        // Cancel any workflows.
 92                        CancelWorkflows(settings, list, item);
 93                        item.File.Approve("Approved by " + source);
 94                        // We don't need to re-retrieve the item as we're now done with it.
 95                    }
 96                    TaskCounts.Approve++;
 97                    Log(settings, string.Format("Approved item: {0} ({1})", item.Title, item.Url));
 98                }
 99            }
100            catch (Exception ex)
101            {
102                TaskCounts.Errors++;
103                Log(settings, string.Format("An error occured approving an item:\r\n{0}", ex.Message));
104            }
105
106            try
107            {
108                if (item.ModerationInformation.Status == SPModerationStatusType.Draft)
109                {
110                    // The item is in a draft state so we have to first publish it and then approve it.
111                    if (!settings.Test)
112                    {
113                        item.File.Publish("Published by " + source);
114                        // Cancel any workflows.
115                        CancelWorkflows(settings, list, item);
116                        item.File.Approve("Approved by " + source);
117                        // We don't need to re-retrieve the item as we're now done with it.
118                    }
119                    TaskCounts.Publish++;
120                    TaskCounts.Approve++;
121                    Log(settings, string.Format("Published item: {0} ({1})", item.Title, item.Url));
122                }
123            }
124            catch (Exception ex)
125            {
126                TaskCounts.Errors++;
127                Log(settings, string.Format("An error occured approving an item:\r\n{0}", ex.Message));
128            }
129        }
130    }
131}
132 
133/// <summary>
134/// Cancels the workflows.  This code is a re-engineering of the code that Microsoft uses
135/// when approving an item via the browser.  That code is in Microsoft.SharePoint.ApplicationPages.ApprovePage.
136/// </summary>
137/// <param name="settings">The settings.</param>
138/// <param name="list">The list.</param>
139/// <param name="item">The item.</param>
140private static void CancelWorkflows(Settings settings, SPList list, SPListItem item)
141{
142    if (list.DefaultContentApprovalWorkflowId != Guid.Empty &&
143        item.DoesUserHavePermissions((SPBasePermissions.ApproveItems |
144        SPBasePermissions.EditListItems)))
145    {
146        // If the user has rights to do so then we need to cancel any workflows that
147        // are associated with the item.  This is based on how the 
148        SPSecurity.RunWithElevatedPrivileges(
149            delegate
150            {
151                foreach (SPWorkflow workflow in item.Workflows)
152                {
153                    if (workflow.ParentAssociation.Id !=
154                        list.DefaultContentApprovalWorkflowId)
155                    {
156                        continue;
157                    }
158                    SPWorkflowManager.CancelWorkflow(workflow);
159                    Log(settings,
160                        string.Format("Cancelling workflow {0} for item: {1} ({2})",
161                        workflow.WebId, item.Title, item.Url));
162                }
163            });
164    }
165}

The syntax of the command I created can be seen below:

C:\>stsadm -help gl-publishitems

stsadm -o gl-publishitems

Publishes all items at a given scope.  Use -test to verify what will be published before executing.

Parameters:
        [-url <url to publish>]
        -scope <Farm | WebApplication | Site | Web | List>
        [-quiet]
        [-test]
        [-logfile <log file>]

Here’s an example of how to publish all items in a given site collection:

stsadm -o gl-publishitems -url "http://intranet/hr" -scope site -logfile "c:\publish.log"