[抱歉,如果这篇文章在您的供稿中重复出现– it didn’最初由于Feedburner的故障而无法吸引许多读者]
- 将jQuery精简为基本要素 (技术)
- 使用JavaScript Client OM处理列表 (技术)
- 结合使用jQuery AJAX和HTTP处理程序 (技术)
- 从HTTP处理程序返回JSON (技术)
- 为客户端OM和jQuery启用Intellisense (小费)
- 调试jQuery / JavaScript (小费)
- 构建AJAX应用程序时的有用工具 (小费)
- 将现有应用程序迁移到jQuery / AJAX– this article
One of the most interesting things I did when 得到ting my head around AJAX/jQuery with SharePoint, was 到ctually migrate something I’d写入AJAX。我想这样做的原因有几个:
- 它是一个真实的应用程序,而不是Hello World
- 这应该是学习的好方法
- 我想到了演示’在SharePoint Saturday UK的演讲中,现场直播的步骤将是有趣的部分
我决定 SP2010功能升级套件 将是此练习的好人选– it’s more of a “mini-app”而不是功能完善的应用程序,因为它实际上由几个SharePoint应用程序页面组成。这些与新‘upgradable’功能功能(请阅读我的 功能升级系列 以获得更多信息),但从根本上说,它查询数据库,显示记录,然后允许更新其中的一些或全部记录–换句话说,相当典型 欺诈 应用程序,这对AJAX-ify很有帮助。
不用说,它’是所有标准ASP.Net/SharePoint控件,因此在执行搜索/更新记录等操作时会发生回发。
的original code
如果你’对升级过程(或构建AJAX应用的思维方式)感兴趣的任何方式,’s probably worth having a quick scroll 至 得到 feel for the original code 在 this point.
的ASPX page:
1: <%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
2: <%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
3: <%@ Import Namespace="Microsoft.SharePoint" %>
4: <%@ Import Namespace="Microsoft.SharePoint.WebControls" %>
5: <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
6: <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
7: <%@ Register Tagprefix="asp" Namespace="System.Web" Assembly="System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %>
8: <%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
9: <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
10: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CentralAdminFeatureUpgrade.aspx.cs" Inherits="COB.SharePoint.Utilities.FeatureUpgradeKit.CentralAdminFeatureUpgrade" DynamicMasterPageFile="~masterurl/default.master" %>
11: <%@ Register TagPrefix="wssuc" TagName="InputFormSection" src="/_controltemplates/InputFormSection.ascx" %>
12: <%@ Register TagPrefix="wssuc" TagName="InputFormControl" src="/_controltemplates/InputFormControl.ascx" %>
13: <%@ Register TagPrefix="wssuc" TagName="LinkSection" src="/_controltemplates/LinkSection.ascx" %>
14: <%@ Register TagPrefix="wssuc" TagName="ButtonSection" src="/_controltemplates/ButtonSection.ascx" %>
15: <%@ Register TagPrefix="wssuc" TagName="ActionBar" src="/_controltemplates/ActionBar.ascx" %>
16: <%@ Register TagPrefix="wssuc" TagName="ToolBar" src="/_controltemplates/ToolBar.ascx" %>
17: <%@ Register TagPrefix="wssuc" TagName="ToolBarButton" src="/_controltemplates/ToolBarButton.ascx" %>
18: <%@ Register TagPrefix="wssuc" TagName="Welcome" src="/_controltemplates/Welcome.ascx" %>
19:
20: <asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
21: <link rel="stylesheet" type="text/css" href="/_layouts/COB/Styles/COB.SharePoint.FeatureUpgradeKit.css"/>
22: <脚本 type="text/javascript" src="/_layouts/jquery-1.4.1.min.js"></脚本>1:
2: <script type="text/javascript" src="/_layouts/COB/js/FeatureUpgradeSliding.js">1: </script>
2: <script type="text/javascript" src="/_layouts/COB/js/FeatureUpgrade.js">1: </script>
2: <% if (false) { %>3: <script type="text/javascript" src="../../../layouts/jquery-1.4.1-vsdoc.js"></脚本>
23: <% 1: }
%>
24: <table class="propertysheet" border="0" width="100%" cellspacing="0" cellpadding="0">
25: <div id="introText">
26: By 克里斯·奥'Brien - <a target="_blank" href="http://www.milwaukeeticketsblog.com">www.sharepointnutsandbolts.com</a>
27: <br />
28: </div>
29: <wssuc:InputFormSection ID="scopeSection" Title="Select Feature scope 至 query for"
30: runat="server">
31: <Template_Description>
32: <SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server" text="Select the scope of the Features are being upgraded" EncodeMethod='HtmlEncode'/>
33: </Template_Description>
34: <Template_InputFormControls>
35: <wssuc:InputFormControl SmallIndent="true" runat="server">
36: <Template_Control>
37: <asp:下拉列表 ID="ddlScopes" runat="server">
38: <asp:ListItem ID="liFarm" runat="server" Value="Farm" />
39: <asp:ListItem ID="liWebApp" runat="server" Value="WebApplication" />
40: <asp:ListItem ID="liSite" runat="server" Value="Site" />
41: <asp:ListItem ID="liWeb" runat="server" Value="Web" />
42: </asp:下拉列表>
43: </Template_Control>
44: </wssuc:InputFormControl>
45: </Template_InputFormControls>
46: </wssuc:InputFormSection>
47: <wssuc:InputFormSection ID="webAppSection" Title="Web application"
48: runat="server" style="display:none;">
49: <Template_Description>
50: <SharePoint:EncodedLiteral ID="EncodedLiteral2" runat="server" text="Select the web application where you wish 至 upgrade site-scoped Features" EncodeMethod='HtmlEncode'/>
51: </Template_Description>
52: <Template_InputFormControls>
53: <wssuc:InputFormControl SmallIndent="true" runat="server">
54: <Template_Control>
55: <SharePoint:WebApplicationSelector runat=server ID="webAppSelector" UseDefaultSelection="true" />
56: </Template_Control>
57: </wssuc:InputFormControl>
58: </Template_InputFormControls>
59: </wssuc:InputFormSection>
60: <wssuc:InputFormSection ID="siteSection" Title="Site Collection"
61: runat="server" style="display:none;">
62: <Template_Description>
63: <SharePoint:EncodedLiteral ID="EncodedLiteral3" runat="server" text="Select the web application 和 site collection where you wish 至 upgrade web-scoped Features" EncodeMethod='HtmlEncode'/>
64: </Template_Description>
65: <Template_InputFormControls>
66: <wssuc:InputFormControl SmallIndent="true" runat="server">
67: <Template_Control>
68: <SharePoint:SiteAdministrationSelector runat=server ID="siteCollectionSelector" />
69: </Template_Control>
70: </wssuc:InputFormControl>
71: </Template_InputFormControls>
72: </wssuc:InputFormSection>
73: <wssuc:InputFormSection ID="featureFilterSection" Title="Feature status"
74: runat="server">
75: <Template_Description>
76: <SharePoint:EncodedLiteral ID="EncodedLiteral4" runat="server" text="Display only Features which need upgrade?" EncodeMethod='HtmlEncode'/>
77: </Template_Description>
78: <Template_InputFormControls>
79: <wssuc:InputFormControl SmallIndent="true" runat="server">
80: <Template_Control>
81: <asp:RadioButtonList ID="rblUpgradeOnly" runat="server">
82: <asp:ListItem
83: Selected="True"
84: Text="Only Features requiring upgrade"
85: Value="NeedUpgrade" />
86: <asp:ListItem
87: Selected="False"
88: Text="All Features"
89: Value="NoUpgrade" />
90: </asp:RadioButtonList>
91: </Template_Control>
92: </wssuc:InputFormControl>
93: </Template_InputFormControls>
94: </wssuc:InputFormSection>
95: <wssuc:InputFormSection ID="searchSection" Title="Click 'Search' 至 run the query"
96: runat="server">
97: <Template_InputFormControls>
98: <wssuc:InputFormControl SmallIndent="true" runat="server">
99: <Template_Control>
100: <asp:按钮 runat="server" class="ms-ButtonHeightWidth" Text="Search" id="searchButton" OnClick="searchButton_Click" />
101: </Template_Control>
102: </wssuc:InputFormControl>
103: </Template_InputFormControls>
104: </wssuc:InputFormSection>
105: </table>
106:
107: <div id="resultsContainer">
108: <asp:面板 ID="pnlNoEntries" CssClass="noResults" runat="server" Visible="false">
109: <asp:标签 runat="server" ID="lblMessage" Text="There are no Feature instances requiring upgrade 在 this scope." />
110: </asp:面板>
111: <asp:面板 ID="pnlUpgradeSuccess" CssClass="upgradeSuccess" runat="server" Visible="false">
112: <strong>的following Feature instances were upgraded successfully:</strong> <br /><br />
113: <asp:标签 ID="lblSuccesses" runat="server" />
114: </asp:面板>
115: <asp:面板 ID="pnlUpgradeFailure" CssClass="upgradeFailure" runat="server" Visible="false">
116: <strong>的following Feature instances failed 至 upgrade:</strong> <br /><br />
117: <asp:标签 ID="lblErrors" runat="server" />
118: </asp:面板>
119: <asp:面板 ID="pnlGrid" runat="server">
120: <br />
121: <SharePoint:SPGridView
122: runat="server"
123: ID="grdFeatures"
124: AutoGenerateColumns="false"
125: RowStyle-BackColor="#DDDDDD"
126: AlternatingRowStyle-BackColor="#EEEEEE" ShowHeaderWhenEmpty="true">
127: <EmptyDataTemplate>
128: </EmptyDataTemplate>
129: </SharePoint:SPGridView>
130:
131: <div id="upgradeControlsNonAjax">
132: <span id="upgradeControlsLeft">
133:
134: </span>
135: <span id="upgradeControlsRight">
136: <asp:按钮 runat="server" class="ms-ButtonHeightWidth upgradeButton" Text="Upgrade Features" id="topOKButton" OnClick="okButton_Click" accesskey="<%$Resources:wss,okbutton_accesskey%>"/>
137: </span>
138: </div>
139: </asp:面板>
140: <br />
141: <asp:标签 ID="lblResults" Visible="false" runat="server" />
142: </div>
143: </asp:Content>
144:
145: <asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
146: Feature upgrade
147: </asp:Content>
148:
149: <asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
150: Feature upgrade
151: </asp:Content>
后台代码:
1: using System;
2: using Microsoft.SharePoint;
3: using Microsoft.SharePoint.WebControls;
4: using System.Web.UI.WebControls;
5: using System.Collections;
6: using System.Web.UI;
7: using System.Data;
8: using Microsoft.SharePoint.Administration;
9: using System.Collections.Generic;
10: using System.Linq;
11: using COB.SharePoint.Utilities.FeatureUpgradeKit.Entities;
12: using COB.SharePoint.Utilities.FeatureUpgradeKit.Core;
13:
14: namespace COB.SharePoint.Utilities.FeatureUpgradeKit
15: {
16: /// <summary>
17: /// Provides an administration page for upgrading Features in SharePoint.
18: /// </summary>
19: /// <created by="Chris O'Brien" date="09 七月 2010" />
20: public partial class CentralAdminFeatureUpgrade : LayoutsPageBase
21: {
22: #region -- Fields 和 child classes --
23:
24: private Hashtable fields = new Hashtable();
25: private DataTable dtFeatures = null;
26: private const string NeedUpgradeValue = "NeedUpgrade";
27: public const string UpgradeCheckBoxId = "chkUpgrade";
28:
29: private class FeatureFields
30: {
31: public const string DisplayName = "DisplayName";
32: public const string Version = "Version";
33: public const string Parent = "Parent";
34: public const string DoUpgrade = "DoUpgrade";
35: public const string FeatureID = "FeatureID";
36: public const string FeatureScope = "Scope";
37: public const string Identifiers = "Identifiers";
38: }
39:
40: private class GridHeaders
41: {
42: public const string DisplayName = "Feature name";
43: public const string Version = "Current version";
44: public const string Parent = "Parent";
45: public const string DoUpgrade = "Upgrade?";
46: public const string FeatureID = "Feature ID";
47: public const string FeatureScope = "Scope";
48: public const string Identifiers = "Identifiers";
49: }
50:
51: #endregion
52:
53: #region -- Page event handlers --
54:
55: protected override void OnInit(EventArgs e)
56: {
57: // initialise SPGridView etc..
58: addColumns();
59: grdFeatures.AllowSorting = false;
60:
61: 至pOKButton.Visible = false;
62: base.OnInit(e);
63: }
64:
65: protected override void OnLoad(EventArgs e)
66: {
67: // any postback on this page is caused by controls related 至 querying Features/working with grid, so we'll rebind data on any postback..
68: if (Page.IsPostBack)
69: {
70: populateFeaturesTable();
71: bindData(dtFeatures);
72: }
73:
74: base.OnLoad(e);
75: }
76:
77: protected void searchButton_Click(object sender, EventArgs e)
78: {
79: if (dtFeatures != null)
80: {
81: 至pOKButton.Visible = (dtFeatures.Rows.Count > 0 && rblUpgradeOnly.SelectedValue == NeedUpgradeValue);
82: pnlNoEntries.Visible = (dtFeatures.Rows.Count == 0);
83: pnlUpgradeFailure.Visible = false;
84: pnlUpgradeSuccess.Visible = false;
85: }
86: }
87:
88: protected void okButton_Click(object sender, EventArgs e)
89: {
90: clearMessages();
91:
92: List<FeatureDetails> upgradeIdentifiers = new List<FeatureDetails>();
93: foreach (SPGridViewRow row in grdFeatures.Rows)
94: {
95: Control c = row.FindControl(UpgradeCheckBoxId);
96: CheckBox chk = c as CheckBox;
97: if ((chk != null) && (chk.Checked))
98: {
99: upgradeIdentifiers.Add(FeatureDetails.CreateFromString(grdFeatures.DataKeys[row.RowIndex].Value.ToString()));
100: }
101: }
102:
103: // upgrade 选择ed Features 和 display results..
104: SPSecurity.RunWithElevatedPrivileges(delegate() {
105: Dictionary<FeatureDetails, IEnumerable<Exception>> upgradeResults = FeatureManager.UpgradeFeatures(upgradeIdentifiers);
106: presentResultMessages(upgradeResults);
107: });
108:
109: // now re-run query 至 update grid..
110: populateFeaturesTable();
111: bindData(dtFeatures);
112:
113: if (dtFeatures.Rows.Count == 0)
114: {
115: pnlNoEntries.Visible = false;
116: 至pOKButton.Visible = false;
117: }
118: }
119:
120: #endregion
121:
122: #region -- UI handling --
123:
124: private void presentResultMessages(Dictionary<FeatureDetails, IEnumerable<Exception>> upgradeResults)
125: {
126: int errorCount = 0;
127: foreach (KeyValuePair<FeatureDetails, IEnumerable<Exception>> kvp in upgradeResults)
128: {
129: List<Exception> exceptions = null;
130: if (kvp.Value != null)
131: {
132: exceptions = kvp.Value.ToList();
133: }
134:
135: if (exceptions != null && exceptions.Count > 0)
136: {
137: pnlUpgradeFailure.Visible = true;
138: if (errorCount > 0)
139: {
140: lblErrors.Text += "<br />";
141: }
142: lblErrors.Text += string.Format("Feature '{0}' with parent '{1}' failed with the following errors:<br /><br />",
143: kvp.Key.FeatureName, kvp.Key.ParentString);
144: foreach (Exception exception in exceptions)
145: {
146: lblErrors.Text += string.Format(" - {0}<br />", exception.ToString());
147: errorCount += exceptions.Count;
148: }
149: }
150: else
151: {
152: pnlUpgradeSuccess.Visible = true;
153: lblSuccesses.Text += string.Format("Feature '{0}' with parent '{1}' upgraded successfully.<br />",
154: kvp.Key.FeatureName, kvp.Key.ParentString);
155: }
156: }
157: }
158:
159: private void clearMessages()
160: {
161: lblErrors.Text = string.Empty;
162: lblSuccesses.Text = string.Empty;
163: lblResults.Text = string.Empty;
164: }
165:
166: private void bindData(DataTable dtFeatures)
167: {
168: // assign default sort expression..
169: DataView sortedView = new DataView(dtFeatures);
170: sortedView.Sort = FeatureFields.DisplayName + " ASC";
171:
172: // add columns 至 grid..
173: grdFeatures.Columns.Clear();
174:
175: foreach (DictionaryEntry de in fields)
176: {
177: bool visibleField = (de.Key.ToString() != GridHeaders.Identifiers);
178: addBoundField(de.Key.ToString(), de.Value.ToString(), visibleField);
179: }
180:
181: if (rblUpgradeOnly.SelectedValue == NeedUpgradeValue)
182: {
183: // add checkbox column..
184: addUpgradeCheckBox(grdFeatures);
185: }
186:
187: grdFeatures.DataKeyNames = new string[] { FeatureFields.Identifiers };
188: grdFeatures.DataSource = sortedView;
189: grdFeatures.DataBind();
190: }
191:
192: private static void addUpgradeCheckBox(SPGridView gridView)
193: {
194: TemplateField upgradeField = new TemplateField
195: {
196: HeaderText = GridHeaders.DoUpgrade,
197: ItemTemplate = new UpgradeCheckBoxGridviewTemplate()
198: };
199:
200: gridView.Columns.Add(upgradeField);
201: }
202:
203: private void addBoundField(string headerText, string dataField, bool visibleField)
204: {
205: SPBoundField userField = new SPBoundField();
206: userField.HeaderText = headerText;
207: userField.DataField = dataField;
208: userField.SortExpression = headerText;
209: userField.Visible = visibleField;
210: grdFeatures.Columns.Add(userField);
211: }
212:
213: private static Control findControlRecursive(Control Root, string Id)
214: {
215: if (Root.ID == Id)
216: return Root;
217:
218: foreach (Control Ctl in Root.Controls)
219: {
220: Control FoundCtl = findControlRecursive(Ctl, Id);
221: if (FoundCtl != null)
222: return FoundCtl;
223: }
224:
225: return null;
226: }
227:
228:
229: #endregion
230:
231: #region -- Feature handling --
232:
233: private void populateFeaturesTable()
234: {
235: string siteUrl = SPContext.Current.Site.Url;
236: DropDownList ddlScopes = (DropDownList)findControlRecursive(scopeSection, "ddlScopes");
237: RadioButtonList rblUpgradeOnly = (RadioButtonList)findControlRecursive(featureFilterSection, "rblUpgradeOnly");
238: bool upgradeOnly = (rblUpgradeOnly.SelectedValue == NeedUpgradeValue);
239: SPFeatureScope scope = (SPFeatureScope)Enum.Parse(typeof(SPFeatureScope), ddlScopes.SelectedValue);
240: SPFeatureQueryResultCollection featuresForUpgrade = null;
241:
242: switch (scope)
243: {
244: case SPFeatureScope.Farm:
245: featuresForUpgrade = SPWebService.AdministrationService.QueryFeatures(scope, upgradeOnly);
246: break;
247: case SPFeatureScope.WebApplication:
248: featuresForUpgrade = SPWebService.QueryFeaturesInAllWebServices(scope, upgradeOnly);
249: break;
250: case SPFeatureScope.Site:
251: featuresForUpgrade = webAppSelector.CurrentItem.QueryFeatures(scope, upgradeOnly);
252: break;
253: case SPFeatureScope.Web:
254: using (SPSite 选择edSite = new SPSite(siteCollectionSelector.CurrentItem.Url))
255: {
256: featuresForUpgrade = 选择edSite.QueryFeatures(scope, upgradeOnly);
257: }
258: break;
259: default:
260: throw new NotImplementedException(string.Format("Cannot use this page 至 upgrade Features 在 scope '{0}'!", scope));
261: }
262:
263: dtFeatures = 得到DataTable(featuresForUpgrade);
264: }
265:
266: private DataTable 得到DataTable(SPFeatureQueryResultCollection featuresForUpgrade)
267: {
268: DataTable dtEntries = new DataTable("FeatureQueryResult");
269: dtEntries.Columns.Add(new DataColumn(FeatureFields.DisplayName));
270: dtEntries.Columns.Add(new DataColumn(FeatureFields.Parent));
271: dtEntries.Columns.Add(new DataColumn(FeatureFields.Version));
272: dtEntries.Columns.Add(new DataColumn(FeatureFields.FeatureID));
273: dtEntries.Columns.Add(new DataColumn(FeatureFields.FeatureScope));
274: dtEntries.Columns.Add(new DataColumn(FeatureFields.Identifiers));
275:
276: foreach (SPFeature feature in featuresForUpgrade)
277: {
278: string parent = FeatureManager.GetFeatureParent(feature);
279: DataRow dr = dtEntries.NewRow();
280: dr[FeatureFields.DisplayName] = feature.Definition.DisplayName;
281: dr[FeatureFields.Parent] = parent;
282: dr[FeatureFields.Version] = feature.Version;
283: dr[FeatureFields.FeatureID] = feature.DefinitionId;
284: dr[FeatureFields.FeatureScope] = feature.Definition.Scope;
285: dr[FeatureFields.Identifiers] = new FeatureDetails()
286: {
287: FeatureScope = feature.Definition.Scope,
288: FeatureID = feature.DefinitionId,
289: ParentID = new Guid(FeatureManager.GetFeatureParentId(feature).ToString()),
290: GrandParentID = new Guid(FeatureManager.GetFeatureGrandParentId(feature).ToString()),
291: FeatureName = feature.Definition.DisplayName,
292: ParentString = parent
293: }.ToString();
294:
295: dtEntries.Rows.Add(dr);
296: }
297:
298: return dtEntries;
299: }
300:
301: #endregion
302:
303: #region -- Misc helpers --
304:
305: private void addColumns()
306: {
307: fields.Add(GridHeaders.DisplayName, FeatureFields.DisplayName);
308: fields.Add(GridHeaders.Version, FeatureFields.Version);
309: fields.Add(GridHeaders.Parent, FeatureFields.Parent);
310: fields.Add(GridHeaders.FeatureID, FeatureFields.FeatureID);
311: fields.Add(GridHeaders.FeatureScope, FeatureFields.FeatureScope);
312: fields.Add(GridHeaders.Identifiers, FeatureFields.Identifiers);
313: }
314:
315:
316:
317: #endregion
318: }
319: }
AJAX所需的更改
这里’是我将应用程序隐蔽到AJAX的过程– I’如果您想查看任何详细信息,请在代码下面向下列出(并发布解决方案文件)。如果你’我个人认为这是重新构建AJAX方式的方法’s quite eye-opening:
- 删除所有隐藏在后面的代码。你自己’即使需要花一些深夜来写它,它也还是需要(以这种形式):)
- 在里面.aspx, remove any of the ASP.Net controls used in the app 和 switch them 至 good ol’老式的HTML控件。例如:
- 的dropdown for the Feature scope is changed from a <asp:dropdownlist> 控制到 <select> 控制。
- 的‘Feature status’ 无线电按钮从 a <asp:RadioButtonList> 到 <input type="radio">.
- 的‘Search’ 和 ‘Upgrade Features’按钮从 <asp:Button> 至 <button>.
- 的<asp:Panel>和<asp:Label> 用于显示结果消息的控件已完全删除。
- 的SPGridView 用于显示记录的控件也被删除–对于这最后两个项目,您’会看到我改用jQuery将HTML插入父div中。
请注意,您经常’ll want 至 保持 许多其他ASP.Net控件–就我而言,我保留了 InputFormSection, WebApplicationSelector和SiteCollectionSelector 控件,因为它们是SharePoint控件,它们负责应用程序页面的外观和感觉,并分别选择Web应用程序/网站集。就选择器控件而言,我当然’ll still need 至 ‘get’选定的值,但事实证明’使用jQuery而非服务器端代码完全可以做到这一点。
- 介绍一些jQuery代码:
- 页面加载时的事件绑定代码–在这里,我们将事件处理程序绑定到按钮单击的HTML。
- 的following jQuery methods:
- 得到WebAppSelectorValue() –由于Microsoft.SharePoint.WebControls.ApplicationSelector控件实际上呈现了一个包含链接和跨度的div,因此以下jQuery将获取该值(假设HTML ID是正确的)- $('a#zz1_webAppSelector span')。text()。
- 得到WebAppSelectorValue() – similar 到bove.
- displayFeaturesTable()–完成获取记录(要升级的功能列表)并将其显示在与SPGridView的输出类似的HTML表中的工作。 关键方法–这会使用jQuery AJAX调用HTTP处理程序(如第3条所示, 使用带有HTTP处理程序的jQuery AJAX),它会在SharePoint中查询要升级的功能,并最终将结果表的HTML注入页面。
- upgradeFeatures()–完成更新所选记录(升级功能)并显示结果的工作。 关键方法–这会使用jQuery AJAX在HTTP处理程序上调用不同的方法,传递要升级的功能ID,从服务器收集结果并相应地更新页面。
- 介绍HTTP处理程序–通常,这是与调用jQuery同时编写的。我检测到网址中的参数,以了解正在处理哪个操作(即获取记录或更新记录)。为每个添加代码:
- 在里面‘QueryFeatures’代码分支,获取需要升级的功能列表,然后对数据进行JSON序列化并返回(如第4条所示, 从HTTP处理程序返回JSON)
- 在里面‘UpgradeFeatures’分支,编写代码以根据传递的ID解析功能,然后调用 SPFeature.Upgrade() 在各个。最后,返回一个包含结果的JSON序列化对象。
的result
进行更改后,该应用肯定很流畅,没有回发。尽管截图可以’t展示它,一旦返回结果,网格就会平滑地淡入(由jQuery提供) 淡入())和升级功能具有相似的体验。如果有的话,有一个(有点讽刺意味的)新问题,一切发生得如此之快以至于没有’似乎处理工作已正确完成。
如果你 want 至 download the code 至 take a close look, there’是文章末尾的链接。否则在这里’jQuery的外观如下:
1: $(function () {
2: $.ajaxSetup({ cache: false });
3: $('#upgradeControls').hide();
4:
5: // bind event handlers..
6: $('#btnSearch').click(displayFeaturesTable);
7: $('#OKButton').click(upgradeFeatures);
8: });
9:
10:
11: function 得到WebAppSelectorValue() {
12: return $('a#zz1_webAppSelector span').text();
13: }
14:
15: function 得到SiteColSelectorValue() {
16: return $('a#zz2_siteCollectionSelector span').text();
17: }
18:
19: function displayFeaturesTable(){
20: $('#resultsGrid').hide();
21: var requiringUpgradeOnly = $('.rblUpgradeOnly:checked').val();
22: var scopeValue = $('#ddlScopes').val();
23: var parentString = null;
24: if (scopeValue == 'Site') {
25: parentString = 得到WebAppSelectorValue();
26: }
27: if (scopeValue == 'Web') {
28: parentString = 得到SiteColSelectorValue();
29: }
30: $.get('/_admin/COB/FeatureUpgradeCompleted.cob',
31: { op: 'QueryFeatures', scope: scopeValue, upgradeOnly: requiringUpgradeOnly, parentUrl: parentString }, function (data) {
32: if (data.ResultCount > 0) {
33: $('#upgradeControls').fadeIn();
34: $('#resultsGrid').html(data.Html).fadeIn();
35: }
36: else {
37: $('#resultsGrid').html("<div class=\"noResults\">There are no Feature instances requiring upgrade 在 this scope.</div>").fadeIn();
38: $('#upgradeControls').hide();
39: }
40: }
41: );
42: };
43:
44:
45: function upgradeFeatures(){
46: var featureDetails = $('.chkUpgrade:checked').map(function () {
47: return this.id;
48: }).get().join(',');
49:
50: var notificationId = SP.UI.通知.addNotification('功能升级开始。', true);
51: SP.UI.Status.removeAllStatus(true);
52: $.ajax({
53: cache: false,
54: type: "GET",
55: dataType: "json",
56: url: '/_admin/COB/FeatureUpgradeCompleted.cob',
57: data: { "op": "UpgradeFeatures", "Features": featureDetails },
58: success: function (data) {
59: SP.UI.通知.removeNotification(notificationId);
60: var successUpgrades = new Array();
61: var failedUpgrades = new Array();
62: var statusId;
63: var statusColour;
64:
65: if (data.HasErrors) {
66: statusColour = 'red';
67: }
68: else {
69: statusColour = 'green';
70: }
71:
72: $.each(data.Results, function (key, value) {
73: if (value.Exceptions != null) {
74: var failedMsg = "Feature '" + value.FeatureDetails.FeatureName + "' with parent <a target='_blank' href='" + value.FeatureDetails.ParentString + "'>" + value.FeatureDetails.ParentString + "</a> failed 至 upgrade. Error message: " + value.Exceptions[0].Message;
75: failedUpgrades.push(failedMsg);
76: }
77: else {
78: var successMsg = "Feature '" + value.FeatureDetails.FeatureName + "' with parent <a target='_blank' href='" + value.FeatureDetails.ParentString + "'>" + value.FeatureDetails.ParentString + "</a> upgraded sucessfully.";
79: successUpgrades.push(successMsg);
80: }
81: })
82:
83: $.each(failedUpgrades, function (index, value) {
84: statusId = SP.UI.Status.addStatus("<u>Failed:</u> ", "<strong>" + value + "</strong>", false);
85: SP.UI.Status.setStatusPriColor(statusId, statusColour);
86: });
87:
88: $.each(successUpgrades, function (index, value) {
89: statusId = SP.UI.Status.addStatus("<u>Success:</u> ", "<strong>" + value + "</strong>", false);
90: SP.UI.Status.setStatusPriColor(statusId, statusColour);
91: });
92:
93: displayFeaturesTable();
94: },
95: failure: function (data) {
96: SP.UI.通知.removeNotification(notificationId);
97: alert("An error occurred whilst upgrading Features - " + data);
98: }
99: });
100: }
101:
102:
103:
和HTTP处理程序:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Web;
6: using System.Web.Script.Serialization;
7: using COB.SharePoint.Utilities.FeatureUpgradeKit.Core;
8: using COB.SharePoint.Utilities.FeatureUpgradeKit.Core.Entities;
9: using COB.SharePoint.Utilities.FeatureUpgradeKit.Entities;
10: using Microsoft.SharePoint;
11: using Microsoft.SharePoint.Administration;
12:
13: namespace COB.SharePoint.Utilities.FeatureUpgradeKit.Handlers
14: {
15: public class CentralAdminUpgradeHandlerCompleted : IHttpHandler
16: {
17: private const string qsOperation = "op";
18: private const string qsScope = "scope";
19: private const string qsUpgradeOnly = "upgradeOnly";
20: private const string qsParentUrl = "parentUrl";
21: private const string qsFeaturesForUpgrade = "Features";
22:
23: #region -- Helper methods --
24:
25: private List<SPFeature> 得到FeaturesForUpgrade(SPFeatureScope scope, bool upgradeOnly, string parentUrl)
26: {
27: SPFeatureQueryResultCollection featuresForUpgrade = null;
28:
29: switch (scope)
30: {
31: case SPFeatureScope.Farm:
32: featuresForUpgrade = SPWebService.AdministrationService.QueryFeatures(scope, upgradeOnly);
33: break;
34: case SPFeatureScope.WebApplication:
35: featuresForUpgrade = SPWebService.QueryFeaturesInAllWebServices(scope, upgradeOnly);
36: break;
37: case SPFeatureScope.Site:
38: SPWebApplication webApp = SPWebApplication.Lookup(new Uri(parentUrl));
39: featuresForUpgrade = webApp.QueryFeatures(scope, upgradeOnly);
40: break;
41: case SPFeatureScope.Web:
42: using (SPSite 选择edSite = new SPSite(parentUrl))
43: {
44: featuresForUpgrade = 选择edSite.QueryFeatures(scope, upgradeOnly);
45: }
46: break;
47: default:
48: throw new NotImplementedException(string.Format("Cannot use this page 至 upgrade Features 在 scope '{0}'!", scope));
49: }
50:
51: List<SPFeature> featuresForUpgradeList = null;
52: featuresForUpgradeList = featuresForUpgrade.ToList<SPFeature>();
53:
54: return featuresForUpgradeList;
55: }
56:
57: private Dictionary<string, FeatureUpgradeResultDetail> 得到SimplifiedResults(Dictionary<FeatureDetails, IEnumerable<Exception>> upgradeResults)
58: {
59: Dictionary<string, FeatureUpgradeResultDetail> simplifiedResults = new Dictionary<string, FeatureUpgradeResultDetail>();
60:
61: foreach (KeyValuePair<FeatureDetails, IEnumerable<Exception>> kvp in upgradeResults)
62: {
63: simplifiedResults.Add(kvp.Key.ToString(), new FeatureUpgradeResultDetail { FeatureDetails = kvp.Key, Exceptions = kvp.Value });
64: }
65:
66: return simplifiedResults;
67: }
68:
69: #endregion
70:
71: #region -- Fetch data/grid methods --
72:
73: private string buildQueryFeaturesResponse(HttpContext context, List<SPFeature> featuresToProcess)
74: {
75: StringBuilder sbFeaturesTable = new StringBuilder();
76:
77: if (featuresToProcess.Count > 0)
78: {
79: writeTableHeader(sbFeaturesTable);
80: writeTableRows(sbFeaturesTable, featuresToProcess);
81: writeTableFooter(sbFeaturesTable);
82: }
83:
84: return sbFeaturesTable.ToString();
85: }
86:
87: private void writeTableHeader(StringBuilder builder)
88: {
89: builder.AppendFormat("<TABLE style=\"BORDER-BOTTOM-STYLE: none; BORDER-RIGHT-STYLE: none; WIDTH: 100%; BORDER-COLLAPSE: collapse; BORDER-TOP-STYLE: none; BORDER-LEFT-STYLE: none\" " +
90: "id=tblFeatures class=ms-listviewtable border=0 cellSpacing=0 ShowHeaderWhenEmpty=true><TBODY>" +
91: "<TR class=ms-viewheadertr><TH class=\"ms-vh2-nofilter ms-vh2-gridview\" scope=col>Scope</TH><TH class=\"ms-vh2-nofilter ms-vh2-gridview\" scope=col>Parent</TH>" +
92: "<TH class=\"ms-vh2-nofilter ms-vh2-gridview\" scope=col>Feature ID</TH><TH class=\"ms-vh2-nofilter ms-vh2-gridview\" scope=col>Feature name</TH>" +
93: "<TH class=\"ms-vh2-nofilter ms-vh2-gridview\" scope=col>Current version</TH><TH class=\"ms-vh2-nofilter ms-vh2-gridview\" scope=col>Upgrade?</TH></TR>");
94: }
95:
96: private void writeTableRows(StringBuilder builder, List<SPFeature> featuresToProcess)
97: {
98: foreach (SPFeature feature in featuresToProcess)
99: {
100: string parent = FeatureManager.GetFeatureParent(feature);
101:
102: builder.AppendFormat("<TR style=\"BACKGROUND-COLOR: #dddddd\"><TD class=ms-vb2><SPAN>{0}</SPAN></TD>" +
103: "<TD class=ms-vb2><SPAN>{1}</SPAN></TD>" +
104: "<TD class=ms-vb2><SPAN>{2}</SPAN></TD>" +
105: "<TD class=ms-vb2><SPAN>{3}</SPAN></TD>" +
106: "<TD class=ms-vb2><SPAN>{4}</SPAN></TD>" +
107: "<TD class=ms-vb2><INPUT id=\"{5}\" CHECKED type=checkbox class=\"chkUpgrade\"></TD></TR>",
108: feature.FeatureDefinitionScope,
109: parent,
110: feature.DefinitionId,
111: feature.Definition.DisplayName,
112: feature.Version,
113: new FeatureDetails()
114: {
115: FeatureScope = feature.Definition.Scope,
116: FeatureID = feature.DefinitionId,
117: ParentID = new Guid(FeatureManager.GetFeatureParentId(feature).ToString()),
118: GrandParentID = new Guid(FeatureManager.GetFeatureGrandParentId(feature).ToString()),
119: FeatureName = feature.Definition.DisplayName,
120: ParentString = parent
121: }.ToString()
122: );
123: }
124: }
125:
126: private void writeTableFooter(StringBuilder builder)
127: {
128: builder.AppendFormat("</TBODY></TABLE>");
129: }
130:
131: #endregion
132:
133: public void ProcessRequest(HttpContext context)
134: {
135: SPSecurity.CatchAccessDeniedException = false;
136: try
137: {
138: string operation = context.Request.QueryString[qsOperation];
139:
140:
141: if (operation == "QueryFeatures")
142: {
143: // collect info..
144: string scope = context.Request.QueryString[qsScope];
145: bool upgradeOnly = true;
146: bool.TryParse(context.Request.QueryString[qsUpgradeOnly], out upgradeOnly);
147: SPFeatureScope upgradeScope = (SPFeatureScope)Enum.Parse(typeof(SPFeatureScope), scope);
148: string parentUrl = context.Request.QueryString[qsParentUrl];
149:
150: // 得到 data..
151: List<SPFeature> featuresToProcess = 得到FeaturesForUpgrade(upgradeScope, upgradeOnly, parentUrl);
152:
153: // build object 至 send 至 client..
154: FeatureQueryResults queryResultsForClient = new FeatureQueryResults
155: {
156: ResultCount = featuresToProcess.Count,
157: Html = buildQueryFeaturesResponse(context, featuresToProcess)
158: };
159:
160: // serialize 和 send..
161: JavaScriptSerializer serializer = new JavaScriptSerializer();
162: StringBuilder sbJsonResults = new StringBuilder();
163: serializer.Serialize(queryResultsForClient, sbJsonResults);
164:
165: context.Response.Clear();
166: context.Response.ContentType = "application/json; charset=utf-8";
167: context.Response.Write(sbJsonResults.ToString());
168: }
169: if (operation == "UpgradeFeatures")
170: {
171: // collect info..
172: string features = context.Request.QueryString[qsFeaturesForUpgrade];
173: string[] featuresArray = features.Split(',');
174:
175: // 得到 data..
176: Dictionary<FeatureDetails, IEnumerable<Exception>> upgradeResults = null;
177: List<FeatureDetails> upgradeIdentifiers = new List<FeatureDetails>();
178: foreach (string featureId in featuresArray)
179: {
180: upgradeIdentifiers.Add(FeatureDetails.CreateFromString(featureId));
181: }
182:
183: SPSecurity.RunWithElevatedPrivileges(delegate()
184: {
185: upgradeResults = FeatureManager.UpgradeFeatures(upgradeIdentifiers);
186: });
187:
188: // build object 至 send 至 client..
189: FeatureUpgradeResults upgradeResultsForClient = new FeatureUpgradeResults();
190: upgradeResultsForClient.HasErrors = FeatureManager.CollectionHasErrors(upgradeResults);
191: upgradeResultsForClient.Results = 得到SimplifiedResults(upgradeResults);
192:
193: // serialize 和 send..
194: JavaScriptSerializer serializer = new JavaScriptSerializer();
195: StringBuilder sbJsonResults = new StringBuilder();
196: serializer.Serialize(upgradeResultsForClient, sbJsonResults);
197:
198: context.Response.Clear();
199: context.Response.ContentType = "application/json; charset=utf-8";
200: context.Response.Write(sbJsonResults.ToString());
201: }
202:
203: }
204: catch (Exception e)
205: {
206: context.Response.Write(string.Format("Sorry, an error occurred on your last action:<br /><br />{0}", e));
207: }
208: }
209:
210: private void writeDebugInfo(HttpContext context)
211: {
212: string operation = context.Request.QueryString[qsOperation];
213: string upgradeOnly = context.Request.QueryString[qsUpgradeOnly];
214: string scope = context.Request.QueryString[qsScope];
215: string parentUrl = context.Request.QueryString[qsParentUrl];
216:
217: string debug = string.Format("Operation = {0}<br/>, Upgrade only = {1}<br/>, Scope = {2}<br/>, Parent URL = {3}..<br/><br/>",
218: operation, upgradeOnly, scope, parentUrl);
219:
220: context.Response.Write(debug);
221: }
222:
223: /// <summary>
224: /// You will need 至 configure this handler in the web.config file of your
225: /// web 和 register it with IIS before being able 至 use it. For more information
226: /// see the following link: http://go.microsoft.com/?linkid=8101007
227: /// </summary>
228: #region IHttpHandler Members
229:
230: public bool IsReusable
231: {
232: // Return false in case your Managed Handler cannot be reused for another request.
233: // Usually this would be false in case you have some state information preserved per request.
234: 得到 { return true; }
235: }
236:
237: #endregion
238: }
239: }
由于我现在在AJAX版本中拥有更多的JavaScript / jQuery,因此利用某些SharePoint 2010似乎是合乎逻辑的一步’s JS frameworks –例如,我的确认消息现在很容易放在SP2010状态栏中:
尽管我最终使用的是功能齐全的应用程序,但我还是决定不继续实施最终的零碎工作‘production-ize’这些更改(进入主要的Codeplex版本)。它没有’感觉还有很多事情要做,但是最终的练习是关于我学习AJAX技术的,而不是关于这个特定工具的,实际上’在这种特殊情况下,每吨AJAX的增加值。无疑这部分是因为该工具的大多数用户会 期望 该操作需要花费一些时间,因此回发是完全可以接受的。万一你’有兴趣的,这是我的观察或清单’d want 至 iron out:
- 将HTTP处理程序中的硬编码HTML替换为 jQuery模板 (当时’我实际上是在工作时发布的)
- 考虑结果的展示速度–目前有一个 SP.UI.通知 消息说像‘功能升级开始。’,但除非需要大量处理才能升级功能,否则事情会如此之快,以至于在显示结果的同时出现!
- HTTP处理程序和jQuery的一些重构会很好
下载文件
对于感兴趣的人,我发布了两个VS解决方案(在AJAX之前和之后)。实际上,它们是Codeplex主项目的特定版本, 在这里可用. 如果你 take a look 和 run into issues or have questions, please post here or on Codeplex.
包起来
因此,我的SharePoint和AJAX系列文章到此结束,我希望它以某种方式有用。尽管这篇文章可能表明AJAX不是’到处都是必要的,最近我注意到一些经验丰富的SharePoint员工在Twitter上抱怨回发地狱,即‘管理用户个人资料属性’SharePoint 2007/2010管理中心中的页面。这是我在AJAX演讲中用作示例并在第一篇文章中引用的页面–如果您对为什么现在应该使用AJAX来构建应用程序,Web部件和页面控件有任何疑问,请随时转到该页面并重新排序一系列属性!我真的相信’如今对于任何开发人员来说都是一种重要的方法,而您’一旦你将永远不会回头’我做了你的第一个。祝您编码愉快!
2条评论:
克里斯,你好
太棒了 系列。学到很多。
谢谢
克里斯,
您真是个好伙伴:)。
做得好
干杯
戈德温·滕青
发表评论