Skip to content

Commit

Permalink
Merge pull request #255 from fluentcms/243-add-plugin-as-cascadeparam…
Browse files Browse the repository at this point in the history
…eter-in-section-component-for-each-plugin

243 add plugin as cascadeparameter in section component for each plugin
  • Loading branch information
pournasserian authored Nov 30, 2023
2 parents 373f88c + 5de71e4 commit 46237ac
Show file tree
Hide file tree
Showing 18 changed files with 229 additions and 97 deletions.
28 changes: 15 additions & 13 deletions src/FluentCMS.Api/DefaultData/DefaultDataLoaderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,18 @@ public static void LoadInitialDataFrom(this IServiceProvider provider, string da
}

// Plugins creation: adding a few default plugins to pages
foreach (var page in defaultData.Pages)
var order = 0;
var _page = defaultData.Pages[0];
foreach (var pluginDef in defaultData.PluginDefinitions)
{
var order = 0;
foreach (var pluginDef in defaultData.PluginDefinitions)
var plugin = new Plugin
{
var plugin = new Plugin
{
DefinitionId = pluginDef.Id,
PageId = page.Id,
Order = order++,
Section = "main"
};
pluginService.Create(plugin).GetAwaiter().GetResult();
}
DefinitionId = pluginDef.Id,
PageId = _page.Id,
Order = order++,
Section = "main"
};
pluginService.Create(plugin).GetAwaiter().GetResult();
}
}
}
Expand All @@ -118,13 +116,17 @@ private static T LoadData<T>(string jsonFile)

private static IEnumerable<Layout> GetLayouts(string dataFolder)
{
foreach (var file in Directory.GetFiles(dataFolder, "*.html"))
foreach (var file in Directory.GetFiles(dataFolder, "*.html").Where(x=> !x.Contains(".header.html")))
{
var layout = new Layout
{
Name = Path.GetFileNameWithoutExtension(file),
Content = File.ReadAllText(file)
};
var headerFile = Path.ChangeExtension(file, ".header.html");
if (File.Exists(headerFile))
layout.Head = File.ReadAllText(headerFile);

yield return layout;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/FluentCMS.Entities/Layout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ public class Layout : AuditEntity, IAuthorizeEntity
public Guid SiteId { get; set; }
public string Name { get; set; } = default!;
public string Content { get; set; } = default!;
public string Head { get; set; } = default!;
}
8 changes: 2 additions & 6 deletions src/FluentCMS.Web.UI/App.razor
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="/" />
<link rel="stylesheet" href="/css/app.min.css">
<HeadContent />
<HeadOutlet @rendermode="@InteractiveServer" />
</head>
<body>
<Routes />
<Routes @rendermode="@InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
27 changes: 27 additions & 0 deletions src/FluentCMS.Web.UI/AppState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using FluentCMS.Api.Models;
using FluentCMS.Entities;
using Microsoft.AspNetCore.Components;

namespace FluentCMS.Web.UI;

public class AppState
{
public SiteResponse? Site { get; set; }
public PageResponse? Page { get; set; }
public Layout? Layout { get; set; }
public string? Route { get; set; }
public Uri? Uri { get; set; }
public string Host { get; set; }

public AppState()
{

}

public AppState(NavigationManager navigation)
{
Host = navigation.BaseUri;
if (Host.EndsWith("/"))
Host = Host.Substring(0, Host.Length - 1);
}
}
25 changes: 0 additions & 25 deletions src/FluentCMS.Web.UI/Components/Section.razor

This file was deleted.

5 changes: 5 additions & 0 deletions src/FluentCMS.Web.UI/DefaultData/layout.header.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="/" />
<link rel="stylesheet" href="/css/app.min.css">
<link rel="icon" type="image/png" href="favicon.png" />
4 changes: 2 additions & 2 deletions src/FluentCMS.Web.UI/DefaultData/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<header class="navbar sticky-top bg-dark flex-md-nowrap p-0 shadow" data-bs-theme="dark">
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6 text-white" href="#">FluentCMS</a>
</header>
<Section name="main" fluentcms />
<Section Name="main" fluentcms />
</main>
</div>
<script src="/lib/js/flowbite.min.js"></script>
<script src="/js/flowbite.min.js"></script>
93 changes: 57 additions & 36 deletions src/FluentCMS.Web.UI/Pages/Home.razor
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,84 @@
@inject HttpClient http
@inject NavigationManager Navigator

<CascadingValue Value="@Route">
<CascadingValue Value="@Site">
<CascadingValue Value="@Page">
@dynamicComponent()
</CascadingValue>
</CascadingValue>
<PageTitle>@AppState?.Page?.Title</PageTitle>

<HeadContent>
@((MarkupString)$"<!-- Begin {@AppState?.Page?.Title} Page Header -->")
@if (AppState?.Layout != null)
{
@((MarkupString)AppState.Layout.Head)
}
@((MarkupString)$"<!-- End {@AppState?.Page?.Title} Page Header -->")
</HeadContent>

@((MarkupString)$"<!-- Begin {@AppState?.Page?.Title} Page Content -->")
<CascadingValue Value="@AppState">
@dynamicComponent()
</CascadingValue>
@((MarkupString)$"<!-- End {@AppState?.Page?.Title} Page Content -->")

@code {
[Parameter]
public string? Route { get; set; }

[Parameter]
public SiteResponse? Site { get; set; }

[Parameter]
public PageResponse? Page { get; set; }
public string? Route { get; set; }

private string _layout = string.Empty;
private AppState? AppState { get; set; }

protected override async Task OnParametersSetAsync()
protected override Task OnParametersSetAsync()
{
await base.OnParametersSetAsync();
if (AppState == null)
AppState = new AppState();

var host = Navigator.BaseUri;
if (host.EndsWith("/"))
host = host.Substring(0, host.Length - 1);
AppState.Host = Navigator.BaseUri.EndsWith("/") ? Navigator.BaseUri.Remove(Navigator.BaseUri.Length - 1) : Navigator.BaseUri;
AppState.Uri = new Uri(Navigator.Uri);

var siteResult = http.GetFromJsonAsync<ApiResult<SiteResponse>>($"Site/GetByUrl?url={host}").GetAwaiter().GetResult();
Site = siteResult?.Data;
var siteResult = http.GetFromJsonAsync<ApiResult<SiteResponse>>($"Site/GetByUrl?url={AppState.Host}").GetAwaiter().GetResult();
AppState.Site = siteResult?.Data;
AppState.Layout = AppState.Site?.Layout;

if (Site is null)
return;
if (AppState.Site != null)
{
var query = HttpUtility.ParseQueryString(string.Empty);
query["siteId"] = AppState.Site.Id.ToString();
query["path"] = AppState.Uri.LocalPath;

if (Route is null)
Route = string.Empty;
var pageResult = http.GetFromJsonAsync<ApiResult<PageResponse>>($"Page/GetByPath?{query}").GetAwaiter().GetResult();
AppState.Page = pageResult?.Data;
if (AppState.Page != null && AppState.Page.Layout != null)
AppState.Layout = AppState.Page.Layout;
}

var query = HttpUtility.ParseQueryString(string.Empty);
query["siteId"] = Site.Id.ToString();
query["path"] = Route;
return base.OnParametersSetAsync();
}

var pageResult = http.GetFromJsonAsync<ApiResult<PageResponse>>($"Page/GetByPath?{query.ToString()}").GetAwaiter().GetResult();
Page = pageResult?.Data;
if (Page is null)
return;
protected override Task OnAfterRenderAsync(bool firstRender)
{
return base.OnAfterRenderAsync(firstRender);
}

_layout = Page.Layout?.Content ?? Site.Layout.Content;
protected override bool ShouldRender()
{
return base.ShouldRender();
}

RenderFragment dynamicComponent() => builder =>
{
var _type = GetType("Section");
if (_type == null)
return;

builder.OpenComponent(1, _type);
builder.AddComponentParameter(2, "Name", "main");
builder.CloseComponent();

return;

var index = 0;
var startIndex = 0;
var lastIndex = 0;
var lastLength = 0;
var _layout = AppState?.Layout?.Content ?? string.Empty;

// ignore comments before trying to parse
var commentRegex = new Regex("<!--.*-->");
Expand All @@ -68,15 +91,14 @@
var matcherAttributeName = "fluentcms";

// find any tag that has our attribute
// TODO: check that tag is not commented
var tagFinderRegex = new Regex($"<(?<TagName>\\w+)[^/>]*{matcherAttributeName}[^/>]*/>", RegexOptions.IgnoreCase);

var tagMatches = tagFinderRegex.Matches(_layout);

// ignore if no tags found
if (tagMatches.Count() == 0)
{
builder.AddMarkupContent(index, _layout);
builder.AddMarkupContent(0, _layout);
return;
}

Expand All @@ -97,9 +119,8 @@
var parameters = ParseAttributes(tag.Value);

//add parameters
var parameterIndex = 0;
foreach (var parameter in parameters)
builder.AddComponentParameter(parameterIndex++, parameter.Key, parameter.Value);
builder.AddComponentParameter(index++, parameter.Key, parameter.Value);

//close component
builder.CloseComponent();
Expand Down
48 changes: 48 additions & 0 deletions src/FluentCMS.Web.UI/Pages/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
@inherits LayoutComponentBase
<div class="f-layout">
<aside id="default-sidebar" aria-label="Sidebar" class="f-sidebar -translate-x-full">
<div class="f-sidebar-body">
<NavLink href="/" class="f-sidebar-item">
<span class="f-sidebar-item-text">Home</span>
</NavLink>
<NavLink href="/about-me" class="f-sidebar-item">
<span class="f-sidebar-item-text">About Me</span>
</NavLink>
<NavLink href="/contact" class="f-sidebar-item">
<span class="f-sidebar-item-text">Contact Me</span>
</NavLink>
<NavLink href="/contact?id=1&me=you" class="f-sidebar-item">
<span class="f-sidebar-item-text">Contact with query</span>
</NavLink>
<NavLink href="/kitchen-sink" class="f-sidebar-item">
<span class="f-sidebar-item-text">Kitchen Sink</span>
</NavLink>
<NavLink href="/doc" class="f-sidebar-item" target="_blank">
<span class="f-sidebar-item-text">Api Doc</span>
</NavLink>
<hr />
<a href="/" class="f-sidebar-item">
<span class="f-sidebar-item-text">Home</span>
</a>
<a href="/about-me" class="f-sidebar-item">
<span class="f-sidebar-item-text">About Me</span>
</a>
<a href="/contact" class="f-sidebar-item">
<span class="f-sidebar-item-text">Contact Me</span>
</a>
<a href="/kitchen-sink" class="f-sidebar-item">
<span class="f-sidebar-item-text">Kitchen Sink</span>
</a>
<a href="/doc" class="f-sidebar-item" target="_blank">
<span class="f-sidebar-item-text">Api Doc</span>
</a>
</div>
</aside>
<main class="f-content">
<header class="navbar sticky-top bg-dark flex-md-nowrap p-0 shadow" data-bs-theme="dark">
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6 text-white" href="#">FluentCMS</a>
</header>
@Body
</main>
</div>
<script src="/js/flowbite.min.js"></script>
31 changes: 31 additions & 0 deletions src/FluentCMS.Web.UI/Pages/Section.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@using FluentCMS.Entities
@using System.Linq

@((MarkupString)$"<!-- Begin Section: {@Name} -->")
@if (AppState?.Page?.Plugins != null)
{
@foreach (var plugin in AppState.Page.Plugins.Where(x => x.Section.ToLower() == Name.ToLower()).OrderBy(x => x.Order))
{
@((MarkupString)$"<!-- Begin Plugin: {@plugin.Definition.Name} -->")
<CascadingValue Value="@plugin" Name="Plugin">
<DynamicComponent Type=@GetType(plugin) />
</CascadingValue>
@((MarkupString)$"<!-- End Plugin: {@plugin.Definition.Name} -->")
}
}
@((MarkupString)$"<!-- End Section: {@Name} -->")

@code {
[Parameter]
public string Name { get; set; } = string.Empty;

[CascadingParameter]
public AppState? AppState { get; set; }

private Type? GetType(PluginResponse plugin)
{
var assembly = typeof(Section).Assembly;
var typeInfo = assembly.DefinedTypes.FirstOrDefault(x => x.Name == plugin.Definition.ViewType);
return typeInfo?.AsType();
}
}
14 changes: 8 additions & 6 deletions src/FluentCMS.Web.UI/Plugins/Counter/CounterView.razor
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
@rendermode InteractiveServer
<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>
<Counter @onclick="IncrementCount">Click me</Counter>
<div style="background-color:lightpink;">
<h1>Counter Plugin id: @Plugin?.Id</h1>
<Button @onclick="IncrementCount">Click me</Button>
<p role="status">Current count: @currentCount</p>
</div>

@code {
private int currentCount = 0;
[CascadingParameter(Name = "Plugin")]
public PluginResponse Plugin { get; set; } = default!;

private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
Expand Down
Loading

0 comments on commit 46237ac

Please sign in to comment.