Compare commits

...

72 Commits

Author SHA1 Message Date
Sebastien Ros
248cc7ecde Updating webpi package files 2015-09-21 15:29:31 -07:00
Sébastien Ros
bb41f1f559 Merge pull request #5831 from OrchardCMS/feature/ownmedia
Implementing own media management
2015-09-21 15:08:19 -07:00
Sébastien Ros
5903f75665 Merge pull request #5828 from OrchardCMS/feature/layoutpermission
Adding ManageLayout permission
2015-09-21 15:07:57 -07:00
Sébastien Ros
46182edc5d Merge pull request #5827 from rtpHarry/1.9.x
[Fixes #5826] Restore placements confirmation message fix
2015-09-21 14:36:44 -07:00
Sebastien Ros
accd4196dc Implementing own media management 2015-09-21 14:09:37 -07:00
Sebastien Ros
d3d85f3519 Adding ManageLayout permission 2015-09-21 14:04:37 -07:00
Sébastien Ros
6e90f704f8 Merge pull request #5833 from mjy78/issue/5829
[Fixes #5829] Cannot delete spam comment when viewing comment details
2015-09-21 13:02:32 -07:00
Sébastien Ros
497b782a6a Merge pull request #5837 from mjy78/issue/5836
#5836 Fix null ref exception in CommentPartDriver
2015-09-21 12:58:01 -07:00
Sebastien Ros
bc935f18b3 Fixing unit tests 2015-09-21 12:45:57 -07:00
Sebastien Ros
12a51ea7af [Fixes #5751, #5794] Removing Edit inline 2015-09-21 12:08:19 -07:00
Sebastien Ros
f0b1639143 [Fixes #5616] Revert "Using master content item to create translations"
This reverts commit fa09e2e83a.
2015-09-21 11:27:46 -07:00
myates
9bb95efaaa #5836 Fix null ref exception in CommentPartDriver 2015-09-21 10:26:14 +10:00
myates
e6c042d1a0 Fix build error 2015-09-19 22:03:12 +10:00
myates
df583ea571 Merge branch 'Orchard-1.9.x' into issue/5829 2015-09-19 21:20:21 +10:00
myates
adbab55ffc Comment service now fetches latest (instead of published) when deleting a comment. 2015-09-18 16:45:10 +10:00
Matthew Harris
20fbd99658 updated all instances of RemoveUrl checks 2015-09-17 22:58:58 +01:00
Matthew Harris
d9a0e040c1 don't overwrite confirmRemoveMessage 2015-09-17 22:54:47 +01:00
Matthew Harris
4aaf4d34d0 amends based on PR feedback 2015-09-17 21:58:12 +01:00
Matthew Harris
7143792c0e fix #5826 2015-09-17 19:44:25 +01:00
Sebastien Ros
4214329a1f Prevent unnecessary db calls for containers 2015-09-16 17:52:33 -07:00
Sebastien Ros
921b2472f1 Adding filter for own content 2015-09-16 17:52:27 -07:00
Sebastien Ros
9e9e19c439 Add setting to hide Container editor 2015-09-16 17:52:13 -07:00
Sebastien Ros
f3595881fe Hide taxonomy link if permission is not granted 2015-09-16 17:52:04 -07:00
Sebastien Ros
ba36eae848 Prevent MediaLibrary popup without permissions
If the user doesn't have ManageMedia permissions,
the markdown editor will use a specific image editor.
2015-09-16 17:51:50 -07:00
Sebastien Ros
0bf76dc7ac Implementing a positioned wrapper for text elements
When adding a text directly to a zone, the position will now
be taken into account. This is also useful for the Capture feature.

Conflicts:
	src/Orchard/Orchard.Framework.csproj
2015-09-16 17:51:23 -07:00
Sipke Schoorstra
66ff5d36e9 Merge pull request #5810 from Codinlab/issue/5809
Fixes #5809 : Layouts : DisplayText encoded twice in Design mode
2015-09-15 14:03:55 +01:00
Thierry Fleury
749ff5dc72 Fixes #5809 : Layouts : DisplayText encoded twice in Design mode 2015-09-15 14:53:52 +02:00
Nicholas Mayne
85c4a99c13 Merge pull request #5798 from mvarblow/patch-4
Fixed typo in Sites.Config comment.
2015-09-13 17:35:28 +01:00
Sipke Schoorstra
a3fe3592c6 Merge pull request #5799 from mvarblow/patch-5
Code clean-up in TermCountProcessor.cs
2015-09-13 13:42:39 +01:00
mvarblow
a2c31a1e5c Code clean-up in TermCountProcessor.cs
Removed an unused field and unused using statements.
2015-09-13 08:33:08 -04:00
mvarblow
3f6dfa3dec Fixed typo in Sites.Config comment. 2015-09-12 20:54:54 -04:00
Sébastien Ros
f9ef297777 Merge pull request #5780 from mjy78/issue/5779
[Fixes #5779] Query and Taxonomy Elements should include value attribute for default option
2015-09-11 15:07:51 -07:00
Sébastien Ros
801b8998db Merge pull request #5783 from mjy78/issue/20377
[Fixes #4206] Ensure comment tokens available to anti-spam module.
2015-09-11 15:03:45 -07:00
myates
92b7ad7554 Issue 20377 - Ensure comment tokens available to antispam module. 2015-09-11 17:15:42 +10:00
myates
18bb847488 Force empty value attribute on default option label in taxonomy and query element dropdown lists. 2015-09-11 14:19:52 +10:00
Sébastien Ros
47133604c3 Merge pull request #5676 from RoyalVeterinaryCollege/3404
[Fixes #3404] Added Required attribute to Enumeration and Numeric Fields
2015-09-10 12:24:28 -07:00
Sebastien Ros
80b93e412a Supporting custom display type for search results 2015-09-09 15:44:53 -07:00
Sebastien Ros
e839f84537 Fixing unit test
The ThemeExtensionFolder stub should have been used
2015-09-09 14:53:16 -07:00
Sebastien Ros
a548e47f90 [Fixes #5283] Adding some documentation for ResourceDefinition.SetVersion
It has to be a vallid SermVer format.
2015-09-09 14:52:41 -07:00
Sebastien Ros
89e8658f49 Improving protected zones in settings UI
Protecting MaxPagedCount
Protecting BaseUrl
Unhiding BaseUrl and SuperUser so that users can see what the values are
2015-09-09 11:27:18 -07:00
Sebastien Ros
6d255f24ab Fixing exception when the Default layer doesn't exist 2015-09-09 09:25:01 -07:00
Sebastien Ros
e8f0ba6ec7 Fixing RecipeManagerTests 2015-09-08 17:12:44 -07:00
Sebastien Ros
60798f9346 Fixing TokenManagerTests 2015-09-08 17:09:53 -07:00
Sebastien Ros
c83f5d88cb Supporting lazy cache manager resolution 2015-09-08 17:04:45 -07:00
Sebastien Ros
2f2134412e [Fixes #5492] Adding JavaScriptEncode token 2015-09-08 16:13:55 -07:00
Sébastien Ros
1e6b6bb24f Merge pull request #5568 from Codinlab/Tokens
Token chaining improvements
2015-09-08 16:08:17 -07:00
Sébastien Ros
16de29dabd Merge pull request #5652 from Codinlab/issues/5614
Fixes #5614 : ContentPickerField and LayoutPart issue
2015-09-08 15:57:38 -07:00
Sebastien Ros
691809cd85 [Fixes #5733] Update default Layout state settings 2015-09-08 15:40:28 -07:00
Sébastien Ros
ce1df8d794 Merge pull request #5677 from dcinzona/Gulpfile-watch-assets.json
Enables gulp watch of Assets.json
2015-09-08 11:37:54 -07:00
Sébastien Ros
74be36d8a3 Merge pull request #5763 from phillipsj/1.9.x
Updating DynamicForms Module.txt
2015-09-08 11:11:12 -07:00
Jamie Phillips
2bb7eb2f91 Updating DynamicForms Module.txt
The CustomForms module name and website were listed.
2015-09-08 13:06:04 -04:00
Sebastien Ros
c4592aa65f Selecting the layer "Default" on the widgets start screen
Fixes a specflow bug which expect "Default" to be the default
selected layer. Appeared since layers are ordered alphabetically.
2015-09-08 09:27:36 -07:00
Sebastien Ros
9bac35b3f1 Removing duplicated using 2015-09-08 09:12:26 -07:00
Lombiq
2a0f866f26 Fixing exception when importing Projection Elements with the default layout selected
Fixed the export of this previously, this was remaining.
2015-09-08 01:01:23 +02:00
Thierry Fleury
371238a882 Add parameterized tokens tests 2015-09-07 15:14:20 +02:00
Thierry Fleury
67e7d1074e Allow Text token chaining when possible.
Correct some errors on token descriptions.
2015-09-07 11:57:20 +02:00
Thierry Fleury
25c34d8bca Fixes #5563 : Allow token chaining on DynamicForms submissions. 2015-09-07 11:57:19 +02:00
Thierry Fleury
75f3a618f0 Enhances TaxonomyField's token chaining. 2015-09-07 11:57:18 +02:00
Thierry Fleury
9947f3973e Add new Chain method for tokens
The new method allows working with parameterized tokens.
2015-09-07 11:57:17 +02:00
Lombiq
a685517118 Normalizing all catch (Exception ex) lines and adding exception fatality check where applicable, fixes #3949 2015-09-07 00:17:57 +02:00
Daniel Stolt
14482162fd Marked some tests as long-running. Fixes #5702. 2015-09-06 00:31:15 +02:00
Sébastien Ros
0f757655b8 Merge pull request #5736 from Codinlab/issue/5735
Fixes #5735 : ContentType name encoding in Lists
2015-09-04 16:35:03 -07:00
Sebastien Ros
39fb266221 Skipping DateTime tests until the right solution is found
c.f. #5702
2015-09-04 15:59:24 -07:00
Sebastien Ros
9b91b1b260 Removing unnecessary resolution 2015-09-04 13:20:08 -07:00
Thierry Fleury
894e90f485 Use new ActionLink extension method. 2015-09-04 15:08:30 +02:00
Thierry Fleury
97bb52bbdd Add LinkExtensions
Allows using IHtmlString as linkText
2015-09-04 15:05:25 +02:00
Thierry Fleury
f5772334e1 Fixes #5735 : ContentType name encoding in Lists 2015-09-03 10:05:02 +02:00
Gustavo Tandeciarz
bbcdd67b95 Cleanup
gulp-util was previously used during development and is no longer required.
2015-08-21 20:15:51 -04:00
Gustavo Tandeciarz
cfc1dc2379 Enables gulp watch of Assets.json
Will restart the watch task when Assets.json is changed.
2015-08-21 20:08:41 -04:00
Jeff
770704f3a7 Added required attribute to enumeration and numeric fields.
Fixes #3404.
2015-08-21 16:40:01 +01:00
Thierry Fleury
9980633ac3 Use Enumerable.Empty 2015-08-20 23:57:38 +02:00
Thierry Fleury
746d3d54c2 Fixes 5614 : ContentPickerField and LayoutPart throws argument null exception. 2015-08-18 12:46:09 +02:00
164 changed files with 1370 additions and 599 deletions

View File

@@ -0,0 +1,9 @@
/**********************************************************************/
/* Install.SQL */
/* Creates a login and makes the user a member of db roles */
/* */
/* Modifications for SQL AZURE - ON MASTER */
/**********************************************************************/
CREATE LOGIN PlaceHolderForUser WITH PASSWORD = 'PlaceHolderForPassword'

View File

@@ -0,0 +1,15 @@
/**********************************************************************/
/* CreateUser.SQL */
/* Creates a user and makes the user a member of db roles */
/* This script runs against the User database and requires connection string */
/* Supports SQL Server and SQL AZURE */
/**********************************************************************/
-- Create database user and map to login
-- and add user to the datareader, datawriter, ddladmin and securityadmin roles
--
CREATE USER PlaceHolderForUser FOR LOGIN PlaceHolderForUser;
GO
EXEC sp_addrolemember 'db_owner', PlaceHolderForUser;
GO

View File

@@ -1,45 +0,0 @@
/**********************************************************************/
/* Install.SQL */
/* Creates a login and makes the user a member of db roles */
/* */
/**********************************************************************/
-- Declare variables for database name, username and password
DECLARE @dbName sysname,
@dbUser sysname,
@dbPwd nvarchar(max);
-- Set variables for database name, username and password
SET @dbName = 'PlaceHolderForDb';
SET @dbUser = 'PlaceHolderForUser';
SET @dbPwd = 'PlaceHolderForPassword';
DECLARE @cmd nvarchar(max)
-- Create login
IF( SUSER_SID(@dbUser) is null )
BEGIN
print '-- Creating login '
SET @cmd = N'CREATE LOGIN ' + quotename(@dbUser) + N' WITH PASSWORD ='''+ replace(@dbPwd, '''', '''''') + N''''
EXEC(@cmd)
END
-- Create database user and map to login
-- and add user to the datareader, datawriter, ddladmin and securityadmin roles
--
SET @cmd = N'USE ' + quotename(@DBName) + N';
IF( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = ''' + replace(@dbUser, '''', '''''') + N'''))
BEGIN
print ''-- Creating user'';
CREATE USER ' + quotename(@dbUser) + N' FOR LOGIN ' + quotename(@dbUser) + N';
print ''-- Adding user'';
EXEC sp_addrolemember ''db_ddladmin'', ''' + replace(@dbUser, '''', '''''') + N''';
print ''-- Adding user'';
EXEC sp_addrolemember ''db_securityadmin'', ''' + replace(@dbUser, '''', '''''') + N''';
print ''-- Adding user'';
EXEC sp_addrolemember ''db_datareader'', ''' + replace(@dbUser, '''', '''''') + N''';
print ''-- Adding user'';
EXEC sp_addrolemember ''db_datawriter'', ''' + replace(@dbUser, '''', '''''') + N''';
END'
EXEC(@cmd)
GO

View File

@@ -1,7 +1,13 @@
<MSDeploy.iisApp>
<iisapp path="Orchard" managedRuntimeVersion="v4.0" />
<iisapp path="Orchard" managedRuntimeVersion="v4.0" />
<setAcl path="Orchard/App_Data" setAclAccess="Modify" />
<setAcl path="Orchard/Media" setAclAccess="Modify" />
<setAcl path="Orchard/bin/HostRestart" setAclAccess="Modify" />
<dbFullSql path="install.sql" />
</MSDeploy.iisApp>
<!-- Runs SQL script to create login and assign permissions, requires transacted="false"
This script runs as the database administrator provided in parameters.xml
-->
<dbfullsql path="createlogin.sql" transacted="false" />
<dbfullsql path="createuser.sql" transacted="false" />
</MSDeploy.iisApp>

View File

@@ -47,19 +47,17 @@
<!-- Prompts for the admin password and uses it for the administrator connection string.
This is use to create a login and assign permissions. The SQL tag indicates it is a parameter required for SQL.
The DbAdminPassword tag indicates it should be used when the user is creating a new database. If they're not, it can be filled in with the DbUserPassword value. -->
<parameter name="Database Administrator Password" description="Password for the database administrator account." tags="New, Password, SQL, dbAdminPassword">
<parameter name="Database Administrator Password" description="Password for the database administrator account." tags="Password, SQL, dbAdminPassword">
</parameter>
<parameter name="Admin Connection String SqlServer"
tags="SQLConnectionString, AdminConnectionString, Hidden, Validate" description="Automatically sets the connection string for the connection request."
defaultValue="Data Source={Database Server};Initial Catalog={Database Name};User Id={Database Administrator};Password={Database Administrator Password}">
<parameterEntry type="ProviderPath" scope="dbfullsql" match="install.sql" />
<parameter name="Admin Connection String SqlServer1" tags="SQLConnectionString, sql, Hidden" description="Automatically sets the connection string for the connection request." defaultValue="Data Source={Database Server};Initial Catalog=MASTER;User Id={Database Administrator};Password={Database Administrator Password}">
<parameterEntry type="ProviderPath" scope="dbfullsql" match="createlogin.sql" />
</parameter>
<parameter name="Non-Admin Connection String SqlServer"
tags="SQLConnectionString, UserConnectionString, Hidden" description="Automatically sets the connection string for the connection request."
defaultValue="Data Source={Database Server};Initial Catalog={Database Name};User Id={Database Username};Password={Database Password}">
<parameter name="Admin Connection String SqlServer2" tags="SQLConnectionString, SQL, Hidden" description="Automatically sets the connection string for the connection request." defaultValue="Data Source={Database Server};Initial Catalog={Database Name};User Id={Database Administrator};Password={Database Administrator Password}">
<parameterEntry type="ProviderPath" scope="dbfullsql" match="createuser.sql" />
</parameter>
<parameter name="Orchard Connection String" friendlyName="Orchard Connection String" description="Orchard SQL Data Connection String Setting" defaultValue="" tags="Sql, SqlCE, SingleLineConnectionString, Hidden">
<parameterEntry kind="TextFile" scope="\\Settings\.txt$" match="(?&lt;=\s*DataConnectionString:\s+)[^\s].*[^\r\n]" />

View File

@@ -13,7 +13,8 @@ var glob = require("glob"),
uglify = require("gulp-uglify"),
rename = require("gulp-rename"),
concat = require("gulp-concat"),
header = require("gulp-header");
header = require("gulp-header"),
fs = require("fs");
/*
** GULP TASKS
@@ -37,32 +38,53 @@ gulp.task("rebuild", function () {
return merge(assetGroupTasks);
});
// Continuous watch (each asset group is built whenever one of its inputs changes).
// Set "Watchers" as sub-processes in order to restart the task when Assets.json changes.
gulp.task("watch", function () {
var pathWin32 = require("path");
getAssetGroups().forEach(function (assetGroup) {
var watchPaths = assetGroup.inputPaths.concat(assetGroup.watchPaths);
gulp.watch(watchPaths, function (event) {
var isConcat = path.basename(assetGroup.outputFileName, path.extname(assetGroup.outputFileName)) !== "@";
if (isConcat)
console.log("Asset file '" + event.path + "' was " + event.type + ", rebuilding asset group with output '" + assetGroup.outputPath + "'.");
else
console.log("Asset file '" + event.path + "' was " + event.type + ", rebuilding asset group.");
var doRebuild = true;
var task = createAssetGroupTask(assetGroup, doRebuild);
var watchers;
function restart() {
if (watchers) {
watchers.forEach(function (w) {
w.remove();
w.end();
});
}
watchers = [];
// Continuous watch (each asset group is built whenever one of its inputs changes).
getAssetGroups().forEach(function (assetGroup) {
var watchPaths = assetGroup.inputPaths.concat(assetGroup.watchPaths);
var watcher = gulp.watch(watchPaths, function (event) {
var isConcat = path.basename(assetGroup.outputFileName, path.extname(assetGroup.outputFileName)) !== "@";
if (isConcat)
console.log("Asset file '" + event.path + "' was " + event.type + ", rebuilding asset group with output '" + assetGroup.outputPath + "'.");
else
console.log("Asset file '" + event.path + "' was " + event.type + ", rebuilding asset group.");
var doRebuild = true;
var task = createAssetGroupTask(assetGroup, doRebuild);
});
watchers.push(watcher);
});
}
var p;
if (p) { p.exit(); }
p = gulp.watch("Orchard.Web/{Core,Modules,Themes}/*/Assets.json", function (event) {
console.log("Asset file '" + event.path + "' was " + event.type + ", resetting asset watchers.");
restart();
});
restart();
});
/*
** ASSET GROUPS
*/
function getAssetGroups() {
var assetManifestPaths = glob.sync("Orchard.Web/{Core,Modules,Themes}/*/Assets.json");
var assetManifestPaths = glob.sync("Orchard.Web/{Core,Modules,Themes}/*/Assets.json", {});
var assetGroups = [];
assetManifestPaths.forEach(function (assetManifestPath) {
var assetManifest = require("./" + assetManifestPath);
var file = './' + assetManifestPath;
var json = fs.readFileSync(file, 'utf8');
assetManifest = eval(json);
assetManifest.forEach(function (assetGroup) {
resolveAssetGroupPaths(assetGroup, assetManifestPath);
assetGroups.push(assetGroup);

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

@@ -98,23 +98,23 @@ namespace Orchard.Tests.Modules.Recipes.Services {
[Test]
public void HarvestRecipesFailsToFindRecipesWhenCalledWithNotExistingExtension() {
var recipes = (List<Recipe>) _recipeHarvester.HarvestRecipes("cantfindme");
var recipes = _recipeHarvester.HarvestRecipes("cantfindme");
Assert.That(recipes.Count, Is.EqualTo(0));
Assert.That(recipes.Count(), Is.EqualTo(0));
}
[Test]
public void HarvestRecipesShouldHarvestRecipeXmlFiles() {
var recipes = (List<Recipe>)_recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count, Is.EqualTo(1));
var recipes = _recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count(), Is.EqualTo(1));
}
[Test]
public void ParseRecipeLoadsRecipeMetaDataIntoModel() {
var recipes = (List<Recipe>) _recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count, Is.EqualTo(1));
var recipes = _recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count(), Is.EqualTo(1));
var sampleRecipe = recipes[0];
var sampleRecipe = recipes.First();
Assert.That(sampleRecipe.Name, Is.EqualTo("cms"));
Assert.That(sampleRecipe.Description, Is.EqualTo("a sample Orchard recipe describing a cms"));
Assert.That(sampleRecipe.Author, Is.EqualTo("orchard"));

View File

@@ -1,4 +1,6 @@
using System;
using System.Linq;
using System.Threading;
using Autofac;
using NUnit.Framework;
using Orchard.Caching;
@@ -78,6 +80,42 @@ namespace Orchard.Tests.Caching {
Is.Not.SameAs(c2.CacheManager.GetCache<string, string>()));
}
[Test]
public void CacheManagerIsNotBlocking() {
var hits = 0;
string result = "";
Enumerable.Range(0, 5).AsParallel().ForAll(x =>
result = _cacheManager.Get("testItem", ctx => {
// by waiting for 100ms we expect all the calls to Get
// to enter this lambda
Thread.Sleep(100);
hits++;
return "testResult";
})
);
Assert.That(result, Is.EqualTo("testResult"));
Assert.That(hits, Is.GreaterThan(1));
}
[Test]
public void CacheManagerIsBlocking() {
var hits = 0;
string result = "";
Enumerable.Range(0, 5).AsParallel().ForAll(x =>
result = _cacheManager.Get("testItem", true, ctx => {
Thread.Sleep(100);
hits++;
return "testResult";
})
);
Assert.That(result, Is.EqualTo("testResult"));
Assert.That(hits, Is.EqualTo(1));
}
class ComponentOne {
public ICacheManager CacheManager { get; set; }

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

@@ -4,7 +4,7 @@ using Orchard.Localization.Models;
namespace Orchard.Tests.Localization {
[TestFixture]
[TestFixture()]
public class DateTimePartsTests {
[Test]

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
@@ -13,7 +12,8 @@ using Orchard.Localization.Services;
namespace Orchard.Tests.Localization {
[TestFixture]
[TestFixture()]
[Category("longrunning")]
public class DefaultDateFormatterTests {
[SetUp]

View File

@@ -89,9 +89,9 @@ namespace Orchard.WarmupStarter {
var result = _initialization(application);
_initializationResult = result;
}
catch (Exception e) {
catch (Exception ex) {
lock (_synLock) {
_error = e;
_error = ex;
_previousError = null;
}
}

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

@@ -54,6 +54,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

@@ -17,6 +17,7 @@ using Orchard.UI.Navigation;
using Orchard.Utility;
using System;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Core.Navigation.Controllers {
[ValidateInput(false)]
@@ -179,6 +180,10 @@ namespace Orchard.Core.Navigation.Controllers {
return View(model);
}
catch (Exception exception) {
if (exception.IsFatal()) {
throw;
}
Logger.Error(T("Creating menu item failed: {0}", exception.Message).Text);
Services.Notifier.Error(T("Creating menu item failed: {0}", exception.Message));
return this.RedirectLocal(returnUrl, () => RedirectToAction("Index"));

View File

@@ -11,6 +11,7 @@ using Orchard.Security.Permissions;
using Orchard.UI;
using Orchard.UI.Navigation;
using Orchard.Utility;
using Orchard.Exceptions;
namespace Orchard.Core.Navigation.Services {
public class NavigationManager : INavigationManager {
@@ -152,6 +153,9 @@ namespace Orchard.Core.Navigation.Services {
items = builder.Build();
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Unexpected error while querying a navigation provider. It was ignored. The menu provided by the provider may not be complete.");
}
if (items != null) {
@@ -170,6 +174,9 @@ namespace Orchard.Core.Navigation.Services {
items = builder.Build();
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Unexpected error while querying a menu provider. It was ignored. The menu provided by the provider may not be complete.");
}
if (items != null) {
@@ -188,6 +195,9 @@ namespace Orchard.Core.Navigation.Services {
imageSets = builder.BuildImageSets();
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Unexpected error while querying a navigation provider. It was ignored. The menu provided by the provider may not be complete.");
}
if (imageSets != null) {

View File

@@ -9,6 +9,7 @@ using Orchard.Logging;
using Orchard.Services;
using Orchard.Tasks;
using Orchard.Tasks.Scheduling;
using Orchard.Exceptions;
namespace Orchard.Core.Scheduling.Services {
[UsedImplicitly]
@@ -65,6 +66,9 @@ namespace Orchard.Core.Scheduling.Services {
}
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Warning(ex, "Unable to process scheduled task #{0} of type {1}", taskEntry.Id, taskEntry.Action);
_transactionManager.Cancel();
}

View File

@@ -11,6 +11,7 @@ using Orchard.Logging;
using Orchard.Security;
using Orchard.Settings;
using Orchard.UI.Notify;
using Orchard.Exceptions;
namespace Orchard.Core.Settings.Drivers {
[UsedImplicitly]
@@ -70,11 +71,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)) {
@@ -87,27 +89,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;
}
_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);
}
}
catch (Exception e) {
_notifier.Warning(T("The base url you entered could not be requested from current location."));
Logger.Warning(e, "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

@@ -9,6 +9,7 @@ using Orchard.ContentManagement.MetaData.Services;
using Orchard.Core.Settings.Metadata.Records;
using Orchard.Data;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Core.Settings.Metadata {
public class ContentDefinitionManager : Component, IContentDefinitionManager {
@@ -124,7 +125,7 @@ namespace Orchard.Core.Settings.Metadata {
}
private IDictionary<string, ContentTypeDefinition> AcquireContentTypeDefinitions() {
return _cacheManager.Get("ContentTypeDefinitions", ctx => {
return _cacheManager.Get("ContentTypeDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);
AcquireContentPartDefinitions();
@@ -139,7 +140,7 @@ namespace Orchard.Core.Settings.Metadata {
}
private IDictionary<string, ContentPartDefinition> AcquireContentPartDefinitions() {
return _cacheManager.Get("ContentPartDefinitions", ctx => {
return _cacheManager.Get("ContentPartDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);
var contentPartDefinitionRecords = _partDefinitionRepository.Table
@@ -152,7 +153,7 @@ namespace Orchard.Core.Settings.Metadata {
}
private IDictionary<string, ContentFieldDefinition> AcquireContentFieldDefinitions() {
return _cacheManager.Get("ContentFieldDefinitions", ctx => {
return _cacheManager.Get("ContentFieldDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);
return _fieldDefinitionRepository.Table.Select(Build).ToDictionary(x => x.Name, y => y);
@@ -282,6 +283,9 @@ namespace Orchard.Core.Settings.Metadata {
return XElement.Parse(settings);
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Unable to parse settings xml");
return null;
}

View File

@@ -24,7 +24,7 @@ namespace Orchard.Core.Settings.Services {
public ILogger Logger { get; set; }
public ISite GetSiteSettings() {
var siteId = _cacheManager.Get("SiteId", ctx => {
var siteId = _cacheManager.Get("SiteId", true, ctx => {
var site = _contentManager.Query("Site")
.List()
.FirstOrDefault();

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

@@ -301,9 +301,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>
@@ -71,6 +72,10 @@
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
<Name>Orchard.Core</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="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

@@ -119,7 +119,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

@@ -120,7 +120,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

@@ -17,7 +17,7 @@ namespace Orchard.ContentPicker.Fields {
public IEnumerable<ContentItem> ContentItems {
get {
return _contentItems.Value;
return _contentItems.Value ?? Enumerable.Empty<ContentItem>();
}
}

View File

@@ -22,7 +22,7 @@ namespace Orchard.ContentTypes.Services {
}
public IEnumerable<StereotypeDescription> GetStereotypes() {
return _cacheManager.Get("ContentType.Stereotypes", context => {
return _cacheManager.Get("ContentType.Stereotypes", true, context => {
// TODO: Implement a signal in ContentDefinitionManager that gets raised whenever a type definition is updated.
// For now, we'll just cache the stereotypes for 1 minute.

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" itemprop="RemoveUrl">@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

@@ -1,10 +1,15 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data;
using Orchard.Data.Migration;
namespace Orchard.DynamicForms {
public class Migrations : DataMigrationImpl {
private readonly byte[] _oldLayoutHash = new byte[] { 0x91, 0x10, 0x3b, 0x97, 0xce, 0x1e, 0x1e, 0xc7, 0x7a, 0x41, 0xf7, 0x82, 0xe8, 0x58, 0x85, 0x91 };
private const string DefaultFormLayoutData =
@"{
""elements"": [
@@ -59,7 +64,52 @@ namespace Orchard.DynamicForms {
.WithSetting("LayoutTypePartSettings.DefaultLayoutData", DefaultFormLayoutData))
.WithSetting("Stereotype", "Widget")
.DisplayedAs("Form Widget"));
return 1;
return 2;
}
public int UpdateFrom1() {
// if the default layout data was unchanged, fix it with the new default
var formLayoutPart = ContentDefinitionManager
.GetTypeDefinition("Form")
.Parts
.FirstOrDefault(x => x.PartDefinition.Name == "LayoutPart");
if (formLayoutPart != null &&
formLayoutPart.Settings["LayoutTypePartSettings.DefaultLayoutData"] != null) {
var layout = formLayoutPart.Settings["LayoutTypePartSettings.DefaultLayoutData"];
if(GetMD5(layout) == _oldLayoutHash) {
ContentDefinitionManager.AlterTypeDefinition("Form", type => type
.WithPart("LayoutPart", p => p
.WithSetting("LayoutTypePartSettings.DefaultLayoutData", DefaultFormLayoutData))
);
}
}
var formWidgetLayoutPart = ContentDefinitionManager
.GetTypeDefinition("FormWidget")
.Parts
.FirstOrDefault(x => x.PartDefinition.Name == "LayoutPart");
if (formWidgetLayoutPart != null &&
formWidgetLayoutPart.Settings["LayoutTypePartSettings.DefaultLayoutData"] != null) {
var layout = formWidgetLayoutPart.Settings["LayoutTypePartSettings.DefaultLayoutData"];
if (GetMD5(layout) == _oldLayoutHash) {
ContentDefinitionManager.AlterTypeDefinition("FormWidget", type => type
.WithPart("LayoutPart", p => p
.WithSetting("LayoutTypePartSettings.DefaultLayoutData", DefaultFormLayoutData))
);
}
}
return 2;
}
private byte[] GetMD5(string text) {
byte[] encodedText = System.Text.Encoding.UTF8.GetBytes(text);
return ((HashAlgorithm)CryptoConfig.CreateFromName("MD5")).ComputeHash(encodedText);
}
}
}

View File

@@ -1,7 +1,7 @@
Name: Custom Forms
Name: Dynamic Forms
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardcustomforms.codeplex.com
Website: http://www.orchardproject.net/
Version: 1.9.1
OrchardVersion: 1.9
Description: Create custom forms like contact forms using layouts.

View File

@@ -7,7 +7,7 @@ namespace Orchard.DynamicForms.Tokens {
public void Describe(DescribeContext context) {
context.For("FormSubmission", T("Dynamic Form submission"), T("Dynamic Form Submission tokens for use in workflows handling the Dynamic Form Submitted event."))
.Token("Field:*", T("Field:<field name>"), T("The posted field value to access."))
.Token("Field:*", T("Field:<field name>"), T("The posted field value to access."), "Text")
.Token("IsValid:*", T("IsValid:<field name>"), T("The posted field validation status."))
;
}
@@ -15,10 +15,20 @@ namespace Orchard.DynamicForms.Tokens {
public void Evaluate(EvaluateContext context) {
context.For<FormSubmissionTokenContext>("FormSubmission")
.Token(token => token.StartsWith("Field:", StringComparison.OrdinalIgnoreCase) ? token.Substring("Field:".Length) : null, GetFieldValue)
.Chain(FilterChainParam, "Text", GetFieldValue)
.Token(token => token.StartsWith("IsValid:", StringComparison.OrdinalIgnoreCase) ? token.Substring("IsValid:".Length) : null, GetFieldValidationStatus);
}
private object GetFieldValue(string fieldName, FormSubmissionTokenContext context) {
private static Tuple<string, string> FilterChainParam(string token) {
int tokenLength = "Field:".Length;
int chainIndex = token.IndexOf('.');
if (token.StartsWith("Field:", StringComparison.OrdinalIgnoreCase) && chainIndex > tokenLength)
return new Tuple<string, string>(token.Substring(tokenLength, chainIndex - tokenLength), token.Substring(chainIndex + 1));
else
return null;
}
private string GetFieldValue(string fieldName, FormSubmissionTokenContext context) {
return context.PostedValues[fieldName];
}

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

@@ -56,7 +56,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)
@@ -76,7 +76,7 @@
return;
if (this.isTemplated)
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

@@ -307,10 +307,9 @@ namespace Orchard.Layouts.Drivers {
var queryPart = query.As<QueryPart>();
var layoutIndex = XmlHelper.Parse<int>(context.ExportableData.Get("LayoutIndex"));
var layout = queryPart.Layouts[layoutIndex];
element.QueryId = queryPart.Id;
element.LayoutId = layout.Id;
element.LayoutId = layoutIndex != -1 ? queryPart.Layouts[layoutIndex].Id : -1;
}
private static string GetLayoutDescription(IEnumerable<LayoutDescriptor> layouts, LayoutRecord l) {

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

@@ -351,6 +351,7 @@
<Compile Include="Helpers\PrefixHelper.cs" />
<Compile Include="Helpers\JsonHelper.cs" />
<Compile Include="Helpers\StringHelper.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Providers\BlueprintElementHarvester.cs" />
<Compile Include="ResourceManifest.cs" />
<Compile Include="Services\CurrentControllerAccessor.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

File diff suppressed because one or more lines are too long

View File

@@ -38,7 +38,7 @@ namespace Orchard.Layouts.Services {
public IEnumerable<ElementDescriptor> DescribeElements(DescribeElementsContext context) {
var contentType = context.Content != null ? context.Content.ContentItem.ContentType : default(string);
var cacheKey = String.Format("LayoutElementTypes-{0}-{1}", contentType ?? "AnyType", context.CacheVaryParam);
return _cacheManager.Get(cacheKey, acquireContext => {
return _cacheManager.Get(cacheKey, true, acquireContext => {
var harvesterContext = new HarvestElementsContext {
Content = context.Content
};
@@ -55,7 +55,7 @@ namespace Orchard.Layouts.Services {
public IEnumerable<CategoryDescriptor> GetCategories(DescribeElementsContext context) {
var contentType = context.Content != null ? context.Content.ContentItem.ContentType : default(string);
return _cacheManager.Get(String.Format("ElementCategories-{0}-{1}", contentType ?? "AnyType", context.CacheVaryParam), acquireContext => {
return _cacheManager.Get(String.Format("ElementCategories-{0}-{1}", contentType ?? "AnyType", context.CacheVaryParam), true, acquireContext => {
var elements = DescribeElements(context);
var categoryDictionary = GetCategories();
var categoryDescriptorDictionary = new Dictionary<string, CategoryDescriptor>();

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

@@ -5,17 +5,17 @@
}
<div class="item-properties actions">
<p>
@Html.ActionLink(T("{0} Properties", (string)Model.ContainerDisplayName).ToString(), "Edit", new { Area = "Contents", Id = (int)Model.ContainerId, ReturnUrl = Html.ViewContext.HttpContext.Request.RawUrl })
@Html.ActionLink(T("{0} Properties", (string)Model.ContainerDisplayName), "Edit", new { Area = "Contents", Id = (int)Model.ContainerId, ReturnUrl = Html.ViewContext.HttpContext.Request.RawUrl })
</p>
</div>
<div class="manage">
@if (itemContentTypes.Any()) {
foreach (var contentType in itemContentTypes) {
@Html.ActionLink(T("New {0}", contentType.DisplayName).ToString(), "Create", "Admin", new { area = "Contents", id = contentType.Name, containerId }, new { @class = "button primaryAction create-content" })
@Html.ActionLink(T("New {0}", contentType.DisplayName), "Create", "Admin", new { area = "Contents", id = contentType.Name, containerId }, new { @class = "button primaryAction create-content" })
}
}
else {
@Html.ActionLink(T("New Content").ToString(), "Create", "Admin", new { area = "Contents", containerId }, new { @class = "button primaryAction create-content" })
@Html.ActionLink(T("New Content"), "Create", "Admin", new { area = "Contents", containerId }, new { @class = "button primaryAction create-content" })
}
<a id="chooseItems" href="#" class="button primaryAction">@T("Choose Items")</a>
</div>

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

@@ -15,11 +15,17 @@ namespace Orchard.MediaLibrary.Controllers {
private readonly IMediaLibraryService _mediaLibraryService;
private readonly IContentManager _contentManager;
public ClientStorageController(IMediaLibraryService mediaManagerService, IContentManager contentManager) {
public ClientStorageController(
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 {
@@ -32,6 +38,15 @@ namespace Orchard.MediaLibrary.Controllers {
[HttpPost]
public ActionResult Upload(string folderPath, string type) {
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 statuses = new List<object>();
// Loop through each file in the request

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

@@ -174,6 +174,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

@@ -58,7 +58,7 @@ namespace Orchard.MediaProcessing.Services {
}
private IDictionary<string, string> GetProfileCache(string profile) {
return _cacheManager.Get("MediaProcessing_" + profile, ctx => {
return _cacheManager.Get("MediaProcessing_" + profile, true, ctx => {
ctx.Monitor(_signals.When("MediaProcessing_Saved_" + profile));
var dictionary = new Dictionary<string, string>();

View File

@@ -32,7 +32,7 @@ namespace Orchard.MediaProcessing.Services {
public ImageProfilePart GetImageProfileByName(string name) {
var profileId = _cacheManager.Get("ProfileId_" + name, ctx => {
var profileId = _cacheManager.Get("ProfileId_" + name, true, ctx => {
var profile = _contentManager.Query<ImageProfilePart, ImageProfilePartRecord>()
.Where(x => x.Name == name)
.Slice(0, 1)

View File

@@ -461,7 +461,7 @@ namespace Orchard.OutputCache.Filters {
private CacheSettings CacheSettings {
get {
return _cacheSettings ?? (_cacheSettings = _cacheManager.Get(CacheSettings.CacheKey, context => {
return _cacheSettings ?? (_cacheSettings = _cacheManager.Get(CacheSettings.CacheKey, true, context => {
context.Monitor(_signals.When(CacheSettings.CacheKey));
return new CacheSettings(_workContext.CurrentSite.As<CacheSettingsPart>());
}));

View File

@@ -99,7 +99,7 @@ namespace Orchard.OutputCache.Services {
}
public IEnumerable<CacheRouteConfig> GetRouteConfigs() {
return _cacheManager.Get(RouteConfigsCacheKey,
return _cacheManager.Get(RouteConfigsCacheKey, true,
ctx => {
ctx.Monitor(_signals.When(RouteConfigsCacheKey));
return _repository.Fetch(c => true).Select(c => new CacheRouteConfig { RouteKey = c.RouteKey, Duration = c.Duration, GraceTime = c.GraceTime }).ToReadOnlyCollection();

View File

@@ -14,7 +14,7 @@ namespace Orchard.Scripting.Dlr.Services {
}
public object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers) {
object execContextType = _cacheManager.Get("---", ctx => (object)_scriptingManager.ExecuteExpression(@"
object execContextType = _cacheManager.Get("---", true, ctx => (object)_scriptingManager.ExecuteExpression(@"
class ExecBlock
def initialize(callbacks)
@callbacks = callbacks
@@ -38,7 +38,7 @@ class ExecContext
end
ExecContext
"));
var ops = _cacheManager.Get("----", ctx => (ObjectOperations)_scriptingManager.ExecuteOperation(x => x));
var ops = _cacheManager.Get("----", true, ctx => (ObjectOperations)_scriptingManager.ExecuteOperation(x => x));
object execContext = _cacheManager.Get(expression, ctx => (object)ops.InvokeMember(execContextType, "alloc", expression));
dynamic result = ops.InvokeMember(execContext, "evaluate", new CallbackApi(this, providers));
return ConvertRubyValue(result);

View File

@@ -20,7 +20,7 @@ namespace Orchard.Scripting {
public Localizer T { get; set; }
public object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers) {
var expr = _cacheManager.Get(expression, ctx => {
var expr = _cacheManager.Get(expression, true, ctx => {
var ast = ParseExpression(expression);
return new { Tree = ast, Errors = ast.GetErrors().ToList() };
});

View File

@@ -73,7 +73,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

@@ -34,11 +34,14 @@ namespace Orchard.Search.Drivers {
var model = new SearchSettingsViewModel();
var searchFields = part.SearchFields;
model.DisplayType = part.DisplayType;
if (updater != null) {
if (updater.TryUpdateModel(model, Prefix, null, null)) {
part.SearchIndex = model.SelectedIndex;
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

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

Some files were not shown because too many files have changed in this diff Show More