502 lines
22 KiB
C#
502 lines
22 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="ImportCatalogPart.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Web.UI.WebControls.WebParts {
|
||
|
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.ComponentModel;
|
||
|
using System.Drawing;
|
||
|
using System.Globalization;
|
||
|
using System.IO;
|
||
|
using System.Security;
|
||
|
using System.Security.Permissions;
|
||
|
using System.Web;
|
||
|
using System.Web.Security;
|
||
|
using System.Web.UI;
|
||
|
using System.Web.UI.WebControls;
|
||
|
using System.Web.Util;
|
||
|
using System.Xml;
|
||
|
|
||
|
/// <devdoc>
|
||
|
/// </devdoc>
|
||
|
public sealed class ImportCatalogPart : CatalogPart {
|
||
|
|
||
|
private WebPart _availableWebPart;
|
||
|
private string _importedPartDescription;
|
||
|
private WebPartDescriptionCollection _availableWebPartDescriptions;
|
||
|
private FileUpload _upload;
|
||
|
private Button _uploadButton;
|
||
|
private string _importErrorMessage;
|
||
|
|
||
|
private const int baseIndex = 0;
|
||
|
private const int importedPartDescriptionIndex = 1;
|
||
|
private const int controlStateArrayLength = 2;
|
||
|
|
||
|
private const string TitlePropertyName = "Title";
|
||
|
private const string DescriptionPropertyName = "Description";
|
||
|
private const string IconPropertyName = "CatalogIconImageUrl";
|
||
|
private const string ImportedWebPartID = "ImportedWebPart";
|
||
|
|
||
|
private static readonly WebPartDescriptionCollection DesignModeAvailableWebPart =
|
||
|
new WebPartDescriptionCollection(new WebPartDescription[] {
|
||
|
new WebPartDescription("webpart1", String.Format(CultureInfo.CurrentCulture,
|
||
|
SR.GetString(SR.CatalogPart_SampleWebPartTitle), "1"), null, null)});
|
||
|
|
||
|
[WebCategory("Appearance")]
|
||
|
[WebSysDefaultValue(SR.ImportCatalogPart_Browse)]
|
||
|
[WebSysDescription(SR.ImportCatalogPart_BrowseHelpText)]
|
||
|
public string BrowseHelpText {
|
||
|
get {
|
||
|
object o = ViewState["BrowseHelpText"];
|
||
|
return (o != null) ? (string)o : SR.GetString(SR.ImportCatalogPart_Browse);
|
||
|
}
|
||
|
set {
|
||
|
ViewState["BrowseHelpText"] = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Themeable(false)]
|
||
|
public override string DefaultButton {
|
||
|
get { return base.DefaultButton; }
|
||
|
set { base.DefaultButton = value; }
|
||
|
}
|
||
|
|
||
|
[WebCategory("Appearance")]
|
||
|
[WebSysDefaultValue(SR.ImportCatalogPart_ImportedPartLabel)]
|
||
|
[WebSysDescription(SR.ImportCatalogPart_ImportedPartLabelText)]
|
||
|
public string ImportedPartLabelText {
|
||
|
get {
|
||
|
object o = ViewState["ImportedPartLabelText"];
|
||
|
return (o != null) ? (string)o : SR.GetString(SR.ImportCatalogPart_ImportedPartLabel);
|
||
|
}
|
||
|
set {
|
||
|
ViewState["ImportedPartLabelText"] = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[WebCategory("Appearance")]
|
||
|
[WebSysDefaultValue(SR.ImportCatalogPart_ImportedPartErrorLabel)]
|
||
|
[WebSysDescription(SR.ImportCatalogPart_PartImportErrorLabelText)]
|
||
|
public string PartImportErrorLabelText {
|
||
|
get {
|
||
|
object o = ViewState["PartImportErrorLabelText"];
|
||
|
return (o != null) ? (string)o : SR.GetString(SR.ImportCatalogPart_ImportedPartErrorLabel);
|
||
|
}
|
||
|
set {
|
||
|
ViewState["PartImportErrorLabelText"] = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[
|
||
|
WebSysDefaultValue(SR.ImportCatalogPart_PartTitle),
|
||
|
]
|
||
|
public override string Title {
|
||
|
get {
|
||
|
string s = (string)ViewState["Title"];
|
||
|
return (s != null) ? s : SR.GetString(SR.ImportCatalogPart_PartTitle);
|
||
|
}
|
||
|
set {
|
||
|
ViewState["Title"] = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[WebCategory("Appearance")]
|
||
|
[WebSysDefaultValue(SR.ImportCatalogPart_UploadButton)]
|
||
|
[WebSysDescription(SR.ImportCatalogPart_UploadButtonText)]
|
||
|
public string UploadButtonText {
|
||
|
get {
|
||
|
object o = ViewState["UploadButtonText"];
|
||
|
return (o != null) ? (string)o : SR.GetString(SR.ImportCatalogPart_UploadButton);
|
||
|
}
|
||
|
set {
|
||
|
ViewState["UploadButtonText"] = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[WebCategory("Appearance")]
|
||
|
[WebSysDefaultValue(SR.ImportCatalogPart_Upload)]
|
||
|
[WebSysDescription(SR.ImportCatalogPart_UploadHelpText)]
|
||
|
public string UploadHelpText {
|
||
|
get {
|
||
|
object o = ViewState["UploadHelpText"];
|
||
|
return (o != null) ? (string)o : SR.GetString(SR.ImportCatalogPart_Upload);
|
||
|
}
|
||
|
set {
|
||
|
ViewState["UploadHelpText"] = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected internal override void CreateChildControls() {
|
||
|
Controls.Clear();
|
||
|
|
||
|
_upload = new FileUpload();
|
||
|
Controls.Add(_upload);
|
||
|
|
||
|
_uploadButton = new Button();
|
||
|
_uploadButton.ID = "Upload";
|
||
|
_uploadButton.CommandName = "upload";
|
||
|
_uploadButton.Click += new EventHandler(OnUpload);
|
||
|
Controls.Add(_uploadButton);
|
||
|
|
||
|
if (!DesignMode && Page != null) {
|
||
|
IScriptManager scriptManager = Page.ScriptManager;
|
||
|
if (scriptManager != null) {
|
||
|
scriptManager.RegisterPostBackControl(_uploadButton);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override WebPartDescriptionCollection GetAvailableWebPartDescriptions() {
|
||
|
if (DesignMode) {
|
||
|
return DesignModeAvailableWebPart;
|
||
|
}
|
||
|
|
||
|
CreateAvailableWebPartDescriptions();
|
||
|
return _availableWebPartDescriptions;
|
||
|
}
|
||
|
|
||
|
private void CreateAvailableWebPartDescriptions() {
|
||
|
if (_availableWebPartDescriptions != null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (WebPartManager == null || String.IsNullOrEmpty(_importedPartDescription)) {
|
||
|
_availableWebPartDescriptions = new WebPartDescriptionCollection();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Run in minimal trust
|
||
|
PermissionSet pset = new PermissionSet(PermissionState.None);
|
||
|
// add in whatever perms are appropriate
|
||
|
pset.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
|
||
|
pset.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Minimal));
|
||
|
|
||
|
pset.PermitOnly();
|
||
|
bool permitOnly = true;
|
||
|
string title = null;
|
||
|
string description = null;
|
||
|
string icon = null;
|
||
|
// Extra try-catch block to prevent elevation of privilege attack via exception filter
|
||
|
try {
|
||
|
try {
|
||
|
// Get the WebPart description from its saved XML description.
|
||
|
using (StringReader sr = new StringReader(_importedPartDescription)) {
|
||
|
using (XmlReader reader = XmlUtils.CreateXmlReader(sr)) {
|
||
|
if (reader != null) {
|
||
|
reader.MoveToContent();
|
||
|
// Check if imported part is authorized
|
||
|
|
||
|
// Get to the metadata
|
||
|
reader.MoveToContent();
|
||
|
reader.ReadStartElement(WebPartManager.ExportRootElement);
|
||
|
reader.ReadStartElement(WebPartManager.ExportPartElement);
|
||
|
reader.ReadStartElement(WebPartManager.ExportMetaDataElement);
|
||
|
|
||
|
// Get the type name
|
||
|
string partTypeName = null;
|
||
|
string userControlTypeName = null;
|
||
|
while (reader.Name != WebPartManager.ExportTypeElement) {
|
||
|
reader.Skip();
|
||
|
if (reader.EOF) {
|
||
|
throw new EndOfStreamException();
|
||
|
}
|
||
|
}
|
||
|
if (reader.Name == WebPartManager.ExportTypeElement) {
|
||
|
partTypeName = reader.GetAttribute(WebPartManager.ExportTypeNameAttribute);
|
||
|
userControlTypeName = reader.GetAttribute(WebPartManager.ExportUserControlSrcAttribute);
|
||
|
}
|
||
|
|
||
|
// If we are in shared scope, we are importing a shared WebPart
|
||
|
bool isShared = (WebPartManager.Personalization.Scope == PersonalizationScope.Shared);
|
||
|
|
||
|
if (!String.IsNullOrEmpty(partTypeName)) {
|
||
|
// Need medium trust to call BuildManager.GetType()
|
||
|
PermissionSet mediumPset = new PermissionSet(PermissionState.None);
|
||
|
mediumPset.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
|
||
|
mediumPset.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Medium));
|
||
|
|
||
|
CodeAccessPermission.RevertPermitOnly();
|
||
|
permitOnly = false;
|
||
|
mediumPset.PermitOnly();
|
||
|
permitOnly = true;
|
||
|
|
||
|
Type partType = WebPartUtil.DeserializeType(partTypeName, true);
|
||
|
|
||
|
CodeAccessPermission.RevertPermitOnly();
|
||
|
permitOnly = false;
|
||
|
pset.PermitOnly();
|
||
|
permitOnly = true;
|
||
|
|
||
|
// First check if the type is authorized
|
||
|
if (!WebPartManager.IsAuthorized(partType, null, null, isShared)) {
|
||
|
_importErrorMessage = SR.GetString(SR.WebPartManager_ForbiddenType);
|
||
|
return;
|
||
|
}
|
||
|
// If the type is not a webpart, create a generic Web Part
|
||
|
if (!partType.IsSubclassOf(typeof(WebPart)) && !partType.IsSubclassOf(typeof(Control))) {
|
||
|
// We only allow for Controls (VSWhidbey 428511)
|
||
|
_importErrorMessage = SR.GetString(SR.WebPartManager_TypeMustDeriveFromControl);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// Check if the path is authorized
|
||
|
if (!WebPartManager.IsAuthorized(typeof(UserControl), userControlTypeName, null, isShared)) {
|
||
|
_importErrorMessage = SR.GetString(SR.WebPartManager_ForbiddenType);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
while (!reader.EOF) {
|
||
|
while (!reader.EOF && !(reader.NodeType == XmlNodeType.Element &&
|
||
|
reader.Name == WebPartManager.ExportPropertyElement)) {
|
||
|
reader.Read();
|
||
|
}
|
||
|
if (reader.EOF) {
|
||
|
break;
|
||
|
}
|
||
|
string name = reader.GetAttribute(WebPartManager.ExportPropertyNameAttribute);
|
||
|
if (name == TitlePropertyName) {
|
||
|
title = reader.ReadElementString();
|
||
|
}
|
||
|
else if (name == DescriptionPropertyName) {
|
||
|
description = reader.ReadElementString();
|
||
|
}
|
||
|
else if (name == IconPropertyName) {
|
||
|
string url = reader.ReadElementString().Trim();
|
||
|
if (!CrossSiteScriptingValidation.IsDangerousUrl(url)) {
|
||
|
icon = url;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
reader.Read();
|
||
|
continue;
|
||
|
}
|
||
|
if (title != null && description != null && icon != null) {
|
||
|
break;
|
||
|
}
|
||
|
reader.Read();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (String.IsNullOrEmpty(title)) {
|
||
|
title = SR.GetString(SR.Part_Untitled);
|
||
|
}
|
||
|
|
||
|
_availableWebPartDescriptions = new WebPartDescriptionCollection(
|
||
|
new WebPartDescription[] {new WebPartDescription(ImportedWebPartID, title, description, icon)});
|
||
|
}
|
||
|
}
|
||
|
catch (XmlException) {
|
||
|
_importErrorMessage = SR.GetString(SR.WebPartManager_ImportInvalidFormat);
|
||
|
return;
|
||
|
}
|
||
|
catch {
|
||
|
_importErrorMessage = (!String.IsNullOrEmpty(_importErrorMessage)) ?
|
||
|
_importErrorMessage :
|
||
|
SR.GetString(SR.WebPart_DefaultImportErrorMessage);
|
||
|
return;
|
||
|
}
|
||
|
finally {
|
||
|
if (permitOnly) {
|
||
|
// revert if you're not just exiting the stack frame anyway
|
||
|
CodeAccessPermission.RevertPermitOnly();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
catch {
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override WebPart GetWebPart(WebPartDescription description) {
|
||
|
if (description == null) {
|
||
|
throw new ArgumentNullException("description");
|
||
|
}
|
||
|
|
||
|
WebPartDescriptionCollection webPartDescriptions = GetAvailableWebPartDescriptions();
|
||
|
if (!webPartDescriptions.Contains(description)) {
|
||
|
throw new ArgumentException(SR.GetString(SR.CatalogPart_UnknownDescription), "description");
|
||
|
}
|
||
|
|
||
|
if (_availableWebPart != null) {
|
||
|
return _availableWebPart;
|
||
|
}
|
||
|
// Import the WebPart from its saved XML description.
|
||
|
using (XmlReader reader = XmlUtils.CreateXmlReader(new StringReader(_importedPartDescription))) {
|
||
|
if (reader != null && WebPartManager != null) {
|
||
|
_availableWebPart = WebPartManager.ImportWebPart(reader, out _importErrorMessage);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If import failed, clear the cached description
|
||
|
if (_availableWebPart == null) {
|
||
|
_importedPartDescription = null;
|
||
|
_availableWebPartDescriptions = null;
|
||
|
}
|
||
|
return _availableWebPart;
|
||
|
}
|
||
|
|
||
|
/// <devdoc>
|
||
|
/// <para>Loads the control state for those properties that should persist across postbacks
|
||
|
/// even when EnableViewState=false.</para>
|
||
|
/// </devdoc>
|
||
|
protected internal override void LoadControlState(object savedState) {
|
||
|
if (savedState == null) {
|
||
|
base.LoadControlState(null);
|
||
|
}
|
||
|
else {
|
||
|
object[] myState = (object[])savedState;
|
||
|
if (myState.Length != controlStateArrayLength) {
|
||
|
throw new ArgumentException(SR.GetString(SR.Invalid_ControlState));
|
||
|
}
|
||
|
|
||
|
base.LoadControlState(myState[baseIndex]);
|
||
|
if (myState[importedPartDescriptionIndex] != null) {
|
||
|
_importedPartDescription = (string)myState[importedPartDescriptionIndex];
|
||
|
// Calling this method to be sure to emit any import error message in time:
|
||
|
// Otherwise, the descriptions will only be constructed (and the error messages
|
||
|
// generated) when the list of imported parts is rendered, which happens after
|
||
|
// the importcatalogpart itself is rendered. The error message being rendered
|
||
|
// by the ICP, we have to call this now
|
||
|
GetAvailableWebPartDescriptions();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <devdoc>
|
||
|
/// Registers the ImportCatalogPart for control state.
|
||
|
/// </devdoc>
|
||
|
protected internal override void OnInit(EventArgs e) {
|
||
|
base.OnInit(e);
|
||
|
Page.RegisterRequiresControlState(this);
|
||
|
}
|
||
|
|
||
|
internal void OnUpload(object sender, EventArgs e) {
|
||
|
string fileName = _upload.FileName;
|
||
|
Stream contents = _upload.FileContent;
|
||
|
if (!String.IsNullOrEmpty(fileName) && contents != null) {
|
||
|
using (StreamReader sr = new StreamReader(contents, true)) {
|
||
|
_importedPartDescription = sr.ReadToEnd();
|
||
|
|
||
|
// Clear cache
|
||
|
_availableWebPart = null;
|
||
|
_availableWebPartDescriptions = null;
|
||
|
_importErrorMessage = null;
|
||
|
|
||
|
if (String.IsNullOrEmpty(_importedPartDescription)) {
|
||
|
_importErrorMessage = SR.GetString(SR.ImportCatalogPart_NoFileName);
|
||
|
}
|
||
|
else {
|
||
|
GetAvailableWebPartDescriptions();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
_importErrorMessage = SR.GetString(SR.ImportCatalogPart_NoFileName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <devdoc>
|
||
|
/// <para>Saves the control state for those properties that should persist across postbacks
|
||
|
/// even when EnableViewState=false.</para>
|
||
|
/// </devdoc>
|
||
|
protected internal override object SaveControlState() {
|
||
|
object[] myState = new object[controlStateArrayLength];
|
||
|
|
||
|
myState[baseIndex] = base.SaveControlState();
|
||
|
myState[importedPartDescriptionIndex] = _importedPartDescription;
|
||
|
|
||
|
for (int i=0; i < controlStateArrayLength; i++) {
|
||
|
if (myState[i] != null) {
|
||
|
return myState;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// More performant to return null than an array of null values
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
protected internal override void Render(HtmlTextWriter writer) {
|
||
|
if (Page != null) {
|
||
|
Page.VerifyRenderingInServerForm(this);
|
||
|
}
|
||
|
|
||
|
base.Render(writer);
|
||
|
}
|
||
|
|
||
|
protected internal override void RenderContents(HtmlTextWriter writer) {
|
||
|
// HACK: Need this for child controls to be created at design-time when control is inside template
|
||
|
EnsureChildControls();
|
||
|
|
||
|
CatalogZoneBase zone = Zone;
|
||
|
if (zone != null && !zone.LabelStyle.IsEmpty) {
|
||
|
zone.LabelStyle.AddAttributesToRender(writer, this);
|
||
|
}
|
||
|
writer.AddAttribute(HtmlTextWriterAttribute.For, _upload.ClientID);
|
||
|
writer.RenderBeginTag(HtmlTextWriterTag.Label);
|
||
|
writer.Write(BrowseHelpText);
|
||
|
writer.RenderEndTag();
|
||
|
writer.WriteBreak();
|
||
|
|
||
|
if (zone != null && !zone.EditUIStyle.IsEmpty) {
|
||
|
_upload.ApplyStyle(zone.EditUIStyle);
|
||
|
}
|
||
|
_upload.RenderControl(writer);
|
||
|
writer.WriteBreak();
|
||
|
|
||
|
if (zone != null && !zone.LabelStyle.IsEmpty) {
|
||
|
zone.LabelStyle.AddAttributesToRender(writer, this);
|
||
|
}
|
||
|
writer.RenderBeginTag(HtmlTextWriterTag.Span);
|
||
|
writer.Write(UploadHelpText);
|
||
|
writer.RenderEndTag();
|
||
|
writer.WriteBreak();
|
||
|
|
||
|
if (zone != null && !zone.EditUIStyle.IsEmpty) {
|
||
|
_uploadButton.ApplyStyle(zone.EditUIStyle);
|
||
|
}
|
||
|
_uploadButton.Text = UploadButtonText;
|
||
|
_uploadButton.RenderControl(writer);
|
||
|
|
||
|
if (_importedPartDescription != null || _importErrorMessage != null || DesignMode) {
|
||
|
writer.WriteBreak();
|
||
|
if (_importErrorMessage != null) {
|
||
|
if (zone != null && !zone.ErrorStyle.IsEmpty) {
|
||
|
zone.ErrorStyle.AddAttributesToRender(writer, this);
|
||
|
}
|
||
|
writer.RenderBeginTag(HtmlTextWriterTag.Span);
|
||
|
writer.Write(PartImportErrorLabelText);
|
||
|
writer.RenderEndTag();
|
||
|
|
||
|
writer.RenderBeginTag(HtmlTextWriterTag.Hr);
|
||
|
writer.RenderEndTag();
|
||
|
|
||
|
if (zone != null && !zone.ErrorStyle.IsEmpty) {
|
||
|
zone.ErrorStyle.AddAttributesToRender(writer, this);
|
||
|
}
|
||
|
writer.RenderBeginTag(HtmlTextWriterTag.Span);
|
||
|
// We encode the error message because it is user-provided via the import file.
|
||
|
writer.WriteEncodedText(_importErrorMessage);
|
||
|
writer.RenderEndTag();
|
||
|
}
|
||
|
else {
|
||
|
if (zone != null && !zone.LabelStyle.IsEmpty) {
|
||
|
zone.LabelStyle.AddAttributesToRender(writer, this);
|
||
|
}
|
||
|
writer.RenderBeginTag(HtmlTextWriterTag.Span);
|
||
|
writer.Write(ImportedPartLabelText);
|
||
|
writer.RenderEndTag();
|
||
|
|
||
|
writer.RenderBeginTag(HtmlTextWriterTag.Hr);
|
||
|
writer.RenderEndTag();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|