diff --git a/zero.Core/Entities/IRoutedEntity.cs b/zero.Core/Entities/IRoutedEntity.cs
new file mode 100644
index 00000000..ff877525
--- /dev/null
+++ b/zero.Core/Entities/IRoutedEntity.cs
@@ -0,0 +1,10 @@
+namespace zero.Core.Entities
+{
+ public interface IRoutedEntity
+ {
+ ///
+ /// Url for this entity
+ ///
+ public string Url { get; set; }
+ }
+}
diff --git a/zero.Core/Entities/Pages/Page.cs b/zero.Core/Entities/Pages/Page.cs
index 68a20038..7fef0c86 100644
--- a/zero.Core/Entities/Pages/Page.cs
+++ b/zero.Core/Entities/Pages/Page.cs
@@ -10,6 +10,9 @@ namespace zero.Core.Entities
///
public class Page : ZeroEntity, IPage
{
+ ///
+ public string Url { get; set; }
+
///
public string ParentId { get; set; }
@@ -25,7 +28,7 @@ namespace zero.Core.Entities
[Collection("Pages")]
- public interface IPage : IZeroEntity, IAppAwareEntity, IZeroDbConventions
+ public interface IPage : IZeroEntity, IAppAwareEntity, IZeroDbConventions, IRoutedEntity
{
///
/// Id of the parent page
diff --git a/zero.Core/Routing/IPageUrlResolver.cs b/zero.Core/Routing/IPageUrlResolver.cs
index 6ebca55f..5a298f62 100644
--- a/zero.Core/Routing/IPageUrlResolver.cs
+++ b/zero.Core/Routing/IPageUrlResolver.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
@@ -9,6 +10,11 @@ namespace zero.Core.Routing
{
public interface IPageUrlResolver
{
+ ///
+ /// Get URL for a page
+ ///
+ Task GetUrl(IPage page);
+
///
/// Get URL for a page
///
diff --git a/zero.Core/Routing/PageUrlProvider-old.cs b/zero.Core/Routing/PageUrlProvider-old.cs
new file mode 100644
index 00000000..a8d8f0af
--- /dev/null
+++ b/zero.Core/Routing/PageUrlProvider-old.cs
@@ -0,0 +1,136 @@
+using Raven.Client.Documents;
+using Raven.Client.Documents.Linq;
+using Raven.Client.Documents.Session;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using zero.Core.Api;
+using zero.Core.Entities;
+using zero.Core.Entities.Messages;
+using zero.Core.Extensions;
+using zero.Core.Messages;
+using zero.Core.Utils;
+
+namespace zero.Core.Routing
+{
+ public class PageUrlProviderOld : IUrlProvider
+ {
+ public string Alias => "zero.pages";
+
+ public const string PAGE_COLLECTION = "page";
+
+ public const string PAGE_TYPE = "pageType";
+
+ protected IDocumentStore Raven { get; private set; }
+
+ protected IMessageAggregator Messages { get; private set; }
+
+ protected IPageUrlResolver PageUrlResolver { get; set; }
+
+
+ protected IApplicationContext Context { get; private set; }
+
+ public PageUrlProviderOld(IDocumentStore raven, IMessageAggregator messages, IPageUrlResolver pageUrlResolver, IApplicationContext context)
+ {
+ Raven = raven;
+ Messages = messages;
+ PageUrlResolver = pageUrlResolver;
+ Context = context;
+ }
+
+
+ ///
+ public async Task Resolve(IRoute route)
+ {
+ PageRoute resolved = new PageRoute(route);
+
+ List ids = new List();
+ RouteReference reference = route.References.SingleOrDefault(x => x.Collection == PAGE_COLLECTION);
+
+ ids.Add(reference.Id);
+ ids.AddRange(route.Dependencies);
+
+ using IAsyncDocumentSession session = Raven.OpenAsyncSession();
+
+ Dictionary pages = await session.LoadAsync(ids);
+
+ if (!pages.TryGetValue(reference.Id, out IPage page) || page.AppId != route.AppId)
+ {
+ return null;
+ }
+
+ resolved.Page = page;
+ resolved.Parents = pages.Where(x => x.Key != reference.Id).Select(x => x.Value).ToList();
+
+ return resolved;
+ }
+
+
+
+
+ ///
+ public async Task> Run()
+ {
+ using IAsyncDocumentSession session = Raven.OpenAsyncSession();
+
+ List allRoutes = new List();
+ IList pages = await session.Query().ToListAsync();
+ IEnumerable> groupedPages = pages.GroupBy(x => x.AppId);
+
+ foreach (var group in groupedPages)
+ {
+ IApplicationContext context = await Context.ForId(group.Key);
+
+ IList routes = TraversePageChildren(context, null, new List() { }, group);
+ allRoutes.AddRange(routes);
+ }
+
+ return allRoutes;
+ }
+
+
+ protected IRoute BuildRoute(IApplicationContext context, IPage page, IEnumerable parents)
+ {
+ UrlInfo info = PageUrlResolver.GetUrl(context, page, parents);
+
+ IRoute route = new Route()
+ {
+ Id = String.Concat("routes.", IdGenerator.Create()),
+ AppId = context.AppId,
+ Url = info.IsUrl ? info.Text.EnsureStartsWith('/') : null,
+ ProviderAlias = Alias
+ };
+
+ route.Params.Add(PAGE_TYPE, page.PageTypeAlias);
+ route.References.Add(new RouteReference(page.Id, PAGE_COLLECTION));
+
+ if (parents != null)
+ {
+ foreach (IPage parent in parents)
+ {
+ route.Dependencies.Add(parent.Id);
+ }
+ }
+
+ return route;
+ }
+
+
+ IList TraversePageChildren(IApplicationContext context, IPage parent, IEnumerable parents, IEnumerable allPages)
+ {
+ List routes = new List();
+ IEnumerable currentPages = allPages.Where(x => x.ParentId == parent?.Id);
+
+ foreach (IPage page in currentPages)
+ {
+ IRoute route = BuildRoute(context, page, parents);
+ routes.Add(route);
+ routes.AddRange(TraversePageChildren(context, page, parents.Union(new List() { page }), allPages));
+ }
+
+ return routes;
+ }
+ }
+}
diff --git a/zero.Core/Routing/PageUrlProvider-test.cs b/zero.Core/Routing/PageUrlProvider-test.cs
new file mode 100644
index 00000000..9424d81b
--- /dev/null
+++ b/zero.Core/Routing/PageUrlProvider-test.cs
@@ -0,0 +1,105 @@
+using Raven.Client.Documents;
+using Raven.Client.Documents.Linq;
+using Raven.Client.Documents.Session;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using zero.Core.Api;
+using zero.Core.Database.Indexes;
+using zero.Core.Entities;
+using zero.Core.Entities.Messages;
+using zero.Core.Extensions;
+using zero.Core.Messages;
+using zero.Core.Utils;
+
+namespace zero.Core.Routing
+{
+ public class PageUrlProviderTest
+ {
+ public string Alias => "zero.pages";
+
+ public const string PAGE_COLLECTION = "page";
+
+ public const string PAGE_TYPE = "pageType";
+
+ protected IDocumentStore Raven { get; private set; }
+
+ protected IMessageAggregator Messages { get; private set; }
+
+ protected IPageUrlResolver PageUrlResolver { get; set; }
+
+ protected IApplicationContext Context { get; private set; }
+
+
+ public PageUrlProviderTest(IDocumentStore raven, IMessageAggregator messages, IPageUrlResolver pageUrlResolver, IApplicationContext context)
+ {
+ Raven = raven;
+ Messages = messages;
+ PageUrlResolver = pageUrlResolver;
+ Context = context;
+ }
+
+
+ ///
+ public bool UrlChanged(IPage previous, IPage current)
+ {
+ return previous == null || !previous.Alias.Equals(current.Alias) || !previous.ParentId.Equals(current.ParentId);
+ }
+
+
+ ///
+ public Task GetDependencies(IPage model)
+ {
+ // TODO get all pages (and other routes) which have this page as a dependency and update them
+ throw new NotImplementedException();
+ }
+
+
+ ///
+ public async Task CreateRoute(IPage model)
+ {
+ using IAsyncDocumentSession session = Raven.OpenAsyncSession();
+
+ Pages_ByHierarchy.Result result = await session.Query()
+ .ProjectInto()
+ .Include(x => x.Path.Select(p => p.Id))
+ .FirstOrDefaultAsync(x => x.Id == model.Id);
+
+ IList parents = (await session.LoadAsync(result.Path.Select(x => x.Id))).Select(x => x.Value).ToList();
+
+ return new Route()
+ {
+ Id = String.Concat("routes.", IdGenerator.Create()),
+ AppId = model.AppId,
+ Url = GetUrl(model, parents),
+ ProviderAlias = Alias
+ };
+ }
+
+
+ ///
+ /// Create url for page
+ ///
+ protected string GetUrl(IPage page, IEnumerable parents)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ stringBuilder.Append('/');
+
+ if (parents != null)
+ {
+ foreach (IPage parent in parents)
+ {
+ stringBuilder.Append(parent.Alias);
+ stringBuilder.Append('/');
+ }
+ }
+
+ stringBuilder.Append(page.Alias);
+
+ return stringBuilder.ToString();
+ }
+ }
+}
diff --git a/zero.Core/Routing/PageUrlResolver.cs b/zero.Core/Routing/PageUrlResolver.cs
index d1d4a7b5..65a29141 100644
--- a/zero.Core/Routing/PageUrlResolver.cs
+++ b/zero.Core/Routing/PageUrlResolver.cs
@@ -1,8 +1,12 @@
-using System;
+using Raven.Client.Documents;
+using Raven.Client.Documents.Session;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Threading.Tasks;
using zero.Core.Api;
+using zero.Core.Database.Indexes;
using zero.Core.Entities;
namespace zero.Core.Routing
@@ -13,6 +17,49 @@ namespace zero.Core.Routing
const bool TRAILING_SLASH = false;
+ protected IDocumentStore Raven { get; private set; }
+
+
+ public PageUrlResolver(IDocumentStore raven)
+ {
+ Raven = raven;
+ }
+
+
+ ///
+ public async Task GetUrl(IPage page)
+ {
+ using IAsyncDocumentSession session = Raven.OpenAsyncSession();
+
+ Pages_ByHierarchy.Result result = await session.Query()
+ .ProjectInto()
+ .Include(x => x.Path.Select(p => p.Id))
+ .FirstOrDefaultAsync(x => x.Id == page.Id);
+
+ IList parents = (await session.LoadAsync(result.Path.Select(x => x.Id))).Select(x => x.Value).ToList();
+
+ StringBuilder stringBuilder = new StringBuilder();
+
+ if (parents != null)
+ {
+ foreach (IPage parent in parents)
+ {
+ stringBuilder.Append(parent.Alias);
+ stringBuilder.Append(PATH_SEPARATOR);
+ }
+ }
+
+ stringBuilder.Append(page.Alias);
+ stringBuilder.Append(PATH_SEPARATOR);
+
+ if (!TRAILING_SLASH)
+ {
+ stringBuilder.Remove(stringBuilder.Length - 1, 1);
+ }
+
+ return stringBuilder.ToString();
+ }
+
///
public UrlInfo GetUrl(IApplicationContext context, IPage page, IEnumerable parents)