File: UI\ProfileServiceManager.cs
Project: ndp\fx\src\xsp\system\Extensions\System.Web.Extensions.csproj (System.Web.Extensions)
//------------------------------------------------------------------------------
// <copyright file="ProfileServiceManager.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Web.UI {
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Configuration;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Text;
    using System.Web.ApplicationServices;
    using System.Web;
    using System.Web.Profile;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.Resources;
    using System.Web.Script.Serialization;
    using System.Web.Configuration;
 
    [
    DefaultProperty("Path"),
    TypeConverter(typeof(EmptyStringExpandableObjectConverter))
    ]
    public class ProfileServiceManager {
        private string[] _loadProperties;
        private string _path;
 
        internal static void ConfigureProfileService(ref StringBuilder sb, HttpContext context, ScriptManager scriptManager, List<ScriptManagerProxy> proxies) {
            string profileServiceUrl = null;
            ArrayList loadedProperties = null;
            ProfileServiceManager profileManager;
 
            if(scriptManager.HasProfileServiceManager) {
                profileManager = scriptManager.ProfileService;
 
                // get ScriptManager.Path
                profileServiceUrl = profileManager.Path.Trim();
                if(profileServiceUrl.Length > 0) {
                    profileServiceUrl = scriptManager.ResolveClientUrl(profileServiceUrl);
                }
 
                // get ScriptManager.LoadProperties
                if(profileManager.HasLoadProperties) {
                    loadedProperties = new ArrayList(profileManager._loadProperties);
                }
            }
 
            // combine proxy Paths (find the first one that has specified one)
            // combine loadedProperties collection (take the union of all)
            if(proxies != null) {
                foreach(ScriptManagerProxy proxy in proxies) {
                    if(proxy.HasProfileServiceManager) {
                        profileManager = proxy.ProfileService;
 
                        // combine urls
                        profileServiceUrl = ApplicationServiceManager.MergeServiceUrls(profileManager.Path, profileServiceUrl, proxy);
 
                        // combine LoadProperties
                        if(profileManager.HasLoadProperties) {
                            if(loadedProperties == null) {
                                loadedProperties = new ArrayList(profileManager._loadProperties);
                            }
                            else {
                                loadedProperties = ProfileServiceManager.MergeProperties(loadedProperties, profileManager._loadProperties);
                            }
                        }
                    }
                }
            }
 
            ProfileServiceManager.GenerateInitializationScript(ref sb, context, scriptManager, profileServiceUrl, loadedProperties);
        }
 
        private static void GenerateInitializationScript(ref StringBuilder sb, HttpContext context, ScriptManager scriptManager, string serviceUrl, ArrayList loadedProperties) {
            string defaultServicePath = null;
            bool loadProperties = loadedProperties != null && loadedProperties.Count > 0;
 
            if (ApplicationServiceHelper.ProfileServiceEnabled) {
                if (sb == null) {
                    sb = new StringBuilder(ApplicationServiceManager.StringBuilderCapacity);
                }
 
                // The default path points to the built-in service (if it is enabled)
                // Note that the client can't default to this path because it doesn't know what the app root is, we must tell it.
                // We must specify the default path to the proxy even if a custom path is provided, because on the client they could
                // reset the path back to the default if they want.
                defaultServicePath = scriptManager.ResolveClientUrl("~/" + System.Web.Script.Services.WebServiceData._profileServiceFileName);
                sb.Append("Sys.Services._ProfileService.DefaultWebServicePath = '");
                sb.Append(HttpUtility.JavaScriptStringEncode(defaultServicePath));
                sb.Append("';\n");
            }
 
            if (!String.IsNullOrEmpty(serviceUrl)) {
                // DevDiv Bug 72257:When custom path is set and loadProperties=True, we shouldn't use the default path
                // loadProperties script always retrieves the properties from default profile provider, which is not correct if ProfileService
                // points to non default path. Hence throw when non default path and loadProperties both are specified.
                if (defaultServicePath == null){
                    defaultServicePath = scriptManager.ResolveClientUrl("~/" + System.Web.Script.Services.WebServiceData._profileServiceFileName);
                }
                if (loadProperties && !String.Equals(serviceUrl, defaultServicePath, StringComparison.OrdinalIgnoreCase)) {
                    throw new InvalidOperationException(AtlasWeb.ProfileServiceManager_LoadProperitesWithNonDefaultPath);
                }
                if (sb == null) {
                    sb = new StringBuilder(ApplicationServiceManager.StringBuilderCapacity);
                }
                sb.Append("Sys.Services.ProfileService.set_path('");
                sb.Append(HttpUtility.JavaScriptStringEncode(serviceUrl));
                sb.Append("');\n");
            }
 
            if (loadProperties) {
                if (sb == null) {
                    sb = new StringBuilder(ApplicationServiceManager.StringBuilderCapacity);
                }
                if (scriptManager.DesignMode) {
                    // Dev10 757178: context is null at design time, so we cannot access ProfileBase.
                    // But at DesignTime this method is only important because if it produces any init script,
                    // it prompts AddFrameworkScripts to add the MicrosoftAjaxApplicationServices.js script reference. 
                    // So just append a comment to ensure at least some script is generated.
                    sb.Append("// loadProperties\n");
                }
                else if (context != null) {
                    // get values for all properties to be pre-loaded.
                    // GetSettingsProperty puts each property into either the top level settings dictionary or if its part of a group,
                    // it creates an entry for the group in the group collection and puts the setting in the dictionary for the group.
                    SortedList<string, object> topLevelSettings = new SortedList<string, object>(loadedProperties.Count);
                    SortedList<string, SortedList<string, object>> profileGroups = null;
 
                    ProfileBase profile = context.Profile;
                    foreach(string propertyFullName in loadedProperties) {
                        GetSettingsProperty(profile, propertyFullName, topLevelSettings, ref profileGroups, /* ensure exists */ true);
                    }
 
                    RenderProfileProperties(sb, topLevelSettings, profileGroups);
                }
            }
        }
 
        internal static ArrayList MergeProperties(ArrayList existingProperties, string[] newProperties) {
            // 
 
 
 
 
 
            foreach(string property in newProperties) {
                if(!String.IsNullOrEmpty(property)) {
                    string trimmedProperty = property.Trim();
                    if((trimmedProperty.Length > 0) && !existingProperties.Contains(trimmedProperty)) {
                        existingProperties.Add(trimmedProperty);
                    }
                }
            }
 
            return existingProperties;
        }
 
        internal static void GetSettingsProperty(
            ProfileBase profile,
            string fullPropertyName,
            SortedList<string, object> topLevelSettings,
            ref SortedList<string, SortedList<string, object>> profileGroups,
            bool ensureExists) {
            // Gets a setting off the profile, putting top level settings into the topLevelSettings list,
            // and putting grouped properties into a group-specific list that is contained within a sortedlist of groups.
            // if ensureExists is true and the given property name doesn't exist on the profile, an exception is thrown.
 
            int dotIndex = fullPropertyName.IndexOf('.');
            string groupName;
            string propertyName;
            SortedList<string, object> containingObject;
 
            if(dotIndex == -1) {
                groupName = null;
                propertyName = fullPropertyName;
                containingObject = topLevelSettings;
            }
            else {
                groupName = fullPropertyName.Substring(0, dotIndex);
                propertyName = fullPropertyName.Substring(dotIndex + 1);
 
                if(profileGroups == null) {
                    profileGroups = new SortedList<string, SortedList<string, object>>();
                    containingObject = new SortedList<string, object>();
                    profileGroups.Add(groupName, containingObject);
                }
                else {
                    containingObject = profileGroups[groupName];
                    if(containingObject == null) {
                        containingObject = new SortedList<string, object>();
                        profileGroups.Add(groupName, containingObject);
                    }
                }
            }
 
            bool exists = ProfileBase.Properties[fullPropertyName] != null;
            if(ensureExists && !exists) {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, AtlasWeb.AppService_UnknownProfileProperty, fullPropertyName));
            }
 
            if(exists) {
                containingObject[propertyName] = profile == null ? null : profile[fullPropertyName];
            }
        }
 
        private static void RenderProfileProperties(StringBuilder sb, SortedList<string, object> topLevelSettings, SortedList<string, SortedList<string, object>> profileGroups) {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
 
            // 1. render top level settings
            sb.Append("Sys.Services.ProfileService.properties = ");
            // 
 
            sb.Append(serializer.Serialize(topLevelSettings, JavaScriptSerializer.SerializationFormat.JavaScript));
            sb.Append(";\n");
 
            // 2. render each group as a ProfileGroup object
            //      These could be done as just the value of the PropertyName in topLevelSettings but the serializer wouldn't recognize PropertyGroup.
            if(profileGroups != null) {
                foreach(KeyValuePair<string, SortedList<string, object>> group in profileGroups) {
                    sb.Append("Sys.Services.ProfileService.properties.");
                    sb.Append(group.Key); // group name
                    sb.Append(" = new Sys.Services.ProfileGroup(");
                    sb.Append(serializer.Serialize(group.Value, JavaScriptSerializer.SerializationFormat.JavaScript));
                    sb.Append(");\n");
                }
            }
        }
 
        internal bool HasLoadProperties {
            get {
                return _loadProperties != null && _loadProperties.Length > 0;
            }
        }
 
        [
        DefaultValue(null),
        Category("Behavior"),
        NotifyParentProperty(true),
        TypeConverter(typeof(StringArrayConverter)),
        ResourceDescription("ProfileServiceManager_LoadProperties"),
        SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays",
            Justification="Required by ASP.NET parser.")
        ]
        public string[] LoadProperties {
            get {
                if(_loadProperties == null) {
                    _loadProperties = new string[0];
                }
                return (string[]) _loadProperties.Clone();
            }
            set {
                if(value != null) {
                    value = (string[])value.Clone();
                }
                _loadProperties = value;
            }
        }
 
        [
        DefaultValue(""),
        Category("Behavior"),
        NotifyParentProperty(true),
        ResourceDescription("ApplicationServiceManager_Path"),
        UrlProperty()
        ]
        public string Path {
            get {
                return _path ?? String.Empty;
	    }
            set {
                _path = value;
            }
        }
    }
}