diff --git a/src/Umbraco.Core/Interfaces/IThumbnailProvider.cs b/src/Umbraco.Core/Interfaces/IThumbnailProvider.cs new file mode 100644 index 0000000000..99a96a519a --- /dev/null +++ b/src/Umbraco.Core/Interfaces/IThumbnailProvider.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.Interfaces +{ + public interface IThumbnailProvider + { + int Priority { get; } + bool CanProvideThumbnail(string fileUrl); + string GetThumbnailUrl(string fileUrl); + } +} diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 4469af3176..08de61bb96 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -350,6 +350,11 @@ namespace Umbraco.Core return String.Equals(compare, compareTo, StringComparison.InvariantCultureIgnoreCase); } + public static bool InvariantContains(this string compare, string compareTo) + { + return compare.IndexOf(compareTo, StringComparison.OrdinalIgnoreCase) >= 0; + } + public static bool InvariantContains(this IEnumerable compare, string compareTo) { return compare.Contains(compareTo, new DelegateEqualityComparer((source, dest) => source.Equals(dest, StringComparison.InvariantCultureIgnoreCase), x => x.GetHashCode())); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index b45944b80c..88741f2d23 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -47,6 +47,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index b70dc4e070..c894b1ccb7 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -931,6 +931,7 @@ + diff --git a/src/Umbraco.Web.UI/umbraco/images/mediaThumbnails/pdf.png b/src/Umbraco.Web.UI/umbraco/images/mediaThumbnails/pdf.png new file mode 100644 index 0000000000..3ed7608e32 Binary files /dev/null and b/src/Umbraco.Web.UI/umbraco/images/mediaThumbnails/pdf.png differ diff --git a/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Css/folderbrowser.css b/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Css/folderbrowser.css index 5114c7c935..31256bec1d 100644 --- a/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Css/folderbrowser.css +++ b/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Css/folderbrowser.css @@ -32,6 +32,17 @@ margin-right: 5px; } +.umbFolderBrowser .filter { + position: absolute; + top: 15px; + right: 20px; +} + +.umbFolderBrowser .filter input { + border: solid 1px #ccc; + width: 200px; +} + .umbFolderBrowser .items { margin: 5px 15px; padding: 0; diff --git a/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Js/folderbrowser.js b/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Js/folderbrowser.js index c37a364047..427bb9f2d5 100644 --- a/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Js/folderbrowser.js +++ b/src/Umbraco.Web.UI/umbraco_client/FolderBrowser/Js/folderbrowser.js @@ -12,6 +12,18 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); _opts: null, _viewModel: null, + _getChildNodes: function () { + _this = this; + + $.getJSON(_this._opts.basePath + "/FolderBrowserService/GetChildNodes/" + _this._parentId + "/" + _this._viewModel.filterTerm(), function(data) { + if (data != undefined && data.length > 0) { + ko.mapping.fromJS(data, {}, _this._viewModel.items); + } else { + _this._viewModel.items([]); + } + }); + }, + // Constructor constructor: function (el, opts) { @@ -32,8 +44,12 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); // Setup the viewmode; this._viewModel = $.extend({}, { parent: this, - panelVisible: ko.observable(true), - items: ko.observableArray([]) + filterTerm: ko.observable(''), + items: ko.observableArray([]) + }); + + this._viewModel.filterTerm.subscribe(function(newValue) { + _this._getChildNodes(); }); // Inject the upload button into the toolbar @@ -44,20 +60,11 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); $(".tabpage:first-child .menubar td[id$='tableContainerButtons'] .sl nobr").after(button); - // Grab children media items - this._viewModel.items.push({ name: "test1.jpg" }); - this._viewModel.items.push({ name: "test2.jpg" }); - this._viewModel.items.push({ name: "test3.jpg" }); - this._viewModel.items.push({ name: "test4.jpg" }); - this._viewModel.items.push({ name: "test5.jpg" }); - this._viewModel.items.push({ name: "test1.jpg" }); - this._viewModel.items.push({ name: "test2.jpg" }); - this._viewModel.items.push({ name: "test3.jpg" }); - this._viewModel.items.push({ name: "test4.jpg" }); - this._viewModel.items.push({ name: "test5.jpg" }); - // Bind the viewmodel ko.applyBindings(this._viewModel, el); + + // Grab children media items + this._getChildNodes(); } // Public @@ -91,8 +98,4 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls"); return $(this).data("api"); }; - $(function () { - $(".umbFolderBrowser").folderBrowser(); - }); - })(jQuery, base2.Base) \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco_client/ui/knockout.mapping.js b/src/Umbraco.Web.UI/umbraco_client/ui/knockout.mapping.js new file mode 100644 index 0000000000..fbe922f902 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/ui/knockout.mapping.js @@ -0,0 +1,20 @@ +/// Knockout Mapping plugin v2.2.4 +/// (c) 2012 Steven Sanderson, Roy Jacobs - http://knockoutjs.com/ +/// License: MIT (http://www.opensource.org/licenses/mit-license.php) +(function(d){"function"===typeof require&&"object"===typeof exports&&"object"===typeof module?d(require("knockout"),exports):"function"===typeof define&&define.amd?define(["knockout","exports"],d):d(ko,ko.mapping={})})(function(d,e){function u(a,c){for(var b in c)c.hasOwnProperty(b)&&c[b]&&(b&&a[b]&&"array"!==e.getType(a[b])?u(a[b],c[b]):a[b]=c[b])}function A(a,c){var b={};u(b,a);u(b,c);return b}function H(a,c){options=A({},a);for(var b=K.length-1;0<=b;b--){var d=K[b];options[d]&&(options[""]instanceof +Object||(options[""]={}),options[""][d]=options[d],delete options[d])}c&&(options.ignore=j(c.ignore,options.ignore),options.include=j(c.include,options.include),options.copy=j(c.copy,options.copy));options.ignore=j(options.ignore,g.ignore);options.include=j(options.include,g.include);options.copy=j(options.copy,g.copy);options.mappedProperties=options.mappedProperties||{};return options}function j(a,c){"array"!==e.getType(a)&&(a="undefined"===e.getType(a)?[]:[a]);"array"!==e.getType(c)&&(c="undefined"=== +e.getType(c)?[]:[c]);return d.utils.arrayGetDistinctValues(a.concat(c))}function B(a,c,b,f,h,x){var C="array"===e.getType(d.utils.unwrapObservable(c)),x=x||"";if(e.isMapped(a))var k=d.utils.unwrapObservable(a)[n],b=A(k,b);var g=function(){return b[f]&&b[f].create instanceof Function},o=function(a){var e=D,E=d.dependentObservable;d.dependentObservable=function(a,b,c){c=c||{};a&&"object"==typeof a&&(c=a);var f=c.deferEvaluation,L=!1;c.deferEvaluation=!0;a=new I(a,b,c);if(!f){var h=a,a=I({read:function(){L|| +(d.utils.arrayRemoveItem(e,h),L=!0);return h.apply(h,arguments)},write:h.hasWriteFunction&&function(a){return h(a)},deferEvaluation:!0});e.push(a)}return a};d.dependentObservable.fn=I.fn;d.computed=d.dependentObservable;a=b[f].create({data:a||c,parent:h});d.dependentObservable=E;d.computed=d.dependentObservable;return a},v=function(){return b[f]&&b[f].update instanceof Function},r=function(a,e){var E={data:e||c,parent:h,target:d.utils.unwrapObservable(a)};d.isWriteableObservable(a)&&(E.observable= +a);return b[f].update(E)};if(k=F.get(c))return k;f=f||"";if(C){var C=[],p=!1,i=function(a){return a};b[f]&&b[f].key&&(i=b[f].key,p=!0);d.isObservable(a)||(a=d.observableArray([]),a.mappedRemove=function(b){var c=typeof b=="function"?b:function(a){return a===i(b)};return a.remove(function(a){return c(i(a))})},a.mappedRemoveAll=function(b){var c=y(b,i);return a.remove(function(a){return d.utils.arrayIndexOf(c,i(a))!=-1})},a.mappedDestroy=function(b){var c=typeof b=="function"?b:function(a){return a=== +i(b)};return a.destroy(function(a){return c(i(a))})},a.mappedDestroyAll=function(b){var c=y(b,i);return a.destroy(function(a){return d.utils.arrayIndexOf(c,i(a))!=-1})},a.mappedIndexOf=function(b){var c=y(a(),i),b=i(b);return d.utils.arrayIndexOf(c,b)},a.mappedCreate=function(b){if(a.mappedIndexOf(b)!==-1)throw Error("There already is an object with the key that you specified.");var c=g()?o(b):b;if(v()){b=r(c,b);d.isWriteableObservable(c)?c(b):c=b}a.push(c);return c});var k=y(d.utils.unwrapObservable(a), +i).sort(),l=y(c,i);p&&l.sort();var p=d.utils.compareArrays(k,l),k={},j,w=d.utils.unwrapObservable(c),t={},u=!0,l=0;for(j=w.length;l SupportedExtensions { get; } + + public bool CanProvideThumbnail(string fileUrl) + { + string thumbUrl; + return TryGetThumbnailUrl(fileUrl, out thumbUrl); + } + + public string GetThumbnailUrl(string fileUrl) + { + string thumbUrl; + TryGetThumbnailUrl(fileUrl, out thumbUrl); + return thumbUrl; + } + + protected bool IsSupportedExtension(string ext) + { + return SupportedExtensions.InvariantContains(ext) || + SupportedExtensions.InvariantContains("*"); + } + + protected abstract bool TryGetThumbnailUrl(string fileUrl, out string thumbUrl); + } +} diff --git a/src/Umbraco.Web/Media/ThumbnailProviders/FileExtensionIconThumbnailProvider.cs b/src/Umbraco.Web/Media/ThumbnailProviders/FileExtensionIconThumbnailProvider.cs new file mode 100644 index 0000000000..60a5f2ccfa --- /dev/null +++ b/src/Umbraco.Web/Media/ThumbnailProviders/FileExtensionIconThumbnailProvider.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using umbraco.IO; + +namespace Umbraco.Web.Media.ThumbnailProviders +{ + public class FileExtensionIconThumbnailProvider : AbstractThumbnailProvider + { + public override int Priority + { + get { return 2000; } + } + + protected override IEnumerable SupportedExtensions + { + get { return new List { "*" }; } + } + + protected override bool TryGetThumbnailUrl(string fileUrl, out string thumbUrl) + { + // Set thumbnail url to empty strin initially + thumbUrl = string.Empty; + + // Make sure file has an extension + var ext = Path.GetExtension(fileUrl); + if (string.IsNullOrEmpty(ext)) + return false; + + // Make sure it has a supported file extension + if (!IsSupportedExtension(ext)) + return false; + + // Make sure the thumbnail exists + var tmpThumbUrl = IOHelper.ResolveUrl(SystemDirectories.Umbraco + "/images/mediaThumbnails/"+ ext.TrimStart('.') +".png"); + if (!File.Exists(IOHelper.MapPath(tmpThumbUrl))) + return false; + + // We've got this far, so thumbnail must exist + thumbUrl = tmpThumbUrl; + return true; + } + } +} diff --git a/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs b/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs new file mode 100644 index 0000000000..d401bec66d --- /dev/null +++ b/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Umbraco.Core; +using Umbraco.Core.Interfaces; +using umbraco.IO; + +namespace Umbraco.Web.Media.ThumbnailProviders +{ + public class ImageThumbnailProvider : AbstractThumbnailProvider + { + public override int Priority + { + get { return 1000; } + } + + protected override IEnumerable SupportedExtensions + { + get { return new List { ".jpeg", ".jpg", ".gif", ".bmp", ".png", ".tiff", ".tif" }; } + } + + protected override bool TryGetThumbnailUrl(string fileUrl, out string thumbUrl) + { + // Set thumbnail url to empty strin initially + thumbUrl = string.Empty; + + // Make sure file has an extension + var ext = Path.GetExtension(fileUrl); + if (string.IsNullOrEmpty(ext)) + return false; + + // Make sure it has a supported file extension + if (!IsSupportedExtension(ext)) + return false; + + // Make sure the thumbnail exists + var tmpThumbUrl = fileUrl.Replace(ext, "_thumb.jpg"); + if (!File.Exists(IOHelper.MapPath(tmpThumbUrl))) + return false; + + // We've got this far, so thumbnail must exist + thumbUrl = tmpThumbUrl; + return true; + } + } +} diff --git a/src/Umbraco.Web/Media/ThumbnailProviders/MediaTypeIconThumbnailProvider.cs b/src/Umbraco.Web/Media/ThumbnailProviders/MediaTypeIconThumbnailProvider.cs new file mode 100644 index 0000000000..e4c8b569d7 --- /dev/null +++ b/src/Umbraco.Web/Media/ThumbnailProviders/MediaTypeIconThumbnailProvider.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using umbraco.IO; + +namespace Umbraco.Web.Media.ThumbnailProviders +{ + public class MediaTypeIconThumbnailProvider : AbstractThumbnailProvider + { + public override int Priority + { + get { return 3000; } + } + + protected override IEnumerable SupportedExtensions + { + get { return new List { "*" }; } + } + + protected override bool TryGetThumbnailUrl(string fileUrl, out string thumbUrl) + { + // Set thumbnail url to empty strin initially + thumbUrl = string.Empty; + + // Make sure file has an extension + var ext = Path.GetExtension(fileUrl); + if (string.IsNullOrEmpty(ext)) + return false; + + // Make sure it has a supported file extension + if (!IsSupportedExtension(ext)) + return false; + + // Make sure the thumbnail exists + var tmpThumbUrl = IOHelper.ResolveUrl(SystemDirectories.Umbraco + "/images/mediaThumbnails/"+ ext.TrimStart('.') +".png"); + if (!File.Exists(IOHelper.MapPath(tmpThumbUrl))) + return false; + + // We've got this far, so thumbnail must exist + thumbUrl = tmpThumbUrl; + return true; + } + } +} diff --git a/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderManager.cs b/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderManager.cs new file mode 100644 index 0000000000..a5a3c11bb0 --- /dev/null +++ b/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProviderManager.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using Umbraco.Core.Interfaces; +using umbraco.BusinessLogic; +using umbraco.BusinessLogic.Utils; + +namespace Umbraco.Web.Media.ThumbnailProviders +{ + public class ThumbnailProviderManager + { + private const string CacheKey = "ThumbnailProviderCache"; + + internal static IEnumerable Providers + { + get + { + EnsureCache(); + + return HttpRuntime.Cache[CacheKey] as List; + } + set + { + HttpRuntime.Cache.Insert(CacheKey, value); + } + } + + public static string GetThumbnailUrl(string fileUrl) + { + var provider = Providers.FirstOrDefault(x => x.CanProvideThumbnail(fileUrl)); + return provider != null ? provider.GetThumbnailUrl(fileUrl) : string.Empty; + } + + private static void EnsureCache() + { + if (HttpRuntime.Cache[CacheKey] != null) + return; + + var providers = new List(); + var types = TypeFinder.FindClassesOfType(); + + foreach (var t in types) + { + IThumbnailProvider typeInstance = null; + + try + { + if (t.IsVisible) + { + typeInstance = Activator.CreateInstance(t) as IThumbnailProvider; + } + } + catch { } + + if (typeInstance != null) + { + try + { + providers.Add(typeInstance); + } + catch (Exception ee) + { + Log.Add(LogTypes.Error, -1, "Can't import IThumbnailProvider '" + t.FullName + "': " + ee); + } + } + } + + providers.Sort((f1, f2) => f1.Priority.CompareTo(f2.Priority)); + + Providers = providers; + } + } +} diff --git a/src/Umbraco.Web/UI/Controls/FolderBrowser.cs b/src/Umbraco.Web/UI/Controls/FolderBrowser.cs index d6086e29d0..8c6ea47ec7 100644 --- a/src/Umbraco.Web/UI/Controls/FolderBrowser.cs +++ b/src/Umbraco.Web/UI/Controls/FolderBrowser.cs @@ -5,6 +5,7 @@ using System.Web.UI; using System.Web.UI.WebControls; using ClientDependency.Core; using umbraco.BasePages; +using umbraco.IO; using umbraco.cms.businesslogic.media; namespace Umbraco.Web.UI.Controls @@ -13,7 +14,8 @@ namespace Umbraco.Web.UI.Controls [ClientDependency(ClientDependencyType.Javascript, "ui/jquery.js", "UmbracoClient", Priority = 1)] [ClientDependency(ClientDependencyType.Javascript, "ui/base2.js", "UmbracoClient", Priority = 1)] [ClientDependency(ClientDependencyType.Javascript, "ui/knockout.js", "UmbracoClient", Priority = 2)] - [ClientDependency(ClientDependencyType.Javascript, "FolderBrowser/js/folderbrowser.js", "UmbracoClient", Priority = 3)] + [ClientDependency(ClientDependencyType.Javascript, "ui/knockout.mapping.js", "UmbracoClient", Priority = 3)] + [ClientDependency(ClientDependencyType.Javascript, "FolderBrowser/js/folderbrowser.js", "UmbracoClient", Priority = 10)] [ToolboxData("<{0}:FolderBrowser runat=server>")] public class FolderBrowser : WebControl { @@ -41,11 +43,11 @@ namespace Umbraco.Web.UI.Controls } } - protected Media ParentNode + protected global::umbraco.cms.businesslogic.media.Media ParentNode { get { - return new Media(ParentId); + return new global::umbraco.cms.businesslogic.media.Media(ParentId); } } @@ -76,13 +78,13 @@ namespace Umbraco.Web.UI.Controls var sb = new StringBuilder(); // Create the breadcrumb - var breadCrumb = new List(); + var breadCrumb = new List(); breadCrumb.Add(ParentNode); var parent = ParentNode; while(parent.Id != -1) { - parent = new Media(parent.ParentId); + parent = new global::umbraco.cms.businesslogic.media.Media(parent.ParentId); breadCrumb.Add(parent); } @@ -104,14 +106,24 @@ namespace Umbraco.Web.UI.Controls } sb.Append(""); + // Create the filter input + sb.Append("
Filter:
"); + // Create thumbnails container sb.Append("
    " + - "
  • " + + "
  • " + "
"); panel.Controls.Add(new LiteralControl(sb.ToString())); Controls.Add(panel); + + Page.ClientScript.RegisterStartupScript(typeof(FolderBrowser), + "RegisterFolderBrowsers", + string.Format("$(function () {{ $(\".umbFolderBrowser\").folderBrowser({{ umbracoPath : '{0}', basePath : '{1}' }}); }});", + IOHelper.ResolveUrl(SystemDirectories.Umbraco), + IOHelper.ResolveUrl(SystemDirectories.Base)), + true); } protected override void Render(HtmlTextWriter writer) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 389093e44c..981c0bd522 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -198,6 +198,10 @@ {5BA5425F-27A7-4677-865E-82246498AA2E} SqlCE4Umbraco + + {31785BC3-256C-4613-B2F5-A1B0BDDED8C1} + Umbraco.Core + {52AB8F1F-FB76-4E8C-885F-0747B6CE71EC} umbraco.macroRenderings @@ -233,6 +237,11 @@ Properties\SolutionInfo.cs + + + + + @@ -1744,6 +1753,7 @@ True Reference.map + @@ -1946,7 +1956,9 @@ - + + ASPXCodeBehind + diff --git a/src/Umbraco.Web/WebServices/FolderBrowserService.cs b/src/Umbraco.Web/WebServices/FolderBrowserService.cs new file mode 100644 index 0000000000..e03b5f2a73 --- /dev/null +++ b/src/Umbraco.Web/WebServices/FolderBrowserService.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web.Script.Serialization; +using Umbraco.Core; +using Umbraco.Web.Media.ThumbnailProviders; +using umbraco.BasePages; +using umbraco.BusinessLogic; +using umbraco.IO; +using umbraco.cms.businesslogic.Tags; +using umbraco.cms.businesslogic.media; +using umbraco.presentation.umbracobase; + +namespace Umbraco.Web.WebServices +{ + [RestExtension("FolderBrowserService")] + public class FolderBrowserService + { + //[RestExtensionMethod(returnXml = false)] + //public static string GetChildNodes(int parentId) + //{ + // return GetChildNodes(parentId, ""); + //} + + [RestExtensionMethod(returnXml = false)] + public static string GetChildNodes(int parentId, string filterTerm) + { + var parentMedia = new global::umbraco.cms.businesslogic.media.Media(parentId); + var currentUser = User.GetCurrent(); + var data = new List(); + + // Check user is logged in + if (currentUser == null) + throw new UnauthorizedAccessException("You must be logged in to use this service"); + + // Check user is allowed to access selected media item + if(!("," + parentMedia.Path + ",").Contains("," + currentUser.StartMediaId + ",")) + throw new UnauthorizedAccessException("You do not have access to this Media node"); + + // Get children and filter + //TODO: Only fetch files, not containers + //TODO: Cache responses to speed up susequent searches + foreach (var child in parentMedia.Children.Where(x => string.IsNullOrEmpty(filterTerm) || + x.Text.InvariantContains(filterTerm) || + Tag.GetTags(x.Id).Any(y => y.TagCaption.InvariantContains(filterTerm)))) + { + var fileProp = child.getProperty("umbracoFile") ?? + child.GenericProperties.FirstOrDefault(x => + x.PropertyType.DataTypeDefinition.DataType.Id == new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c")); + + var fileUrl = fileProp != null ? fileProp.Value.ToString() : ""; + var thumbUrl = ThumbnailProviderManager.GetThumbnailUrl(fileUrl); + + data.Add(new + { + Id = child.Id, + Name = child.Text, + MediaTypeAlias = child.ContentType.Alias, + FileUrl = fileUrl, + ThumbnailUrl = !string.IsNullOrEmpty(thumbUrl) + ? thumbUrl + : IOHelper.ResolveUrl(SystemDirectories.Umbraco + "/images/thumbnails/" + child.ContentType.Thumbnail) + }); + } + + return new JavaScriptSerializer().Serialize(data); + } + } +}