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"