When working with clients on their SharePoint deployment I often get questions about the quota templates and how they work. Quotas are kind of odd because of the disconnected nature of the quota template and the quota that is assigned to a site collection. Often people assume that changing the template settings affects all site collections using that template. Unfortunately that is not the case. The issue is made worse by the fact that the “Site Collection Quotas and Locks” page within Central Admin shows the values of the template assigned and not the actual quota assigned to the site – so administrators will change the template settings, then look at a site collection using that template and it “appears” to them that the template change has affected the site. But if you go to the command line and run the enumsites
command you can see that the quota values have not in fact been changed.
So how do you get the value applied? Simply click the “OK” button on the “Site Collection Quotas and Locks” page. Okay, so that’s all nice and peachy if you have one or two site collections, but what if you have several hundred? That’s where my new command comes in: gl-syncquotas
. Synchronizing quotas using the object model is actually pretty easy – you just get an instance of the quota template (SPQuotaTemplate
), which inherits from SPQuota
, and set the Quota
property of the SPSite
object – that’s it.
Here’s the code showing how I did it:
1using System;
2using System.Text;
3using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
4using Lapointe.SharePoint.STSADM.Commands.SPValidators;
5using System.Collections.Specialized;
6using Microsoft.SharePoint;
7using Microsoft.SharePoint.Administration;
8
9namespace Lapointe.SharePoint.STSADM.Commands.Quotas
10{
11 public class SyncQuotas : SPOperation
12 {
13 private SPQuotaTemplateCollection m_quotaColl;
14 private SPQuotaTemplate m_quota;
15 private bool m_setQuota = false;
16
17 /// <summary>
18 /// Initializes a new instance of the <see cref="SyncQuotas"/> class.
19 /// </summary>
20 public SyncQuotas()
21 {
22 SPParamCollection parameters = new SPParamCollection();
23 parameters.Add(new SPParam("url", "url", false, null, new SPUrlValidator()));
24 parameters.Add(new SPParam("scope", "s", false, "site", new SPRegexValidator("(?i:^Farm$|^WebApplication$|^Site$)")));
25 parameters.Add(new SPParam("quota", "q", false, null, new SPNonEmptyValidator()));
26 parameters.Add(new SPParam("setquota", "set"));
27
28
29 StringBuilder sb = new StringBuilder();
30 sb.Append("\r\n\r\nSynchronizes site quota settings with those defined in the quota templates.\r\n\r\nParameters:");
31 sb.Append("\r\n\t[-scope <Farm | WebApplication | Site (default)>]");
32 sb.Append("\r\n\t[-url <url>]");
33 sb.Append("\r\n\t[-quota <quota template name to synchronize (sites using other quotas will not be affected)>]");
34 sb.Append("\r\n\t[-setquota (if scope is site or web application and a quota is specified apply the quota to any site that does not currently have a quota assigned)]");
35
36 Init(parameters, sb.ToString());
37 }
38
39 /// <summary>
40 /// Gets the help message.
41 /// </summary>
42 /// <param name="command">The command.</param>
43 /// <returns></returns>
44 public override string GetHelpMessage(string command)
45 {
46 return HelpMessage;
47 }
48
49 /// <summary>
50 /// Executes the specified command.
51 /// </summary>
52 /// <param name="command">The command.</param>
53 /// <param name="keyValues">The key values.</param>
54 /// <param name="output">The output.</param>
55 /// <returns></returns>
56 public override int Execute(string command, StringDictionary keyValues, out string output)
57 {
58 output = string.Empty;
59 Verbose = true;
60
61 string scope = Params["scope"].Value.ToLowerInvariant();
62
63 SPFarm farm = SPFarm.Local;
64 SPWebService webService = farm.Services.GetValue<SPWebService>("");
65
66 m_quotaColl = webService.QuotaTemplates;
67 m_setQuota = Params["setquota"].UserTypedIn;
68
69 if (Params["quota"].UserTypedIn)
70 {
71 m_quota = m_quotaColl[Params["quota"].Value];
72 if (m_quota == null)
73 throw new ArgumentException("The specified quota template name could not be found.");
74 }
75
76 SPEnumerator enumerator;
77 if (scope == "farm")
78 {
79 enumerator = new SPEnumerator(SPFarm.Local);
80 }
81 else if (scope == "webapplication")
82 {
83 enumerator = new SPEnumerator(SPWebApplication.Lookup(new Uri(Params["url"].Value.TrimEnd('/'))));
84 }
85 else
86 {
87 // scope == "site"
88 using (SPSite site = new SPSite(Params["url"].Value.TrimEnd('/')))
89 {
90 Sync(site);
91 }
92 return OUTPUT_SUCCESS;
93 }
94
95 enumerator.SPSiteEnumerated += enumerator_SPSiteEnumerated;
96 enumerator.Enumerate();
97
98 return OUTPUT_SUCCESS;
99 }
100
101 /// <summary>
102 /// Validates the specified key values.
103 /// </summary>
104 /// <param name="keyValues">The key values.</param>
105 public override void Validate(StringDictionary keyValues)
106 {
107 if (Params["scope"].Validate())
108 {
109 Params["url"].IsRequired = true;
110 Params["url"].Enabled = true;
111 if (Params["scope"].Value.ToLowerInvariant() == "farm")
112 {
113 Params["url"].IsRequired = false;
114 Params["url"].Enabled = false;
115 }
116 }
117 if (Params["setquota"].UserTypedIn)
118 {
119 Params["quota"].IsRequired = true;
120 if (Params["scope"].Value.ToLowerInvariant() == "farm")
121 throw new SPSyntaxException("Scope of \"farm\" is not valid when \"setquota\" is specified.");
122 }
123 base.Validate(keyValues);
124 }
125
126 /// <summary>
127 /// Handles the SPSiteEnumerated event of the enumerator control.
128 /// </summary>
129 /// <param name="sender">The source of the event.</param>
130 /// <param name="e">The <see cref="Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPEnumerator.SPSiteEventArgs"/> instance containing the event data.</param>
131 private void enumerator_SPSiteEnumerated(object sender, SPEnumerator.SPSiteEventArgs e)
132 {
133 Sync(e.Site);
134 }
135
136 /// <summary>
137 /// Syncs the specified site quota with the quota template.
138 /// </summary>
139 /// <param name="site">The site.</param>
140 private void Sync(SPSite site)
141 {
142 SPQuota currentQuota = site.Quota;
143 SPQuotaTemplate currentTemplate = null;
144 foreach (SPQuotaTemplate quota in m_quotaColl)
145 {
146 if (currentQuota.QuotaID == quota.QuotaID)
147 {
148 currentTemplate = quota;
149 break;
150 }
151 }
152 if (currentTemplate == null)
153 {
154 if (!m_setQuota)
155 {
156 Log("WARNING: No quota template has been assigned to site {0}. Use the -setquota parameter to assign a quota.", site.Url);
157 return;
158 }
159 Log("PROGRESS: Synchronizing {0}", site.Url);
160 Log("PROGRESS: No quota template assigned to site. Assigning template...", site.Url);
161 site.Quota = m_quota;
162
163 Log("PROGRESS: Template \"{0}\" assigned to site.", m_quota.Name);
164 return;
165 }
166
167 if (m_quota == null || (m_quota != null && currentQuota.QuotaID == m_quota.QuotaID))
168 {
169 Log("PROGRESS: Synchronizing {0}", site.Url);
170 Log("PROGRESS: Currently using template \"{0}\".", currentTemplate.Name);
171
172 if (site.Quota.InvitedUserMaximumLevel == currentTemplate.InvitedUserMaximumLevel &&
173 site.Quota.StorageMaximumLevel == currentTemplate.StorageMaximumLevel &&
174 site.Quota.StorageWarningLevel == currentTemplate.StorageWarningLevel)
175 {
176 Log("PROGRESS: No changes necessary, quota already synchronized with template.");
177 return;
178 }
179 site.Quota = currentTemplate;
180
181 Log("PROGRESS: Storage maximum updated from {0}MB to {1}MB",
182 ((currentQuota.StorageMaximumLevel / 1024) / 1024).ToString(),
183 ((site.Quota.StorageMaximumLevel / 1024) / 1024).ToString());
184 Log("PROGRESS: Storage warning updated from {0}MB to {1}MB",
185 ((currentQuota.StorageWarningLevel / 1024) / 1024).ToString(),
186 ((site.Quota.StorageWarningLevel / 1024) / 1024).ToString());
187 Log("PROGRESS: Invited user maximum updated from {0} to {1}",
188 currentQuota.InvitedUserMaximumLevel.ToString(),
189 site.Quota.InvitedUserMaximumLevel.ToString());
190 }
191 }
192 }
193}
The help for the command is shown below:
C:\>stsadm -help gl-syncquotas
stsadm -o gl-syncquotas
Synchronizes site quota settings with those defined in the quota templates.
Parameters:
[-scope <Farm | WebApplication | Site (default)>]
[-url <url>]
[-quota <quota template name to synchronize (sites using other quotas will not be affected)>]
[-setquota (if scope is site or web application and a quota is specified apply the quota to any site that does not currently have a quota assigned)]
The following table summarizes the command and its various parameters:
Command Name | Availability | Build Date |
---|---|---|
gl-syncquotas | WSS v3, MOSS 2007 | Released: 2/12/2009 |
Parameter Name | Short Form | Required | Description | Example Usage |
---|---|---|---|---|
url | Yes if scope is not Farm | URL of the web application or site collection. | -url http://mysites | |
scope | s | No – defaults to site | The scope to use. Valid values are “Farm”, “WebApplication”, and “Site” | -scope site , -s site |
quota | q | No | The name of the quota template to synchronize. If not specified then all site collections will be synchronized regardless of the quota assigned to the site. | -quota "Personal Site" , -q "Personal Site" |
setquota | set | No | If the scope is site or webapplication and a site collection has no quota assigned to it then the quota template specified via the –quota parameter will be assigned to the site. | -setquota , -set |
The following is an example of how to synchronize all the “My Site” sites and assign the “Personal Site” quota template to any site collections under the http://mysites web application that do not currently have a quota assigned:
stsadm -o gl-syncquotas -scope webapplication -url http://mysites -quota "Personal Site" -setquota