mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Implementing media search
This commit is contained in:
@@ -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");
|
||||
|
@@ -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);
|
||||
|
@@ -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) {
|
||||
|
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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 {
|
||||
|
||||
}
|
||||
}
|
@@ -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>
|
||||
|
@@ -106,4 +106,11 @@
|
||||
<Place Parts_OEmbed="Content:5" />
|
||||
</Match>
|
||||
|
||||
|
||||
<!-- Explorer -->
|
||||
<Place
|
||||
Parts_MediaLibrary_Actions="Actions:5"
|
||||
Parts_MediaLibrary_Navigation="Navigation:5"
|
||||
/>
|
||||
|
||||
</Placement>
|
||||
|
@@ -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) {
|
||||
|
@@ -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; }
|
||||
}
|
||||
}
|
@@ -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) {
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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()
|
||||
}))
|
||||
}
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -0,0 +1 @@
|
||||
<li></li>
|
@@ -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\**\*" />
|
||||
|
Reference in New Issue
Block a user