Merge branch '1.9.x' into dev

Conflicts:
	src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/EditPlacement.cshtml
	src/Orchard.Web/Modules/Orchard.Layouts/Assets/JavaScript/Models/Element.js
	src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj
	src/Orchard.Web/Modules/Orchard.Layouts/Scripts/LayoutEditor.js
	src/Orchard.Web/Modules/Orchard.Layouts/Scripts/LayoutEditor.min.js
	src/Orchard.Web/Modules/Orchard.Layouts/Scripts/Models.js
	src/Orchard.Web/Modules/Orchard.Layouts/Scripts/Models.min.js
	src/Orchard.Web/Modules/Orchard.Layouts/Views/LayoutEditor.Template.Toolbox.cshtml
	src/Orchard.Web/Modules/Orchard.MediaLibrary/Controllers/ClientStorageController.cs
	src/Orchard.Web/Modules/Orchard.Search/Drivers/SearchSettingsPartDriver.cs
	src/Orchard.Web/Modules/Orchard.Search/ViewModels/SearchSettingsIndexViewModel.cs
	src/Orchard.Web/Modules/Orchard.Search/Views/EditorTemplates/Parts/Search.SiteSettings.cshtml
	src/Orchard.Web/Modules/Orchard.Widgets/Controllers/AdminController.cs
	src/Orchard/Orchard.Framework.csproj
This commit is contained in:
Sebastien Ros 2015-09-21 16:15:40 -07:00
commit 80c600fa02
83 changed files with 648 additions and 381 deletions

View File

@ -264,9 +264,6 @@ namespace Orchard.Tests.Modules.DesignerTools.Services
new JProperty("name", "TestingPart"),
new JProperty("value", "ContentPart"),
new JProperty("children", new JArray(
new JObject(
new JProperty("name", "Zones"),
new JProperty("value", "ZoneCollection")),
new JObject(
new JProperty("name", "Id"),
new JProperty("value", "0")),
@ -327,9 +324,6 @@ namespace Orchard.Tests.Modules.DesignerTools.Services
new JProperty("name", "TestingPart"),
new JProperty("value", "ContentPart"),
new JProperty("children", new JArray(
new JObject(
new JProperty("name", "Zones"),
new JProperty("value", "ZoneCollection")),
new JObject(
new JProperty("name", "Id"),
new JProperty("value", "0")),

View File

@ -512,7 +512,7 @@ Features:
Dependencies: Beta
");
moduleExtensionFolder.Manifests.Add("Classic", @"
themeExtensionFolder.Manifests.Add("Classic", @"
Name: Classic
Version: 1.0.3
OrchardVersion: 1

View File

@ -8,7 +8,7 @@
<autofac defaultAssembly="Orchard.Framework">
<!--
To create tenant specific configurations, copy this file to ~/Congig/Sites.{tenant}.config
To create tenant specific configurations, copy this file to ~/Config/Sites.{tenant}.config
where {tenant} is the technical name of the targetted tenant
Allowed scopes: per-dependency, single-instance or per-lifetime-scope
@ -47,4 +47,4 @@
</components>
</autofac>
</configuration>
</configuration>

View File

@ -52,7 +52,7 @@ namespace Orchard.Core.Containers.Drivers {
if (updater != null) {
var oldContainerId = model.ContainerId;
updater.TryUpdateModel(model, "Containable", null, new[] { "ShowContainerPicker", "ShowPositionEditor" });
if (oldContainerId != model.ContainerId) {
if (oldContainerId != model.ContainerId && settings.ShowContainerPicker) {
if (commonPart != null) {
var containerItem = _contentManager.Get(model.ContainerId, VersionOptions.Latest);
commonPart.Container = containerItem;
@ -61,20 +61,23 @@ namespace Orchard.Core.Containers.Drivers {
part.Position = model.Position;
}
var containers = _contentManager
.Query<ContainerPart, ContainerPartRecord>(VersionOptions.Latest)
.List()
.Where(container => container.ItemContentTypes.Any(type => type.Name == part.TypeDefinition.Name));
if (settings.ShowContainerPicker) {
var containers = _contentManager
.Query<ContainerPart, ContainerPartRecord>(VersionOptions.Latest)
.List()
.Where(container => container.ItemContentTypes.Any(type => type.Name == part.TypeDefinition.Name));
var listItems = new[] { new SelectListItem { Text = T("(None)").Text, Value = "0" } }
.Concat(containers.Select(x => new SelectListItem {
Value = Convert.ToString(x.Id),
Text = x.ContentItem.TypeDefinition.DisplayName + ": " + _contentManager.GetItemMetadata(x.ContentItem).DisplayText,
Selected = x.Id == model.ContainerId,
}))
.ToList();
var listItems = new[] { new SelectListItem { Text = T("(None)").Text, Value = "0" } }
.Concat(containers.Select(x => new SelectListItem {
Value = Convert.ToString(x.Id),
Text = x.ContentItem.TypeDefinition.DisplayName + ": " + _contentManager.GetItemMetadata(x.ContentItem).DisplayText,
Selected = x.Id == model.ContainerId,
}))
.ToList();
model.AvailableContainers = new SelectList(listItems, "Value", "Text", model.ContainerId);
}
model.AvailableContainers = new SelectList(listItems, "Value", "Text", model.ContainerId);
model.Position = part.Position;
return shapeHelper.EditorTemplate(TemplateName: "Containable", Model: model, Prefix: "Containable");

View File

@ -88,6 +88,10 @@ namespace Orchard.Core.Containers.Drivers {
protected override DriverResult Editor(ContainerPart part, IUpdateModel updater, dynamic shapeHelper) {
return ContentShape("Parts_Container_Edit", () => {
if(!part.ContainerSettings.DisplayContainerEditor) {
return null;
}
var containables = !part.ContainerSettings.RestrictItemContentTypes ? _containerService.GetContainableTypes().ToList() : new List<ContentTypeDefinition>(0);
var model = new ContainerViewModel {
AdminMenuPosition = part.AdminMenuPosition,

View File

@ -48,6 +48,10 @@ namespace Orchard.Core.Containers.Settings {
}
public class ContainerTypePartSettings {
public ContainerTypePartSettings() {
DisplayContainerEditor = true;
}
public bool? ItemsShownDefault { get; set; }
public int? PageSizeDefault { get; set; }
public bool? PaginatedDefault { get; set; }
@ -55,6 +59,7 @@ namespace Orchard.Core.Containers.Settings {
public bool RestrictItemContentTypes { get; set; }
public bool? EnablePositioning { get; set; }
public string AdminListViewName { get; set; }
public bool DisplayContainerEditor { get; set; }
}
public class ContainerSettingsHooks : ContentDefinitionEditorEventsBase {
@ -93,7 +98,8 @@ namespace Orchard.Core.Containers.Settings {
EnablePositioning = model.EnablePositioning,
AdminListViewName = model.AdminListViewName,
AvailableItemContentTypes = _containerService.GetContainableTypes().ToList(),
ListViewProviders = _listViewService.Providers.ToList()
ListViewProviders = _listViewService.Providers.ToList(),
DisplayContainerEditor = model.DisplayContainerEditor
};
yield return DefinitionTemplate(viewModel);
@ -122,6 +128,7 @@ namespace Orchard.Core.Containers.Settings {
builder.WithSetting("ContainerTypePartSettings.RestrictItemContentTypes", viewModel.RestrictItemContentTypes.ToString());
builder.WithSetting("ContainerTypePartSettings.EnablePositioning", viewModel.EnablePositioning.ToString());
builder.WithSetting("ContainerTypePartSettings.AdminListViewName", viewModel.AdminListViewName);
builder.WithSetting("ContainerTypePartSettings.DisplayContainerEditor", viewModel.DisplayContainerEditor.ToString());
yield return DefinitionTemplate(viewModel);
}

View File

@ -16,5 +16,6 @@ namespace Orchard.Core.Containers.ViewModels {
[UIHint("ListViewPicker")]
public string AdminListViewName { get; set; }
public bool DisplayContainerEditor { get; set; }
}
}

View File

@ -2,6 +2,11 @@
@{
Script.Require("ShapesBase");
}
<fieldset>
@Html.CheckBoxFor(m => m.DisplayContainerEditor)
@Html.LabelFor(m => m.DisplayContainerEditor, @T("Display settings editor").ToString(), new { @class = "forcheckbox" })
<span class="hint">@T("When checked, users can change the settings for each content item.")</span>
</fieldset>
<fieldset>
<label for="@Html.FieldIdFor(m => m.ItemsShownDefault)">@T("Default Items Shown")</label>
@Html.EditorFor(m => m.ItemsShownDefault)

View File

@ -65,8 +65,7 @@ namespace Orchard.Core.Contents.Controllers {
Pager pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var versionOptions = VersionOptions.Latest;
switch (model.Options.ContentsStatus)
{
switch (model.Options.ContentsStatus) {
case ContentsStatus.Published:
versionOptions = VersionOptions.Published;
break;
@ -112,6 +111,10 @@ namespace Orchard.Core.Contents.Controllers {
query = _cultureFilter.FilterCulture(query, model.Options.SelectedCulture);
}
if(model.Options.ContentsStatus == ContentsStatus.Owner) {
query = query.Where<CommonPartRecord>(cr => cr.OwnerId == Services.WorkContext.CurrentUser.Id);
}
model.Options.SelectedFilter = model.TypeName;
model.Options.FilterOptions = GetListableTypes(false)
.Select(ctd => new KeyValuePair<string, string>(ctd.Name, ctd.DisplayName))

View File

@ -49,12 +49,12 @@ namespace Orchard.Core.Contents.ViewModels {
Created
}
public enum ContentsStatus
{
public enum ContentsStatus {
Draft,
Published,
AllVersions,
Latest
Latest,
Owner
}
public enum ContentsBulkAction {

View File

@ -56,6 +56,7 @@
</select>
<label for="contentResults" class="bulk-order">@T("Filter by")</label>
<select id="contentResults" name="Options.ContentsStatus">
@Html.SelectOption((ContentsStatus)Model.Options.ContentsStatus, ContentsStatus.Owner, T("owned by me").ToString())
@Html.SelectOption((ContentsStatus)Model.Options.ContentsStatus, ContentsStatus.Latest, T("latest").ToString())
@Html.SelectOption((ContentsStatus)Model.Options.ContentsStatus, ContentsStatus.Published, T("published").ToString())
@Html.SelectOption((ContentsStatus)Model.Options.ContentsStatus, ContentsStatus.Draft, T("unpublished").ToString())

View File

@ -69,11 +69,12 @@ namespace Orchard.Core.Settings.Drivers {
var previousBaseUrl = model.Site.BaseUrl;
updater.TryUpdateModel(model, Prefix, null, new [] { "Site.SuperUser", "Site.MaxPageSize" });
// Update all properties but not SuperUser, MaxPageSize and BaseUrl.
updater.TryUpdateModel(model, Prefix, null, new [] { "Site.SuperUser", "Site.MaxPageSize", "Site.BaseUrl", "Site.MaxPagedCount" });
// only a user with SiteOwner permission can change the site owner
if (_authorizer.Authorize(StandardPermissions.SiteOwner)) {
updater.TryUpdateModel(model, Prefix, new[] { "Site.SuperUser", "Site.MaxPageSize" }, null);
updater.TryUpdateModel(model, Prefix, new[] { "Site.SuperUser", "Site.MaxPageSize", "Site.BaseUrl", "Site.MaxPagedCount" }, null);
// ensures the super user is fully empty
if (String.IsNullOrEmpty(model.SuperUser)) {
@ -86,30 +87,30 @@ namespace Orchard.Core.Settings.Drivers {
updater.AddModelError("SuperUser", T("The user {0} was not found", model.SuperUser));
}
}
}
// ensure the base url is absolute if provided
if (!String.IsNullOrWhiteSpace(model.Site.BaseUrl)) {
if (!Uri.IsWellFormedUriString(model.Site.BaseUrl, UriKind.Absolute)) {
updater.AddModelError("BaseUrl", T("The base url must be absolute."));
}
// ensure the base url is absolute if provided
if (!String.IsNullOrWhiteSpace(model.Site.BaseUrl)) {
if (!Uri.IsWellFormedUriString(model.Site.BaseUrl, UriKind.Absolute)) {
updater.AddModelError("BaseUrl", T("The base url must be absolute."));
}
// if the base url has been modified, try to ping it
else if (!String.Equals(previousBaseUrl, model.Site.BaseUrl, StringComparison.OrdinalIgnoreCase)) {
try {
var request = WebRequest.Create(model.Site.BaseUrl) as HttpWebRequest;
if (request != null) {
using (request.GetResponse() as HttpWebResponse) {}
else if (!String.Equals(previousBaseUrl, model.Site.BaseUrl, StringComparison.OrdinalIgnoreCase)) {
try {
var request = WebRequest.Create(model.Site.BaseUrl) as HttpWebRequest;
if (request != null) {
using (request.GetResponse() as HttpWebResponse) { }
}
}
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
_notifier.Warning(T("The base url you entered could not be requested from current location."));
Logger.Warning(ex, "Could not query base url: {0}", model.Site.BaseUrl);
}
_notifier.Warning(T("The base url you entered could not be requested from current location."));
Logger.Warning(ex, "Could not query base url: {0}", model.Site.BaseUrl);
}
}
}
}
return ContentShape("Parts_Settings_SiteSettingsPart",
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Settings.SiteSettingsPart", Model: model, Prefix: Prefix));

View File

@ -16,7 +16,10 @@
</div>
<div>
<label for="@Html.FieldIdFor(m => m.BaseUrl)">@T("Base URL")</label>
@Html.TextBoxFor(m => m.BaseUrl, new { @class = "text medium is-url" })
@Html.TextBoxFor(m => m.BaseUrl,
(object)(AuthorizedFor(Orchard.Security.StandardPermissions.SiteOwner)
? (dynamic)new { @class = "text medium is-url" }
: (dynamic)new { @class = "text medium is-url", @readonly = "readonly" }))
<span class="hint">@T("Enter the fully qualified base URL of the web site.")</span>
<span class="hint">@T("e.g., http://localhost:30320/orchardlocal, http://www.yourdomain.com")</span>
</div>
@ -45,14 +48,15 @@
@Html.EditorFor(x => x.PageTitleSeparator)
@Html.ValidationMessage("PageTitleSeparator", "*")
</div>
@if (AuthorizedFor(Orchard.Security.StandardPermissions.SiteOwner)) {
<div>
<label for="SuperUser">@T("Super user")</label>
@Html.EditorFor(x => x.SuperUser)
@Html.TextBoxFor(x => x.SuperUser,
(object)(AuthorizedFor(Orchard.Security.StandardPermissions.SiteOwner)
? (dynamic)new { @class = "text single-line" }
: (dynamic)new { @class = "text single-line", @readonly = "readonly" }))
@Html.ValidationMessage("SuperUser", "*")
<span class="hint">@T("Enter an existing account name, or nothing if you don't want a Super user account")</span>
</div>
}
<div>
<label for="SiteDebugMode">@T("Resource Debug Mode")</label>
@Html.DropDownList("ResourceDebugMode", resourceDebugMode)

View File

@ -319,9 +319,12 @@ namespace Orchard.Core.Shapes {
var progress = 1;
var flatPositionComparer = new FlatPositionComparer();
var ordering = unordered.Select(item => {
var position = (item == null || item.GetType().GetProperty("Metadata") == null || item.Metadata.GetType().GetProperty("Position") == null)
? null
: item.Metadata.Position;
string position = null;
var itemPosition = item as IPositioned;
if (itemPosition != null) {
position = itemPosition.Position;
}
return new { item, position };
}).ToList();

View File

@ -236,7 +236,13 @@
}
if (_this.filter("[itemprop~='RemoveUrl']").length == 1) {
if (!confirm(confirmRemoveMessage)) {
// use a custom message if its set in data-message
var dataMessage = _this.data('message');
if (dataMessage === undefined) {
dataMessage = confirmRemoveMessage;
}
if (!confirm(dataMessage)) {
return false;
}
}

View File

@ -25,6 +25,7 @@
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -73,6 +74,10 @@
<Name>Orchard.Core</Name>
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\Orchard.MediaLibrary\Orchard.MediaLibrary.csproj">
<Project>{73a7688a-5bd3-4f7e-adfa-ce36c5a10e3b}</Project>
<Name>Orchard.MediaLibrary</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="Content\Admin\Images\grippie.png" />

View File

@ -5,70 +5,72 @@
editors.each(function() {
var idPostfix = $(this).attr('id').substr('wmd-input'.length);
var editor = new Markdown.Editor(converter, idPostfix, {
handler: function() { window.open("http://daringfireball.net/projects/markdown/syntax"); }
});
editor.hooks.set("insertImageDialog", function(callback) {
// see if there's an image selected that they intend on editing
var wmd = $('#wmd-input' + idPostfix);
if (Boolean($(this).data("manage-media"))) {
editor.hooks.set("insertImageDialog", function (callback) {
// see if there's an image selected that they intend on editing
var wmd = $('#wmd-input' + idPostfix);
var editImage, content = wmd.selection ? wmd.selection.createRange().text : null;
var adminIndex = location.href.toLowerCase().indexOf("/admin/");
if (adminIndex === -1) return;
var url = location.href.substr(0, adminIndex) + "/Admin/Orchard.MediaLibrary?dialog=true";
$.colorbox({
href: url,
iframe: true,
reposition: true,
width: "90%",
height: "90%",
onLoad: function () {
// hide the scrollbars from the main window
$('html, body').css('overflow', 'hidden');
},
onClosed: function () {
$('html, body').css('overflow', '');
var editImage, content = wmd.selection ? wmd.selection.createRange().text : null;
var adminIndex = location.href.toLowerCase().indexOf("/admin/");
if (adminIndex === -1) return;
var url = location.href.substr(0, adminIndex) + "/Admin/Orchard.MediaLibrary?dialog=true";
$.colorbox({
href: url,
iframe: true,
reposition: true,
width: "90%",
height: "90%",
onLoad: function () {
// hide the scrollbars from the main window
$('html, body').css('overflow', 'hidden');
},
onClosed: function () {
$('html, body').css('overflow', '');
var selectedData = $.colorbox.selectedData;
var selectedData = $.colorbox.selectedData;
if (selectedData == null) // Dialog cancelled, do nothing
return;
if (selectedData == null) // Dialog cancelled, do nothing
return;
var newContent = '';
for (var i = 0; i < selectedData.length; i++) {
var renderMedia = location.href.substr(0, adminIndex) + "/Admin/Orchard.MediaLibrary/MediaItem/" + selectedData[i].id + "?displayType=Raw";
$.ajax({
async: false,
type: 'GET',
url: renderMedia,
success: function (data) {
newContent += data;
}
});
}
var result = $.parseHTML(newContent);
var img = $(result).filter('img');
// if this is an image, use the callback which will format it in markdown
if (img.length > 0 && img.attr('src')) {
callback(img.attr('src'));
}
// otherwise, insert the raw HTML
else {
if (wmd.selection) {
wmd.selection.replace('.*', newContent);
} else {
wmd.text(newContent);
var newContent = '';
for (var i = 0; i < selectedData.length; i++) {
var renderMedia = location.href.substr(0, adminIndex) + "/Admin/Orchard.MediaLibrary/MediaItem/" + selectedData[i].id + "?displayType=Raw";
$.ajax({
async: false,
type: 'GET',
url: renderMedia,
success: function (data) {
newContent += data;
}
});
}
var result = $.parseHTML(newContent);
var img = $(result).filter('img');
// if this is an image, use the callback which will format it in markdown
if (img.length > 0 && img.attr('src')) {
callback(img.attr('src'));
}
// otherwise, insert the raw HTML
else {
if (wmd.selection) {
wmd.selection.replace('.*', newContent);
} else {
wmd.text(newContent);
}
callback();
}
callback();
}
}
});
return true;
});
return true;
});
}
editor.run();
});

View File

@ -21,7 +21,8 @@
{"id", "wmd-input" + "-" + idPostfix},
{"class", "wmd-input"},
{"data-mediapicker-uploadpath", Model.AddMediaPath},
{"data-mediapicker-title", T("Insert/Update Media")}
{"data-mediapicker-title", T("Insert/Update Media")},
{"data-manage-media", AuthorizedFor(Orchard.MediaLibrary.Permissions.ManageMediaContent) ? "true" : "false" }
};
// The markdown editor itself doesn't seem to (yet) support autofocus, but we'll set it on the textarea nonetheless.

View File

@ -144,7 +144,7 @@ namespace Orchard.AntiSpam.Controllers {
if (spam != null) {
spam.As<SpamFilterPart>().Status = SpamStatus.Spam;
_spamService.ReportSpam(spam.As<SpamFilterPart>());
Services.ContentManager.Publish(spam);
Services.ContentManager.Unpublish(spam);
}
return this.RedirectLocal(returnUrl, "~/");
@ -158,7 +158,7 @@ namespace Orchard.AntiSpam.Controllers {
var spam = Services.ContentManager.Get(id, VersionOptions.Latest);
if (spam != null) {
spam.As<SpamFilterPart>().Status = SpamStatus.Ham;
_spamService.ReportSpam(spam.As<SpamFilterPart>());
_spamService.ReportHam(spam.As<SpamFilterPart>());
Services.ContentManager.Publish(spam);
}

View File

@ -1,6 +1,5 @@
using System;
using Orchard.AntiSpam.Models;
using Orchard.AntiSpam.Services;
using Orchard.AntiSpam.Settings;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
@ -8,11 +7,9 @@ using Orchard.Localization;
namespace Orchard.AntiSpam.Drivers {
public class SpamFilterPartDriver : ContentPartDriver<SpamFilterPart> {
private readonly ISpamService _spamService;
private const string TemplateName = "Parts/SpamFilter";
public SpamFilterPartDriver(IOrchardServices services, ISpamService spamService) {
_spamService = spamService;
public SpamFilterPartDriver(IOrchardServices services) {
T = NullLocalizer.Instance;
Services = services;
}
@ -25,8 +22,6 @@ namespace Orchard.AntiSpam.Drivers {
}
protected override DriverResult Editor(SpamFilterPart part, ContentManagement.IUpdateModel updater, dynamic shapeHelper) {
part.Status = _spamService.CheckForSpam(part);
if (part.Settings.GetModel<SpamFilterPartSettings>().DeleteSpam) {
updater.AddModelError("Spam", T("Spam detected."));
}

View File

@ -1,5 +1,5 @@
using Orchard.AntiSpam.Models;
using Orchard.AntiSpam.Services;
using Orchard.AntiSpam.Settings;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
@ -7,14 +7,22 @@ using Orchard.Data;
namespace Orchard.AntiSpam.Handlers {
public class SpamFilterPartHandler : ContentHandler {
private readonly ITransactionManager _transactionManager;
private readonly ISpamService _spamService;
public SpamFilterPartHandler(
IRepository<SpamFilterPartRecord> repository,
ITransactionManager transactionManager
ITransactionManager transactionManager,
ISpamService spamService
) {
_transactionManager = transactionManager;
_spamService = spamService;
Filters.Add(StorageFilter.For(repository));
OnCreating<SpamFilterPart>((context, part) => {
part.Status = _spamService.CheckForSpam(part);
});
OnPublishing<SpamFilterPart>((context, part) => {
if (part.Status == SpamStatus.Spam) {
if (part.Settings.GetModel<SpamFilterPartSettings>().DeleteSpam) {

View File

@ -126,6 +126,7 @@ namespace Orchard.AntiSpam.Services {
CommentAuthorEmail = _tokenizer.Replace(settings.CommentAuthorEmailPattern, data),
CommentAuthorUrl = _tokenizer.Replace(settings.CommentAuthorUrlPattern, data),
CommentContent = _tokenizer.Replace(settings.CommentContentPattern, data),
CommentType = part.ContentItem.ContentType.ToLower()
};
if(workContext.HttpContext != null) {

View File

@ -117,7 +117,7 @@ namespace Orchard.Comments.Drivers {
// prevent users from commenting on a closed thread by hijacking the commentedOn property
var commentsPart = commentedOn.As<CommentsPart>();
if (!commentsPart.CommentsActive) {
if (commentsPart == null || !commentsPart.CommentsActive) {
_orchardServices.TransactionManager.Cancel();
return Editor(part, shapeHelper);
}

View File

@ -118,7 +118,8 @@ namespace Orchard.Comments.Services {
}
public void DeleteComment(int commentId) {
_orchardServices.ContentManager.Remove(_orchardServices.ContentManager.Get<CommentPart>(commentId).ContentItem);
// Get latest because the comment may be unpublished if the anti-spam module has caught it
_orchardServices.ContentManager.Remove(_orchardServices.ContentManager.Get<CommentPart>(commentId, VersionOptions.Latest).ContentItem);
}
public bool CommentsDisabledForCommentedContent(int id) {

View File

@ -43,7 +43,7 @@
<fieldset class="action">
<button class="primaryAction" type="submit" name="submit.Save" value="Save">@T("Save")</button>
<button class="primaryAction" type="submit" name="submit.Restore" value="Restore">@T("Restore")</button>
<button class="primaryAction" type="submit" name="submit.Restore" value="Restore" itemprop="RemoveUrl" data-message="@T("Are you sure you want to restore these placements?")">@T("Restore")</button>
</fieldset>
}

View File

@ -136,7 +136,7 @@ namespace Orchard.DynamicForms.Drivers {
var runtimeValues = GetRuntimeValues(element);
if (!String.IsNullOrWhiteSpace(optionLabel)) {
yield return new SelectListItem { Text = displayType != "Design" ? _tokenizer.Replace(optionLabel, tokenData) : optionLabel };
yield return new SelectListItem { Text = displayType != "Design" ? _tokenizer.Replace(optionLabel, tokenData) : optionLabel, Value = string.Empty };
}
if (queryId == null)

View File

@ -138,7 +138,7 @@ namespace Orchard.DynamicForms.Drivers {
var runtimeValues = GetRuntimeValues(element);
if (!String.IsNullOrWhiteSpace(optionLabel)) {
yield return new SelectListItem { Text = displayType != "Design" ? _tokenizer.Replace(optionLabel, tokenData) : optionLabel };
yield return new SelectListItem { Text = displayType != "Design" ? _tokenizer.Replace(optionLabel, tokenData) : optionLabel, Value = string.Empty };
}
if (taxonomyId == null)

View File

@ -8,34 +8,34 @@
<label for="@Html.FieldIdFor(m => m.Value)" @if(settings.Required) { <text>class="required"</text> }>@Model.DisplayName</label>
@switch (settings.ListMode) {
case ListMode.Dropdown:
@Html.DropDownListFor(m => m.Value, new SelectList(options, Model.Value))
@Html.DropDownListFor(m => m.Value, new SelectList(options, Model.Value), settings.Required ? new {required = "required"} : null)
break;
case ListMode.Radiobutton:
foreach (var option in options) {
if (string.IsNullOrWhiteSpace(option)) {
<label>@Html.RadioButton("Value", "", string.IsNullOrWhiteSpace(Model.Value))<i>@T("unset")</i></label>
}
if (string.IsNullOrWhiteSpace(option)) {
<label>@Html.RadioButton("Value", "", string.IsNullOrWhiteSpace(Model.Value), settings.Required ? new {required = "required"} : null)<i>@T("unset")</i></label>
}
else {
<label>@Html.RadioButton("Value", option, (option == Model.Value))@option</label>
}
<label>@Html.RadioButton("Value", option, (option == Model.Value), settings.Required ? new {required = "required"} : null)@option</label>
}
}
break;
case ListMode.Listbox:
<input name="@Html.FieldNameFor(m => m.SelectedValues)" type="hidden" />
@Html.ListBoxFor(m => m.SelectedValues, new MultiSelectList(options, Model.SelectedValues))
break;
@Html.ListBoxFor(m => m.SelectedValues, new MultiSelectList(options, Model.SelectedValues), settings.Required ? new {required = "required"} : null)
break;
case ListMode.Checkbox:
int index = 0;
int index = 0;
<input name="@Html.FieldNameFor(m => m.SelectedValues)" type="hidden" />
foreach (var option in options) {
index++;
if (!string.IsNullOrWhiteSpace(option)) {
<div>
<input type="checkbox" name="@Html.FieldNameFor(m => m.SelectedValues)" value="@option" @((Model.SelectedValues != null && Model.SelectedValues.Contains(option)) ? "checked=\"checked\"" : "") class="check-box" id="@Html.FieldIdFor(m => m.SelectedValues)-@index" />
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.SelectedValues)-@index">@T(option)</label>
<input type="checkbox" name="@Html.FieldNameFor(m => m.SelectedValues)" value="@option" @((Model.SelectedValues != null && Model.SelectedValues.Contains(option)) ? "checked=\"checked\"" : "") class="check-box" id="@Html.FieldIdFor(m => m.SelectedValues)-@index" @if(settings.Required) {<text> required="required"</text> } />
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.SelectedValues)-@index">@T(option)</label>
</div>
}
}

View File

@ -4,7 +4,9 @@
<fieldset>
<label for="@Html.FieldIdFor(m => m.Value)" @if(Model.Settings.Required) { <text>class="required"</text> }>@Model.Field.DisplayName</label>
@Html.TextBoxFor(m => m.Value, new { @class = "text small", type = "text", min = (Model.Settings.Minimum.HasValue) ? Model.Settings.Minimum.Value : 0, max = (Model.Settings.Maximum.HasValue) ? Model.Settings.Maximum.Value : 1000000, step = Math.Pow(10, 0 - Model.Settings.Scale).ToString(CultureInfo.InvariantCulture) })
@(Model.Settings.Required
? Html.TextBoxFor(m => m.Value, new {@class = "text-small", type = "text", min = (Model.Settings.Minimum.HasValue) ? Model.Settings.Minimum.Value : 0, max = (Model.Settings.Maximum.HasValue) ? Model.Settings.Maximum.Value : 1000000, step = Math.Pow(10, 0 - Model.Settings.Scale).ToString(CultureInfo.InvariantCulture), required = "required"})
: Html.TextBoxFor(m => m.Value, new {@class = "text-small", type = "text", min = (Model.Settings.Minimum.HasValue) ? Model.Settings.Minimum.Value : 0, max = (Model.Settings.Maximum.HasValue) ? Model.Settings.Maximum.Value : 1000000, step = Math.Pow(10, 0 - Model.Settings.Scale).ToString(CultureInfo.InvariantCulture)}))
@Html.ValidationMessageFor(m => m.Value)
@if (HasText(Model.Settings.Hint)) {
<span class="hint">@Model.Settings.Hint</span>

View File

@ -45,6 +45,15 @@ namespace Orchard.ImageEditor.Controllers {
[Themed(false)]
public ActionResult Edit(string folderPath, string filename) {
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia))
return new HttpUnauthorizedResult();
// Check permission.
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent) && !_mediaLibraryService.CanManageMediaFolder(folderPath)) {
return new HttpUnauthorizedResult();
}
var media = Services.ContentManager.Query<MediaPart, MediaPartRecord>().Where(x => x.FolderPath == folderPath && x.FileName == filename).Slice(0, 1).FirstOrDefault();
if (media == null) {
@ -64,12 +73,21 @@ namespace Orchard.ImageEditor.Controllers {
[HttpPost]
public ActionResult Upload(int id, string content, int width, int height) {
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia))
return new HttpUnauthorizedResult();
var media = Services.ContentManager.Get(id).As<MediaPart>();
if (media == null) {
return HttpNotFound();
}
// Check permission.
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent) && !_mediaLibraryService.CanManageMediaFolder(media.FolderPath)) {
return new HttpUnauthorizedResult();
}
const string signature = "data:image/jpeg;base64,";
if (!content.StartsWith(signature, StringComparison.OrdinalIgnoreCase)) {
@ -96,7 +114,7 @@ namespace Orchard.ImageEditor.Controllers {
}
public ActionResult Proxy(string url) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia))
return HttpNotFound();
var sslFailureCallback = new RemoteCertificateValidationCallback((o, cert, chain, errors) => true);

View File

@ -11,9 +11,9 @@ namespace Orchard.Layouts {
builder
.AddImageSet("layouts")
.Add(T("Layouts"), "8.5", layouts => layouts
.Action("List", "Admin", new {id = "Layout", area = "Contents"})
.Action("List", "Admin", new {id = "Layout", area = "Contents"}).Permission(Permissions.ManageLayouts)
.LinkToFirstChild(false)
.Add(T("Elements"), "1", elements => elements.Action("Index", "BlueprintAdmin", new {area = "Orchard.Layouts"})));
.Add(T("Elements"), "1", elements => elements.Action("Index", "BlueprintAdmin", new {area = "Orchard.Layouts"}).Permission(Permissions.ManageLayouts)));
}
}
}

View File

@ -74,45 +74,6 @@
return host.addElement(contentType);
};
$scope.toggleInlineEditing = function () {
if (!$scope.element.inlineEditingIsActive) {
$scope.element.inlineEditingIsActive = true;
$element.find(".layout-toolbar-container").show();
var selector = "#layout-editor-" + $scope.$id + " .layout-html .layout-content-markup[data-templated=false]";
var firstContentEditorId = $(selector).first().attr("id");
tinymce.init({
selector: selector,
theme: "modern",
schema: "html5",
plugins: [
"advlist autolink lists link image charmap print preview hr anchor pagebreak",
"searchreplace wordcount visualblocks visualchars code fullscreen",
"insertdatetime media nonbreaking table contextmenu directionality",
"emoticons template paste textcolor colorpicker textpattern",
"fullscreen autoresize"
],
toolbar: "undo redo cut copy paste | bold italic | bullist numlist outdent indent formatselect | alignleft aligncenter alignright alignjustify ltr rtl | link unlink charmap | code fullscreen close",
convert_urls: false,
valid_elements: "*[*]",
// Shouldn't be needed due to the valid_elements setting, but TinyMCE would strip script.src without it.
extended_valid_elements: "script[type|defer|src|language]",
statusbar: false,
skin: "orchardlightgray",
inline: true,
fixed_toolbar_container: "#layout-editor-" + $scope.$id + " .layout-toolbar-container",
init_instance_callback: function (editor) {
if (editor.id == firstContentEditorId)
tinymce.execCommand("mceFocus", false, editor.id);
}
});
}
else {
tinymce.remove("#layout-editor-" + $scope.$id + " .layout-content-markup");
$element.find(".layout-toolbar-container").hide();
$scope.element.inlineEditingIsActive = false;
}
};
$(document).on("cut copy paste", function (e) {
// If the pseudo clipboard was already invoked (which happens on the first clipboard
// operation after page load even if native clipboard support exists) then sit this
@ -164,23 +125,12 @@
element.find(".layout-toolbar-container").click(function (e) {
e.stopPropagation();
});
// Intercept mousedown on editor while in inline editing mode to
// prevent current editor from losing focus.
element.mousedown(function (e) {
if (scope.element.inlineEditingIsActive) {
e.preventDefault();
e.stopPropagation();
}
})
// Unfocus and unselect everything on click outside of canvas.
$(window).click(function (e) {
// Except when in inline editing mode.
if (!scope.element.inlineEditingIsActive) {
scope.$apply(function () {
scope.element.activeElement = null;
scope.element.focusedElement = null;
});
}
scope.$apply(function () {
scope.element.activeElement = null;
scope.element.focusedElement = null;
});
});
}
};

View File

@ -35,13 +35,6 @@
templateUrl: environment.templateUrl("Html"),
replace: true,
link: function (scope, element) {
// Mouse down events must not be intercepted by drag and drop while inline editing is active,
// otherwise clicks in inline editors will have no effect.
element.find(".layout-content-markup").mousedown(function (e) {
if (scope.element.editor.inlineEditingIsActive) {
e.stopPropagation();
}
});
}
};
}

View File

@ -15,7 +15,7 @@
var resetFocus = false;
var element = $scope.element;
if (element.editor.isDragging || element.editor.inlineEditingIsActive)
if (element.editor.isDragging)
return;
// If native clipboard support exists, the pseudo-clipboard will have been disabled.

View File

@ -9,7 +9,6 @@
this.focusedElement = null;
this.dropTargetElement = null;
this.isDragging = false;
this.inlineEditingIsActive = false;
this.isResizing = false;
this.resetToolboxElements = function () {

View File

@ -57,7 +57,7 @@
this.setIsActive = function (value) {
if (!this.editor)
return;
if (this.editor.isDragging || this.editor.inlineEditingIsActive || this.editor.isResizing)
if (this.editor.isDragging || this.editor.isResizing)
return;
if (value)
@ -81,7 +81,7 @@
return;
if (this.isTemplated && !this.allowSealedFocus())
return;
if (this.editor.isDragging || this.editor.inlineEditingIsActive || this.editor.isResizing)
if (this.editor.isDragging || this.editor.isResizing)
return;
this.editor.focusedElement = this;

View File

@ -34,7 +34,8 @@ namespace Orchard.Layouts.Controllers {
ICultureAccessor cultureAccessor,
IShapeFactory shapeFactory,
ITransactionManager transactionManager,
ISignals signals) {
ISignals signals,
IOrchardServices orchardServices) {
_elementBlueprintService = elementBlueprintService;
_notifier = notifier;
@ -43,12 +44,19 @@ namespace Orchard.Layouts.Controllers {
_shapeFactory = shapeFactory;
_transactionManager = transactionManager;
_signals = signals;
Services = orchardServices;
T = NullLocalizer.Instance;
}
public IOrchardServices Services { get; set; }
public Localizer T { get; set; }
public ActionResult Index() {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
var blueprints = _elementBlueprintService.GetBlueprints().ToArray();
var viewModel = new BlueprintsIndexViewModel {
Blueprints = blueprints
@ -57,6 +65,10 @@ namespace Orchard.Layouts.Controllers {
}
public ActionResult Browse() {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
var categories = RemoveBlueprints(_elementManager.GetCategories(DescribeElementsContext.Empty)).ToArray();
var viewModel = new BrowseElementsViewModel {
Categories = categories
@ -65,6 +77,10 @@ namespace Orchard.Layouts.Controllers {
}
public ActionResult Create(string id) {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
if (String.IsNullOrWhiteSpace(id))
return RedirectToAction("Browse");
@ -80,6 +96,10 @@ namespace Orchard.Layouts.Controllers {
[HttpPost]
public ActionResult Create(string id, CreateElementBlueprintViewModel model) {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
var describeContext = DescribeElementsContext.Empty;
var descriptor = _elementManager.GetElementDescriptorByTypeName(describeContext, id);
var baseElement = _elementManager.ActivateElement(descriptor);
@ -100,7 +120,11 @@ namespace Orchard.Layouts.Controllers {
return RedirectToAction("Edit", new { id = blueprint.Id });
}
public ViewResult Edit(int id) {
public ActionResult Edit(int id) {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
var blueprint = _elementBlueprintService.GetBlueprint(id);
var describeContext = DescribeElementsContext.Empty;
var descriptor = _elementManager.GetElementDescriptorByTypeName(describeContext, blueprint.BaseElementTypeName);
@ -125,6 +149,10 @@ namespace Orchard.Layouts.Controllers {
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(int id, ElementDataViewModel model) {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
var blueprint = _elementBlueprintService.GetBlueprint(id);
var describeContext = DescribeElementsContext.Empty;
var descriptor = _elementManager.GetElementDescriptorByTypeName(describeContext, blueprint.BaseElementTypeName);
@ -154,6 +182,10 @@ namespace Orchard.Layouts.Controllers {
}
public ActionResult Properties(int id) {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
var blueprint = _elementBlueprintService.GetBlueprint(id);
var describeContext = DescribeElementsContext.Empty;
var descriptor = _elementManager.GetElementDescriptorByTypeName(describeContext, blueprint.BaseElementTypeName);
@ -171,6 +203,10 @@ namespace Orchard.Layouts.Controllers {
[HttpPost]
public ActionResult Properties(int id, ElementBlueprintPropertiesViewModel model) {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
var blueprint = _elementBlueprintService.GetBlueprint(id);
var describeContext = DescribeElementsContext.Empty;
var descriptor = _elementManager.GetElementDescriptorByTypeName(describeContext, blueprint.BaseElementTypeName);
@ -191,7 +227,12 @@ namespace Orchard.Layouts.Controllers {
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult Delete(int id) {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
var blueprint = _elementBlueprintService.GetBlueprint(id);
if (blueprint == null)
@ -204,7 +245,12 @@ namespace Orchard.Layouts.Controllers {
[FormValueRequired("submit.BulkEdit")]
[ActionName("Index")]
[HttpPost]
public ActionResult BulkDelete(IEnumerable<int> blueprintIds) {
if (!Services.Authorizer.Authorize(Permissions.ManageLayouts, T("Not authorized to manage layouts."))) {
return new HttpUnauthorizedResult();
}
if (blueprintIds == null || !blueprintIds.Any()) {
_notifier.Error(T("Please select the blueprints to delete."));
}

View File

@ -142,18 +142,7 @@ namespace Orchard.Layouts.Controllers {
_objectStore.Set(session, state);
return RedirectToAction("Edit", new {session = session});
}
public RedirectToRouteResult Add(string session, string typeName, int? contentId = null, string contentType = null) {
var state = new ElementSessionState {
TypeName = typeName,
ContentId = contentId,
ContentType = contentType
};
_objectStore.Set(session, state);
return RedirectToAction("Edit", new { session = session });
}
public ViewResult Edit(string session) {
var sessionState = _objectStore.Get<ElementSessionState>(session);
var contentId = sessionState.ContentId;

View File

@ -6,6 +6,7 @@ using Orchard.ContentManagement;
using Orchard.Layouts.Elements;
using Orchard.Layouts.Framework.Elements;
using Orchard.Layouts.Services;
using Orchard.Localization;
using Orchard.UI.Admin;
namespace Orchard.Layouts.Controllers {
@ -15,15 +16,25 @@ namespace Orchard.Layouts.Controllers {
private readonly ILayoutManager _layoutManager;
private readonly ILayoutModelMapper _mapper;
public LayoutController(IContentManager contentManager, ILayoutManager layoutManager, ILayoutModelMapper mapper) {
public LayoutController(
IContentManager contentManager,
ILayoutManager layoutManager,
ILayoutModelMapper mapper,
IOrchardServices orchardServices) {
_contentManager = contentManager;
_layoutManager = layoutManager;
_mapper = mapper;
Services = orchardServices;
T = NullLocalizer.Instance;
}
public IOrchardServices Services { get; set; }
public Localizer T { get; set; }
[HttpPost, ValidateInput(enableValidation: false)]
public ContentResult ApplyTemplate(int? templateId = null, string layoutData = null, int? contentId = null, string contentType = null) {
public ActionResult ApplyTemplate(int? templateId = null, string layoutData = null, int? contentId = null, string contentType = null) {
var template = templateId != null ? _layoutManager.GetLayout(templateId.Value) : null;
var templateElements = template != null ? _layoutManager.LoadElements(template).ToList() : default(IEnumerable<Element>);
var describeContext = CreateDescribeElementsContext(contentId, contentType);

View File

@ -33,7 +33,6 @@ namespace Orchard.Layouts {
.WithPart("LayoutPart", p => p
.WithSetting("LayoutTypePartSettings.IsTemplate", "True"))
.DisplayedAs("Layout")
.Listable()
.Draftable());
ContentDefinitionManager.AlterTypeDefinition("LayoutWidget", type => type

View File

@ -368,6 +368,7 @@
<Compile Include="Helpers\PrefixHelper.cs" />
<Compile Include="Helpers\JsonHelper.cs" />
<Compile Include="Helpers\StringHelper.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Recipes\Builders\CustomElementsStep.cs" />
<Compile Include="Recipes\Executors\CustomElementsStep.cs" />
<Compile Include="Providers\BlueprintElementHarvester.cs" />

View File

@ -0,0 +1,40 @@
using System.Collections.Generic;
using Orchard.Environment.Extensions.Models;
using Orchard.Security.Permissions;
namespace Orchard.Layouts {
public class Permissions : IPermissionProvider {
public static readonly Permission ManageLayouts = new Permission { Description = "Managing Layouts", Name = "ManageLayouts" };
public virtual Feature Feature { get; set; }
public IEnumerable<Permission> GetPermissions() {
return new[] {
ManageLayouts,
};
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return new[] {
new PermissionStereotype {
Name = "Administrator",
Permissions = new[] { ManageLayouts }
},
new PermissionStereotype {
Name = "Editor",
Permissions = new[] { ManageLayouts }
},
new PermissionStereotype {
Name = "Moderator",
},
new PermissionStereotype {
Name = "Author"
},
new PermissionStereotype {
Name = "Contributor",
},
};
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@
@foreach (var contentItemShape in Model.ContentItems) {
var contentItem = (ContentItem)contentItemShape.ContentItem;
var displayTextHtmlString = Html.ItemDisplayText(contentItem);
var displayText = displayTextHtmlString != null ? displayTextHtmlString.ToString() : T("-").ToString();
var displayText = displayTextHtmlString != null ? (IHtmlString)displayTextHtmlString : T("-");
<div class="layout-snippet">
@displayText
</div>

View File

@ -1,10 +1,6 @@
<div class="layout-toolbox-wrapper">
<div class="layout-toolbox">
<label>
<input type="checkbox" ng-checked="element.inlineEditingIsActive" ng-click="toggleInlineEditing()" />
@T("Edit HTML content inline")
</label>
<ul class="layout-toolbox-groups" ng-hide="element.inlineEditingIsActive">
<ul class="layout-toolbox-groups">
<li class="layout-toolbox-group" ng-class="{collapsed: layoutIsCollapsed}">
<a href="#" class="layout-toolbox-group-heading" ng-click="toggleLayoutIsCollapsed($event)">@T("Layout")</a>
<ul class="layout-toolbox-items">

View File

@ -53,10 +53,7 @@ namespace Orchard.Localization.Controllers {
var contentItemTranslation = _contentManager.New<LocalizationPart>(masterContentItem.ContentType);
contentItemTranslation.MasterContentItem = masterContentItem;
// build the editor using the master content item so that
// the form is pre-populated with the original values
var content = _contentManager.BuildEditor(masterContentItem);
var content = _contentManager.BuildEditor(contentItemTranslation);
return View(content);
}

View File

@ -15,7 +15,7 @@ namespace Orchard.MediaLibrary {
builder.AddImageSet("media-library")
.Add(T("Media"), "6",
menu => menu.Add(T("Media"), "0", item => item.Action("Index", "Admin", new { area = "Orchard.MediaLibrary" })
.Permission(Permissions.ManageMediaContent)));
.Permission(Permissions.ManageOwnMedia)));
}
}
}

View File

@ -14,6 +14,7 @@ using Orchard.Themes;
using Orchard.UI.Navigation;
using Orchard.ContentManagement.MetaData;
using Orchard.Validation;
using System.Collections.Generic;
namespace Orchard.MediaLibrary.Controllers {
[ValidateInput(false)]
@ -41,17 +42,21 @@ namespace Orchard.MediaLibrary.Controllers {
public ILogger Logger { get; set; }
public ActionResult Index(string folderPath = "", bool dialog = false) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Cannot view media")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Cannot view media")))
return new HttpUnauthorizedResult();
// If the user is trying to access a folder above his boundaries, redirect him to his home folder
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent) && !_mediaLibraryService.CanManageMediaFolder(folderPath)) {
return RedirectToAction("Index", new { folderPath = rootMediaFolder.MediaPath, dialog });
}
// 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 rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
var viewModel = new MediaManagerIndexViewModel {
DialogMode = dialog,
FolderPath = folderPath,
@ -73,7 +78,7 @@ namespace Orchard.MediaLibrary.Controllers {
}
public ActionResult Import(string folderPath) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Cannot import media")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Cannot import media")))
return new HttpUnauthorizedResult();
var mediaProviderMenu = _navigationManager.BuildMenu("mediaproviders");
@ -91,9 +96,20 @@ namespace Orchard.MediaLibrary.Controllers {
[Themed(false)]
public ActionResult MediaItems(string folderPath, int skip = 0, int count = 0, string order = "created", string mediaType = "") {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Cannot view media")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Cannot view media")))
return new HttpUnauthorizedResult();
// Check permission.var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent) && !_mediaLibraryService.CanManageMediaFolder(folderPath)) {
var model = new MediaManagerMediaItemsViewModel {
MediaItems = new List<MediaManagerMediaItemViewModel>(),
MediaItemsCount = 0,
FolderPath = folderPath
};
return View(model);
}
var mediaParts = _mediaLibraryService.GetMediaContentItems(folderPath, skip, count, order, mediaType, VersionOptions.Latest);
var mediaPartsCount = _mediaLibraryService.GetMediaContentItemsCount(folderPath, mediaType, VersionOptions.Latest);
@ -113,9 +129,19 @@ namespace Orchard.MediaLibrary.Controllers {
[Themed(false)]
public ActionResult ChildFolders(string folderPath = null) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Cannot get child folder listing")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Cannot get child folder listing")))
return new HttpUnauthorizedResult();
// Check permission.
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent) && !_mediaLibraryService.CanManageMediaFolder(folderPath)) {
var model = new MediaManagerChildFoldersViewModel {
Children = new IMediaFolder[0]
};
return View(model);
}
var viewModel = new MediaManagerChildFoldersViewModel {
Children = _mediaLibraryService.GetMediaFolders(folderPath)
};
@ -127,11 +153,13 @@ namespace Orchard.MediaLibrary.Controllers {
[Themed(false)]
public ActionResult RecentMediaItems(int skip = 0, int count = 0, string order = "created", string mediaType = "") {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Cannot view media")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Cannot view media")))
return new HttpUnauthorizedResult();
var mediaParts = _mediaLibraryService.GetMediaContentItems(skip, count, order, mediaType);
var mediaPartsCount = _mediaLibraryService.GetMediaContentItemsCount(mediaType);
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder().MediaPath;
var mediaParts = _mediaLibraryService.GetMediaContentItems(rootMediaFolder, skip, count, order, mediaType);
var mediaPartsCount = _mediaLibraryService.GetMediaContentItemsCount(rootMediaFolder, mediaType);
var mediaItems = mediaParts.Select(x => new MediaManagerMediaItemViewModel {
@ -149,12 +177,13 @@ namespace Orchard.MediaLibrary.Controllers {
[Themed(false)]
public ActionResult MediaItem(int id, string displayType = "SummaryAdmin") {
var contentItem = Services.ContentManager.Get(id, VersionOptions.Latest);
var contentItem = Services.ContentManager.Get<MediaPart>(id, VersionOptions.Latest);
if (contentItem == null)
return HttpNotFound();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, contentItem, T("Cannot view media")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, contentItem, T("Cannot view media"))
|| !_mediaLibraryService.CanManageMediaFolder(contentItem.FolderPath))
return new HttpUnauthorizedResult();
dynamic model = Services.ContentManager.BuildDisplay(contentItem, displayType);
@ -164,13 +193,20 @@ namespace Orchard.MediaLibrary.Controllers {
[HttpPost]
public ActionResult Delete(int[] mediaItemIds) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Couldn't delete media items")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Couldn't delete media items")))
return new HttpUnauthorizedResult();
var mediaItems = Services.ContentManager
.Query(VersionOptions.Latest)
.ForContentItems(mediaItemIds)
.List()
.Select(x => x.As<MediaPart>())
.Where(x => x != null);
try {
foreach (var media in Services.ContentManager.Query(VersionOptions.Latest).ForContentItems(mediaItemIds).List()) {
if (media != null) {
Services.ContentManager.Remove(media);
foreach (var media in mediaItems) {
if (_mediaLibraryService.CanManageMediaFolder(media.FolderPath)) {
Services.ContentManager.Remove(media.ContentItem);
}
}
@ -184,12 +220,16 @@ namespace Orchard.MediaLibrary.Controllers {
[HttpPost]
public ActionResult Clone(int mediaItemId) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Couldn't clone media items")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Couldn't clone media items")))
return new HttpUnauthorizedResult();
try {
var media = Services.ContentManager.Get(mediaItemId).As<MediaPart>();
if(!_mediaLibraryService.CanManageMediaFolder(media.FolderPath)) {
return new HttpUnauthorizedResult();
}
var newFileName = Path.GetFileNameWithoutExtension(media.FileName) + " Copy" + Path.GetExtension(media.FileName);
_mediaLibraryService.CopyFile(media.FolderPath, media.FileName, media.FolderPath, newFileName);

View File

@ -17,10 +17,13 @@ namespace Orchard.MediaLibrary.Controllers {
public class ClientStorageController : Controller {
private readonly IMediaLibraryService _mediaLibraryService;
public ClientStorageController(IMediaLibraryService mediaManagerService, IOrchardServices orchardServices) {
public ClientStorageController(
IMediaLibraryService mediaManagerService,
IContentManager contentManager,
IOrchardServices orchardServices) {
_mediaLibraryService = mediaManagerService;
Services = orchardServices;
Services = orchardServices;
T = NullLocalizer.Instance;
}
@ -42,7 +45,13 @@ namespace Orchard.MediaLibrary.Controllers {
[HttpPost]
public ActionResult Upload(string folderPath, string type) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Cannot manage media"))) {
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia)) {
return new HttpUnauthorizedResult();
}
// Check permission.
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent) && !_mediaLibraryService.CanManageMediaFolder(folderPath)) {
return new HttpUnauthorizedResult();
}

View File

@ -32,9 +32,15 @@ namespace Orchard.MediaLibrary.Controllers {
public Localizer T { get; set; }
public ActionResult Create(string folderPath) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Couldn't create media folder")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Couldn't create media folder")))
return new HttpUnauthorizedResult();
// If the user is trying to access a folder above his boundaries, redirect him to his home folder
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent) && !_mediaLibraryService.CanManageMediaFolder(folderPath)) {
return RedirectToAction("Create", new { folderPath = rootMediaFolder.MediaPath });
}
var viewModel = new MediaManagerFolderCreateViewModel {
Hierarchy = _mediaLibraryService.GetMediaFolders(folderPath),
FolderPath = folderPath
@ -45,12 +51,16 @@ namespace Orchard.MediaLibrary.Controllers {
[HttpPost, ActionName("Create")]
public ActionResult Create() {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Couldn't create media folder")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Couldn't create media folder")))
return new HttpUnauthorizedResult();
var viewModel = new MediaManagerFolderCreateViewModel();
UpdateModel(viewModel);
if (!_mediaLibraryService.CanManageMediaFolder(viewModel.FolderPath)) {
return new HttpUnauthorizedResult();
}
try {
_mediaLibraryService.CreateFolder(viewModel.FolderPath, viewModel.Name);
Services.Notifier.Information(T("Media folder created"));
@ -66,9 +76,13 @@ namespace Orchard.MediaLibrary.Controllers {
}
public ActionResult Edit(string folderPath) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Couldn't edit media folder")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Couldn't edit media folder")))
return new HttpUnauthorizedResult();
if (!_mediaLibraryService.CanManageMediaFolder(folderPath)) {
return new HttpUnauthorizedResult();
}
var viewModel = new MediaManagerFolderEditViewModel {
FolderPath = folderPath,
Name = folderPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Last()
@ -80,12 +94,16 @@ namespace Orchard.MediaLibrary.Controllers {
[HttpPost, ActionName("Edit")]
[FormValueRequired("submit.Save")]
public ActionResult Edit() {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Couldn't edit media folder")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Couldn't edit media folder")))
return new HttpUnauthorizedResult();
var viewModel = new MediaManagerFolderEditViewModel();
UpdateModel(viewModel);
if (!_mediaLibraryService.CanManageMediaFolder(viewModel.FolderPath)) {
return new HttpUnauthorizedResult();
}
try {
_mediaLibraryService.RenameFolder(viewModel.FolderPath, viewModel.Name);
Services.Notifier.Information(T("Media folder renamed"));
@ -101,12 +119,16 @@ namespace Orchard.MediaLibrary.Controllers {
[HttpPost, ActionName("Edit")]
[FormValueRequired("submit.Delete")]
public ActionResult Delete() {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Couldn't delete media folder")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Couldn't delete media folder")))
return new HttpUnauthorizedResult();
var viewModel = new MediaManagerFolderEditViewModel();
UpdateModel(viewModel);
if (!_mediaLibraryService.CanManageMediaFolder(viewModel.FolderPath)) {
return new HttpUnauthorizedResult();
}
try {
_mediaLibraryService.DeleteFolder(viewModel.FolderPath);
Services.Notifier.Information(T("Media folder deleted"));
@ -122,9 +144,13 @@ namespace Orchard.MediaLibrary.Controllers {
[HttpPost]
public ActionResult Move(string folderPath, int[] mediaItemIds) {
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent, T("Couldn't move media items")))
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Couldn't move media items")))
return new HttpUnauthorizedResult();
if (!_mediaLibraryService.CanManageMediaFolder(folderPath)) {
return new HttpUnauthorizedResult();
}
foreach (var media in Services.ContentManager.Query().ForPart<MediaPart>().ForContentItems(mediaItemIds).List()) {
// don't try to rename the file if there is no associated media file

View File

@ -9,12 +9,18 @@ using Orchard.MediaLibrary.ViewModels;
using Orchard.Themes;
using Orchard.UI.Admin;
using Orchard.ContentManagement;
using Orchard.MediaLibrary.Services;
namespace Orchard.MediaLibrary.Controllers {
[Admin, Themed(false)]
public class OEmbedController : Controller {
public OEmbedController(IOrchardServices services) {
private readonly IMediaLibraryService _mediaLibraryService;
public OEmbedController(
IOrchardServices services,
IMediaLibraryService mediaManagerService) {
Services = services;
_mediaLibraryService = mediaManagerService;
}
public IOrchardServices Services { get; set; }
@ -32,6 +38,15 @@ namespace Orchard.MediaLibrary.Controllers {
[ActionName("Index")]
[ValidateInput(false)]
public ActionResult IndexPOST(string folderPath, string url, string type, string title, string html, string thumbnail, string width, string height, string description) {
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia))
return new HttpUnauthorizedResult();
// Check permission.
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent) && !_mediaLibraryService.CanManageMediaFolder(folderPath)) {
return new HttpUnauthorizedResult();
}
var viewModel = new OEmbedViewModel {
Url = url,
FolderPath = folderPath

View File

@ -13,12 +13,19 @@ namespace Orchard.MediaLibrary.Controllers {
public class WebSearchController : Controller {
private readonly IMediaLibraryService _mediaLibraryService;
private readonly IContentManager _contentManager;
public WebSearchController(IMediaLibraryService mediaManagerService, IContentManager contentManager) {
public WebSearchController(
IMediaLibraryService mediaManagerService,
IContentManager contentManager,
IOrchardServices orchardServices) {
_mediaLibraryService = mediaManagerService;
_contentManager = contentManager;
Services = orchardServices;
}
public IOrchardServices Services { get; set; }
public ActionResult Index(string folderPath, string type) {
var viewModel = new ImportMediaViewModel {
FolderPath = folderPath,
@ -30,7 +37,15 @@ namespace Orchard.MediaLibrary.Controllers {
[HttpPost]
public JsonResult ImagePost(string folderPath, string type, string url) {
public ActionResult ImagePost(string folderPath, string type, string url) {
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia))
return new HttpUnauthorizedResult();
// Check permission.
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
if (!Services.Authorizer.Authorize(Permissions.ManageMediaContent) && !_mediaLibraryService.CanManageMediaFolder(folderPath)) {
return new HttpUnauthorizedResult();
}
try {
var buffer = new WebClient().DownloadData(url);

View File

@ -178,6 +178,7 @@
<Compile Include="Providers\OEmbedMenu.cs" />
<Compile Include="Providers\WebSearchMenu.cs" />
<Compile Include="ResourceManifest.cs" />
<Compile Include="Security\MediaAuthorizationEventHandler.cs" />
<Compile Include="Services\IMediaLibraryService.cs" />
<Compile Include="Services\MediaLibraryService.cs" />
<Compile Include="Services\Shapes.cs" />

View File

@ -4,13 +4,15 @@ using Orchard.Security.Permissions;
namespace Orchard.MediaLibrary {
public class Permissions : IPermissionProvider {
public static readonly Permission ManageMediaContent = new Permission { Description = "Managing Media", Name = "ManageMediaContent" };
public static readonly Permission ManageMediaContent = new Permission { Description = "Manage Media", Name = "ManageMediaContent" };
public static readonly Permission ManageOwnMedia = new Permission { Description = "Manage Own Media", Name = "ManageOwnMedia", ImpliedBy = new[] { ManageMediaContent } };
public virtual Feature Feature { get; set; }
public IEnumerable<Permission> GetPermissions() {
return new[] {
ManageMediaContent,
ManageOwnMedia,
};
}
@ -33,6 +35,7 @@ namespace Orchard.MediaLibrary {
},
new PermissionStereotype {
Name = "Contributor",
Permissions = new[] {ManageOwnMedia}
},
};
}

View File

@ -15,7 +15,7 @@ namespace Orchard.MediaLibrary.Providers {
builder.AddImageSet("clientstorage")
.Add(T("My Computer"), "5",
menu => menu.Action("Index", "ClientStorage", new { area = "Orchard.MediaLibrary" })
.Permission(Permissions.ManageMediaContent));
.Permission(Permissions.ManageOwnMedia));
}
}
}

View File

@ -15,7 +15,7 @@ namespace Orchard.MediaLibrary.Providers {
builder.AddImageSet("oembed")
.Add(T("Media Url"), "10",
menu => menu.Action("Index", "OEmbed", new { area = "Orchard.MediaLibrary" })
.Permission(Permissions.ManageMediaContent));
.Permission(Permissions.ManageOwnMedia));
}
}
}

View File

@ -15,7 +15,7 @@ namespace Orchard.MediaLibrary.Providers {
builder.AddImageSet("websearch")
.Add(T("Web Search"), "7",
menu => menu.Action("Index", "WebSearch", new { area = "Orchard.MediaLibrary" })
.Permission(Permissions.ManageMediaContent));
.Permission(Permissions.ManageOwnMedia));
}
}
}

View File

@ -0,0 +1,35 @@
using Orchard.ContentManagement;
using Orchard.MediaLibrary.Models;
using Orchard.MediaLibrary.Services;
using Orchard.Security;
namespace Orchard.MediaLibrary.Security {
public class MediaAuthorizationEventHandler : IAuthorizationServiceEventHandler {
private readonly IAuthorizer _authorizer;
private readonly IMediaLibraryService _mediaLibraryService;
public MediaAuthorizationEventHandler(
IAuthorizer authorizer,
IMediaLibraryService mediaLibraryService) {
_authorizer = authorizer;
_mediaLibraryService = mediaLibraryService;
}
public void Checking(CheckAccessContext context) { }
public void Complete(CheckAccessContext context) { }
public void Adjust(CheckAccessContext context) {
var mediaPart = context.Content.As<MediaPart>();
if (mediaPart != null) {
if(_authorizer.Authorize(Permissions.ManageMediaContent)) {
context.Granted = true;
return;
}
if(_authorizer.Authorize(Permissions.ManageOwnMedia)) {
context.Granted = _mediaLibraryService.CanManageMediaFolder(mediaPart.FolderPath);
}
}
}
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using Orchard.ContentManagement;
@ -36,7 +37,7 @@ namespace Orchard.MediaLibrary.Services {
/// <returns>The public URL for the media.</returns>
string GetMediaPublicUrl(string mediaPath, string fileName);
MediaFolder GetRootMediaFolder();
IMediaFolder GetRootMediaFolder();
/// <summary>
/// Retrieves the media folders within a given relative path.
@ -131,4 +132,30 @@ namespace Orchard.MediaLibrary.Services {
/// <returns>The path to the uploaded file.</returns>
string UploadMediaFile(string folderPath, string fileName, Stream inputStream);
}
public static class MediaLibrayServiceExtensions {
public static bool CanManageMediaFolder(this IMediaLibraryService service, string folderPath) {
// The current user can manage a media if he has access to the whole hierarchy
// or the media is under his personal storage folder.
var rootMediaFolder = service.GetRootMediaFolder();
if (rootMediaFolder == null) {
return true;
}
var mediaPath = folderPath + "\\";
var rootPath = rootMediaFolder.MediaPath + "\\";
return mediaPath.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase);
}
public static string GetRootedFolderPath(this IMediaLibraryService service, string folderPath) {
var rootMediaFolder = service.GetRootMediaFolder();
if (rootMediaFolder != null) {
return Path.Combine(rootMediaFolder.MediaPath, folderPath ?? "");
}
return folderPath;
}
}
}

View File

@ -210,7 +210,21 @@ namespace Orchard.MediaLibrary.Services {
return GetPublicUrl(Path.Combine(mediaPath, fileName));
}
public MediaFolder GetRootMediaFolder() {
public IMediaFolder GetRootMediaFolder() {
if (_orchardServices.Authorizer.Authorize(Permissions.ManageMediaContent)) {
return null;
}
if (_orchardServices.Authorizer.Authorize(Permissions.ManageOwnMedia)) {
var currentUser = _orchardServices.WorkContext.CurrentUser;
var userPath = _storageProvider.Combine("Users", currentUser.UserName);
return new MediaFolder() {
Name = currentUser.UserName,
MediaPath = userPath
};
}
return null;
}

View File

@ -60,7 +60,7 @@ namespace Orchard.MediaLibrary.Services {
UrlHelper url) {
var user = _membershipService.ValidateUser(userName, password);
if (!_authorizationService.TryCheckAccess(Permissions.ManageMediaContent, user, null)) {
if (!_authorizationService.TryCheckAccess(Permissions.ManageOwnMedia, user, null)) {
throw new OrchardCoreException(T("Access denied"));
}
@ -72,6 +72,11 @@ namespace Orchard.MediaLibrary.Services {
directoryName = "media";
}
// If the user only has access to his own folder, rewrite the folder name
if (!_authorizationService.TryCheckAccess(Permissions.ManageMediaContent, user, null)) {
directoryName = Path.Combine(_mediaLibraryService.GetRootedFolderPath(directoryName));
}
try {
// delete the file if it already exists, e.g. an updated image in a blog post
// it's safe to delete the file as each content item gets a specific folder

View File

@ -112,7 +112,7 @@ var mediaLibrarySettings = {
<div class="media-library-folder-title" data-bind="click: folderClicked, attr: { 'data-media-path': folderPath() }, css: {selected: isSelected()}">
<a 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, afterRender: afterRenderMediaFolderTemplate}, visible: isExpanded()">
<ul data-bind="template: { name: 'media-folder-template', foreach: childFolders, afterRender: afterRenderMediaFolderTemplate}, visible: isExpanded()">
</ul>
</div>
</li>

View File

@ -6,10 +6,12 @@
<div class="breadCrumbs">
<p>@Html.ActionLink(T("Media Library").ToString(), "Index", "Admin", new { area = "Orchard.MediaLibrary" }, new {}) &#62;
@if (Model.FolderPath != null) {
foreach (var folder in Model.FolderPath.Split('/')) {
if (!String.IsNullOrEmpty(folder)) {
@Html.ActionLink(folder, "Edit", new {folderPath = folder})
<text>&#62;</text>
var fullPath = "";
foreach (var folder in Model.FolderPath.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar })) {
if (!String.IsNullOrEmpty(folder)) {
fullPath = Path.Combine(fullPath, folder);
@Html.ActionLink(folder, "Index", "Admin", new {folderPath = fullPath }, null)
<text>&#62;</text>
}
}
}

View File

@ -75,7 +75,7 @@ namespace Orchard.Search.Controllers {
// ignore search results which content item has been removed or unpublished
var foundItems = _contentManager.GetMany<IContent>(foundIds, VersionOptions.Published, new QueryHints()).ToList();
foreach (var contentItem in foundItems) {
list.Add(_contentManager.BuildDisplay(contentItem, "Summary"));
list.Add(_contentManager.BuildDisplay(contentItem, searchSettingPart.DisplayType));
}
searchHits.TotalItemCount -= foundIds.Count() - foundItems.Count();

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
@ -49,10 +47,13 @@ namespace Orchard.Search.Drivers {
var model = new SearchSettingsFieldsViewModel();
var searchFields = part.SearchFields;
model.DisplayType = part.DisplayType;
if (updater != null) {
if (updater.TryUpdateModel(model, Prefix, null, null)) {
part.SearchFields = model.Entries.ToDictionary(x => x.Index, x => x.Fields.Where(e => e.Selected).Select(e => e.Field).ToArray());
part.FilterCulture = model.FilterCulture;
part.DisplayType = model.DisplayType;
}
}
else if (_indexManager.HasIndexProvider()) {

View File

@ -24,5 +24,10 @@ namespace Orchard.Search.Models {
get { return this.Retrieve(x => x.SearchIndex); }
set { this.Store(x => x.SearchIndex, value); }
}
public string DisplayType {
get { return this.Retrieve(x => x.DisplayType, "Summary"); }
set { this.Store(x => x.DisplayType, value); }
}
}
}

View File

@ -16,6 +16,7 @@ namespace Orchard.Search.ViewModels {
public IList<IndexSettingsEntry> Entries { get; set; }
public bool FilterCulture { get; set; }
public string DisplayType { get; set; }
}
public class IndexSettingsEntry {

View File

@ -44,5 +44,9 @@
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.FilterCulture)">@T("Narrow search to current culture only")</label>
<span class="hint">@T("If checked, search results will only include content items localized in the current culture of the request.")</span>
</div>
<div>
<label for="@Html.FieldIdFor(m => m.DisplayType)">@T("Display Type")</label>
@Html.TextBoxFor(m => m.DisplayType, new { @class = "text single-line" })
<span class="hint">@T("The display type to use for content items in the search results page.")</span>
</div>
</fieldset>

View File

@ -1,17 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.ContentManagement;
using Orchard.Taxonomies.Models;
namespace Orchard.Taxonomies.Services {
public class TermCountProcessor : ITermCountProcessor {
private readonly IContentManager _contentManager;
private readonly ITaxonomyService _taxonomyService;
public TermCountProcessor(IContentManager contentManager, ITaxonomyService taxonomyService) {
_contentManager = contentManager;
public TermCountProcessor(ITaxonomyService taxonomyService) {
_taxonomyService = taxonomyService;
}
@ -31,4 +25,4 @@ namespace Orchard.Taxonomies.Services {
}
}
}
}
}

View File

@ -30,9 +30,9 @@
var selectedTerms = Newtonsoft.Json.JsonConvert.SerializeObject(Model.Terms.Where(x => x.IsChecked).Select(x => new { label = x.Name, value = x.Id, levels = 0, disabled = true }));
}
<fieldset class="taxonomy-wrapper" data-name-prefix="@Html.FieldNameFor(m => m)" data-id-prefix="@Html.FieldIdFor(m => m)">
<legend @if(Model.Settings.Required) { <text>class="required"</text> }>@Model.DisplayName</legend>
<label @if(Model.Settings.Required) { <text>class="required"</text> }>@Model.DisplayName</label>
@if (Model.Settings.Autocomplete) {
<div class="terms-editor" data-all-terms="@allTerms" data-selected-terms="@selectedTerms" data-allow-new-terms="@Model.Settings.AllowCustomTerms.ToString().ToLower()" data-singlechoice="@Model.Settings.SingleChoice.ToString().ToLower()">
<div class="terms-editor text text-medium" data-all-terms="@allTerms" data-selected-terms="@selectedTerms" data-allow-new-terms="@Model.Settings.AllowCustomTerms.ToString().ToLower()" data-singlechoice="@Model.Settings.SingleChoice.ToString().ToLower()">
<ul></ul>
@if (Model.Settings.SingleChoice) {
<div class="hint">@T("Enter a single term. Hit <i>tab</i> or <i>enter</i> to apply the term.") @if (!Model.Settings.AllowCustomTerms) { <text>@T("This taxonomy does not allow you to create new terms.") </text> }</div>
@ -67,7 +67,7 @@
</ul>
</div>
@if (!Model.Terms.Any()) {
@if (!Model.Terms.Any() && AuthorizedFor(Orchard.Taxonomies.Permissions.CreateTerm)) {
<div class="no-terms">
@T("There are no terms defined for {0} yet.", Model.DisplayName)
<a href="@Url.Action("Index", "TermAdmin", new { taxonomyId = Model.TaxonomyId, area = "Orchard.Taxonomies" })">@T("Create some terms")</a>

View File

@ -42,7 +42,7 @@
}
</ul>
@if (!Model.Terms.Any()) {
@if (!Model.Terms.Any() && AuthorizedFor(Orchard.Taxonomies.Permissions.CreateTerm)) {
<div class="no-terms">
@T("There are no terms defined for {0} yet.", Model.DisplayName)
<a href="@Url.Action("Index", "TermAdmin", new { taxonomyId = Model.TaxonomyId, area = "Orchard.Taxonomies" })">@T("Create some terms")</a>

View File

@ -69,7 +69,8 @@ namespace Orchard.Widgets.Controllers {
}
LayerPart currentLayer = layerId == null
? layers.FirstOrDefault(layer => layer.Name == "Default") ?? layers.FirstOrDefault()
// look for the "Default" layer, or take the first if it doesn't exist
? layers.FirstOrDefault(x => x.Name == "Default") ?? layers.FirstOrDefault()
: layers.FirstOrDefault(layer => layer.Id == layerId);
if (currentLayer == null && layerId != null) { // Incorrect layer id passed

View File

@ -68,7 +68,13 @@
return false;
}
return confirm(confirmRemoveMessage);
// use a custom message if its set in data-message
var dataMessage = $(this).data('message');
if (dataMessage === undefined) {
dataMessage = confirmRemoveMessage;
}
return confirm(dataMessage);
});
$(".check-all").change(function () {

View File

@ -97,7 +97,7 @@
@using (Script.Foot()) {
<script type="text/javascript">
//<![CDATA[
var confirmRemoveMessage = '@T("Are you sure you want to remove this element ?")';
var confirmRemoveMessage = '@T("Are you sure you want to remove this element?")';
//]]>
</script>
}

View File

@ -21,14 +21,6 @@ namespace Orchard.ContentManagement {
public virtual ContentItem ContentItem { get; set; }
//interesting thought, should/could parts also have zones (would then have zones on the page, content item and parts...)?
private readonly IZoneCollection _zones = new ZoneCollection();
public virtual IZoneCollection Zones {
get {
return _zones;
}
}
/// <summary>
/// The ContentItem's identifier.
/// </summary>

View File

@ -0,0 +1,5 @@
namespace Orchard.DisplayManagement {
public interface IPositioned {
string Position { get; }
}
}

View File

@ -1,5 +1,4 @@
using System.Diagnostics;
using Orchard.DisplayManagement.Shapes;
using Orchard.DisplayManagement.Shapes;
namespace Orchard.DisplayManagement {
/// <summary>

View File

@ -0,0 +1,26 @@
using System.Web;
namespace Orchard.DisplayManagement {
public class PositionWrapper : IHtmlString, IPositioned {
private IHtmlString _value;
public string Position { get; private set; }
public PositionWrapper(IHtmlString value, string position) {
_value = value;
Position = position;
}
public PositionWrapper(string value, string position)
: this(new HtmlString(HttpUtility.HtmlEncode(value)), position) {
}
public string ToHtmlString() {
return _value.ToHtmlString();
}
public override string ToString() {
return _value.ToString();
}
}
}

View File

@ -4,11 +4,11 @@ using System.Dynamic;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web;
namespace Orchard.DisplayManagement.Shapes {
[DebuggerTypeProxy(typeof(ShapeDebugView))]
public class Shape : Composite, IShape, IEnumerable<object> {
public class Shape : Composite, IShape, IPositioned, IEnumerable<object> {
private const string DefaultPosition = "5";
private readonly IList<object> _items = new List<object>();
@ -22,6 +22,12 @@ namespace Orchard.DisplayManagement.Shapes {
public virtual IDictionary<string, string> Attributes { get { return _attributes; } }
public virtual IEnumerable<dynamic> Items { get { return _items; } }
public string Position {
get {
return Metadata.Position;
}
}
public Shape() {
Metadata = new ShapeMetadata();
}
@ -33,13 +39,14 @@ namespace Orchard.DisplayManagement.Shapes {
}
try {
// todo: (sebros) this is a temporary implementation to prevent common known scenarios throwing exceptions. The final solution would need to filter based on the fact that it is a Shape instance
if (item is MvcHtmlString ||
item is String) {
// need to implement positioned wrapper for non-shape objects
if (position != null && item is IHtmlString) {
item = new PositionWrapper((IHtmlString)item, position);
}
else if (position != null && item is string) {
item = new PositionWrapper((string)item, position);
}
else if (item is IShape) {
((dynamic)item).Metadata.Position = position;
((IShape)item).Metadata.Position = position;
}
}
catch {

View File

@ -151,6 +151,8 @@
</ItemGroup>
<ItemGroup>
<Compile Include="BackgroundHttpContextFactory.cs" />
<Compile Include="DisplayManagement\IPositioned.cs" />
<Compile Include="DisplayManagement\PositionWrapper.cs" />
<Compile Include="Data\Migration\Schema\AddUniqueConstraintCommand.cs" />
<Compile Include="Data\Migration\Schema\DropUniqueConstraintCommand.cs" />
<Compile Include="Environment\Extensions\Models\LifecycleStatus.cs" />

View File

@ -155,6 +155,10 @@ namespace Orchard.UI.Resources {
return this;
}
/// <summary>
/// Sets the version of the resource.
/// </summary>
/// <param name="version">The version to set, in the form of <code>major.minor[.build[.revision]]</code></param>
public ResourceDefinition SetVersion(string version) {
Version = version;
return this;