Implementing media search

This commit is contained in:
Sebastien Ros
2013-09-23 09:52:06 -07:00
parent 790cb94d5b
commit d920b164e0
25 changed files with 383 additions and 93 deletions

View File

@@ -483,6 +483,22 @@ namespace Orchard.Tests.Modules.Indexing {
Assert.That(SearchBuilder.Parse("body", "*@!woo*@!").Count(), Is.EqualTo(0));
}
[Test]
public void ShouldSearchFolderStructure() {
_provider.CreateIndex("default");
_provider.Store("default", _provider.New(1).Add("media-path", "images").Store());
_provider.Store("default", _provider.New(2).Add("media-path", "images/pets/puppies").Store());
_provider.Store("default", _provider.New(3).Add("media-path", "images/pets/kitties").Store());
// Assert.That(SearchBuilder.WithField("media-path", "Pets").Mandatory().Count(), Is.EqualTo(0));
Assert.That(SearchBuilder.WithField("media-path", "Images").Mandatory().Count(), Is.EqualTo(3));
Assert.That(SearchBuilder.WithField("media-path", "Images").ExactMatch().Mandatory().Count(), Is.EqualTo(1));
Assert.That(SearchBuilder.WithField("media-path", "Images/Pets").Mandatory().Count(), Is.EqualTo(2));
Assert.That(SearchBuilder.WithField("media-path", "Images/Pets").ExactMatch().Mandatory().Count(), Is.EqualTo(0));
Assert.That(SearchBuilder.WithField("media-path", "Images/Pets/Puppies").ExactMatch().Mandatory().Count(), Is.EqualTo(1));
Assert.That(SearchBuilder.WithField("media-path", "Images/Pets/Puppies").Mandatory().Count(), Is.EqualTo(1));
}
[Test]
public void FieldsCanContainMultipleValue() {
_provider.CreateIndex("default");

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Lucene.Models;
@@ -23,7 +24,6 @@ namespace Lucene.Services {
/// </summary>
public class LuceneIndexProvider : IIndexProvider {
private readonly IAppDataFolder _appDataFolder;
private readonly ShellSettings _shellSettings;
public static readonly Version LuceneVersion = Version.LUCENE_29;
private readonly Analyzer _analyzer ;
private readonly string _basePath;
@@ -31,11 +31,10 @@ namespace Lucene.Services {
public LuceneIndexProvider(IAppDataFolder appDataFolder, ShellSettings shellSettings) {
_appDataFolder = appDataFolder;
_shellSettings = shellSettings;
_analyzer = CreateAnalyzer();
// TODO: (sebros) Find a common way to get where tenant's specific files should go. "Sites/Tenant" is hard coded in multiple places
_basePath = _appDataFolder.Combine("Sites", _shellSettings.Name, "Indexes");
_basePath = _appDataFolder.Combine("Sites", shellSettings.Name, "Indexes");
Logger = NullLogger.Instance;
@@ -152,13 +151,13 @@ namespace Lucene.Services {
if (!documentIds.Any()) {
return;
}
using(var writer = new IndexWriter(GetDirectory(indexName), _analyzer, false, IndexWriter.MaxFieldLength.UNLIMITED)) {
var query = new BooleanQuery();
try {
foreach (var id in documentIds) {
query.Add(new BooleanClause(new TermQuery(new Term("id", id.ToString())), Occur.SHOULD));
query.Add(new BooleanClause(new TermQuery(new Term("id", id.ToString(CultureInfo.InvariantCulture))), Occur.SHOULD));
}
writer.DeleteDocuments(query);

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Lucene.Models;
using Lucene.Net.Analysis;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
@@ -33,6 +34,7 @@ namespace Lucene.Services {
private bool _exactMatch;
private float _boost;
private Query _query;
private readonly Analyzer _analyzer = LuceneIndexProvider.CreateAnalyzer();
public ILogger Logger { get; set; }
@@ -68,10 +70,9 @@ namespace Lucene.Services {
query = QueryParser.Escape(query);
}
var analyzer = LuceneIndexProvider.CreateAnalyzer();
foreach (var defaultField in defaultFields) {
CreatePendingClause();
_query = new QueryParser(LuceneIndexProvider.LuceneVersion, defaultField, analyzer).Parse(query);
_query = new QueryParser(LuceneIndexProvider.LuceneVersion, defaultField, _analyzer).Parse(query);
}
return this;
@@ -159,8 +160,6 @@ namespace Lucene.Services {
_query = null;
_boost = 0;
_asFilter = false;
_sort = String.Empty;
_comparer = 0;
}
private void CreatePendingClause() {
@@ -189,7 +188,7 @@ namespace Lucene.Services {
_clauses.Add(new BooleanClause(_query, _occur));
}
_query = null;
InitPendingClause();
}
public ISearchBuilder SortBy(string name) {

View File

@@ -25,8 +25,7 @@ namespace Orchard.Localization.Handlers {
OnVersioning<LocalizationPart>((context, part, versionedPart) => LazyLoadHandlers(versionedPart));
OnIndexed<LocalizationPart>((context, localized) => context.DocumentIndex
.Add("culture", CultureInfo.GetCultureInfo(localized.Culture != null ? localized.Culture.Culture : _cultureManager.GetSiteCulture()).LCID)
.Store()
.Add("culture", CultureInfo.GetCultureInfo(localized.Culture != null ? localized.Culture.Culture : _cultureManager.GetSiteCulture()).LCID).Store()
);
}

View File

@@ -48,13 +48,29 @@ namespace Orchard.MediaLibrary.Controllers {
mediaTypes.Add(contentTypeDefinition.Name);
}
// let other modules enhance the ui by providing custom navigation and actions
var explorer = Services.ContentManager.New("MediaLibraryExplorer");
explorer.Weld(new MediaLibraryExplorerPart());
var explorerShape = Services.ContentManager.BuildDisplay(explorer);
var viewModel = new MediaManagerIndexViewModel {
DialogMode = dialog,
Folders = _mediaLibraryService.GetMediaFolders(null).Select(GetFolderHierarchy),
FolderPath = folderPath,
MediaTypes = mediaTypes.ToArray()
MediaTypes = mediaTypes.ToArray(),
CustomActionsShapes = explorerShape.Actions,
CustomNavigationShapes = explorerShape.Navigation,
};
foreach (var shape in explorerShape.Actions.Items) {
shape.MediaManagerIndexViewModel = viewModel;
}
foreach (var shape in explorerShape.Navigation.Items) {
shape.MediaManagerIndexViewModel = viewModel;
}
return View(viewModel);
}

View File

@@ -0,0 +1,13 @@
using Orchard.ContentManagement.Drivers;
using Orchard.MediaLibrary.Models;
namespace Orchard.MediaLibrary.Drivers {
public class MediaLibraryExplorerPartDriver : ContentPartDriver<MediaLibraryExplorerPart> {
protected override DriverResult Display(MediaLibraryExplorerPart part, string displayType, dynamic shapeHelper) {
return Combined(
ContentShape("Parts_MediaLibrary_Navigation", () => shapeHelper.Parts_MediaLibrary_Navigation()),
ContentShape("Parts_MediaLibrary_Actions", () => shapeHelper.Parts_MediaLibrary_Actions())
);
}
}
}

View File

@@ -24,6 +24,41 @@ namespace Orchard.MediaLibrary.Handlers {
part._publicUrl.Loader(x => _mediaLibraryService.GetMediaPublicUrl(part.FolderPath, part.FileName));
}
});
OnIndexing<MediaPart>((context, part) =>
context.DocumentIndex
.Add("media-folderpath", Normalize(part.FolderPath)).Store()
.Add("media-filename", Normalize(part.FileName)).Store()
.Add("media-mimetype", Normalize(part.MimeType)).Store()
.Add("media-caption", part.Caption).Analyze()
.Add("media-alternatetext", part.AlternateText).Analyze()
);
OnIndexing<ImagePart>((context, part) =>
context.DocumentIndex
.Add("image-height", part.Height).Analyze().Store()
.Add("image-width", part.Width).Analyze().Store()
);
OnIndexing<DocumentPart>((context, part) =>
context.DocumentIndex
.Add("document-length", part.Length).Analyze().Store()
);
OnIndexing<VideoPart>((context, part) =>
context.DocumentIndex
.Add("video-length", part.Length).Analyze().Store()
);
OnIndexing<AudioPart>((context, part) =>
context.DocumentIndex
.Add("audio-length", part.Length).Analyze().Store()
);
OnIndexing<OEmbedPart>((context, part) =>
context.DocumentIndex
.Add("oembed-source", part.Source).Analyze().Store()
);
}
protected void RemoveMedia(MediaPart part) {
@@ -32,5 +67,10 @@ namespace Orchard.MediaLibrary.Handlers {
_storageProvider.DeleteFile(path);
}
}
private string Normalize(string text) {
// when not indexed with Analyze() searches are case sensitive
return text.Replace("\\", "/").ToLowerInvariant();
}
}
}

View File

@@ -0,0 +1,11 @@
using Orchard.ContentManagement;
namespace Orchard.MediaLibrary.Models {
/// <summary>
/// Provides an extension point to add custom shapes to the
/// Navigation and Actions zones of the Media Library explorer
/// </summary>
public class MediaLibraryExplorerPart : ContentPart {
}
}

View File

@@ -116,6 +116,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Drivers\MediaLibraryExplorerPartDriver.cs" />
<Compile Include="ModalAdminFilter.cs" />
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" />
@@ -146,6 +147,7 @@
<Compile Include="Models\FolderHierarchy.cs" />
<Compile Include="Models\MediaFile.cs" />
<Compile Include="Models\MediaFolder.cs" />
<Compile Include="Models\MediaLibraryExplorerPart.cs" />
<Compile Include="Models\WebSearchSettingsPart.cs" />
<Compile Include="Models\ImagePart.cs" />
<Compile Include="Models\DocumentPart.cs" />
@@ -328,6 +330,12 @@
<ItemGroup>
<Content Include="Views\Media.Summary.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Parts\MediaLibrary.Actions.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Parts\MediaLibrary.Navigation.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -106,4 +106,11 @@
<Place Parts_OEmbed="Content:5" />
</Match>
<!-- Explorer -->
<Place
Parts_MediaLibrary_Actions="Actions:5"
Parts_MediaLibrary_Navigation="Navigation:5"
/>
</Placement>

View File

@@ -1,4 +1,12 @@
$(function () {
var enhanceViewModel = function(viewModel) {
// extension point for other modules to alter the view model
};
var baseViewModel = function() {
};
$(function () {
(function (settings) {
function mediaPartViewModel(data) {
var self = this;
@@ -44,6 +52,10 @@
function mediaIndexViewModel() {
var self = this;
// placeholder function called to retrieve content when scrolling
self.loadMediaItemsUrl = function (folderPath, skip, count, order, mediaType) {
};
// values
self.selection = ko.observableArray([]);
self.focus = ko.observable();
@@ -54,21 +66,26 @@
self.orderMedia = ko.observableArray(['created']);
self.mediaType = ko.observableArray([]);
self.getMediaItems = function(folderPath, max) {
self.getMediaItems = function (count, append) {
var folderPath = self.displayed() || '';
if (self.pendingRequest()) {
return;
}
if (self.results().length > 0 && self.results().length >= self.mediaItemsCount) {
return;
if (append) {
if (self.results().length > 0 && self.results().length >= self.mediaItemsCount) {
return;
}
} else {
self.results([]);
}
self.pendingRequest(true);
var url = folderPath
? settings.mediaItemsActionUrl + '?folderPath=' + encodeURIComponent(folderPath) + '&skip=' + self.results().length + '&count=' + max + '&order=' + self.orderMedia() + '&mediaType=' + self.mediaType()
: settings.recentMediaItemsActionUrl + '?skip=' + self.results().length + '&count=' + max + '&order=' + self.orderMedia() + '&mediaType=' + self.mediaType();
var url = self.loadMediaItemsUrl(folderPath, self.results().length, count, self.orderMedia(), self.mediaType());
console.log(url);
$.ajax({
type: "GET",
url: url,
@@ -133,9 +150,13 @@
self.displayFolder = function(folderPath) {
self.results([]);
self.getMediaItems(folderPath, 20);
self.displayed(folderPath);
self.loadMediaItemsUrl = function (f, skip, count, order, mediaType) {
return settings.mediaItemsActionUrl + '?folderPath=' + encodeURIComponent(f) + '&skip=' + skip + '&count=' + count + '&order=' + order + '&mediaType=' + mediaType;
};
self.getMediaItems(20);
};
self.selectFolder = function(folderPath) {
@@ -146,46 +167,13 @@
self.selectRecent = function() {
History.pushState({ action: 'selectRecent' }, '', '?recent');
self.loadMediaItemsUrl = function (folderPath, skip, count, order, mediaType) {
return settings.recentMediaItemsActionUrl + '?skip=' + skip + '&count=' + count + '&order=' + order + '&mediaType=' + mediaType;
};
self.results([]);
self.displayed(null);
var max = 20;
if (self.pendingRequest()) {
return;
}
if (self.results().length > 0 && self.results().length >= self.mediaItemsCount) {
console.log('no more content, mediaItemsCount: ' + self.mediaItemsCount);
return;
}
self.pendingRequest(true);
var url = settings.recentMediaItemsActionUrl + '?skip=' + self.results().length + '&count=' + max + '&order=' + self.orderMedia() + '&mediaType=' + self.mediaType();
$.ajax({
type: "GET",
url: url,
}).done(function(data) {
var mediaItems = data.mediaItems;
self.mediaItemsCount = data.mediaItemsCount;
for (var i = 0; i < mediaItems.length; i++) {
var item = new mediaPartViewModel(mediaItems[i]);
self.results.push(item);
// pre-select result which are already part of the selection
var selection = self.selection();
for (var j = 0; j < selection.length; j++) {
if (selection[j].data.id == item.data.id) {
viewModel.toggleSelect(item, true);
}
}
}
}).fail(function(data) {
console.error(data);
}).always(function() {
self.pendingRequest(false);
});
self.getMediaItems(20);
};
self.toggleSelect = function(searchResult, force) {
@@ -216,12 +204,7 @@
self.scrolled = function(data, event) {
var elem = event.target;
if (elem.scrollTop > (elem.scrollHeight - elem.offsetHeight - 300)) {
if (self.displayed()) {
self.getMediaItems(self.displayed(), 20);
} else {
self.getMediaItems(null, 20);
}
self.getMediaItems(20);
}
};
@@ -242,6 +225,9 @@
}
var viewModel = new mediaIndexViewModel();
enhanceViewModel(viewModel);
ko.applyBindings(viewModel);
if (settings.hasFolderPath) {

View File

@@ -7,5 +7,7 @@ namespace Orchard.MediaLibrary.ViewModels {
public string FolderPath { get; set; }
public bool DialogMode { get; set; }
public string[] MediaTypes { get; set; }
public dynamic CustomActionsShapes { get; set; }
public dynamic CustomNavigationShapes { get; set; }
}
}

View File

@@ -20,31 +20,15 @@
<a href="#" data-bind="visible: displayed(), click: importMedia" class="button" id="button-import">@T("Import")</a>
<a href="#" data-bind="visible: displayed(), attr: { href: '@HttpUtility.JavaScriptStringEncode(Url.Action("Edit", "Folder", new { area = "Orchard.MediaLibrary"}))?folderPath=' + encodeURIComponent(displayed()) }" class="button" id="button-edit-folder">@T("Edit Folder")</a>
<a href="#" data-bind="attr: { href: '@HttpUtility.JavaScriptStringEncode(Url.Action("Create", "Folder", new { area = "Orchard.MediaLibrary"}))?folderPath=' + encodeURIComponent(displayed() ? displayed() : '') }" class="button" id="button-create-folder">@T("Create Folder")</a>
<label for="filterMediaType">@T("Show")</label>
<select id="filterMediaType" name="FilteredMediaType" data-bind="selectedOptions: mediaType">
@Html.SelectOption("", true, T("Any (show all)").ToString())
@foreach(var mediaType in Model.MediaTypes) {
@Html.SelectOption(mediaType, false, mediaType)
}
</select>
<label for="orderMedia">@T("Ordered by")</label>
<select data-bind="selectedOptions: orderMedia" id="orderMedia" name="OrderMedia">
@Html.SelectOption("title", false, T("title").ToString())
@Html.SelectOption("created", true, T("recently created").ToString())
@Html.SelectOption("published", false, T("recently published").ToString())
@Html.SelectOption("modified", false, T("recently modified").ToString())
</select>
<div id="media-library-toolbar-actions">
</div>
@Display(Model.CustomActionsShapes)
</div>
<div id="media-library-main">
<div id="media-library-main-navigation">
<ul>
<li><a href="#" data-bind="click: selectRecent, css: { selected: !displayed() }" id="button-recent"><i class="icon-time"></i>@T("Recent")</a>
@Display(Model.CustomNavigationShapes)
<li id="media-library-main-navigation-tree">
<ul>
@foreach (var folder in viewModel.Folders) {

View File

@@ -0,0 +1,21 @@
@using Orchard.MediaLibrary.ViewModels
@{
MediaManagerIndexViewModel viewModel = Model.MediaManagerIndexViewModel;
}
<label for="filterMediaType">@T("Show")</label>
<select id="filterMediaType" name="FilteredMediaType" data-bind="selectedOptions: mediaType">
@Html.SelectOption("", true, T("Any (show all)").ToString())
@foreach (var mediaType in viewModel.MediaTypes) {
@Html.SelectOption(mediaType, false, mediaType)
}
</select>
<label for="orderMedia">@T("Ordered by")</label>
<select data-bind="selectedOptions: orderMedia" id="orderMedia" name="OrderMedia">
@Html.SelectOption("title", false, T("title").ToString())
@Html.SelectOption("created", true, T("recently created").ToString())
@Html.SelectOption("published", false, T("recently published").ToString())
@Html.SelectOption("modified", false, T("recently modified").ToString())
</select>

View File

@@ -0,0 +1,6 @@
@using Orchard.MediaLibrary.ViewModels
@{
MediaManagerIndexViewModel viewModel = Model.MediaManagerIndexViewModel;
}
<li><a href="#" data-bind="click: selectRecent, css: { selected: !displayed() }" id="button-recent"><i class="icon-time"></i>@T("Recent")</a>

View File

@@ -0,0 +1,68 @@
using System;
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.Environment.Extensions;
using Orchard.Indexing;
using Orchard.MediaLibrary.Models;
using Orchard.MediaLibrary.ViewModels;
using Orchard.Themes;
using Orchard.UI.Admin;
namespace Orchard.Search.Controllers {
[Admin]
[Themed(false)]
[OrchardFeature("Orchard.Search.MediaLibrary")]
public class MediaController : Controller {
private readonly IIndexProvider _indexProvider;
private readonly IContentManager _contentManager;
public MediaController(IIndexProvider indexProvider, IContentManager contentManager) {
_indexProvider = indexProvider;
_contentManager = contentManager;
}
public ActionResult MediaItems(string folderPath, int skip = 0, int count = 0, string order = "created", string mediaType = "", string search = "") {
var builder = _indexProvider.CreateSearchBuilder("Media");
if (!String.IsNullOrEmpty(search)) {
builder.WithField("title", search);
builder.WithField("author", search);
builder.WithField("media-caption", search);
builder.WithField("media-alternatetext", search);
builder.WithField("media-filename", search);
}
if (!String.IsNullOrEmpty(mediaType)) {
builder.WithField("type", mediaType).Mandatory().AsFilter();
}
if (!String.IsNullOrEmpty(folderPath)) {
var path = folderPath.Replace("\\", "/");
builder.WithField("media-folderpath", path.ToLowerInvariant()).AsFilter();
}
builder.SortBy(order);
if (order == "title") {
builder.Ascending();
}
var mediaPartsCount = builder.Count();
var contentItemIds = builder.Slice(skip, count).Search().Select(x => x.ContentItemId).ToArray();
var mediaParts = _contentManager.GetMany<MediaPart>(contentItemIds, VersionOptions.Published, QueryHints.Empty);
var mediaItems = mediaParts.Select(x => new MediaManagerMediaItemViewModel {
MediaPart = x,
Shape = _contentManager.BuildDisplay(x, "Thumbnail")
}).ToList();
var viewModel = new MediaManagerMediaItemsViewModel {
MediaItems = mediaItems,
MediaItemsCount = mediaPartsCount
};
return View(viewModel);
}
}
}

View File

@@ -0,0 +1,15 @@
using Orchard.ContentManagement.Drivers;
using Orchard.Environment.Extensions;
using Orchard.MediaLibrary.Models;
namespace Orchard.Search.Drivers {
[OrchardFeature("Orchard.Search.MediaLibrary")]
public class MediaLibraryExplorerPartDriver : ContentPartDriver<MediaLibraryExplorerPart> {
protected override DriverResult Display(MediaLibraryExplorerPart part, string displayType, dynamic shapeHelper) {
return Combined(
ContentShape("Parts_Search_MediaLibrary_Navigation", () => shapeHelper.Parts_Search_MediaLibrary_Navigation()),
ContentShape("Parts_Search_MediaLibrary_Actions", () => shapeHelper.Parts_Search_MediaLibrary_Actions())
);
}
}
}

View File

@@ -2,6 +2,8 @@
using Orchard.ContentManagement.MetaData;
using Orchard.Data;
using Orchard.Data.Migration;
using Orchard.Environment.Extensions;
using Orchard.Indexing;
using Orchard.Search.Models;
namespace Orchard.Search {
@@ -45,4 +47,26 @@ namespace Orchard.Search {
return 2;
}
}
[OrchardFeature("Orchard.Search.MediaLibrary")]
public class MediaMigration : DataMigrationImpl {
private readonly IIndexManager _indexManager;
public MediaMigration(IIndexManager indexManager) {
_indexManager = indexManager;
}
public int Create() {
_indexManager.GetSearchIndexProvider().CreateIndex("Media");
ContentDefinitionManager.AlterTypeDefinition("Image", cfg => cfg.WithSetting("TypeIndexing.Indexes", "Media"));
ContentDefinitionManager.AlterTypeDefinition("Video", cfg => cfg.WithSetting("TypeIndexing.Indexes", "Media"));
ContentDefinitionManager.AlterTypeDefinition("Document", cfg => cfg.WithSetting("TypeIndexing.Indexes", "Media"));
ContentDefinitionManager.AlterTypeDefinition("Audio", cfg => cfg.WithSetting("TypeIndexing.Indexes", "Media"));
ContentDefinitionManager.AlterTypeDefinition("OEmbed", cfg => cfg.WithSetting("TypeIndexing.Indexes", "Media"));
return 1;
}
}
}

View File

@@ -22,3 +22,8 @@ Features:
Description: Provides a search tab in Content Picker.
Dependencies: Orchard.ContentPicker, Orchard.Search
Category: Search
Orchard.Search.MediaLibrary:
Name: Media Library Search
Description: Provides search menu item in the Media Library explorer.
Dependencies: Orchard.MediaLibrary, Orchard.Search
Category: Search

View File

@@ -62,6 +62,8 @@
<Compile Include="Controllers\ContentPickerController.cs" />
<Compile Include="ContentAdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Controllers\MediaController.cs" />
<Compile Include="Drivers\MediaLibraryExplorerPartDriver.cs" />
<Compile Include="Drivers\SearchFormPartDriver.cs" />
<Compile Include="Models\SearchFormPart.cs" />
<Compile Include="ResourceManifest.cs" />
@@ -86,6 +88,10 @@
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard.Framework</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.MediaLibrary\Orchard.MediaLibrary.csproj">
<Project>{73a7688a-5bd3-4f7e-adfa-ce36c5a10e3b}</Project>
<Name>Orchard.MediaLibrary</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="Module.txt" />
@@ -126,6 +132,15 @@
<ItemGroup>
<Content Include="Views\ContentPicker\NoIndex.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Parts\Search.MediaLibrary.Actions.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Parts\Search.MediaLibrary.Navigation.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Media\MediaItems.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -1,4 +1,10 @@
<Placement>
<Place Parts_Search_SiteSettings="Content:1"/>
<Place Parts_Search_SearchForm="Content:1"/>
<Place
Parts_Search_MediaLibrary_Actions="Actions:6"
Parts_Search_MediaLibrary_Navigation="Navigation:1"
/>
</Placement>

View File

@@ -0,0 +1,24 @@
@using Orchard.Utility.Extensions
@model Orchard.MediaLibrary.ViewModels.MediaManagerMediaItemsViewModel
@{
Response.ContentType = "text/json";
@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(
new {
mediaItemsCount = Model.MediaItemsCount,
mediaItems = Model.MediaItems.Select(x => new {
id = x.MediaPart.Id,
contentType = x.MediaPart.ContentItem.ContentType,
contentTypeClass = x.MediaPart.ContentItem.ContentType.HtmlClassify(),
title = x.MediaPart.Title,
alternateText = x.MediaPart.AlternateText,
caption = x.MediaPart.Caption,
resource = x.MediaPart.MediaUrl,
mimeType = x.MediaPart.MimeType,
mimeTypeClass = x.MediaPart.MimeType.HtmlClassify(),
thumbnail = Display(x.Shape).ToString(),
editLink = Url.ItemEditUrl(x.MediaPart)
}).ToArray()
}))
}

View File

@@ -0,0 +1,27 @@
Search <form style="display:inline" data-bind="submit: doSearch"><input data-bind="value: searchText" id="input-media-library-search" type="text" class="text"/></form>
@using (Script.Foot()) {
<script type="text/javascript">
//<![CDATA[
var previous = enhanceViewModel;
enhanceViewModel = function(viewModel) {
previous(viewModel);
viewModel.searchText = ko.observable();
viewModel.doSearch = function () {
var self = viewModel;
self.loadMediaItemsUrl = function (folderPath, skip, count, order, mediaType) {
var searchActionUrl = '@HttpUtility.JavaScriptStringEncode(Url.Action("MediaItems", "Media", new {area = "Orchard.Search"}))';
return searchActionUrl + '?folderPath=' + folderPath + '&skip=' + skip + '&count=' + count + '&order=' + order + '&mediaType=' + mediaType + '&search=' + self.searchText();
};
self.getMediaItems(20);
};
};
//]]>
</script>

View File

@@ -161,6 +161,7 @@
</None>
<None Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
<SubType>Designer</SubType>
</None>
</ItemGroup>
<PropertyGroup>
@@ -236,8 +237,7 @@
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target> -->
<Target Name="ExcludeRootBinariesDeployment">
<Target Name="ExcludeRootBinariesDeployment">
<ItemGroup>
<RootBinFiles Include="bin\*">
<InProject>false</InProject>
@@ -246,14 +246,12 @@
<PropertyGroup>
<ExcludeFilesFromDeployment>@(RootBinFiles->'Modules\**\bin\%(Filename)%(Extension)');@(RootBinFiles->'Themes\**\%(Filename)%(Extension)');@(RootBinFiles->'Core\**\bin\%(Filename)%(Extension)');**\*.Debug.config;**\*.Release.config;**\obj\**;**\bin\*.xml;**\*.cs;**\*.csproj;Modules\**\Tests\**;Modules\**\Specs\**;Themes\bin\**;**\.hgignore;**\.hgtags;**\.hg\**;**\*.csproj.user;Properties\**\*;App_Data\Dependencies\*;App_Data\RecipeQueue\*;App_Data\Logs\*;App_Data\**\mappings.bin;App_Data\**\cache.dat;App_Data\**\hrestart.txt</ExcludeFilesFromDeployment>
</PropertyGroup>
</Target>
</Target>
<Target Name="ExcludeRootBinariesPackage" DependsOnTargets="ExcludeRootBinariesDeployment" BeforeTargets="ExcludeFilesFromPackage">
<ItemGroup>
<ExcludeFromPackageFiles Include="$(ExcludeFilesFromDeployment)" />
</ItemGroup>
</Target>
<Target Name="CopySqlCeBinaries">
<ItemGroup>
<SqlCeBinariesx86 Include="$(ProjectDir)..\..\lib\sqlce\x86\**\*" />