#20542 Collapsible media library tree in admin.

Dynamically loading MediaLibrary directory tree using ajax and knockout templates
This commit is contained in:
Benjamin Grabkowitz
2014-03-07 17:07:20 -05:00
committed by Sebastien Ros
parent ba09ba8053
commit 5d2a31ffc0
8 changed files with 798 additions and 683 deletions

View File

@@ -48,7 +48,6 @@ namespace Orchard.MediaLibrary.Controllers {
var viewModel = new MediaManagerIndexViewModel { var viewModel = new MediaManagerIndexViewModel {
DialogMode = dialog, DialogMode = dialog,
Folders = _mediaLibraryService.GetMediaFolders(null).Select(GetFolderHierarchy),
FolderPath = folderPath, FolderPath = folderPath,
MediaTypes = _mediaLibraryService.GetMediaTypes(), MediaTypes = _mediaLibraryService.GetMediaTypes(),
CustomActionsShapes = explorerShape.Actions, CustomActionsShapes = explorerShape.Actions,
@@ -99,6 +98,20 @@ namespace Orchard.MediaLibrary.Controllers {
return View(viewModel); return View(viewModel);
} }
[Themed(false)]
public ActionResult ChildFolders(string folderPath = null) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Cannot get child folder listing")))
return new HttpUnauthorizedResult();
var childFolders = _mediaLibraryService.GetMediaFolders(folderPath);
var viewModel = new MediaManagerChildFoldersViewModel {
Children = childFolders
};
return View(viewModel);
}
[Themed(false)] [Themed(false)]
public ActionResult RecentMediaItems(int skip = 0, int count = 0, string order = "created", string mediaType = "") { public ActionResult RecentMediaItems(int skip = 0, int count = 0, string order = "created", string mediaType = "") {
var mediaParts = _mediaLibraryService.GetMediaContentItems(skip, count, order, mediaType, VersionOptions.Latest); var mediaParts = _mediaLibraryService.GetMediaContentItems(skip, count, order, mediaType, VersionOptions.Latest);

View File

@@ -172,6 +172,7 @@
<Compile Include="Settings\MediaLibraryPickerFieldEditorEvents.cs" /> <Compile Include="Settings\MediaLibraryPickerFieldEditorEvents.cs" />
<Compile Include="Settings\MediaLibraryPickerFieldSettings.cs" /> <Compile Include="Settings\MediaLibraryPickerFieldSettings.cs" />
<Compile Include="Tokens\FieldTokens.cs" /> <Compile Include="Tokens\FieldTokens.cs" />
<Compile Include="ViewModels\MediaManagerChildFoldersViewModel.cs" />
<Compile Include="ViewModels\ImportMediaViewModel.cs" /> <Compile Include="ViewModels\ImportMediaViewModel.cs" />
<Compile Include="ViewModels\MediaLibraryPickerFieldViewModel.cs" /> <Compile Include="ViewModels\MediaLibraryPickerFieldViewModel.cs" />
<Compile Include="ViewModels\MediaManagerMediaItemsViewModel.cs" /> <Compile Include="ViewModels\MediaManagerMediaItemsViewModel.cs" />
@@ -184,9 +185,6 @@
<ItemGroup> <ItemGroup>
<Content Include="Views\Admin\Index.cshtml" /> <Content Include="Views\Admin\Index.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Views\Admin\MediaManagerFolder.cshtml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Views\Media.cshtml" /> <Content Include="Views\Media.cshtml" />
</ItemGroup> </ItemGroup>
@@ -345,6 +343,9 @@
<ItemGroup> <ItemGroup>
<Content Include="Views\Fields\MediaLibraryPicker.Summary.cshtml" /> <Content Include="Views\Fields\MediaLibraryPicker.Summary.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Views\Admin\ChildFolders.cshtml" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -81,6 +81,28 @@ $(function () {
self.orderMedia = ko.observableArray(['created']); self.orderMedia = ko.observableArray(['created']);
self.mediaType = ko.observableArray([]); self.mediaType = ko.observableArray([]);
self.mediaFolders = ko.observableArray([]);
(function () {
var getChildFolderListUrl = function (f) {
return settings.childFolderListingActionUrl + '?folderPath=' + encodeURIComponent(f);
};
var url = getChildFolderListUrl(settings.folderPath);
$.ajax({
type: "GET",
url: url,
cache: false
}).done(function (data) {
var childFolders = data.childFolders;
for (var x = 0; x < childFolders.length; x++) {
self.mediaFolders.push(new mediaFolderViewModel(childFolders[x]));
}
}).fail(function (data) {
console.error(data);
});
})();
self.getMediaItems = function (count, append) { self.getMediaItems = function (count, append) {
var folderPath = self.displayed() || ''; var folderPath = self.displayed() || '';
@@ -164,7 +186,6 @@ $(function () {
}); });
self.displayFolder = function(folderPath) { self.displayFolder = function(folderPath) {
self.results([]); self.results([]);
self.displayed(folderPath); self.displayed(folderPath);
@@ -175,7 +196,7 @@ $(function () {
self.getMediaItems(pageCount); self.getMediaItems(pageCount);
}; };
self.selectFolder = function(folderPath) { self.selectFolder = function (folderPath) {
History.pushState({ action: 'displayFolder', folderPath: folderPath }, '', '?folderPath=' + folderPath); History.pushState({ action: 'displayFolder', folderPath: folderPath }, '', '?folderPath=' + folderPath);
self.displayFolder(folderPath); self.displayFolder(folderPath);
}; };
@@ -242,6 +263,73 @@ $(function () {
var viewModel = new mediaIndexViewModel(); var viewModel = new mediaIndexViewModel();
function mediaFolderViewModel(data) {
var self = this;
self.mediaIndexViewModel = viewModel;
self.folderPath = ko.observable(data.folderPath);
self.name = ko.observable(data.name);
self.childFolders = ko.observableArray([]);
self.childFoldersFetchStatus = 0; //0 = unfetched, 1 = fetching, 2 = fetched
self.isExpanded = ko.observable(false);
self.isVisible = ko.observable(true);
self.isSelected = ko.computed(function() {
return (self.mediaIndexViewModel.displayed() == self.folderPath());
});
self.folderClicked = function () {
self.mediaIndexViewModel.selectFolder(self.folderPath());
var childFolders = self.childFolders();
if (self.isExpanded()) {
for (var x = 0; x < childFolders.length; x++) {
childFolders[x].isVisible(false);
}
self.isExpanded(false);
} else {
if (self.childFoldersFetchStatus !== 0) {
for (var x = 0; x < childFolders.length; x++) {
childFolders[x].isVisible(true);
}
} else {
self.childFoldersFetchStatus = 1;
var getChildFolderListUrl = function (f) {
return settings.childFolderListingActionUrl + '?folderPath=' + encodeURIComponent(f);
};
var url = getChildFolderListUrl(self.folderPath());
$.ajax({
type: "GET",
url: url,
cache: false
}).done(function (data) {
var newChildFolders = data.childFolders;
for (var y = 0; y < newChildFolders.length; y++) {
var newChildFolder = new mediaFolderViewModel(newChildFolders[y]);
newChildFolder.isVisible(true);
self.childFolders.push(newChildFolder);
}
self.childFoldersFetchStatus = 2;
}).fail(function (data) {
console.error(data);
self.childFoldersFetchStatus = 0;
});
}
self.isExpanded(true);
}
};
}
enhanceViewModel(viewModel); enhanceViewModel(viewModel);
ko.applyBindings(viewModel); ko.applyBindings(viewModel);

View File

@@ -0,0 +1,8 @@
using System.Collections.Generic;
using Orchard.MediaLibrary.Models;
namespace Orchard.MediaLibrary.ViewModels {
public class MediaManagerChildFoldersViewModel {
public IEnumerable<MediaFolder> Children { get; set; }
}
}

View File

@@ -4,7 +4,6 @@ using Orchard.MediaLibrary.Models;
namespace Orchard.MediaLibrary.ViewModels { namespace Orchard.MediaLibrary.ViewModels {
public class MediaManagerIndexViewModel { public class MediaManagerIndexViewModel {
public IEnumerable<FolderHierarchy> Folders { get; set; }
public string FolderPath { get; set; } public string FolderPath { get; set; }
public bool DialogMode { get; set; } public bool DialogMode { get; set; }
public IEnumerable<ContentTypeDefinition> MediaTypes { get; set; } public IEnumerable<ContentTypeDefinition> MediaTypes { get; set; }

View File

@@ -0,0 +1,15 @@
@using Orchard.Utility.Extensions
@model Orchard.MediaLibrary.ViewModels.MediaManagerChildFoldersViewModel
@{
Response.ContentType = "text/json";
@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(
new {
childFolders = Model.Children.Select(x => new {
folderPath = x.MediaPath,
name = x.Name,
lastUpdated = x.LastUpdated
}).ToArray()
}))
}

View File

@@ -30,12 +30,7 @@
@Display(Model.CustomNavigationShapes) @Display(Model.CustomNavigationShapes)
<li id="media-library-main-navigation-tree"> <li id="media-library-main-navigation-tree">
<ul> <ul data-bind="template: { name: 'media-folder-template', foreach: mediaFolders }">
@foreach (var folder in viewModel.Folders) {
<li>
@Display.Partial(TemplateName: "MediaManagerFolder", Model: folder)
</li>
}
</ul> </ul>
</li> </li>
</ul> </ul>
@@ -95,6 +90,7 @@ var mediaLibrarySettings = {
mediaItemActionUrl: '@HttpUtility.JavaScriptStringEncode(Url.Action("MediaItem", "Admin"))', mediaItemActionUrl: '@HttpUtility.JavaScriptStringEncode(Url.Action("MediaItem", "Admin"))',
mediaItemsActionUrl: '@HttpUtility.JavaScriptStringEncode(Url.Action("MediaItems", "Admin"))', mediaItemsActionUrl: '@HttpUtility.JavaScriptStringEncode(Url.Action("MediaItems", "Admin"))',
recentMediaItemsActionUrl: '@HttpUtility.JavaScriptStringEncode(Url.Action("RecentMediaItems", "Admin"))', recentMediaItemsActionUrl: '@HttpUtility.JavaScriptStringEncode(Url.Action("RecentMediaItems", "Admin"))',
childFolderListingActionUrl: '@HttpUtility.JavaScriptStringEncode(Url.Action("ChildFolders", "Admin"))',
importActionUrl: '@HttpUtility.JavaScriptStringEncode(Url.Action("Import", "Admin"))', importActionUrl: '@HttpUtility.JavaScriptStringEncode(Url.Action("Import", "Admin"))',
moveActionUrl: '@Url.Action("Move", "Folder", new {area = "Orchard.MediaLibrary"})', moveActionUrl: '@Url.Action("Move", "Folder", new {area = "Orchard.MediaLibrary"})',
deleteActionUrl: '@Url.Action("Delete", "Admin", new {area = "Orchard.MediaLibrary"})', deleteActionUrl: '@Url.Action("Delete", "Admin", new {area = "Orchard.MediaLibrary"})',
@@ -106,4 +102,15 @@ var mediaLibrarySettings = {
}; };
//]]> //]]>
</script> </script>
<script type="text/html" id="media-folder-template">
<li data-bind="visible: isVisible()">
<div class="media-library-folder">
<div class="media-library-folder-title" data-bind="click: folderClicked, css: { selected: isSelected() }">
<a data-bind="disable: $root.pendingRequest" href="#" class="media-library-navigation-folder-link"><i data-bind="css: {'icon-folder-open-alt': isExpanded(), 'icon-folder-close-alt': !isExpanded()}"></i><span data-bind="text: name"></span></a>
</div>
<ul data-bind="template: { name: 'media-folder-template', foreach: childFolders }, visible: childFolders().length > 0">
</ul>
</div>
</li>
</script>
} }

View File

@@ -1,16 +0,0 @@
@model Orchard.MediaLibrary.Models.FolderHierarchy
<div class="media-library-folder">
<div class="media-library-folder-title" data-bind="click: function(data, event) { selectFolder('@HttpUtility.JavaScriptStringEncode(Model.Root.MediaPath)'); }, css: { selected: displayed() == $element.getAttribute('data-media-path') }" data-media-path="@Model.Root.MediaPath" >
<a data-bind="disable: $root.pendingRequest" href="#" class="media-library-navigation-folder-link" data-mediapath="@Model.Root.MediaPath"><i class="icon-folder-close-alt"></i>@Model.Root.Name</a>
</div>
@if (Model.Children.Any()) {
<ul>
@foreach (var folder in Model.Children) {
<li>
@Display.Partial(TemplateName: "MediaManagerFolder", Model: folder)
</li>
}
</ul>
}
</div>