|
//------------------------------------------------------------------------------
// <copyright file="BlobPersonalizationState.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.UI.WebControls.WebParts {
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Web;
using System.Web.UI;
using System.Web.Util;
/// <devdoc>
/// </devdoc>
internal sealed class BlobPersonalizationState : PersonalizationState {
private const int PersonalizationVersion = (int)PersonalizationVersions.WhidbeyRTM;
private const string WebPartManagerPersonalizationID = "__wpm";
private bool _isPostRequest;
private IDictionary _personalizedControls;
private IDictionary _sharedState;
private IDictionary _userState;
private byte[] _rawUserData;
private IDictionary _extractedState;
/// <devdoc>
/// </devdoc>
public BlobPersonalizationState(WebPartManager webPartManager) : base(webPartManager) {
//
// Note that we don't use the IsPostBack property of Page because that
// is based on the presence of view state, which could be on the query string
// in a non-POST request as well. Instead we use the actual verb associated
// with the request.
// Note that there are other types of HttpVerb besides GET and POST. We only
// save personalization data for POST requests. (VSWhidbey 423433)
_isPostRequest = (webPartManager.Page.Request.HttpVerb == HttpVerb.POST);
}
/// <internalonly />
public override bool IsEmpty {
get {
return ((_extractedState == null) || (_extractedState.Count == 0));
}
}
/// <devdoc>
/// </devdoc>
private bool IsPostRequest {
get {
return _isPostRequest;
}
}
/// <devdoc>
/// </devdoc>
private PersonalizationScope PersonalizationScope {
get {
return WebPartManager.Personalization.Scope;
}
}
/// <devdoc>
/// This is for symmetry with the UserState property.
/// </devdoc>
private IDictionary SharedState {
get {
return _sharedState;
}
}
/// <devdoc>
/// User state is always loaded even if the WebPartManager is in shared
/// scope. So we on-demand deserialize the bytes.
/// </devdoc>
private IDictionary UserState {
get {
if (_rawUserData != null) {
_userState = DeserializeData(_rawUserData);
_rawUserData = null;
}
if (_userState == null) {
_userState = new HybridDictionary(/* caseInsensitive */ false);
}
return _userState;
}
}
/// <devdoc>
/// Does the work of applying personalization data into a control
/// </devdoc>
private void ApplyPersonalization(Control control, string personalizationID, bool isWebPartManager,
PersonalizationScope extractScope, GenericWebPart genericWebPart) {
Debug.Assert(control != null);
Debug.Assert(!String.IsNullOrEmpty(personalizationID));
if (_personalizedControls == null) {
_personalizedControls = new HybridDictionary(/* caseInsensitive */ false);
}
else {
// We shouldn't be applying personalization to the same control more than once
if (_personalizedControls.Contains(personalizationID)) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_CantApply, personalizationID));
}
}
IDictionary personalizableProperties =
PersonalizableAttribute.GetPersonalizablePropertyEntries(control.GetType());
if (SharedState == null) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_NotLoaded));
}
PersonalizationInfo sharedInfo = (PersonalizationInfo)SharedState[personalizationID];
PersonalizationInfo userInfo = null;
IDictionary defaultProperties = null;
IDictionary initialProperties = null;
PersonalizationDictionary customInitialProperties = null;
// WebPart.SetPersonalizationDirty() should only mark a control as dirty in the following circumstances:
// 1. During its IPersonalizable.Load() method
// 2. During its IVersioningPersonalizable.Load() method
// 3. During or after its ITrackingPersonalizable.EndLoad() method
// By exclusion, WebPart.SetPersonalizationDirty() should be a no-op in the following circumstances:
// 1. Before its IPersonalizable.Load() method
// 2. While we are setting the values of its [Personalizable] properties
// (VSWhidbey 392533)
ControlInfo ci = new ControlInfo();
ci._allowSetDirty = false;
_personalizedControls[personalizationID] = ci;
if (sharedInfo != null && sharedInfo._isStatic && !sharedInfo.IsMatchingControlType(control)) {
// Mismatch in saved data, so ignore it
sharedInfo = null;
if (PersonalizationScope == PersonalizationScope.Shared) {
SetControlDirty(control, personalizationID, isWebPartManager, true);
}
}
IPersonalizable customPersonalizable = control as IPersonalizable;
ITrackingPersonalizable trackingPersonalizable = control as ITrackingPersonalizable;
// The WebPart on which to set HasSharedData and HasUserData
WebPart hasDataWebPart = null;
if (!isWebPartManager) {
if (genericWebPart != null) {
hasDataWebPart = genericWebPart;
}
else {
Debug.Assert(control is WebPart);
hasDataWebPart = (WebPart)control;
}
}
try {
if (trackingPersonalizable != null) {
trackingPersonalizable.BeginLoad();
}
if (PersonalizationScope == PersonalizationScope.User) {
if (UserState == null) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_NotLoaded));
}
userInfo = (PersonalizationInfo)UserState[personalizationID];
if (userInfo != null && userInfo._isStatic && !userInfo.IsMatchingControlType(control)) {
// Mismatch in saved data, so ignore it
userInfo = null;
SetControlDirty(control, personalizationID, isWebPartManager, true);
}
if (customPersonalizable != null) {
PersonalizationDictionary customProperties = MergeCustomProperties(
sharedInfo, userInfo, isWebPartManager, hasDataWebPart, ref customInitialProperties);
if (customProperties != null) {
ci._allowSetDirty = true;
customPersonalizable.Load(customProperties);
ci._allowSetDirty = false;
}
}
if (!isWebPartManager) {
// Properties do not apply to the WebPartManager
IDictionary unusedSharedProperties = null;
IDictionary unusedUserProperties = null;
// To compute default properties in user scope, we must first
// apply the shared properties. Only differences detected from
// shared scope are to be persisted.
if (sharedInfo != null) {
IDictionary properties = sharedInfo._properties;
if ((properties != null) && (properties.Count != 0)) {
hasDataWebPart.SetHasSharedData(true);
unusedSharedProperties = SetPersonalizedProperties(control, personalizableProperties,
properties, PersonalizationScope.Shared);
}
}
defaultProperties = GetPersonalizedProperties(control, personalizableProperties, null, null,
extractScope);
// Now apply the user properties and hang on to the initial values
if (userInfo != null) {
IDictionary properties = userInfo._properties;
if ((properties != null) && (properties.Count != 0)) {
hasDataWebPart.SetHasUserData(true);
// We pass the extractScope as the PersonalizationScope in which to set the properties. For
// a shared WebPart, we want to only apply the user values to user properties, and not to
// shared properties. However, for an unshared WebPart, we want to apply the user values
// to both user and shared properties, since there is effectively no difference for an
// unshared WebPart. (VSWhidbey 349356)
unusedUserProperties = SetPersonalizedProperties(control, personalizableProperties,
properties, extractScope);
}
if ((trackingPersonalizable == null) || (trackingPersonalizable.TracksChanges == false)) {
initialProperties = properties;
}
}
bool hasUnusedProperties = ((unusedSharedProperties != null) || (unusedUserProperties != null));
if (hasUnusedProperties) {
IVersioningPersonalizable versioningPersonalizable = control as IVersioningPersonalizable;
if (versioningPersonalizable != null) {
IDictionary unusedProperties = null;
// Merge any unused properties, so they can be handed off to the
// control via IVersioningPersonalizable
if (unusedSharedProperties != null) {
unusedProperties = unusedSharedProperties;
if (unusedUserProperties != null) {
foreach (DictionaryEntry entry in unusedUserProperties) {
unusedProperties[entry.Key] = entry.Value;
}
}
}
else {
unusedProperties = unusedUserProperties;
}
ci._allowSetDirty = true;
versioningPersonalizable.Load(unusedProperties);
ci._allowSetDirty = false;
}
else {
// There were some unused properties, and they couldn't be loaded.
// Mark this control as dirty, so we clean up its personalization
// state later...
SetControlDirty(control, personalizationID, isWebPartManager, true);
}
}
}
}
else {
// Shared Personalization Scope
if (customPersonalizable != null) {
PersonalizationDictionary customProperties = MergeCustomProperties(
sharedInfo, userInfo, isWebPartManager, hasDataWebPart, ref customInitialProperties);
if (customProperties != null) {
ci._allowSetDirty = true;
customPersonalizable.Load(customProperties);
ci._allowSetDirty = false;
}
}
if (!isWebPartManager) {
IDictionary unusedProperties = null;
// Compute default properties. These are basically what was persisted
// in the markup
defaultProperties = GetPersonalizedProperties(control, personalizableProperties, null, null,
extractScope);
// Now apply shared properties and hang on to the initial values
if (sharedInfo != null) {
IDictionary properties = sharedInfo._properties;
if ((properties != null) && (properties.Count != 0)) {
hasDataWebPart.SetHasSharedData(true);
unusedProperties = SetPersonalizedProperties(control, personalizableProperties,
properties, PersonalizationScope.Shared);
}
if ((trackingPersonalizable == null) ||
(trackingPersonalizable.TracksChanges == false)) {
initialProperties = properties;
}
}
if (unusedProperties != null) {
IVersioningPersonalizable versioningPersonalizable = control as IVersioningPersonalizable;
if (versioningPersonalizable != null) {
ci._allowSetDirty = true;
versioningPersonalizable.Load(unusedProperties);
ci._allowSetDirty = false;
}
else {
// There were some unused properties, and they couldn't be loaded.
// Mark this control as dirty, so we clean up its personalization
// state later...
SetControlDirty(control, personalizationID, isWebPartManager, true);
}
}
}
}
}
finally {
ci._allowSetDirty = true;
if (trackingPersonalizable != null) {
trackingPersonalizable.EndLoad();
}
}
// Track this as one of the personalized controls
ci._control = control;
ci._personalizableProperties = personalizableProperties;
ci._defaultProperties = defaultProperties;
ci._initialProperties = initialProperties;
ci._customInitialProperties = customInitialProperties;
}
/// <internalonly />
public override void ApplyWebPartPersonalization(WebPart webPart) {
ValidateWebPart(webPart);
// Do not apply personalization to the UnauthorizedWebPart. It is never rendered
// in the page, so there is no point to applying the personalization to it.
// The personalization data from the original WebPart will be round-tripped in
// ExtractWebPartPersonalization(). We do apply personalization to the ErrorWebPart,
// because we want it to render with many of the personalized property values of the
// original WebPart.
if (webPart is UnauthorizedWebPart) {
return;
}
string personalizationID = CreatePersonalizationID(webPart, null);
// In ApplyPersonalization(), we need to extract the default properites in the same scope we will
// extract the properties in ExtractPersonalization().
PersonalizationScope extractScope = PersonalizationScope;
if ((extractScope == PersonalizationScope.User) && (!webPart.IsShared)) {
// This implies a user owned WebPart in User mode, so extract all
// the properties
extractScope = PersonalizationScope.Shared;
}
ApplyPersonalization(webPart, personalizationID, /* isWebPartManager */ false, extractScope,
/* genericWebPart */ null);
GenericWebPart genericWebPart = webPart as GenericWebPart;
if (genericWebPart != null) {
Control containedControl = genericWebPart.ChildControl;
personalizationID = CreatePersonalizationID(containedControl, genericWebPart);
ApplyPersonalization(containedControl, personalizationID, /* isWebPartManager */ false, extractScope,
genericWebPart);
}
}
/// <internalonly />
public override void ApplyWebPartManagerPersonalization() {
ApplyPersonalization(WebPartManager, WebPartManagerPersonalizationID, /* isWebPartManager */ true,
PersonalizationScope, /* genericWebPart */ null);
}
/// <devdoc>
/// Returns false if the set of new properties are the same as old ones; true if there
/// are differences.
/// </devdoc>
private bool CompareProperties(IDictionary newProperties, IDictionary oldProperties) {
int newCount = 0;
int oldCount = 0;
if (newProperties != null) {
newCount = newProperties.Count;
}
if (oldProperties != null) {
oldCount = oldProperties.Count;
}
if (newCount != oldCount) {
return true;
}
if (newCount != 0) {
foreach (DictionaryEntry entry in newProperties) {
object name = entry.Key;
object newValue = entry.Value;
if (oldProperties.Contains(name)) {
object oldValue = oldProperties[name];
if (Object.Equals(newValue, oldValue) == false) {
return true;
}
}
else {
return true;
}
}
}
return false;
}
private string CreatePersonalizationID(string ID, string genericWebPartID) {
Debug.Assert(!String.IsNullOrEmpty(ID));
if (!String.IsNullOrEmpty(genericWebPartID)) {
return ID + Control.ID_SEPARATOR + genericWebPartID;
}
else {
return ID;
}
}
private string CreatePersonalizationID(Control control, WebPart associatedGenericWebPart) {
if (associatedGenericWebPart != null) {
return CreatePersonalizationID(control.ID, associatedGenericWebPart.ID);
}
return CreatePersonalizationID(control.ID, null);
}
/// <devdoc>
/// Deserializes personalization data packed as a blob of binary data
/// into a dictionary with personalization IDs mapped to
/// PersonalizationInfo objects.
/// </devdoc>
private static IDictionary DeserializeData(byte[] data) {
IDictionary deserializedData = null;
if ((data != null) && (data.Length > 0)) {
Exception deserializationException = null;
int version = -1;
object[] items = null;
int offset = 0;
// Deserialize the data
try {
ObjectStateFormatter formatter =
new ObjectStateFormatter(null /* Page(used to determine encryption mode) */, false /*throwOnErrorDeserializing*/);
if (!HttpRuntime.DisableProcessRequestInApplicationTrust) {
// This is more of a consistency and defense-in-depth fix. Currently we believe
// only user code or code with restricted permissions will be running on the stack.
// However, to mirror the fix for Session State, and also to hedge against future
// scenarios where our current assumptions may change, we should restrict the running
// thread to only the permission set currently defined for the app domain.
// VSWhidbey 427533
if (HttpRuntime.NamedPermissionSet != null && HttpRuntime.ProcessRequestInApplicationTrust) {
HttpRuntime.NamedPermissionSet.PermitOnly();
}
}
items = (object[])formatter.DeserializeWithAssert(new MemoryStream(data));
if (items != null && items.Length != 0) {
version = (int)items[offset++];
}
}
catch (Exception e) {
deserializationException = e;
}
if (version == (int)PersonalizationVersions.WhidbeyBeta2 || version == (int)PersonalizationVersions.WhidbeyRTM) {
try {
// Build up the dictionary of PersonalizationInfo objects
int infoListCount = (int)items[offset++];
if (infoListCount > 0) {
deserializedData = new HybridDictionary(infoListCount, /* caseInsensitive */ false);
}
for (int i = 0; i < infoListCount; i++) {
string controlID;
bool isStatic;
Type controlType = null;
VirtualPath controlVPath = null;
// If this is a dynamic WebPart or control, the Type is not saved in personalization,
// so the first item is the controlID. If this is a static WebPart or control, the
// first item is the control Type.
object item = items[offset++];
if (item is string) {
controlID = (string)item;
isStatic = false;
}
else {
controlType = (Type)item;
if (controlType == typeof(UserControl)) {
controlVPath = VirtualPath.CreateNonRelativeAllowNull((string)items[offset++]);
}
controlID = (string)items[offset++];
isStatic = true;
}
IDictionary properties = null;
int propertyCount = (int)items[offset++];
if (propertyCount > 0) {
properties = new HybridDictionary(propertyCount, /* caseInsensitive */ false);
for (int j = 0; j < propertyCount; j++) {
string propertyName = ((IndexedString)items[offset++]).Value;
object propertyValue = items[offset++];
properties[propertyName] = propertyValue;
}
}
PersonalizationDictionary customProperties = null;
int customPropertyCount = (int)items[offset++];
if (customPropertyCount > 0) {
customProperties = new PersonalizationDictionary(customPropertyCount);
for (int j = 0; j < customPropertyCount; j++) {
string propertyName = ((IndexedString)items[offset++]).Value;
object propertyValue = items[offset++];
PersonalizationScope propertyScope =
(bool)items[offset++] ? PersonalizationScope.Shared : PersonalizationScope.User;
bool isSensitive = false;
if (version == (int)PersonalizationVersions.WhidbeyRTM) {
isSensitive = (bool)items[offset++];
}
customProperties[propertyName] =
new PersonalizationEntry(propertyValue, propertyScope, isSensitive);
}
}
PersonalizationInfo info = new PersonalizationInfo();
info._controlID = controlID;
info._controlType = controlType;
info._controlVPath = controlVPath;
info._isStatic = isStatic;
info._properties = properties;
info._customProperties = customProperties;
deserializedData[controlID] = info;
}
}
catch (Exception e) {
deserializationException = e;
}
}
// Check that there was no deserialization error, and that
// the data conforms to our known version
if ((deserializationException != null) ||
(version != (int)PersonalizationVersions.WhidbeyBeta2 && version != (int)PersonalizationVersions.WhidbeyRTM)) {
throw new ArgumentException(SR.GetString(SR.BlobPersonalizationState_DeserializeError),
"data", deserializationException);
}
}
if (deserializedData == null) {
deserializedData = new HybridDictionary(/* caseInsensitive */ false);
}
return deserializedData;
}
/// <devdoc>
/// Does the actual work of extracting personalizated data from a control
/// </devdoc>
private void ExtractPersonalization(Control control, string personalizationID, bool isWebPartManager,
PersonalizationScope scope, bool isStatic, GenericWebPart genericWebPart) {
Debug.Assert(control != null);
Debug.Assert(!String.IsNullOrEmpty(personalizationID));
if (_extractedState == null) {
_extractedState = new HybridDictionary(/* caseInsensitive */ false);
}
if (_personalizedControls == null) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_NotApplied));
}
ControlInfo ci = (ControlInfo)_personalizedControls[personalizationID];
// The ControlInfo should always have been already created in ApplyPersonalization().
// However, it will be null if the Control's ID has changed since we loaded personalization data.
// This is not supported, but we should throw a helpful exception. (VSWhidbey 372354)
if (ci == null) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_CantExtract, personalizationID));
}
ITrackingPersonalizable trackingPersonalizable = control as ITrackingPersonalizable;
IPersonalizable customPersonalizable = control as IPersonalizable;
IDictionary properties = ci._initialProperties;
PersonalizationDictionary customProperties = ci._customInitialProperties;
bool changed = false;
try {
if (trackingPersonalizable != null) {
trackingPersonalizable.BeginSave();
}
if (!IsPostRequest) {
// In non-POST requests, we only save those WebParts that indicated explicitely that
// they have changed. For other WebParts, we just round-trip the initial state
// that was loaded.
if (ci._dirty) {
// Always save IPersonalizable data if the WebPart has indicated that it is dirty
if (customPersonalizable != null) {
PersonalizationDictionary tempCustomProperties = new PersonalizationDictionary();
customPersonalizable.Save(tempCustomProperties);
if ((tempCustomProperties.Count != 0) ||
((customProperties != null) && (customProperties.Count != 0))) {
if (scope == PersonalizationScope.User) {
tempCustomProperties.RemoveSharedProperties();
}
customProperties = tempCustomProperties;
}
}
if (!isWebPartManager) {
// WebPartManager does not have personalizable properties
properties =
GetPersonalizedProperties(control, ci._personalizableProperties,
ci._defaultProperties, ci._initialProperties, scope);
}
changed = true;
}
}
else {
bool extractProperties = true;
bool diffWithInitialProperties = true;
if (ci._dirty) {
// WebPart is indicating that it is dirty, so there is no need
// for us to perform a diff
diffWithInitialProperties = false;
}
else if ((trackingPersonalizable != null) &&
(trackingPersonalizable.TracksChanges) &&
(ci._dirty == false)) {
// WebPart is indicating that it is not dirty, and since it
// tracks dirty-ness, theres no need to do additional work.
extractProperties = false;
}
if (extractProperties) {
// Always save IPersonalizable data if the WebPart has indicated that it is dirty
if (customPersonalizable != null && (ci._dirty || customPersonalizable.IsDirty)) {
PersonalizationDictionary tempCustomProperties = new PersonalizationDictionary();
customPersonalizable.Save(tempCustomProperties);
// The new custom properties should be used either if they are
// non-empty, or they are, but the original ones weren't, since
// that implies a change as well.
if ((tempCustomProperties.Count != 0) ||
((customProperties != null) && (customProperties.Count != 0))) {
if (tempCustomProperties.Count != 0) {
if (scope == PersonalizationScope.User) {
tempCustomProperties.RemoveSharedProperties();
}
customProperties = tempCustomProperties;
}
else {
customProperties = null;
}
// No point doing the diff, since we've already determined that the
// custom properties are dirty.
diffWithInitialProperties = false;
changed = true;
}
}
if (!isWebPartManager) {
// WebPartManager does not have personalizable properties
IDictionary newProperties =
GetPersonalizedProperties(control, ci._personalizableProperties,
ci._defaultProperties, ci._initialProperties, scope);
if (diffWithInitialProperties) {
bool different = CompareProperties(newProperties, ci._initialProperties);
if (different == false) {
extractProperties = false;
}
}
if (extractProperties) {
properties = newProperties;
changed = true;
}
}
}
}
}
finally {
if (trackingPersonalizable != null) {
trackingPersonalizable.EndSave();
}
}
PersonalizationInfo extractedInfo = new PersonalizationInfo();
extractedInfo._controlID = personalizationID;
if (isStatic) {
UserControl uc = control as UserControl;
if (uc != null) {
extractedInfo._controlType = typeof(UserControl);
extractedInfo._controlVPath = uc.TemplateControlVirtualPath;
}
else {
extractedInfo._controlType = control.GetType();
}
}
extractedInfo._isStatic = isStatic;
extractedInfo._properties = properties;
extractedInfo._customProperties = customProperties;
_extractedState[personalizationID] = extractedInfo;
if (changed) {
SetDirty();
}
if ((properties != null && properties.Count > 0) ||
(customProperties != null && customProperties.Count > 0)) {
// The WebPart on which to set HasSharedData and HasUserData
WebPart hasDataWebPart = null;
if (!isWebPartManager) {
if (genericWebPart != null) {
hasDataWebPart = genericWebPart;
}
else {
Debug.Assert(control is WebPart);
hasDataWebPart = (WebPart)control;
}
}
if (hasDataWebPart != null) {
if (PersonalizationScope == PersonalizationScope.Shared) {
hasDataWebPart.SetHasSharedData(true);
}
else {
hasDataWebPart.SetHasUserData(true);
}
}
}
}
/// <internalonly />
public override void ExtractWebPartPersonalization(WebPart webPart) {
ValidateWebPart(webPart);
// Round-trip the personalization data for a ProxyWebPart
ProxyWebPart proxyWebPart = webPart as ProxyWebPart;
if (proxyWebPart != null) {
RoundTripWebPartPersonalization(proxyWebPart.OriginalID, proxyWebPart.GenericWebPartID);
return;
}
PersonalizationScope extractScope = PersonalizationScope;
if ((extractScope == PersonalizationScope.User) && (!webPart.IsShared)) {
// This implies a user owned WebPart in User mode, so save all
// the properties
extractScope = PersonalizationScope.Shared;
}
bool isStatic = webPart.IsStatic;
string personalizationID = CreatePersonalizationID(webPart, null);
ExtractPersonalization(webPart, personalizationID, /* isWebPartManager */ false, extractScope, isStatic,
/* genericWebPart */ null);
GenericWebPart genericWebPart = webPart as GenericWebPart;
if (genericWebPart != null) {
Control containedControl = genericWebPart.ChildControl;
personalizationID = CreatePersonalizationID(containedControl, genericWebPart);
ExtractPersonalization(containedControl, personalizationID, /* isWebPartManager */ false,
extractScope, isStatic, genericWebPart);
}
}
/// <internalonly />
public override void ExtractWebPartManagerPersonalization() {
ExtractPersonalization(WebPartManager, WebPartManagerPersonalizationID, /* isWebPartManager */ true,
PersonalizationScope, /* isStatic */ true, /* genericWebPart */ null);
}
// Returns the AuthorizationFilter string for a WebPart before it is instantiated.
// Returns null if there is no personalized value for AuthorizationFilter, or if the
// personalized value has a type other than string.
public override string GetAuthorizationFilter(string webPartID) {
if (String.IsNullOrEmpty(webPartID)) {
throw ExceptionUtil.ParameterNullOrEmpty("webPartID");
}
return GetPersonalizedValue(webPartID, "AuthorizationFilter") as string;
}
/// <devdoc>
/// </devdoc>
internal static IDictionary GetPersonalizedProperties(Control control, PersonalizationScope scope) {
IDictionary personalizableProperties =
PersonalizableAttribute.GetPersonalizablePropertyEntries(control.GetType());
return GetPersonalizedProperties(control, personalizableProperties, null, null, scope);
}
/// <devdoc>
/// Does the work of retrieving personalized properties. If the scope is User, the shared
/// personalizable properties are not retrieved. If a non-null defaultPropertyState is
/// handed in, only the properties that are different from the default values are retrieved.
/// </devdoc>
private static IDictionary GetPersonalizedProperties(Control control,
IDictionary personalizableProperties,
IDictionary defaultPropertyState,
IDictionary initialPropertyState,
PersonalizationScope scope) {
Debug.Assert(control != null);
if (personalizableProperties.Count == 0) {
return null;
}
bool ignoreSharedProperties = (scope == PersonalizationScope.User);
IDictionary properties = null;
foreach (DictionaryEntry entry in personalizableProperties) {
PersonalizablePropertyEntry property = (PersonalizablePropertyEntry)entry.Value;
if (ignoreSharedProperties && (property.Scope == PersonalizationScope.Shared)) {
continue;
}
PropertyInfo pi = property.PropertyInfo;
Debug.Assert(pi != null);
//
string name = (string)entry.Key;
object value = FastPropertyAccessor.GetProperty(control, name, control.DesignMode);
bool saveProperty = true;
// Only compare to default value if there is no initial value.
if ((initialPropertyState == null || !initialPropertyState.Contains(name)) && defaultPropertyState != null) {
object defaultValue = defaultPropertyState[name];
if (Object.Equals(value, defaultValue)) {
saveProperty = false;
}
}
if (saveProperty) {
if (properties == null) {
properties = new HybridDictionary(personalizableProperties.Count, /* caseInsensitive */ false);
}
properties[name] = value;
}
}
return properties;
}
// Returns the value of a personalized property on a control
// Returns null if there is no personalized value for the property
private object GetPersonalizedValue(string personalizationID, string propertyName) {
Debug.Assert(!String.IsNullOrEmpty(personalizationID));
Debug.Assert(!String.IsNullOrEmpty(propertyName));
if (SharedState == null) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_NotLoaded));
}
PersonalizationInfo sharedInfo = (PersonalizationInfo)SharedState[personalizationID];
IDictionary sharedProperties = (sharedInfo != null) ? sharedInfo._properties : null;
if (PersonalizationScope == PersonalizationScope.Shared) {
if (sharedProperties != null) {
return sharedProperties[propertyName];
}
}
else {
if (UserState == null) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_NotLoaded));
}
PersonalizationInfo userInfo = (PersonalizationInfo)UserState[personalizationID];
IDictionary userProperties = (userInfo != null) ? userInfo._properties : null;
if (userProperties != null && userProperties.Contains(propertyName)) {
return userProperties[propertyName];
}
else if (sharedProperties != null) {
return sharedProperties[propertyName];
}
}
return null;
}
/// <devdoc>
/// </devdoc>
public void LoadDataBlobs(byte[] sharedData, byte[] userData) {
_sharedState = DeserializeData(sharedData);
_rawUserData = userData;
}
// Returns a PersonalizationDictionary containing a merged view of the custom properties
// in both the sharedInfo and the userInfo.
private PersonalizationDictionary MergeCustomProperties(PersonalizationInfo sharedInfo,
PersonalizationInfo userInfo,
bool isWebPartManager, WebPart hasDataWebPart,
ref PersonalizationDictionary customInitialProperties) {
PersonalizationDictionary customProperties = null;
bool hasSharedCustomProperties = (sharedInfo != null && sharedInfo._customProperties != null);
bool hasUserCustomProperties = (userInfo != null && userInfo._customProperties != null);
// Fill or set the customProperties dictionary
if (hasSharedCustomProperties && hasUserCustomProperties) {
customProperties = new PersonalizationDictionary();
foreach (DictionaryEntry entry in sharedInfo._customProperties) {
customProperties[(string)entry.Key] = (PersonalizationEntry)entry.Value;
}
foreach (DictionaryEntry entry in userInfo._customProperties) {
customProperties[(string)entry.Key] = (PersonalizationEntry)entry.Value;
}
}
else if (hasSharedCustomProperties) {
customProperties = sharedInfo._customProperties;
}
else if (hasUserCustomProperties) {
customProperties = userInfo._customProperties;
}
// Set the customInitialProperties dictionary
if (PersonalizationScope == PersonalizationScope.Shared && hasSharedCustomProperties) {
customInitialProperties = sharedInfo._customProperties;
}
else if (PersonalizationScope == PersonalizationScope.User && hasUserCustomProperties) {
customInitialProperties = userInfo._customProperties;
}
// Set the HasSharedData and HasUserData flags
if (hasSharedCustomProperties && !isWebPartManager) {
hasDataWebPart.SetHasSharedData(true);
}
if (hasUserCustomProperties && !isWebPartManager) {
hasDataWebPart.SetHasUserData(true);
}
return customProperties;
}
private void RoundTripWebPartPersonalization(string ID, string genericWebPartID) {
if (String.IsNullOrEmpty(ID)) {
throw ExceptionUtil.ParameterNullOrEmpty("ID");
}
// Round-trip personalization for control/WebPart
string personalizationID = CreatePersonalizationID(ID, genericWebPartID);
RoundTripWebPartPersonalization(personalizationID);
// Round-trip personalization for GenericWebPart, if necessary
if (!String.IsNullOrEmpty(genericWebPartID)) {
string genericPersonalizationID = CreatePersonalizationID(genericWebPartID, null);
RoundTripWebPartPersonalization(genericPersonalizationID);
}
}
private void RoundTripWebPartPersonalization(string personalizationID) {
Debug.Assert(personalizationID != null);
// Can't check that personalizationID is valid, since there may be no data
// for even a valid ID.
if (PersonalizationScope == PersonalizationScope.Shared) {
if (SharedState == null) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_NotLoaded));
}
if (SharedState.Contains(personalizationID)) {
_extractedState[personalizationID] = (PersonalizationInfo)SharedState[personalizationID];
}
}
else {
if (UserState == null) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_NotLoaded));
}
if (UserState.Contains(personalizationID)) {
_extractedState[personalizationID] = (PersonalizationInfo)UserState[personalizationID];
}
}
}
/// <devdoc>
/// </devdoc>
public byte[] SaveDataBlob() {
return SerializeData(_extractedState);
}
/// <devdoc>
/// Serializes a dictionary of IDs mapped to PersonalizationInfo
/// objects into a binary blob.
/// </devdoc>
private static byte[] SerializeData(IDictionary data) {
byte[] serializedData = null;
if ((data == null) || (data.Count == 0)) {
return serializedData;
}
ArrayList infoList = new ArrayList();
foreach (DictionaryEntry entry in data) {
PersonalizationInfo info = (PersonalizationInfo)entry.Value;
if (((info._properties != null) && (info._properties.Count != 0)) ||
((info._customProperties != null) && (info._customProperties.Count != 0))){
infoList.Add(info);
}
}
if (infoList.Count != 0) {
ArrayList items = new ArrayList();
items.Add(PersonalizationVersion);
items.Add(infoList.Count);
foreach (PersonalizationInfo info in infoList) {
// Only need to save the type information for static WebParts
if (info._isStatic) {
items.Add(info._controlType);
if (info._controlVPath != null) {
items.Add(info._controlVPath.AppRelativeVirtualPathString);
}
}
items.Add(info._controlID);
int propertyCount = 0;
if (info._properties != null) {
propertyCount = info._properties.Count;
}
items.Add(propertyCount);
if (propertyCount != 0) {
foreach (DictionaryEntry propertyEntry in info._properties) {
items.Add(new IndexedString((string)propertyEntry.Key));
items.Add(propertyEntry.Value);
}
}
int customPropertyCount = 0;
if (info._customProperties != null) {
customPropertyCount = info._customProperties.Count;
}
items.Add(customPropertyCount);
if (customPropertyCount != 0) {
foreach (DictionaryEntry customPropertyEntry in info._customProperties) {
items.Add(new IndexedString((string)customPropertyEntry.Key));
PersonalizationEntry personalizationEntry = (PersonalizationEntry)customPropertyEntry.Value;
items.Add(personalizationEntry.Value);
// PERF: Add a boolean instead of the Enum value
items.Add(personalizationEntry.Scope == PersonalizationScope.Shared);
// The IsSensitive property was added between Whidbey Beta2 and Whidbey RTM.
// VSWhidbey 502554 and 536907
items.Add(personalizationEntry.IsSensitive);
}
}
}
if (items.Count != 0) {
ObjectStateFormatter formatter = new ObjectStateFormatter(null, false);
MemoryStream ms = new MemoryStream(1024);
object[] state = items.ToArray();
if (!HttpRuntime.DisableProcessRequestInApplicationTrust){
// This is more of a consistency and defense-in-depth fix. Currently we believe
// only user code or code with restricted permissions will be running on the stack.
// However, to mirror the fix for Session State, and also to hedge against future
// scenarios where our current assumptions may change, we should restrict the running
// thread to only the permission set currently defined for the app domain.
// VSWhidbey 491449
if (HttpRuntime.NamedPermissionSet != null && HttpRuntime.ProcessRequestInApplicationTrust) {
HttpRuntime.NamedPermissionSet.PermitOnly();
}
}
formatter.SerializeWithAssert(ms, state);
serializedData = ms.ToArray();
}
}
return serializedData;
}
/// <devdoc>
/// Only actually sets the control as dirty if we have already started applying personalization
/// data (info != null), and we are forcing the control to be dirty (forceSetDirty), or the control
/// has called SetPersonalizationDirty() at the right time (info._allowSetDirty).
/// </devdoc>
private void SetControlDirty(Control control, string personalizationID, bool isWebPartManager,
bool forceSetDirty) {
Debug.Assert(control != null);
Debug.Assert(!String.IsNullOrEmpty(personalizationID));
if (_personalizedControls == null) {
throw new InvalidOperationException(SR.GetString(SR.BlobPersonalizationState_NotApplied));
}
ControlInfo info = (ControlInfo)_personalizedControls[personalizationID];
if (info != null && (forceSetDirty || info._allowSetDirty)) {
info._dirty = true;
}
}
/// <devdoc>
/// Called by WebPartPersonalization to copy the personalized values from one control
/// to another.
/// </devdoc>
internal static IDictionary SetPersonalizedProperties(Control control, IDictionary propertyState) {
IDictionary personalizableProperties =
PersonalizableAttribute.GetPersonalizablePropertyEntries(control.GetType());
// We pass PersonalizationScope.Shared, since we want to apply all values to their properties.
return SetPersonalizedProperties(control, personalizableProperties, propertyState, PersonalizationScope.Shared);
}
/// <devdoc>
/// Does the work of setting personalized properties
/// </devdoc>
private static IDictionary SetPersonalizedProperties(Control control, IDictionary personalizableProperties,
IDictionary propertyState, PersonalizationScope scope) {
if (personalizableProperties.Count == 0) {
// all properties were not used
return propertyState;
}
if ((propertyState == null) || (propertyState.Count == 0)) {
return null;
}
IDictionary unusedProperties = null;
foreach (DictionaryEntry entry in propertyState) {
string name = (string)entry.Key;
object value = entry.Value;
PersonalizablePropertyEntry property = (PersonalizablePropertyEntry)personalizableProperties[name];
bool propertySet = false;
// Do not apply a user value to a shared property. This scenario can happen if there
// is already User data for a property, then the property is changed from Personalizable(User)
// to Personalizable(Shared). (VSWhidbey 349456)
if (property != null &&
(scope == PersonalizationScope.Shared || property.Scope == PersonalizationScope.User)) {
PropertyInfo pi = property.PropertyInfo;
Debug.Assert(pi != null);
// If SetProperty() throws an exception, the property will be added to the unusedProperties collection
try {
FastPropertyAccessor.SetProperty(control, name, value, control.DesignMode);
propertySet = true;
}
catch {
}
}
if (!propertySet) {
if (unusedProperties == null) {
unusedProperties = new HybridDictionary(propertyState.Count, /* caseInsensitive */ false);
}
unusedProperties[name] = value;
}
}
return unusedProperties;
}
/// <internalonly />
public override void SetWebPartDirty(WebPart webPart) {
ValidateWebPart(webPart);
string personalizationID;
personalizationID = CreatePersonalizationID(webPart, null);
SetControlDirty(webPart, personalizationID, /* isWebPartManager */ false, /* forceSetDirty */ false);
GenericWebPart genericWebPart = webPart as GenericWebPart;
if (genericWebPart != null) {
Control containedControl = genericWebPart.ChildControl;
personalizationID = CreatePersonalizationID(containedControl, genericWebPart);
SetControlDirty(containedControl, personalizationID, /* isWebPartManager */ false, /* forceSetDirty */ false);
}
}
/// <internalonly />
public override void SetWebPartManagerDirty() {
SetControlDirty(WebPartManager, WebPartManagerPersonalizationID, /* isWebPartManager */ true,
/* forceSetDirty */ false);
}
/// <devdoc>
/// Used to track personalization information, i.e. the data,
/// and the associated object type and ID.
/// </devdoc>
private sealed class PersonalizationInfo {
public Type _controlType;
public VirtualPath _controlVPath;
public string _controlID;
public bool _isStatic;
public IDictionary _properties;
public PersonalizationDictionary _customProperties;
public bool IsMatchingControlType(Control c) {
if (c is ProxyWebPart) {
// This code path is currently never hit, since we only load personalization data
// for ErrorWebPart, and we only replace dynamic WebParts with the ErrorWebPart,
// and we only check IsMatchingControlType() for static WebParts. However, if this
// ever changes in the future, we will want to return true for ProxyWebParts.
return true;
}
else if (_controlType == null) {
// _controlType will be null if there is no longer a Type on the system with the
// saved type name.
return false;
}
else if (_controlType == typeof(UserControl)) {
UserControl uc = c as UserControl;
if (uc != null) {
return uc.TemplateControlVirtualPath == _controlVPath;
}
return false;
}
else {
return _controlType.IsAssignableFrom(c.GetType());
}
}
}
/// <devdoc>
/// Used to track personalization information for a Control instance.
/// </devdoc>
private sealed class ControlInfo {
public Control _control;
public IDictionary _personalizableProperties;
public bool _dirty;
public bool _allowSetDirty;
public IDictionary _defaultProperties;
public IDictionary _initialProperties;
public PersonalizationDictionary _customInitialProperties;
}
private enum PersonalizationVersions {
WhidbeyBeta2 = 1,
WhidbeyRTM = 2,
}
}
}
|