Thursday, August 30, 2012

Maximum File Upload Size in IIS 7

I have investigated and done a lot of testing as the information online is somewhat inaccurate/misleading.  In the end i Determined that the following setting all need to be configured to allow for large file uploads.  If not the default settings will stop uploads greater then about 20 MB.

The following change need made to the web.config file.
<system.web>
    <httpRuntime requestValidationMode="2.0" executionTimeout="600" maxRequestLength="2000000" />
  <system.web>
  <system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="2000000000" />
      </requestFiltering>
    </security>
  </system.webServer>

The executionTimeout is extending the maximum script execution to 10 minutes witch will be required for larger uploads.  The maxRequestLength is the maximum size in kilobytes while the maxAllowedContentLength is the same maximum size but in bytes.  So maxAllowedContentLength should be 1000 times the maxRequestLength.

These numbers of course can be adjusted as needed.

On a final note you may want to check C:\Windows\System32\inetsrv\config\applicationHost.config and verify that the following line is present and set to Allow.
<section name="requestFiltering" overrideModeDefault="Allow" />

Umbraco Using Courier to Deploy MultiType Data

When using Umbraco and doing production deployments with the Courier package I ran into an issue.  We have had the process working fine with built in components but recently I deployed a new property that used the MultiType DataType which  allows for arrays of data fields grouped together.  For example we have the following grouping and multiple images can be added to the one parameter in Umbraco.

RotatingImages
  Item
    ImageSrc(Media Picker)
    MobileSrc(Media Picker)
    AltText(Text)
    LinkTo(Content Picker)

The problem comes in when we publish this item, the content and media pickers contain the node id's and the data is stored as XML.  As such Courier will not process the ID's when it does the deployment process.  To fix this a custom PropertyDataResolverProvider is needed.  The following code can be included in the App_Code directory to fix this problem.



using Umbraco.Courier.Core;
using Umbraco.Courier.Core.Enums;
using Umbraco.Courier.Core.Helpers;
using Umbraco.Courier.DataResolvers;
using Umbraco.Courier.ItemProviders;

using System.Collections.Generic;
using System.Linq;

namespace JSP {
    /// <summary>
    /// Custm Data Resolver for use with MultiType data objects.  Will convers a list of xml nodes to guids for deployment and back again.
    /// </summary>
    public class MultiType : PropertyDataResolverProvider
    {
        /// <summary>
        /// The datatype guid of the multitype item we are running on.
        /// </summary>
        public override Guid DataTypeId
        {
            get { return new Guid("f17bb230-3941-4813-a923-e7c3efd067d8"); }
        }

        /// <summary>
        /// Get the xpath elements from the data types that contain document or media id's.
        /// </summary>
        /// <param name="propertyData">Property to examin.</param>
        /// <param name="documentXPath">XPath string to select all fields with document ID's.</param>
        /// <param name="mediaXPath">XPath string to select all fields with media ID's.</param>
        private void GetXPath(ContentProperty propertyData, out string documentXPath, out string mediaXPath)
        {
            // Get the data definition for this item.
            var dataDefinition = new umbraco.cms.businesslogic.datatype.DataTypeDefinition(propertyData.DataType);
            
            // Get the item configureation and locate the preValue property.
            var preValue = umbraco.library.GetPreValues(dataDefinition.DataType.DataTypeDefinitionId);
            preValue.MoveNext();
            var preValueIterator = preValue.Current.SelectChildren("preValue", "");
            preValueIterator.MoveNext();

            // Deserialize the preValue data.
            dynamic data = new System.Web.Script.Serialization.JavaScriptSerializer().DeserializeObject(preValueIterator.Current.Value);
            // Get the MultiType Children properties.
            var mprop = ((object[])((Dictionary<string, object>)data)["MultiTypes"]).Cast<Dictionary<string, object>>();
            
            // Get all content picker properties and add the alias to the list.  Resutls: //Node1 | //Node2 | ...
            documentXPath = String.Join(" | ", mprop.Where(i => Convert.ToInt32(i["Type"]) == (int)_4Ben.DataTypes.MultiType.ControlType.ContentPicker).Select(i => "//" + i["Alias"].ToString()));
            // Get all media picker properties and add the alias to the list.  Resutls: //Node1 | //Node2 | ...
            mediaXPath = String.Join(" | ", mprop.Where(i => Convert.ToInt32(i["Type"]) == (int)_4Ben.DataTypes.MultiType.ControlType.MediaPicker).Select(i => "//" + i["Alias"].ToString()));
        }

        /// <summary>
        /// Run when a property of the specified type is packaged up.
        /// </summary>
        public override void PackagingProperty(Item item, ContentProperty propertyData)
        {
            string documentXpath;
            string mediaXpath;

            // Get the elements from this data type that need to be converted.  This could be hard coded but the queries are quick.
            GetXPath(propertyData, out documentXpath, out mediaXpath);

            // Document References
            List<string> replacedIds = new List<string>();
            propertyData.Value = XmlDependencies.ReplaceIds(propertyData.Value.ToString(), documentXpath, IdentifierReplaceDirection.FromNodeIdToGuid, out replacedIds);

            // List all id's found and make them dependencies.
            foreach (string guid in replacedIds)
            {
                // Add as a dependency. (working?)
                item.Dependencies.Add(guid, ProviderIDCollection.documentItemProviderGuid);
            }

            // Media References
            //dataXpath = ConfigurationManager.AppSettings["courierMultiTypeMediaNodes"].ToString();
            replacedIds = new List<string>();
            propertyData.Value = XmlDependencies.ReplaceIds(propertyData.Value.ToString(), mediaXpath, IdentifierReplaceDirection.FromNodeIdToGuid, out replacedIds);

            // List all id's found and make them dependencies.
            foreach (string guid in replacedIds)
            {
                // Add as a dependency. (working?)
                item.Dependencies.Add(guid, ProviderIDCollection.mediaItemProviderGuid);

                // Could add as a resource but in reality the entire node is needed witch wil transwer the resource so this line should not be run.
                //item.Resources.Add(new umbraco.cms.businesslogic.media.Media(new Guid(guid)).getProperty("umbracoFile").Value.ToString());
            }
        }

        /// <summary>
        /// Run when a property of the specified type is extracted.
        /// </summary>
        public override void ExtractingProperty(Item item, ContentProperty propertyData)
        {
            string documentXpath;
            string mediaXpath;

            // Get the elements from this data type that need to be converted.  This could be hard coded but the queries are quick.
            GetXPath(propertyData, out documentXpath, out mediaXpath);

            // Document References
            List<string> replacedIds = new List<string>();
            propertyData.Value = XmlDependencies.ReplaceIds(propertyData.Value.ToString(), documentXpath, IdentifierReplaceDirection.FromGuidToNodeId, out replacedIds);

            // Media References
            replacedIds = new List<string>();
            propertyData.Value = XmlDependencies.ReplaceIds(propertyData.Value.ToString(), mediaXpath, IdentifierReplaceDirection.FromGuidToNodeId, out replacedIds);

        }
    }
}