diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..8893f4c
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,141 @@
+# Contributing to this project
+
+Please take a moment to review this document in order to make the contribution
+process easy and effective for everyone involved.
+
+Following these guidelines helps to communicate that you respect the time of
+the developers managing and developing this open source project. In return,
+they should reciprocate that respect in addressing your issue or assessing
+patches and features.
+
+
+## Using the issue tracker
+
+The issue tracker is the preferred channel for [bug reports](#bugs),
+[features requests](#features) and [submitting pull
+requests](#pull-requests), but please respect the following restrictions:
+
+* Please **do not** use the issue tracker for personal support requests (use
+ [Our Umbraco](https://our.umbraco.org/projects/backoffice-extensions/nexu/) or Twitter).
+
+* Please **do not** derail or troll issues. Keep the discussion on topic and
+ respect the opinions of others.
+
+
+
+## Bug reports
+
+A bug is a _demonstrable problem_ that is caused by the code in the repository.
+Good bug reports are extremely helpful - thank you!
+
+Guidelines for bug reports:
+
+1. **Use the GitHub issue search** — check if the issue has already been
+ reported.
+
+2. **Check if the issue has been fixed** — try to reproduce it using the
+ latest `master` or development branch in the repository.
+
+3. **Isolate the problem** — create a reduced test case and a live example.
+
+A good bug report shouldn't leave others needing to chase you up for more
+information. Please try to be as detailed as possible in your report. What is
+your environment? What steps will reproduce the issue? What browser(s) and OS
+experience the problem? What would you expect to be the outcome? All these
+details will help people to fix any potential bugs.
+
+Example:
+
+> Short and descriptive example bug report title
+>
+> A summary of the issue and the browser/OS environment in which it occurs. If
+> suitable, include the steps required to reproduce the bug.
+>
+> 1. This is the first step
+> 2. This is the second step
+> 3. Further steps, etc.
+>
+> `` - a link to the reduced test case
+>
+> Any other information you want to share that is relevant to the issue being
+> reported. This might include the lines of code that you have identified as
+> causing the bug, and potential solutions (and your opinions on their
+> merits).
+
+
+
+## Feature requests
+
+Feature requests are welcome. But take a moment to find out whether your idea
+fits with the scope and aims of the project. It's up to *you* to make a strong
+case to convince the project's developers of the merits of this feature. Please
+provide as much detail and context as possible.
+
+
+
+## Pull requests
+
+Good pull requests - patches, improvements, new features - are a fantastic
+help. They should remain focused in scope and avoid containing unrelated
+commits.
+
+**Please ask first** before embarking on any significant pull request (e.g.
+implementing features, refactoring code, porting to a different language),
+otherwise you risk spending a lot of time working on something that the
+project's developers might not want to merge into the project.
+
+Please adhere to the coding conventions used throughout a project (indentation,
+accurate comments, etc.) and any other requirements (such as test coverage).
+
+Follow this process if you'd like your work considered for inclusion in the
+project:
+
+1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
+ and configure the remotes:
+
+ ```bash
+ # Clone your fork of the repo into the current directory
+ git clone https://github.com//
+ # Navigate to the newly cloned directory
+ cd
+ # Assign the original repo to a remote called "upstream"
+ git remote add upstream https://github.com//
+ ```
+
+2. If you cloned a while ago, get the latest changes from upstream:
+
+ ```bash
+ git checkout develop
+ git pull upstream develop
+ ```
+
+3. Create a new topic branch (off the main project `develop` branch) to
+ contain your feature, change, or fix:
+
+ ```bash
+ git checkout -b
+ ```
+
+4. Commit your changes in logical chunks. Please adhere to these [git commit
+ message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
+ or your code is unlikely be merged into the main project. Use Git's
+ [interactive rebase](https://help.github.com/articles/interactive-rebase)
+ feature to tidy up your commits before making them public.
+
+5. Locally merge (or rebase) the upstream development branch into your topic branch:
+
+ ```bash
+ git pull [--rebase] upstream develop
+ ```
+
+6. Push your topic branch up to your fork:
+
+ ```bash
+ git push origin
+ ```
+
+7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
+ with a clear title and description.
+
+**IMPORTANT**: By submitting a patch, you agree to allow the project owner to
+license your work under the same license as that used by the project.
\ No newline at end of file
diff --git a/README.md b/README.md
index 35ade84..83417a4 100644
--- a/README.md
+++ b/README.md
@@ -21,4 +21,6 @@ The documentation for this package can be found [here](docs/index.md)
## Special thanks ##
-[Jeavon Leopold](https://twitter.com/crumpled_jeavon) for his help in setting up the build automation. Sorry for bugging you on Slack :-)
\ No newline at end of file
+[Jeavon Leopold](https://twitter.com/crumpled_jeavon) for his help in setting up the build automation. Sorry for bugging you on Slack :-)
+
+And of course all the [contributors](https://github.com/dawoe/umbraco-nexu/graphs/contributors)
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core.Tests/NexuApiControllerTests.cs b/Source/Our.Umbraco.Nexu.Core.Tests/NexuApiControllerTests.cs
index 578a248..98e3979 100644
--- a/Source/Our.Umbraco.Nexu.Core.Tests/NexuApiControllerTests.cs
+++ b/Source/Our.Umbraco.Nexu.Core.Tests/NexuApiControllerTests.cs
@@ -48,6 +48,11 @@ public class NexuApiControllerTests : BaseRoutingTest
///
private Mock contentServiceMock;
+ ///
+ /// The media service.
+ ///
+ private Mock mediaServiceMock;
+
///
/// The controller.
///
@@ -106,6 +111,7 @@ public override void Initialize()
this.nexuServiceMock = new Mock();
this.mappingEngineMock = new Mock();
this.contentServiceMock = new Mock();
+ this.mediaServiceMock = new Mock();
// Mocked settings are now necessary
SettingsForTests.ConfigureSettings(SettingsForTests.GenerateMockSettings());
@@ -126,7 +132,8 @@ public override void Initialize()
this.UmbracoContext,
this.nexuServiceMock.Object,
this.mappingEngineMock.Object,
- this.contentServiceMock.Object)
+ this.contentServiceMock.Object,
+ this.mediaServiceMock.Object)
{
Request = new HttpRequestMessage
{
@@ -195,6 +202,445 @@ public void TestGetIncomingLinks()
Assert.IsNotNull(model);
}
+ ///
+ /// The check descendants for incoming links should return false when there are no children.
+ ///
+ [Test]
+ [Category("Api")]
+ public void CheckContentDescendantsForIncomingLinksShouldReturnFalseWhenThereAreNoChildren()
+ {
+ // arrange
+ var contentId = 123;
+
+ var children = new List();
+
+ this.contentServiceMock.Setup(x => x.GetChildren(contentId)).Returns(children);
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(It.IsAny(), false))
+ .Returns(new List());
+
+ // act
+ var result = this.controller.CheckContentDescendantsForIncomingLinks(contentId);
+
+ // arrange
+ Assert.IsFalse(result);
+
+ this.contentServiceMock.Verify(x => x.GetChildren(contentId), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(It.IsAny(), false), Times.Never);
+ }
+
+ ///
+ /// The check descendants for incoming links should return false when they have no incoming links.
+ ///
+ [Test]
+ [Category("Api")]
+ public void CheckContentDescendantsForIncomingLinksShouldReturnFalseWhenTheyHaveNoIncomingLinks()
+ {
+ // arrange
+ var contentId = 123;
+
+ var children = new List();
+
+ var child1 = new Mock();
+ child1.Setup(x => x.Id).Returns(456);
+
+ children.Add(child1.Object);
+
+ var child2 = new Mock();
+ child2.Setup(x => x.Id).Returns(789);
+
+ children.Add(child2.Object);
+
+ var descendants = new List();
+
+ var descendant1 = new Mock();
+ descendant1.Setup(x => x.Id).Returns(000);
+
+ this.contentServiceMock.Setup(x => x.GetChildren(contentId)).Returns(children);
+ this.contentServiceMock.Setup(x => x.GetChildren(child1.Object.Id)).Returns(new List());
+ this.contentServiceMock.Setup(x => x.GetChildren(child2.Object.Id)).Returns(descendants);
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(It.IsAny(), false))
+ .Returns(new List());
+
+ // act
+ var result = this.controller.CheckContentDescendantsForIncomingLinks(contentId);
+
+ // arrange
+ Assert.IsFalse(result);
+
+ this.contentServiceMock.Verify(x => x.GetChildren(contentId), Times.Once);
+ this.contentServiceMock.Verify(x => x.GetChildren(child1.Object.Id), Times.Once);
+ this.contentServiceMock.Verify(x => x.GetChildren(child2.Object.Id), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(It.IsAny(), false), Times.Exactly(children.Count + descendants.Count));
+ }
+
+ ///
+ /// The check descendants for incoming links should return true when one has incoming links.
+ ///
+ [Test]
+ [Category("Api")]
+ public void CheckContentDescendantsForIncomingLinksShouldReturnTrueWhenIncomingLinksForFirstLevel()
+ {
+ // arrange
+ var contentId = 123;
+
+ var children = new List();
+
+ var child1 = new Mock();
+ child1.Setup(x => x.Id).Returns(456);
+
+ children.Add(child1.Object);
+
+ var child2 = new Mock();
+ child2.Setup(x => x.Id).Returns(000);
+
+ children.Add(child2.Object);
+
+ var child3 = new Mock();
+ child3.Setup(x => x.Id).Returns(789);
+
+ children.Add(child3.Object);
+
+ this.contentServiceMock.Setup(x => x.GetChildren(contentId)).Returns(children);
+
+ var relations = new List();
+
+ relations.Add(Mock.Of());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(456, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(000, false))
+ .Returns(relations);
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(789, false))
+ .Returns(new List());
+
+ // act
+ var result = this.controller.CheckContentDescendantsForIncomingLinks(contentId);
+
+ // arrange
+ Assert.IsTrue(result);
+
+ this.contentServiceMock.Verify(x => x.GetChildren(contentId), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(456, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(000, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(789, false), Times.Never);
+ }
+
+ ///
+ /// The check content descendants for incoming links should return true when incoming links for deeper level.
+ ///
+ [Test]
+ [Category("Api")]
+ public void CheckContentDescendantsForIncomingLinksShouldReturnTrueWhenIncomingLinksForDeeperLevel()
+ {
+ // arrange
+ var contentId = 123;
+
+ var children = new List();
+
+ var child1 = new Mock();
+ child1.Setup(x => x.Id).Returns(456);
+
+ children.Add(child1.Object);
+
+ var child2 = new Mock();
+ child2.Setup(x => x.Id).Returns(000);
+
+ children.Add(child2.Object);
+
+ var child3 = new Mock();
+ child3.Setup(x => x.Id).Returns(789);
+
+ children.Add(child3.Object);
+
+ var descendants_1 = new List();
+
+ var descendant1 = new Mock();
+ descendant1.Setup(x => x.Id).Returns(006);
+
+ descendants_1.Add(descendant1.Object);
+
+ var descendants_2 = new List();
+
+ var descendant2 = new Mock();
+ descendant2.Setup(x => x.Id).Returns(007);
+
+ descendants_2.Add(descendant2.Object);
+
+ this.contentServiceMock.Setup(x => x.GetChildren(contentId)).Returns(children);
+ this.contentServiceMock.Setup(x => x.GetChildren(child1.Object.Id)).Returns(descendants_1);
+ this.contentServiceMock.Setup(x => x.GetChildren(child2.Object.Id)).Returns(descendants_2);
+ this.contentServiceMock.Setup(x => x.GetChildren(child3.Object.Id)).Returns(new List());
+
+ var relations = new List();
+
+ relations.Add(Mock.Of());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(456, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(000, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(789, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(006, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(007, false))
+ .Returns(relations);
+
+ // act
+ var result = this.controller.CheckContentDescendantsForIncomingLinks(contentId);
+
+ // arrange
+ Assert.IsTrue(result);
+
+ this.contentServiceMock.Verify(x => x.GetChildren(contentId), Times.Once);
+ this.contentServiceMock.Verify(x => x.GetChildren(child1.Object.Id), Times.Once);
+ this.contentServiceMock.Verify(x => x.GetChildren(child2.Object.Id), Times.Once);
+ this.contentServiceMock.Verify(x => x.GetChildren(child3.Object.Id), Times.Never);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(456, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(000, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(789, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(006, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(007, false), Times.Once);
+ }
+
+ ///
+ /// The check media descendants for incoming links should return false when there are no children.
+ ///
+ [Test]
+ [Category("Api")]
+ public void CheckMediaDescendantsForIncomingLinksShouldReturnFalseWhenThereAreNoChildren()
+ {
+ // arrange
+ var mediaId = 123;
+
+ var children = new List();
+
+ this.mediaServiceMock.Setup(x => x.GetChildren(mediaId)).Returns(children);
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(It.IsAny(), false))
+ .Returns(new List());
+
+ // act
+ var result = this.controller.CheckMediaDescendantsForIncomingLinks(mediaId);
+
+ // arrange
+ Assert.IsFalse(result);
+
+ this.mediaServiceMock.Verify(x => x.GetChildren(mediaId), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(It.IsAny(), false), Times.Never);
+ }
+
+ ///
+ /// The check media descendants for incoming links should return false when they have no incoming links.
+ ///
+ [Test]
+ [Category("Api")]
+ public void CheckMediaDescendantsForIncomingLinksShouldReturnFalseWhenTheyHaveNoIncomingLinks()
+ {
+ // arrange
+ var mediaId = 123;
+
+ var children = new List();
+
+ var child1 = new Mock();
+ child1.Setup(x => x.Id).Returns(456);
+
+ children.Add(child1.Object);
+
+ var child2 = new Mock();
+ child2.Setup(x => x.Id).Returns(789);
+
+ children.Add(child2.Object);
+
+ var descendants = new List();
+
+ var descendant1 = new Mock();
+ descendant1.Setup(x => x.Id).Returns(000);
+
+ this.mediaServiceMock.Setup(x => x.GetChildren(mediaId)).Returns(children);
+ this.mediaServiceMock.Setup(x => x.GetChildren(child1.Object.Id)).Returns(new List());
+ this.mediaServiceMock.Setup(x => x.GetChildren(child2.Object.Id)).Returns(descendants);
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(It.IsAny(), false))
+ .Returns(new List());
+
+ // act
+ var result = this.controller.CheckMediaDescendantsForIncomingLinks(mediaId);
+
+ // arrange
+ Assert.IsFalse(result);
+
+ this.mediaServiceMock.Verify(x => x.GetChildren(mediaId), Times.Once);
+ this.mediaServiceMock.Verify(x => x.GetChildren(child1.Object.Id), Times.Once);
+ this.mediaServiceMock.Verify(x => x.GetChildren(child2.Object.Id), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(It.IsAny(), false), Times.Exactly(children.Count + descendants.Count));
+ }
+
+ ///
+ /// The check media descendants for incoming links should return true when one has incoming links.
+ ///
+ [Test]
+ [Category("Api")]
+ public void CheckMediaDescendantsForIncomingLinksShouldReturnTrueWhenOneHasIncomingLinksForFirstLevel()
+ {
+ // arrange
+ var mediaId = 123;
+
+ var children = new List();
+
+ var child1 = new Mock();
+ child1.Setup(x => x.Id).Returns(456);
+
+ children.Add(child1.Object);
+
+ var child2 = new Mock();
+ child2.Setup(x => x.Id).Returns(000);
+
+ children.Add(child2.Object);
+
+ var child3 = new Mock();
+ child3.Setup(x => x.Id).Returns(789);
+
+ children.Add(child3.Object);
+
+ this.mediaServiceMock.Setup(x => x.GetChildren(mediaId)).Returns(children);
+
+ var relations = new List();
+
+ relations.Add(Mock.Of());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(456, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(000, false))
+ .Returns(relations);
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(789, false))
+ .Returns(new List());
+
+ // act
+ var result = this.controller.CheckMediaDescendantsForIncomingLinks(mediaId);
+
+ // arrange
+ Assert.IsTrue(result);
+
+ this.mediaServiceMock.Verify(x => x.GetChildren(mediaId), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(456, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(000, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(789, false), Times.Never);
+ }
+
+ ///
+ /// The check media descendants for incoming links should return true when incoming links for deeper level.
+ ///
+ [Test]
+ [Category("Api")]
+ public void CheckMediaDescendantsForIncomingLinksShouldReturnTrueWhenIncomingLinksForDeeperLevel()
+ {
+ // arrange
+ var contentId = 123;
+
+ var children = new List();
+
+ var child1 = new Mock();
+ child1.Setup(x => x.Id).Returns(456);
+
+ children.Add(child1.Object);
+
+ var child2 = new Mock();
+ child2.Setup(x => x.Id).Returns(000);
+
+ children.Add(child2.Object);
+
+ var child3 = new Mock();
+ child3.Setup(x => x.Id).Returns(789);
+
+ children.Add(child3.Object);
+
+ var descendants_1 = new List();
+
+ var descendant1 = new Mock();
+ descendant1.Setup(x => x.Id).Returns(006);
+
+ descendants_1.Add(descendant1.Object);
+
+ var descendants_2 = new List();
+
+ var descendant2 = new Mock();
+ descendant2.Setup(x => x.Id).Returns(007);
+
+ descendants_2.Add(descendant2.Object);
+
+ this.mediaServiceMock.Setup(x => x.GetChildren(contentId)).Returns(children);
+ this.mediaServiceMock.Setup(x => x.GetChildren(child1.Object.Id)).Returns(descendants_1);
+ this.mediaServiceMock.Setup(x => x.GetChildren(child2.Object.Id)).Returns(descendants_2);
+ this.mediaServiceMock.Setup(x => x.GetChildren(child3.Object.Id)).Returns(new List());
+
+ var relations = new List();
+
+ relations.Add(Mock.Of());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(456, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(000, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(789, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(006, false))
+ .Returns(new List());
+
+ this.nexuServiceMock.Setup(x => x.GetNexuRelationsForContent(007, false))
+ .Returns(relations);
+
+ // act
+ var result = this.controller.CheckMediaDescendantsForIncomingLinks(contentId);
+
+ // arrange
+ Assert.IsTrue(result);
+
+ this.mediaServiceMock.Verify(x => x.GetChildren(contentId), Times.Once);
+ this.mediaServiceMock.Verify(x => x.GetChildren(child1.Object.Id), Times.Once);
+ this.mediaServiceMock.Verify(x => x.GetChildren(child2.Object.Id), Times.Once);
+ this.mediaServiceMock.Verify(x => x.GetChildren(child3.Object.Id), Times.Never);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(456, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(000, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(789, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(006, false), Times.Once);
+
+ this.nexuServiceMock.Verify(x => x.GetNexuRelationsForContent(007, false), Times.Once);
+ }
+
+
///
/// Test getting rebuild status.
///
diff --git a/Source/Our.Umbraco.Nexu.Core.Tests/NexuMappingTests.cs b/Source/Our.Umbraco.Nexu.Core.Tests/NexuMappingTests.cs
index 9487be3..d6b9baa 100644
--- a/Source/Our.Umbraco.Nexu.Core.Tests/NexuMappingTests.cs
+++ b/Source/Our.Umbraco.Nexu.Core.Tests/NexuMappingTests.cs
@@ -1,20 +1,18 @@
namespace Our.Umbraco.Nexu.Core.Tests
{
- using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Threading;
using AutoMapper;
using global::Umbraco.Core.Models;
- using global::Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero;
using global::Umbraco.Core.Services;
using Moq;
using NUnit.Framework;
- using Our.Umbraco.Nexu.Core.Mapping.Profiles;
using Our.Umbraco.Nexu.Core.Mapping.TypeConverters;
using Our.Umbraco.Nexu.Core.Models;
@@ -29,6 +27,11 @@ public class NexuMappingTests
///
private Mock contentServiceMock;
+ ///
+ /// The localization service mock.
+ ///
+ private Mock localizationServiceMock;
+
///
/// Test setup.
///
@@ -36,8 +39,9 @@ public class NexuMappingTests
public void Setup()
{
this.contentServiceMock = new Mock();
+ this.localizationServiceMock = new Mock();
- Mapper.CreateMap, IEnumerable>().ConvertUsing(new RelationsToRelatedDocumentsConverter(this.contentServiceMock.Object));
+ Mapper.CreateMap, IEnumerable>().ConvertUsing(new RelationsToRelatedDocumentsConverter(this.contentServiceMock.Object, this.localizationServiceMock.Object));
Mapper.CreateMap()
.ForMember(x => x.Properties, opt => opt.Ignore())
@@ -59,12 +63,35 @@ public void TearDown()
///
[Test]
[Category("Mappings")]
- public void TestMapRelationsToRelatedDocuments()
+ public void TestMapRelationsToRelatedDocumentssWithDictionaryItemsInPropAndTabnames()
{
// arrange
+ this.localizationServiceMock.Setup(x => x.DictionaryItemExists(It.IsAny())).Returns(true);
+ this.localizationServiceMock.Setup(x => x.GetDictionaryItemByKey(It.IsAny()))
+ .Returns(
+ (string s) =>
+ {
+ var language = Mock.Of();
+
+ Mock.Get(language).SetupGet(x => x.IsoCode)
+ .Returns(Thread.CurrentThread.CurrentCulture.Name);
+
+ var translation = Mock.Of();
+
+ Mock.Get(translation).SetupGet(x => x.Value).Returns(s);
+ Mock.Get(translation).SetupGet(x => x.Language).Returns(language);
+
+ var dictionary = Mock.Of();
+
+ Mock.Get(dictionary).SetupGet(x => x.ItemKey).Returns(s);
+ Mock.Get(dictionary).SetupGet(x => x.Translations)
+ .Returns(new List { translation });
+ return dictionary;
+ });
+
var relation123Mock = new Mock();
relation123Mock.SetupGet(x => x.ParentId).Returns(123);
- relation123Mock.SetupGet(x => x.Comment).Returns("media [[Images]] || rte [[Content]]");
+ relation123Mock.SetupGet(x => x.Comment).Returns("#media [[#Images]] || rte [[Content]]");
var relation456Mock = new Mock();
relation456Mock.SetupGet(x => x.ParentId).Returns(456);
@@ -124,6 +151,7 @@ public void TestMapRelationsToRelatedDocuments()
var destination = Mapper.Map>(input).ToList();
// verify
+ this.localizationServiceMock.Verify(x => x.GetDictionaryItemByKey(It.IsAny()), Times.Exactly(2));
this.contentServiceMock.Verify(x => x.GetByIds(It.IsAny>()), Times.Once);
Assert.AreEqual(input.Select(x => x.ParentId), actualContentIds);
@@ -157,6 +185,106 @@ public void TestMapRelationsToRelatedDocuments()
}
+ [Test]
+ [Category("Mappings")]
+ public void TestMapRelationsToRelatedDocuments()
+ {
+ // arrange
+ var relation123Mock = new Mock();
+ relation123Mock.SetupGet(x => x.ParentId).Returns(123);
+ relation123Mock.SetupGet(x => x.Comment).Returns("media [[Images]] || rte [[Content]]");
+
+ var relation456Mock = new Mock();
+ relation456Mock.SetupGet(x => x.ParentId).Returns(456);
+ relation456Mock.SetupGet(x => x.Comment).Returns("picker [[Links]] || rte [[Content]]");
+
+ var relation789Mock = new Mock();
+ relation789Mock.SetupGet(x => x.ParentId).Returns(789);
+ relation789Mock.SetupGet(x => x.Comment).Returns("picker [[Images]] || media [[Images]]");
+
+ var input = new List
+ {
+ relation123Mock.Object,
+ relation456Mock.Object,
+ relation789Mock.Object
+ };
+
+ IEnumerable actualContentIds = new List();
+
+ var contentTypeMock = new Mock();
+ contentTypeMock.SetupGet(x => x.Icon).Returns("page");
+
+ var contentItems = new List();
+
+ var content123Mock = new Mock();
+ content123Mock.SetupGet(x => x.Id).Returns(123);
+ content123Mock.SetupGet(x => x.Name).Returns("Content 123");
+ content123Mock.SetupGet(x => x.Published).Returns(true);
+ content123Mock.SetupGet(x => x.Trashed).Returns(false);
+ content123Mock.SetupGet(x => x.ContentType).Returns(contentTypeMock.Object);
+
+ var content456Mock = new Mock();
+ content456Mock.SetupGet(x => x.Id).Returns(456);
+ content456Mock.SetupGet(x => x.Name).Returns("Content 456");
+ content456Mock.SetupGet(x => x.Published).Returns(false);
+ content456Mock.SetupGet(x => x.Trashed).Returns(false);
+ content456Mock.SetupGet(x => x.ContentType).Returns(contentTypeMock.Object);
+
+ var content789MOck = new Mock();
+ content789MOck.SetupGet(x => x.Id).Returns(789);
+ content789MOck.SetupGet(x => x.Name).Returns("Content 789");
+ content789MOck.SetupGet(x => x.Published).Returns(false);
+ content789MOck.SetupGet(x => x.Trashed).Returns(true);
+ content789MOck.SetupGet(x => x.ContentType).Returns(contentTypeMock.Object);
+
+ contentItems.Add(content123Mock.Object);
+ contentItems.Add(content456Mock.Object);
+ contentItems.Add(content789MOck.Object);
+
+ this.contentServiceMock.Setup(x => x.GetByIds(It.IsAny>()))
+ .Callback(
+ (IEnumerable ids) =>
+ {
+ actualContentIds = ids;
+ }).Returns(contentItems);
+
+ // act
+ var destination = Mapper.Map>(input).ToList();
+
+ // verify
+ this.contentServiceMock.Verify(x => x.GetByIds(It.IsAny>()), Times.Once);
+
+ Assert.AreEqual(input.Select(x => x.ParentId), actualContentIds);
+
+ Assert.IsNotNull(destination);
+ Assert.AreEqual(input.Count, destination.Count());
+
+ // properties
+ var related123Props = destination.First(x => x.Id == 123).Properties;
+
+ Assert.IsTrue(related123Props.Keys.Contains("Images"));
+ Assert.IsTrue(related123Props.Keys.Contains("Content"));
+
+ Assert.IsTrue(related123Props["Images"].Exists(x => x.Trim() == "media"));
+ Assert.IsTrue(related123Props["Content"].Exists(x => x.Trim() == "rte"));
+
+ var related456Props = destination.First(x => x.Id == 456).Properties;
+
+ Assert.IsTrue(related456Props.Keys.Contains("Links"));
+ Assert.IsTrue(related456Props.Keys.Contains("Content"));
+
+ Assert.IsTrue(related456Props["Links"].Exists(x => x.Trim() == "picker"));
+ Assert.IsTrue(related456Props["Content"].Exists(x => x.Trim() == "rte"));
+
+ var related789Props = destination.First(x => x.Id == 789).Properties;
+
+ Assert.IsTrue(related789Props.Keys.Contains("Images"));
+
+ Assert.IsTrue(related789Props["Images"].Exists(x => x.Trim() == "picker"));
+ Assert.IsTrue(related789Props["Images"].Exists(x => x.Trim() == "media"));
+
+ }
+
///
/// The test mapping from content to related document.
///
diff --git a/Source/Our.Umbraco.Nexu.Core.Tests/Properties/AssemblyInfo.cs b/Source/Our.Umbraco.Nexu.Core.Tests/Properties/AssemblyInfo.cs
index 1d76501..e5e25bb 100644
--- a/Source/Our.Umbraco.Nexu.Core.Tests/Properties/AssemblyInfo.cs
+++ b/Source/Our.Umbraco.Nexu.Core.Tests/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.3.0.0")]
-[assembly: AssemblyFileVersion("1.3.0.0")]
+[assembly: AssemblyVersion("1.4.0.0")]
+[assembly: AssemblyFileVersion("1.4.0.0")]
diff --git a/Source/Our.Umbraco.Nexu.Core/BootStrapper.cs b/Source/Our.Umbraco.Nexu.Core/BootStrapper.cs
index 5518c4b..8bce50f 100644
--- a/Source/Our.Umbraco.Nexu.Core/BootStrapper.cs
+++ b/Source/Our.Umbraco.Nexu.Core/BootStrapper.cs
@@ -8,9 +8,12 @@
using System.Web.Routing;
using AutoMapper;
-
+
using ObjectResolution;
+ using Our.Umbraco.Nexu.Core.Mapping.Profiles;
+ using Our.Umbraco.Nexu.Core.WebApi;
+
using global::Umbraco.Core;
using global::Umbraco.Core.Events;
using global::Umbraco.Core.Models;
@@ -18,9 +21,6 @@
using global::Umbraco.Web;
using global::Umbraco.Web.UI.JavaScript;
- using Our.Umbraco.Nexu.Core.Mapping.Profiles;
- using Our.Umbraco.Nexu.Core.WebApi;
-
///
/// Bootstrapper to handle umbraco startup events
///
@@ -28,7 +28,7 @@ internal class BootStrapper : ApplicationEventHandler
{
///
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
- {
+ {
using (ApplicationContext.Current.ProfilingLogger.TraceDuration("Begin ApplicationStarted", "End ApplicationStarted"))
{
// set up mappings
@@ -53,7 +53,7 @@ protected override void ApplicationInitialized(UmbracoApplicationBase umbracoApp
// resolve property parsers
PropertyParserResolver.Current =
new PropertyParserResolver(PluginManager.Current.ResolvePropertyParsers());
-
+
// resolve grid editor parsers
GridEditorParserResolver.Current = new GridEditorParserResolver(PluginManager.Current.ResolveGridEditorParsers());
}
@@ -98,6 +98,8 @@ private void ServerVariablesParserParsing(object sender, Dictionary(c => c.GetIncomingLinks(-1)));
urlDictionairy.Add("GetIncomingLinks", urlHelper.GetUmbracoApiService("GetIncomingLinks", null));
+ urlDictionairy.Add("CheckContentDescendantsForIncomingLinks", urlHelper.GetUmbracoApiService("CheckContentDescendantsForIncomingLinks", null));
+ urlDictionairy.Add("CheckMediaDescendantsForIncomingLinks", urlHelper.GetUmbracoApiService("CheckMediaDescendantsForIncomingLinks", null));
urlDictionairy.Add("GetRebuildStatus", urlHelper.GetUmbracoApiService("GetRebuildStatus"));
urlDictionairy.Add("Rebuild", urlHelper.GetUmbracoApiService("Rebuild", null));
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/controllers/base-delete-controller.js b/Source/Our.Umbraco.Nexu.Core/Client/controllers/base-delete-controller.js
new file mode 100644
index 0000000..2795530
--- /dev/null
+++ b/Source/Our.Umbraco.Nexu.Core/Client/controllers/base-delete-controller.js
@@ -0,0 +1,24 @@
+angular.module('umbraco').controller('Our.Umbraco.Nexu.BaseDeleteController',
+ ['$scope', '$controller', 'Our.Umbraco.Nexu.Resource',
+ function ($scope, $controller, nexuResource) {
+ // inherit core delete controller
+ angular.extend(this, $controller('Umbraco.Editors.Media.DeleteController', { $scope: $scope }));
+
+ $scope.links = {};
+ $scope.descendantsHaveLinks = false;
+ $scope.isLoading = true;
+
+ nexuResource.getIncomingLinks($scope.currentNode.id).then(function (result) {
+ $scope.links = result.data;
+
+ if (result.data.length == 0) {
+ nexuResource.checkDescendants($scope.currentNode.id, $scope.isMedia).then(function (result) {
+ $scope.descendantsHaveLinks = result.data;
+ $scope.isLoading = false;
+ });
+ } else {
+ $scope.isLoading = false;
+ }
+
+ });
+ }]);
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/controllers/content-delete-controller.js b/Source/Our.Umbraco.Nexu.Core/Client/controllers/content-delete-controller.js
index 31cf9af..69824a7 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/controllers/content-delete-controller.js
+++ b/Source/Our.Umbraco.Nexu.Core/Client/controllers/content-delete-controller.js
@@ -1,14 +1,8 @@
angular.module('umbraco').controller('Our.Umbraco.Nexu.ContentDeleteController',
- ['$scope', '$controller', 'Our.Umbraco.Nexu.Resource',
- function ($scope, $controller, nexuResource) {
- // inherit core delete controller
- angular.extend(this, $controller('Umbraco.Editors.Content.DeleteController', { $scope: $scope }));
+ ['$scope', '$controller',
+ function ($scope, $controller) {
+ $scope.isMedia = false;
- $scope.links = {};
- $scope.isLoading = true;
-
- nexuResource.getIncomingLinks($scope.currentNode.id).then(function (result) {
- $scope.links = result.data;
- $scope.isLoading = false;
- });
+ // inherit base delete controller
+ angular.extend(this, $controller('Our.Umbraco.Nexu.BaseDeleteController', { $scope: $scope }));
}]);
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/controllers/dashboard-controller.js b/Source/Our.Umbraco.Nexu.Core/Client/controllers/dashboard-controller.js
index 0511c55..c6ef528 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/controllers/dashboard-controller.js
+++ b/Source/Our.Umbraco.Nexu.Core/Client/controllers/dashboard-controller.js
@@ -17,11 +17,11 @@
if ($scope.RebuildStatus.IsProcessing && $scope.autoRefresh) {
$timeout(function() { $scope.getRebuildStatus() }, 5000, true);
- }
+ }
});
};
- $scope.rebuild = function() {
+ $scope.rebuild = function() {
nexuResource.rebuild(-1)
.then(function(result) {
$scope.getRebuildStatus();
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/controllers/media-delete-controller.js b/Source/Our.Umbraco.Nexu.Core/Client/controllers/media-delete-controller.js
index af382ca..f960b11 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/controllers/media-delete-controller.js
+++ b/Source/Our.Umbraco.Nexu.Core/Client/controllers/media-delete-controller.js
@@ -1,14 +1,9 @@
angular.module('umbraco').controller('Our.Umbraco.Nexu.MediaDeleteController',
- ['$scope', '$controller', 'Our.Umbraco.Nexu.Resource',
- function ($scope, $controller, nexuResource) {
- // inherit core delete controller
- angular.extend(this, $controller('Umbraco.Editors.Media.DeleteController', { $scope: $scope }));
+ ['$scope', '$controller',
+ function ($scope, $controller) {
+ $scope.isMedia = true;
- $scope.links = {};
- $scope.isLoading = true;
-
- nexuResource.getIncomingLinks($scope.currentNode.id).then(function (result) {
- $scope.links = result.data;
- $scope.isLoading = false;
- });
+ // inherit base delete controller
+ angular.extend(this, $controller('Our.Umbraco.Nexu.BaseDeleteController', { $scope: $scope }));
+
}]);
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/controllers/unpublish-confirmation-controller.js b/Source/Our.Umbraco.Nexu.Core/Client/controllers/unpublish-confirmation-controller.js
index 6c323ce..f25596c 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/controllers/unpublish-confirmation-controller.js
+++ b/Source/Our.Umbraco.Nexu.Core/Client/controllers/unpublish-confirmation-controller.js
@@ -1,17 +1,19 @@
angular.module('umbraco').controller('Our.Umbraco.Nexu.UnPublishConfirmationController',
- ['$scope','notificationsService',
+ ['$scope','notificationsService',
function ($scope, notificationsService) {
$scope.links = $scope.notification.args.links;
- $scope.publish = function (notification) {
+ $scope.descendantsHaveLinks = $scope.notification.args.descendantsHaveLinks;
+
+ $scope.publish = function (notification) {
notificationsService.remove(notification);
// execute the deferred unpublish request
- notification.args.deferredPromise.resolve(notification.args.originalRequest);
+ notification.args.deferredPromise.resolve(notification.args.originalRequest);
};
- $scope.cancel = function (notification) {
+ $scope.cancel = function (notification) {
notificationsService.remove(notification);
};
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/directives/nexuDescendantsWarning.js b/Source/Our.Umbraco.Nexu.Core/Client/directives/nexuDescendantsWarning.js
new file mode 100644
index 0000000..134c8e1
--- /dev/null
+++ b/Source/Our.Umbraco.Nexu.Core/Client/directives/nexuDescendantsWarning.js
@@ -0,0 +1,9 @@
+angular.module("umbraco.directives")
+ .directive('nexuDescendantsWarning', function () {
+ return {
+ restrict: "E", // restrict to an element
+ replace: true, // replace the html element with the template
+ transclude: true,
+ templateUrl: '/App_Plugins/Nexu/views/nexu-descendants-warning.html'
+ };
+ });
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/interceptor.js b/Source/Our.Umbraco.Nexu.Core/Client/interceptor.js
index d501348..5b41d62 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/interceptor.js
+++ b/Source/Our.Umbraco.Nexu.Core/Client/interceptor.js
@@ -8,7 +8,7 @@
// Redirect any requests to built in content delete to our custom delete
if (request.url.indexOf("views/content/delete.html") === 0) {
- request.url = '/App_Plugins/Nexu/views/content-delete.html';
+ request.url = '/App_Plugins/Nexu/views/content-delete.html';
}
// Redirect any requests to built in media delete to our custom delete
@@ -19,15 +19,15 @@
var unpublishUrlRegex = /^\/umbraco\/backoffice\/UmbracoApi\/Content\/PostUnPublish\?id=(\d*)$/i;
// check if unpublished api call is made
- if(unpublishUrlRegex.test(request.url)) {
+ if(unpublishUrlRegex.test(request.url)) {
// get the id from the url
var id = unpublishUrlRegex.exec(request.url)[1];
- // get nexuResource
+ // get nexuResource
var nexuService = $injector.get('Our.Umbraco.Nexu.Resource');
// create deferred request
- var deferred = $q.defer();
+ var deferred = $q.defer();
// get incoming links
nexuService.getIncomingLinks(id)
@@ -41,20 +41,38 @@
args: {
links: result.data,
deferredPromise: deferred,
- originalRequest : request
+ originalRequest: request,
+ descendantsHaveLinks : false
}
- });
+ });
} else {
- // execute request as normal
- deferred.resolve(request);
+ nexuService.checkDescendants(id, false).then(function(result) {
+ if (result.data) {
+ notificationsService.add({
+ // the path of our custom notification view
+ view: "/App_Plugins/Nexu/views/unpublish-confirmation.html",
+ // arguments object we want to pass to our custom notification
+ args: {
+ links: [],
+ deferredPromise: deferred,
+ originalRequest: request,
+ descendantsHaveLinks: true
+ }
+ });
+ } else {
+ // execute request as normal
+ deferred.resolve(request);
+ }
+ });
+
}
});
// return deferred promise
return deferred.promise;
-
+
}
-
+
return request || $q.when(request);
}
};
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/lang/en-GB.xml b/Source/Our.Umbraco.Nexu.Core/Client/lang/en-GB.xml
index 25940ab..0fb66f1 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/lang/en-GB.xml
+++ b/Source/Our.Umbraco.Nexu.Core/Client/lang/en-GB.xml
@@ -11,5 +11,6 @@
This page is linked to from following itemsThis media item is linked to from following itemsAre you sure you want to unpublish
+ One of the sub items is linked from another content item
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/lang/en-US.xml b/Source/Our.Umbraco.Nexu.Core/Client/lang/en-US.xml
index 4a42869..0de07d1 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/lang/en-US.xml
+++ b/Source/Our.Umbraco.Nexu.Core/Client/lang/en-US.xml
@@ -11,5 +11,6 @@
This page is linked to from following itemsThis media item is linked to from following itemsAre you sure you want to unpublish
+ One of the sub items is linked from another content item
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/lang/nl-NL.xml b/Source/Our.Umbraco.Nexu.Core/Client/lang/nl-NL.xml
index 7cfc5eb..6158f39 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/lang/nl-NL.xml
+++ b/Source/Our.Umbraco.Nexu.Core/Client/lang/nl-NL.xml
@@ -11,5 +11,6 @@
Er wordt naar deze pagina gelinkt vanuit deze paginasEr wordt naar dit bestand gelinkt vanuit deze paginasBent u zeker dat u deze pagina wil depubliceren
+ Eén van de onderliggende items is gelinkt in een andere pagina
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/package.manifest b/Source/Our.Umbraco.Nexu.Core/Client/package.manifest
index 8056271..5cce275 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/package.manifest
+++ b/Source/Our.Umbraco.Nexu.Core/Client/package.manifest
@@ -1,14 +1,16 @@
-{
- javascript: [
- '~/App_Plugins/Nexu/interceptor.js',
- '~/App_Plugins/Nexu/resource.js',
- '~/App_Plugins/Nexu/controllers/content-delete-controller.js',
- '~/App_Plugins/Nexu/controllers/media-delete-controller.js',
- '~/App_Plugins/Nexu/controllers/unpublish-confirmation-controller.js',
- '~/App_Plugins/Nexu/controllers/dashboard-controller.js',
- '~/App_Plugins/Nexu/directives/nexuLinks.js'
- ],
- css: [
- '~/App_Plugins/Nexu/nexu.css'
- ]
+{
+ javascript: [
+ '~/App_Plugins/Nexu/interceptor.js',
+ '~/App_Plugins/Nexu/resource.js',
+ '~/App_Plugins/Nexu/controllers/base-delete-controller.js',
+ '~/App_Plugins/Nexu/controllers/content-delete-controller.js',
+ '~/App_Plugins/Nexu/controllers/media-delete-controller.js',
+ '~/App_Plugins/Nexu/controllers/unpublish-confirmation-controller.js',
+ '~/App_Plugins/Nexu/controllers/dashboard-controller.js',
+ '~/App_Plugins/Nexu/directives/nexuLinks.js',
+ '~/App_Plugins/Nexu/directives/nexuDescendantsWarning.js'
+ ],
+ css: [
+ '~/App_Plugins/Nexu/nexu.css'
+ ]
}
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/resource.js b/Source/Our.Umbraco.Nexu.Core/Client/resource.js
index 12a6f85..3673c05 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/resource.js
+++ b/Source/Our.Umbraco.Nexu.Core/Client/resource.js
@@ -4,6 +4,13 @@
getIncomingLinks: function (id) {
return $http.get(Umbraco.Sys.ServerVariables.Nexu.GetIncomingLinks + "?contentId=" + id);
},
+ checkDescendants: function (id, isMedia) {
+ if (isMedia) {
+ return $http.get(Umbraco.Sys.ServerVariables.Nexu.CheckMediaDescendantsForIncomingLinks + "?mediaId=" + id);
+ }
+
+ return $http.get(Umbraco.Sys.ServerVariables.Nexu.CheckContentDescendantsForIncomingLinks + "?contentId=" + id);
+ },
getRebuildStatus : function() {
return $http.get(Umbraco.Sys.ServerVariables.Nexu.GetRebuildStatus);
},
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/views/content-delete.html b/Source/Our.Umbraco.Nexu.Core/Client/views/content-delete.html
index 99ce7b2..88cd75a 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/views/content-delete.html
+++ b/Source/Our.Umbraco.Nexu.Core/Client/views/content-delete.html
@@ -1,7 +1,12 @@
+
+
+
-
+
+
+
Are you sure you want to delete{{currentNode.name}} ?
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/views/media-delete.html b/Source/Our.Umbraco.Nexu.Core/Client/views/media-delete.html
index 8d11bb6..5b0735a 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/views/media-delete.html
+++ b/Source/Our.Umbraco.Nexu.Core/Client/views/media-delete.html
@@ -1,14 +1,19 @@
-
+
+
-
+
-
- Are you sure you want to delete{{currentNode.name}} ?
-
+
-
-
+
-
+
+ Are you sure you want to delete{{currentNode.name}} ?
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/views/nexu-descendants-warning.html b/Source/Our.Umbraco.Nexu.Core/Client/views/nexu-descendants-warning.html
new file mode 100644
index 0000000..4eaecdd
--- /dev/null
+++ b/Source/Our.Umbraco.Nexu.Core/Client/views/nexu-descendants-warning.html
@@ -0,0 +1,3 @@
+
+
One of the sub items is linked from another content item
+
\ No newline at end of file
diff --git a/Source/Our.Umbraco.Nexu.Core/Client/views/nexu-links.html b/Source/Our.Umbraco.Nexu.Core/Client/views/nexu-links.html
index 6b1bd65..cbc8320 100644
--- a/Source/Our.Umbraco.Nexu.Core/Client/views/nexu-links.html
+++ b/Source/Our.Umbraco.Nexu.Core/Client/views/nexu-links.html
@@ -1,11 +1,11 @@
-