Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1855 Implement Blogs plugin #1862

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/FluentCMS.sln
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentCMS.Web.Plugins.Admin
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentCMS.Web.Plugins.Contents.TextHTML", "Frontend\Plugins\FluentCMS.Web.Plugins.Contents.TextHTML\FluentCMS.Web.Plugins.Contents.TextHTML.csproj", "{818F8CD0-5218-4480-A541-BD9D59416CDE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentCMS.Web.Plugins.Contents.Blogs", "Frontend\Plugins\FluentCMS.Web.Plugins.Contents.Blogs\FluentCMS.Web.Plugins.Contents.Blogs.csproj", "{9293D902-7341-4EA8-96F6-D29FFDE80AC4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -101,6 +103,10 @@ Global
{818F8CD0-5218-4480-A541-BD9D59416CDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{818F8CD0-5218-4480-A541-BD9D59416CDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{818F8CD0-5218-4480-A541-BD9D59416CDE}.Release|Any CPU.Build.0 = Release|Any CPU
{9293D902-7341-4EA8-96F6-D29FFDE80AC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9293D902-7341-4EA8-96F6-D29FFDE80AC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9293D902-7341-4EA8-96F6-D29FFDE80AC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9293D902-7341-4EA8-96F6-D29FFDE80AC4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -120,6 +126,7 @@ Global
{BB5FD6A0-0840-4381-AB68-AD61499368ED} = {5961A5E0-54A6-42F5-92A2-9A9E48DE2878}
{70B5742E-C249-4B51-985C-2B4B7BDAB489} = {BB5FD6A0-0840-4381-AB68-AD61499368ED}
{818F8CD0-5218-4480-A541-BD9D59416CDE} = {BB5FD6A0-0840-4381-AB68-AD61499368ED}
{9293D902-7341-4EA8-96F6-D29FFDE80AC4} = {BB5FD6A0-0840-4381-AB68-AD61499368ED}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2E9F4217-7A58-48A4-9850-84CD0CDA31DA}
Expand Down
1 change: 1 addition & 0 deletions src/FluentCMS/FluentCMS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<ProjectReference Include="..\Backend\Repositories\FluentCMS.Repositories.LiteDb\FluentCMS.Repositories.LiteDb.csproj" />
<ProjectReference Include="..\Frontend\FluentCMS.Web.UI\FluentCMS.Web.UI.csproj" />
<ProjectReference Include="..\Frontend\Plugins\FluentCMS.Web.Plugins.Contents.TextHTML\FluentCMS.Web.Plugins.Contents.TextHTML.csproj" />
<ProjectReference Include="..\Frontend\Plugins\FluentCMS.Web.Plugins.Contents.Blogs\FluentCMS.Web.Plugins.Contents.Blogs.csproj" />
<ProjectReference Include="..\Frontend\Plugins\FluentCMS.Web.Plugins.Admin\FluentCMS.Web.Plugins.Admin.csproj" />
</ItemGroup>
<ItemGroup>
Expand Down
52 changes: 52 additions & 0 deletions src/FluentCMS/Template/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,26 @@
"Type": "TextHTMLEditPlugin"
}
]
},
{
"Name": "Blogs Plugin",
"Description": "",
"Assembly": "FluentCMS.Web.Plugins.Contents.Blogs.dll",
"Types": [
{
"Name": "Blogs List",
"Type": "BlogViewPlugin",
"IsDefault": true
},
{
"Name": "Manage Blogs",
"Type": "BlogManagePlugin"
},
{
"Name": "Blog Detail",
"Type": "BlogDetailPlugin"
}
]
}
],
"Pages": [
Expand Down Expand Up @@ -402,6 +422,38 @@
}
]
},
{
"Definition": "Blogs Plugin",
"Section": "Main",
"Type": "BlogContent",
"Content": [
{
"Title": "Introduction to Responsive Web Design",
"Content": "In today's digital landscape, responsive web design is crucial for reaching users on multiple devices. This post explores the principles of responsive design and offers tips for implementation.",
"Description": "In this post You'll learn about Responsive web design"
},
{
"Title": "Exploring the Power of C# LINQ",
"Content": "LINQ (Language-Integrated Query) in C# is a powerful tool for querying and manipulating data. This post dives into the basics of LINQ and demonstrates its usage with practical examples.",
"Description": "In this post You'll learn about LINQ"
},
{
"Title": "Getting Started with Blazor: A Comprehensive Guide",
"Content": "<p>Blazor, Microsoft's innovative web framework, empowers developers to craft interactive web applications seamlessly using C#. This tutorial provides a concise overview of Blazor's core concepts, showcasing its capability to run C# code directly in the browser through WebAssembly.</p><p><br></p><h2>Example</h2><p>Let's explore a basic example: a Blazor component that implements a simple counter.</p><div class=\"ql-code-block-container\" spellcheck=\"false\"><div class=\"ql-code-block\" data-language=\"plain\">Copy code</div><div class=\"ql-code-block\" data-language=\"plain\">@page \"/counter\"</div><div class=\"ql-code-block\" data-language=\"plain\"><br></div><div class=\"ql-code-block\" data-language=\"plain\">&lt;h1&gt;Counter&lt;/h1&gt;</div><div class=\"ql-code-block\" data-language=\"plain\"><br></div><div class=\"ql-code-block\" data-language=\"plain\">&lt;p&gt;Current count: @currentCount&lt;/p&gt;</div><div class=\"ql-code-block\" data-language=\"plain\"><br></div><div class=\"ql-code-block\" data-language=\"plain\">&lt;button class=\"btn btn-primary\" @onclick=\"IncrementCount\"&gt;Click me&lt;/button&gt;</div><div class=\"ql-code-block\" data-language=\"plain\"><br></div><div class=\"ql-code-block\" data-language=\"plain\">@code {</div><div class=\"ql-code-block\" data-language=\"plain\"> private int currentCount = 0;</div><div class=\"ql-code-block\" data-language=\"plain\"><br></div><div class=\"ql-code-block\" data-language=\"plain\"> private void IncrementCount()</div><div class=\"ql-code-block\" data-language=\"plain\"> {</div><div class=\"ql-code-block\" data-language=\"plain\"> currentCount++;</div><div class=\"ql-code-block\" data-language=\"plain\"> }</div><div class=\"ql-code-block\" data-language=\"plain\">}</div></div><p>In this snippet, a Blazor component is defined to maintain and display a count. Upon clicking the button, the count increments without requiring a page refresh, demonstrating Blazor's efficient handling of user interactions and UI updates.</p><p><br></p><h2>Conclusion</h2><p>Blazor presents an enticing opportunity for .NET developers, whether seasoned or new to web development, to harness the power of C# in crafting dynamic web applications. With its seamless integration and intuitive features, Blazor streamlines the development process, making it an invaluable tool in the modern web development landscape.</p><p><br></p><p><br></p><p><br></p><p><br></p>",
"Description": "In this post You'll learn about blazor"
},
{
"Title": "Mastering CSS Grid for Modern Web Layouts",
"Content": "<p>In the realm of web design, CSS Grid has revolutionized the way developers approach layout creation. This comprehensive guide delves into the intricacies of CSS Grid, exploring its two-dimensional grid system that allows for the creation of complex layouts with ease. We will cover the fundamental concepts such as grid containers, grid items, and grid lines, and demonstrate how to use them to build responsive and dynamic web pages.</p><p>You will learn how to define grid tracks, create grid cells, and manage grid areas. Additionally, we will explore advanced techniques like grid auto-placement, grid alignment properties, and the use of CSS Grid with media queries for responsive design. By the end of this post, you will have a solid understanding of how to leverage CSS Grid to create modern, efficient, and visually appealing web layouts.</p>",
"Description": "In this post You'll learn about CSS Grid"
},
{
"Title": "Advanced JavaScript Techniques for Efficient Web Development",
"Content": "<p>JavaScript remains a cornerstone of modern web development, offering endless possibilities for enhancing user interaction and functionality. This post focuses on advanced JavaScript techniques that can significantly improve the efficiency and performance of your web applications. We will explore topics such as asynchronous programming with Promises and async/await, the use of modern JavaScript features like destructuring and template literals, and the importance of modularizing your code with ES6 modules.</p><p>Furthermore, we will delve into the world of JavaScript frameworks and libraries, discussing how they can streamline your development process and help you build scalable and maintainable applications. Whether you are a seasoned JavaScript developer or just starting out, this post will equip you with the knowledge and tools necessary to take your web development skills to the next level.</p>",
"Description": "In this post You'll learn about Advanced JavaScript Techniques"
}
]
},
{
"Definition": "TextHTML Plugin",
"Section": "Main",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace FluentCMS.Web.Plugins.Contents.Blogs;

public class BlogContent : IContent
{
public Guid Id { get; set; }
public string Title { get; set; } = String.Empty;
public string Description { get; set; } = String.Empty;
public string Content { get; set; } = String.Empty;
public string Image { get; set; } = String.Empty;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@if (Item != null)
{
<div>
<h1 class="text-3xl font-bold mb-4">@Item.Title</h1>
<p class="text-lg mb-4">@Item.Description</p>

<div class="">
@((MarkupString)Item.Content)
</div>

</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
namespace FluentCMS.Web.Plugins.Contents.Blogs;

public partial class BlogDetailPlugin
{
// #region Base

[Inject]
protected NavigationManager NavigationManager { get; set; } = default!;

[Parameter]
public string? SectionName { get; set; }

[Parameter]
public PluginViewState? Plugin { get; set; } = default!;

[Inject]
protected ApiClientFactory ApiClient { get; set; } = default!;

[Inject]
protected IHttpContextAccessor? HttpContextAccessor { get; set; }

protected virtual void NavigateBack()
{
var url = new Uri(NavigationManager.Uri).LocalPath;
NavigateTo(url);
}

protected virtual void NavigateTo(string path)
{
if (HttpContextAccessor?.HttpContext != null && !HttpContextAccessor.HttpContext.Response.HasStarted)
HttpContextAccessor.HttpContext.Response.Redirect(path);
else
NavigationManager.NavigateTo(path);
}

protected virtual string GetUrl(string viewName, object? parameters = null)
{
var uri = new Uri(NavigationManager.Uri);
var oldQueryParams = HttpUtility.ParseQueryString(uri.Query);

// this gets the page path from root without QueryString
var pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path);

var newQueryParams = new Dictionary<string, string?>()
{
{ "pluginId", Plugin!.Id.ToString() },
{ "viewName", viewName }
};

if (parameters != null)
{
foreach (var propInfo in parameters.GetType().GetProperties())
newQueryParams[propInfo.Name] = propInfo.GetValue(parameters)?.ToString();
}

foreach (var key in oldQueryParams.AllKeys)
{
if (string.IsNullOrEmpty(key) || newQueryParams.ContainsKey(key))
continue;

newQueryParams[key] = oldQueryParams[key];
}

return QueryHelpers.AddQueryString(pagePathWithoutQueryString, newQueryParams);
}
// #endregion

[SupplyParameterFromQuery(Name = "id")]
private Guid Id { get; set; }

private BlogContent Item { get; set; }

protected override async Task OnInitializedAsync()
{
if (Plugin is not null)
{
var response = await ApiClient.PluginContent.GetByIdAsync(nameof(BlogContent), Plugin.Id, Id);

if (response?.Data != null)
Item = response.Data.Data.ToContent<BlogContent>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@if (Model != null)
{
<EditForm Model="@Model" OnSubmit="HandleSubmit" FormName="@FormName" Enhance>
<DataAnnotationsValidator />
<Spacer />
<Grid GutterX="GridGutter.Large">
<FormHiddenInput @bind-Value="@Model.Id" />
<FormInput @bind-Value="Model.Title" Placeholder="Enter blog title" Label="Title" />
<FormTextarea Placeholder="Enter blog description" Rows="5" @bind-Value="@Model!.Description" Label="Description" />
<FormRichTextEditor Placeholder="Enter blog Content" @bind-Value="@Model!.Content" Label="Content" />

<GridItem Small="GridItemColumn.Twelve">
<Stack>
<Button Color="Color.Primary" Type="ButtonType.Submit">
Submit
</Button>
<Button @onclick="HandleCancel">
Cancel
</Button>
</Stack>
</GridItem>
</Grid>
</EditForm>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace FluentCMS.Web.Plugins.Contents.Blogs;

public partial class BlogEditForm
{
[Parameter]
public BlogContent Model { get; set; }

[Parameter]
public string FormName { get; set; }

[Parameter]
public EventCallback<BlogContent> OnSubmit { get; set; }

[Parameter]
public EventCallback OnCancel { get; set; }

private async Task HandleSubmit()
{
await OnSubmit.InvokeAsync(Model);
}

private async Task HandleCancel()
{
await OnCancel.InvokeAsync();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
@rendermode InteractiveServer

<div class="m-4">

@if(CurrentState == BlogManagementState.List)
{
<Grid ItemsSmall="GridItems.Start" JustifySmall="GridJustify.Between">
<GridItem>
<Typography Size="TextSize.H5">Blogs</Typography>
</GridItem>
<GridItem>
<Stack>
<Button @onclick="NavigateBack">
Back
</Button>

<Button Color="Color.Primary" @onclick="OnAddItemClicked">
<Icon Name="IconName.Plus" />
Add Blog
</Button>
</Stack>
</GridItem>
</Grid>
<Spacer />

@if (Items != null)
{
<div class="">
@foreach (var item in Items)
{
<div class="flex pt-4 border-t justify-between items-center">
<div class="flex-1">
<div class="mb-4">
<h2 class="text-xl font-bold"><a href="@GetDetailUrl("Blog Detail", new { Id = item.Id })">@item.Title</a></h2>
<p class="text-gray-800">@item.Description</p>
</div>
</div>
<Stack>
<Button Color="Color.Danger" Outline @onclick="() => OnDeleteItemClicked(item)">delete</Button>
<Button Color="Color.Primary" Outline @onclick="() => OnEditItemClicked(item)">Edit</Button>
</Stack>
</div>
}
</div>
}
}
else if (CurrentState == BlogManagementState.Create)
{
<Typography Size="TextSize.H5">Create Blog Content</Typography>
<Spacer />
<Card>
<CardBody>
<BlogEditForm Model="@Model" FormName="@CONTENT_TYPE_NAME" OnSubmit="OnCreateBlog" OnCancel="NavigateBack"/>
</CardBody>
</Card>
}
else if (CurrentState == BlogManagementState.Edit)
{
<Typography Size="TextSize.H5">Edit Blog Content</Typography>
<Spacer />
<Card>
<CardBody>
<BlogEditForm Model="@Model" FormName="@CONTENT_TYPE_NAME" OnSubmit="OnEditBlog" OnCancel="NavigateBack"/>
</CardBody>
</Card>
}
</div>

<Confirm OnConfirm="OnDeleteItem" OnCancel="OnConfirmClose" Visible="DeleteConfirmOpen">
Are you sure to delete this content?
</Confirm>
Loading