2011年1月19日,星期三

SP2010 AJAX第8部分:将现有应用程序迁移到jQuery / AJAX

[抱歉,如果这篇文章在您的供稿中重复出现– it didn’最初由于Feedburner的故障而无法吸引许多读者]

  1. 将jQuery精简为基本要素 (技术)
  2. 使用JavaScript Client OM处理列表 (技术)
  3. 结合使用jQuery AJAX和HTTP处理程序 (技术)
  4. 从HTTP处理程序返回JSON (技术)
  5. 为客户端OM和jQuery启用Intellisense (小费)
  6. 调试jQuery / JavaScript (小费)
  7. 构建AJAX应用程序时的有用工具 (小费)
  8. 将现有应用程序迁移到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很有帮助。 

FeatureUpgradeKit_NonAjax

FeatureUpgradeKit_Upgraded_NonAjax

不用说,它’是所有标准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:                         &nbsp;
 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:

  1. 删除所有隐藏在后面的代码。你自己’即使需要花一些深夜来写它,它也还是需要(以这种形式):)
  2. 在里面.aspx, remove any of the ASP.Net controls used in the app 和 switch them 至 good ol’老式的HTML控件。例如:
    1. 的dropdown for the Feature scope is changed from a <asp:dropdownlist> 控制到 <select> 控制。
    2. 的‘Feature status’ 无线电按钮从 a <asp:RadioButtonList><input type="radio">.
    3. 的‘Search’ 和 ‘Upgrade Features’按钮从 <asp:Button><button>.
    4. <asp:Panel><asp:Label> 用于显示结果消息的控件已完全删除。
    5. SPGridView 用于显示记录的控件也被删除–对于这最后两个项目,您’会看到我改用jQuery将HTML插入父div中。

      请注意,您经常’ll want 至 保持 许多其他ASP.Net控件–就我而言,我保留了 InputFormSection, WebApplicationSelectorSiteCollectionSelector 控件,因为它们是SharePoint控件,它们负责应用程序页面的外观和感觉,并分别选择Web应用程序/网站集。就选择器控件而言,我当然’ll still need 至 ‘get’选定的值,但事实证明’使用jQuery而非服务器端代码完全可以做到这一点。
  3. 介绍一些jQuery代码:
    1. 页面加载时的事件绑定代码–在这里,我们将事件处理程序绑定到按钮单击的HTML。
    2. 的following jQuery methods:
      1. 得到WebAppSelectorValue() –由于Microsoft.SharePoint.WebControls.ApplicationSelector控件实际上呈现了一个包含链接和跨度的div,因此以下jQuery将获取该值(假设HTML ID是正确的)- $('a#zz1_webAppSelector span')。text()。
      2. 得到WebAppSelectorValue() – similar 到bove.
      3. displayFeaturesTable()–完成获取记录(要升级的功能列表)并将其显示在与SPGridView的输出类似的HTML表中的工作。 关键方法–这会使用jQuery AJAX调用HTTP处理程序(如第3条所示, 使用带有HTTP处理程序的jQuery AJAX),它会在SharePoint中查询要升级的功能,并最终将结果表的HTML注入页面。
      4. upgradeFeatures()–完成更新所选记录(升级功能)并显示结果的工作。 关键方法–这会使用jQuery AJAX在HTTP处理程序上调用不同的方法,传递要升级的功能ID,从服务器收集结果并相应地更新页面。
  4. 介绍HTTP处理程序–通常,这是与调用jQuery同时编写的。我检测到网址中的参数,以了解正在处理哪个操作(即获取记录或更新记录)。为每个添加代码:
    1. 在里面‘QueryFeatures’代码分支,获取需要升级的功能列表,然后对数据进行JSON序列化并返回(如第4条所示, 从HTTP处理程序返回JSON)
    2. 在里面‘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状态栏中:

FeatureUpgradeKitAjax

尽管我最终使用的是功能齐全的应用程序,但我还是决定不继续实施最终的零碎工作‘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条评论:

加里马说过...

克里斯,你好

太棒了 系列。学到很多。

谢谢

匿名 said...

克里斯,

您真是个好伙伴:)。

做得好

干杯
戈德温·滕青