added BlazorAppRadzenHtmlEditor

This commit is contained in:
M. Akif Tokatlioglu
2024-05-12 21:26:38 +03:00
parent 09c17ff370
commit b14fe4e481
49 changed files with 1356 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34601.278
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorAppRadzenHtmlEditor", "BlazorAppRadzenHtmlEditor\BlazorAppRadzenHtmlEditor.csproj", "{F294D76E-112B-49BC-ACAD-06F83FD41D64}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F294D76E-112B-49BC-ACAD-06F83FD41D64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F294D76E-112B-49BC-ACAD-06F83FD41D64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F294D76E-112B-49BC-ACAD-06F83FD41D64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F294D76E-112B-49BC-ACAD-06F83FD41D64}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {33D655BE-FD9B-42AE-BDF3-CADB373E4149}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.4" />
<PackageReference Include="Radzen.Blazor" Version="4.31.3" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="_content/Radzen.Blazor/css/standard-base.css">
@* <link rel="stylesheet" href="app.css" /> *@
@* <link rel="stylesheet" href="BlazorAppRadzenHtmlEditor.styles.css" /> *@
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet @rendermode="InteractiveServer" />
</head>
<body>
<Routes @rendermode="InteractiveServer" />
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
<script src="_framework/blazor.web.js"></script>
</body>
</html>

View File

@@ -0,0 +1,65 @@
@inherits LayoutComponentBase
@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager
@inject DialogService DialogService
@inject ContextMenuService ContextMenuService
@inject TooltipService TooltipService
@inject NotificationService NotificationService
<RadzenDialog />
<RadzenNotification />
<RadzenTooltip />
<RadzenContextMenu />
@* <RadzenComponents /> *@
<RadzenLayout style="grid-template-areas: 'rz-sidebar rz-header' 'rz-sidebar rz-body';">
<RadzenHeader>
<RadzenRow JustifyContent="JustifyContent.Start" AlignItems="AlignItems.Center" Gap="0">
<RadzenColumn Size="5">
<RadzenSidebarToggle Click="@SidebarToggleClick"></RadzenSidebarToggle>
</RadzenColumn>
<RadzenColumn Size="7">
<RadzenStack AlignItems="AlignItems.Center" Orientation="Orientation.Horizontal" JustifyContent="JustifyContent.End"></RadzenStack>
</RadzenColumn>
</RadzenRow>
</RadzenHeader>
<RadzenBody Expanded="@sidebarExpanded">
<RadzenRow class="rz-mx-auto rz-px-4 rz-pt-2 rz-pt-md-4 rz-pt-lg-6 rz-pt-xl-12 rz-pb-2 rz-pb-lg-12" Style="max-width: 1440px;">
<RadzenColumn Size="12">
@Body
</RadzenColumn>
</RadzenRow>
</RadzenBody>
<RadzenSidebar Expanded="@sidebarExpanded" style="z-index: 2">
<RadzenStack AlignItems="Radzen.AlignItems.Center" class="rz-py-4 rz-py-lg-6" Style="padding: var(--rz-panel-menu-item-padding); border-bottom: var(--rz-panel-menu-item-border);">
@* <RadzenImage Path="images/logo.png" style="width: 48px; height: 48px;"></RadzenImage>
*@
<RadzenText Text="appname" TextStyle="Radzen.Blazor.TextStyle.Subtitle1" class="rz-mb-0" style="color: var(--rz-sidebar-color);" />
</RadzenStack>
<NavMenu />
<RadzenStack AlignItems="Radzen.AlignItems.Center" Gap="0" class="rz-py-4 rz-py-lg-6" Style="padding: var(--rz-panel-menu-item-padding);">
<RadzenText Text="appname v1.0.0" TextStyle="Radzen.Blazor.TextStyle.Caption" style="color: var(--rz-text-disabled-color);" TagName="Radzen.Blazor.TagName.P" TextAlign="Radzen.TextAlign.Center" />
<RadzenText Text="Copyright Ⓒ 2024" TextStyle="Radzen.Blazor.TextStyle.Caption" class="rz-mb-0" style="color: var(--rz-text-disabled-color);" TagName="Radzen.Blazor.TagName.P" TextAlign="Radzen.TextAlign.Center" />
</RadzenStack>
</RadzenSidebar>
</RadzenLayout>
@code {
bool sidebarExpanded = true;
void SidebarToggleClick()
{
sidebarExpanded = !sidebarExpanded;
}
}

View File

@@ -0,0 +1,8 @@
<RadzenPanelMenu>
<RadzenPanelMenuItem Text="Home" Path="/" />
<RadzenPanelMenuItem Text="Posts" Path="/Posts" />
<RadzenPanelMenuItem Text="Posts Panel" Path="/BlogPost" />
</RadzenPanelMenu>

View File

@@ -0,0 +1,102 @@
@page "/BlogPost/Create"
<RadzenText Text="Create" TextStyle="TextStyle.H5" />
@if (blogPostViewModel == null)
{
<p>Loading...</p>
}
else
{
<RadzenStack>
<RadzenFieldset Text="New Post">
<RadzenStack Gap="2rem">
<EditForm Context="editFormNewPost" Model="@blogPostViewModel" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<RadzenRow AlignItems="AlignItems.Center" Class="rz-mb-2">
<RadzenColumn SizeSM="12" SizeMD="2" SizeLG="2">
<RadzenLabel Text="Title" for="title" />
</RadzenColumn>
<RadzenColumn SizeSM="12" SizeMD="10" SizeLG="10">
<InputText id="title" class="form-control" placeholder="Title"
@bind-Value="blogPostViewModel.Title" />
<ValidationMessage For="@(() => blogPostViewModel.Title)" />
</RadzenColumn>
</RadzenRow>
<RadzenRow AlignItems="AlignItems.Center" Class="rz-mb-2">
<RadzenColumn SizeSM="12" SizeMD="2" SizeLG="2">
<RadzenLabel Text="Content" for="content" />
</RadzenColumn>
<RadzenColumn SizeSM="12" SizeMD="10" SizeLG="10">
@*
<InputText id="content" class="form-control" placeholder="Content"
@bind-Value="blogPostViewModel.Content" />
<ValidationMessage For="@(() => blogPostViewModel.Content)" />
*@
<RadzenHtmlEditor @bind-Value=@blogPostViewModel.Content
Input=@OnInput Change=@OnChange
Paste=@OnPaste Execute=@OnExecute
UploadUrl="upload/image" />
<ValidationMessage For="@(() => blogPostViewModel.Content)" />
</RadzenColumn>
</RadzenRow>
<RadzenButton Text="Save" Icon="save"
ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Success" />
</EditForm>
</RadzenStack>
</RadzenFieldset>
</RadzenStack>
<RadzenButton Text="Back" Icon="arrow_back" Class="rz-mt-2"
Click="NavigatetoBlogPostIndex"
ButtonStyle="ButtonStyle.Primary" />
}
@code {
private BlogPostViewModel? blogPostViewModel;
protected override void OnInitialized()
{
blogPostViewModel = new();
}
void OnPaste(HtmlEditorPasteEventArgs args)
{
}
void OnChange(string html)
{
}
void OnInput(string html)
{
}
void OnExecute(HtmlEditorExecuteEventArgs args)
{
}
protected async Task HandleValidSubmit()
{
if (blogPostViewModel == null) return;
var blogPost = Mapper.Map<BlogPostViewModel, BlogPost>(blogPostViewModel);
bool result = await BlogPostService.AddBlogPostAsync(blogPost);
if (result)
NavigationManager.NavigateTo("/BlogPost/");
}
private void NavigatetoBlogPostIndex() => NavigationManager.NavigateTo("/BlogPost");
}

View File

@@ -0,0 +1,79 @@
@page "/BlogPost/Delete/{id:int}"
<h3>Delete</h3>
@if (blogPostViewModel == null)
{
<p>Loading...</p>
}
else
{
<RadzenStack>
<RadzenFieldset Text="Post Delete">
<RadzenStack>
<RadzenRow AlignItems="AlignItems.Center" Class="rz-mb-2">
<RadzenColumn SizeSM="12" SizeMD="2" SizeLG="2">
<RadzenLabel Text="Id" for="id" />
</RadzenColumn>
<RadzenColumn SizeSM="12" SizeMD="10" SizeLG="10">
<RadzenTextBox id="id" class="form-control" placeholder="Id" ReadOnly=true
Value="@blogPostViewModel.Id.ToString()" />
</RadzenColumn>
</RadzenRow>
<RadzenRow AlignItems="AlignItems.Center" Class="rz-mb-2">
<RadzenColumn SizeSM="12" SizeMD="2" SizeLG="2">
<RadzenLabel Text="Title" for="title" />
</RadzenColumn>
<RadzenColumn SizeSM="12" SizeMD="10" SizeLG="10">
<RadzenTextBox id="title" class="form-control" placeholder="Title" ReadOnly=true
Value="@blogPostViewModel.Title" />
</RadzenColumn>
</RadzenRow>
</RadzenStack>
<RadzenButton Text="REMOVE" Icon="delete_forever" ButtonStyle="ButtonStyle.Danger"
Click="RemoveButtonClick" />
</RadzenFieldset>
</RadzenStack>
<RadzenButton Text="Back" Icon="arrow_back" Class="rz-mt-2"
Click="NavigatetoBlogPostIndex"
ButtonStyle="ButtonStyle.Primary" />
}
@code {
[Parameter]
public int id { get; set; }
BlogPostViewModel? blogPostViewModel;
protected override async Task OnInitializedAsync()
{
if (blogPostViewModel == null)
{
var blogPost = await BlogPostService.GetbyId(id);
if (blogPost == null)
return;
blogPostViewModel = Mapper.Map<BlogPost, BlogPostViewModel>(blogPost);
}
}
private async void RemoveButtonClick()
{
bool result = await BlogPostService.DeletebyIdAsync(id);
if (result)
NavigationManager.NavigateTo("/BlogPost");
}
private void NavigatetoBlogPostIndex() => NavigationManager.NavigateTo("/BlogPost");
}

View File

@@ -0,0 +1,78 @@
@page "/BlogPost/Detail/{id:int}"
<h3>Detail</h3>
@if (blogPostViewModel == null)
{
<p>Loading...</p>
}
else
{
<RadzenStack>
<RadzenFieldset Text="Post Detail">
<RadzenStack>
<RadzenRow AlignItems="AlignItems.Center" Class="rz-mb-2">
<RadzenColumn SizeSM="12" SizeMD="2" SizeLG="2">
<RadzenLabel Text="Id" for="id" />
</RadzenColumn>
<RadzenColumn SizeSM="12" SizeMD="10" SizeLG="10">
<RadzenTextBox id="id" class="form-control" placeholder="Id" ReadOnly=true
Value="@blogPostViewModel.Id.ToString()" />
</RadzenColumn>
</RadzenRow>
<RadzenRow AlignItems="AlignItems.Center" Class="rz-mb-2">
<RadzenColumn SizeSM="12" SizeMD="2" SizeLG="2">
<RadzenLabel Text="Title" for="title" />
</RadzenColumn>
<RadzenColumn SizeSM="12" SizeMD="10" SizeLG="10">
<RadzenTextBox id="title" class="form-control" placeholder="Title" ReadOnly=true
Value="@blogPostViewModel.Title" />
</RadzenColumn>
</RadzenRow>
<RadzenRow AlignItems="AlignItems.Center" Class="rz-mb-2">
<RadzenColumn SizeSM="12" SizeMD="2" SizeLG="2">
<RadzenLabel Text="Content" for="content" />
</RadzenColumn>
<RadzenColumn SizeSM="12" SizeMD="10" SizeLG="10">
<RadzenTextBox id="content" class="form-control" placeholder="Content" ReadOnly=true
Value="@blogPostViewModel.Content" />
</RadzenColumn>
</RadzenRow>
</RadzenStack>
</RadzenFieldset>
</RadzenStack>
<RadzenButton Text="Back" Icon="arrow_back" Class="rz-mt-2"
Click="NavigatetoBlogPostIndex"
ButtonStyle="ButtonStyle.Primary" />
}
@code {
[Parameter]
public int id { get; set; }
BlogPostViewModel? blogPostViewModel;
protected override async Task OnInitializedAsync()
{
if (blogPostViewModel == null)
{
var blogPost = await BlogPostService.GetbyId(id);
if (blogPost == null)
return;
blogPostViewModel = Mapper.Map<BlogPost, BlogPostViewModel>(blogPost);
}
}
private void NavigatetoBlogPostIndex() => NavigationManager.NavigateTo("/BlogPost");
}

View File

@@ -0,0 +1,113 @@
@page "/BlogPost/Edit/{id:int}"
<h3>Edit</h3>
@if (blogPostViewModel == null)
{
<p>Loading...</p>
}
else
{
<RadzenStack>
<RadzenFieldset Text="Edit Post">
<RadzenStack Gap="2rem">
<EditForm Context="editFormEdit" Model="@blogPostViewModel" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<RadzenRow AlignItems="AlignItems.Center" Class="rz-mb-2">
<RadzenColumn SizeSM="12" SizeMD="2" SizeLG="2">
<RadzenLabel Text="Title" for="title" />
</RadzenColumn>
<RadzenColumn SizeSM="12" SizeMD="10" SizeLG="10">
<InputText id="title" class="form-control" placeholder="Title"
@bind-Value="blogPostViewModel.Title" />
<ValidationMessage For="@(() => blogPostViewModel.Title)" />
</RadzenColumn>
</RadzenRow>
<RadzenRow AlignItems="AlignItems.Center" Class="rz-mb-2">
<RadzenColumn SizeSM="12" SizeMD="2" SizeLG="2">
<RadzenLabel Text="Content" for="content" />
</RadzenColumn>
<RadzenColumn SizeSM="12" SizeMD="10" SizeLG="10">
@*
<InputText id="content" class="form-control" placeholder="Content"
@bind-Value="blogPostViewModel.Content" />
<ValidationMessage For="@(() => blogPostViewModel.Content)" />
*@
<RadzenHtmlEditor @bind-Value=@blogPostViewModel.Content
Input=@OnInput Change=@OnChange
Paste=@OnPaste Execute=@OnExecute
UploadUrl="upload/image" />
<ValidationMessage For="@(() => blogPostViewModel.Content)" />
</RadzenColumn>
</RadzenRow>
<RadzenButton Text="Save" Icon="save"
ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Success" />
</EditForm>
</RadzenStack>
</RadzenFieldset>
</RadzenStack>
<RadzenButton Text="Back" Icon="arrow_back" Class="rz-mt-2"
Click="NavigatetoBlogPostIndex"
ButtonStyle="ButtonStyle.Primary" />
}
@code {
[Parameter]
public int id { get; set; }
BlogPostViewModel? blogPostViewModel;
protected override async Task OnInitializedAsync()
{
if (blogPostViewModel == null)
{
var blogPost = await BlogPostService.GetbyId(id);
if (blogPost == null)
return;
blogPostViewModel = Mapper.Map<BlogPost, BlogPostViewModel>(blogPost);
}
}
void OnPaste(HtmlEditorPasteEventArgs args)
{
}
void OnChange(string html)
{
}
void OnInput(string html)
{
}
void OnExecute(HtmlEditorExecuteEventArgs args)
{
}
protected async Task HandleValidSubmit()
{
if (blogPostViewModel == null) return;
var blogPost = Mapper.Map<BlogPostViewModel, BlogPost>(blogPostViewModel);
bool result = await BlogPostService.UpdateBlogPostAsync(id, blogPost);
if (result)
NavigationManager.NavigateTo("/BlogPost/");
}
private void NavigatetoBlogPostIndex() => NavigationManager.NavigateTo("/BlogPost");
}

View File

@@ -0,0 +1,81 @@
@page "/BlogPost"
<PageTitle>Posts</PageTitle>
<RadzenRow>
<RadzenColumn SizeSM="12" SizeMD="12" SizeLG="4">
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center">
<RadzenText Text="Posts" TextStyle="TextStyle.H5" />
<RadzenButton Text="Create" Icon="add_circle_outline"
Click="NavigatetoCreate"
ButtonStyle="ButtonStyle.Success" class="rz-mb-2 rz-p-2" />
</RadzenStack>
</RadzenColumn>
</RadzenRow>
<RadzenDataGrid KeyProperty="Id" IsLoading="@isLoading" ShowPagingSummary=true
Count="@totalCount" Data="@blogPosts" LoadData="@LoadData"
FilterPopupRenderMode="PopupRenderMode.OnDemand"
FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive"
FilterMode="FilterMode.Advanced" AllowSorting="true" AllowFiltering="true"
AllowPaging="true" PageSize="@itemPageSize" PagerHorizontalAlign="HorizontalAlign.Center"
TItem="BlogPostViewModel" ColumnWidth="200px">
<Columns>
<RadzenDataGridColumn TItem="BlogPostViewModel" Property="Id" Filterable="false" Title="Id" Frozen="true" Width="30px" MinWidth="30px" TextAlign="TextAlign.Center" />
<RadzenDataGridColumn TItem="BlogPostViewModel" Property="Title" Title="Title" />
<RadzenDataGridColumn TItem="BlogPostViewModel" Property="Content" Title="Content" />
<RadzenDataGridColumn TItem="BlogPostViewModel" Context="blogPost" Filterable="false" Sortable="false" Width="150px" TextAlign="TextAlign.Center">
<Template Context="blogPost">
<RadzenRow JustifyContent="JustifyContent.Center">
<RadzenButton Icon="pageview" ButtonStyle="ButtonStyle.Info" Variant="Variant.Flat" Size="ButtonSize.Medium"
Click="@(args => NavigatetoDetail(blogPost.Id))" @onclick:stopPropagation="true">
</RadzenButton>
<RadzenButton Icon="edit" ButtonStyle="ButtonStyle.Warning" Variant="Variant.Flat" Size="ButtonSize.Medium"
Click="@(args => NavigatetoEdit(blogPost.Id))" @onclick:stopPropagation="true">
</RadzenButton>
<RadzenButton Icon="delete_forever" ButtonStyle="ButtonStyle.Danger" Variant="Variant.Flat" Size="ButtonSize.Medium"
Click="@(args => NavigatetoDelete(blogPost.Id))" @onclick:stopPropagation="true">
</RadzenButton>
</RadzenRow>
</Template>
</RadzenDataGridColumn>
</Columns>
</RadzenDataGrid>
@code {
const int itemPageSize = 10;
private bool isLoading;
private int totalCount;
private IEnumerable<BlogPostViewModel>? blogPosts;
private async Task LoadData(LoadDataArgs args)
{
isLoading = true;
var result = await BlogPostService.GetBlogPostsAsync(filter: args.Filter, top: args.Top, skip: args.Skip, orderby: args.OrderBy, count: true);
blogPosts = Mapper.Map<IEnumerable<BlogPost>, IEnumerable<BlogPostViewModel>>(result.Result);
totalCount = result.TotalCount;
isLoading = false;
}
private void NavigatetoCreate() => NavigationManager.NavigateTo("/BlogPost/Create");
private void NavigatetoDetail(int id) => NavigationManager.NavigateTo($"/BlogPost/Detail/{id}");
private void NavigatetoEdit(int id) => NavigationManager.NavigateTo($"/BlogPost/Edit/{id}");
private void NavigatetoDelete(int id) => NavigationManager.NavigateTo($"/BlogPost/Delete/{id}");
}

View File

@@ -0,0 +1,7 @@
@using BlazorAppRadzenHtmlEditor.Data;
@using BlazorAppRadzenHtmlEditor.Models;
@using BlazorAppRadzenHtmlEditor.Services;
@using BlazorAppRadzenHtmlEditor.ViewModels;
@inject NavigationManager NavigationManager
@inject BlogPostService BlogPostService

View File

@@ -0,0 +1,36 @@
@page "/Error"
@using System.Diagnostics
<PageTitle>Error</PageTitle>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
protected override void OnInitialized() =>
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
}

View File

@@ -0,0 +1,7 @@
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.

View File

@@ -0,0 +1,69 @@
@page "/PostSingle/{id:int}"
@using BlazorAppRadzenHtmlEditor.Data;
@using BlazorAppRadzenHtmlEditor.Models;
@using BlazorAppRadzenHtmlEditor.Services;
@using BlazorAppRadzenHtmlEditor.ViewModels;
@inject NavigationManager NavigationManager
@inject BlogPostService BlogPostService
@if (blogPostViewModel == null)
{
<p>Loading...</p>
}
else
{
<RadzenStack>
<RadzenCard Class="rz-mb-6 rz-mx-auto" Style="width: 100%; padding: 0;">
<RadzenStack Orientation="Orientation.Horizontal" JustifyContent="JustifyContent.Start" Gap="1rem" Class="rz-p-4">
<RadzenStack Gap="0">
<RadzenText TextStyle="TextStyle.H5">
<b>
@blogPostViewModel.Title
</b>
</RadzenText>
</RadzenStack>
</RadzenStack>
<RadzenCard class="rz-shadow-3 rz-border-radius-0 rz-p-8" style="margin-left:-15px;margin-right:-15px;">
<RadzenRow RowGap="0">
<RadzenColumn Size="12">
<RadzenText TextStyle="TextStyle.Body1">
@((MarkupString)blogPostViewModel.Content)
</RadzenText>
</RadzenColumn>
</RadzenRow>
</RadzenCard>
<RadzenStack Orientation="Orientation.Horizontal" JustifyContent="JustifyContent.End" Gap="0" Style="margin:15px">
</RadzenStack>
</RadzenCard>
</RadzenStack>
<RadzenButton Text="Back" Icon="arrow_back" Class="rz-mt-2"
Click="NavigatetoPosts"
ButtonStyle="ButtonStyle.Primary" />
}
@code {
[Parameter]
public int id { get; set; }
BlogPostViewModel? blogPostViewModel;
protected override async Task OnInitializedAsync()
{
if (blogPostViewModel == null)
{
var blogPost = await BlogPostService.GetbyId(id);
if (blogPost == null)
return;
blogPostViewModel = Mapper.Map<Models.BlogPost, BlogPostViewModel>(blogPost);
}
}
private void NavigatetoPosts() => NavigationManager.NavigateTo("/Posts");
}

View File

@@ -0,0 +1,76 @@
@page "/Posts"
@using BlazorAppRadzenHtmlEditor.Data;
@using BlazorAppRadzenHtmlEditor.Models;
@using BlazorAppRadzenHtmlEditor.Services;
@using BlazorAppRadzenHtmlEditor.ViewModels;
@inject NavigationManager NavigationManager
@inject BlogPostService BlogPostService
<PageTitle>Posts</PageTitle>
<RadzenDataList IsLoading=@isLoading
LoadData="@LoadData" Count="@totalCount" Data="@blogPosts"
TItem="BlogPostViewModel" PageSize="@itemPageSize"
WrapItems="true" AllowPaging="true" PagerHorizontalAlign="HorizontalAlign.Center" ShowPagingSummary="true">
<Template Context="blogpost">
<RadzenCard Class="rz-mb-6 rz-mx-auto" Style="width: 100%; padding: 0;">
<RadzenStack Orientation="Orientation.Horizontal" JustifyContent="JustifyContent.Start" Gap="1rem" Class="rz-p-4">
<RadzenStack Gap="0">
<RadzenText TextStyle="TextStyle.H5">
<b>
@blogpost.Title
</b>
</RadzenText>
</RadzenStack>
</RadzenStack>
<RadzenCard class="rz-shadow-3 rz-border-radius-0 rz-p-8" style="margin-left:-15px;margin-right:-15px;">
<RadzenRow RowGap="0">
<RadzenColumn Size="12">
<RadzenText TextStyle="TextStyle.Body1">
@((MarkupString)blogpost.ContentShort)
<RadzenButton ButtonStyle="ButtonStyle.Secondary" Size="ButtonSize.ExtraSmall"
Text="Show More..."
Click="() => NavigatetoPostSingle(blogpost.Id)" />
</RadzenText>
</RadzenColumn>
</RadzenRow>
</RadzenCard>
<RadzenStack Orientation="Orientation.Horizontal" JustifyContent="JustifyContent.End" Gap="0">
<RadzenButton ButtonStyle="ButtonStyle.Primary" Text="Show Post" Style="margin:15px"
Click="() => NavigatetoPostSingle(blogpost.Id)" />
</RadzenStack>
</RadzenCard>
</Template>
</RadzenDataList>
@code {
const int itemPageSize = 10;
private bool isLoading;
private int totalCount;
private IEnumerable<BlogPostViewModel>? blogPosts;
private async Task LoadData(LoadDataArgs args)
{
isLoading = true;
var result = await BlogPostService.GetBlogPostsAsync(filter: args.Filter, top: args.Top, skip: args.Skip, orderby: args.OrderBy, count: true);
blogPosts = Mapper.Map<IEnumerable<Models.BlogPost>, IEnumerable<BlogPostViewModel>>(result.Result);
totalCount = result.TotalCount;
isLoading = false;
}
private void NavigatetoPostSingle(int id) => NavigationManager.NavigateTo($"/PostSingle/{id}");
}

View File

@@ -0,0 +1,6 @@
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
@* <FocusOnNavigate RouteData="routeData" Selector="h1" /> *@
</Found>
</Router>

View File

@@ -0,0 +1,16 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorAppRadzenHtmlEditor
@using BlazorAppRadzenHtmlEditor.Components
@using MapsterMapper
@using Radzen
@using Radzen.Blazor
@inject IMapper Mapper

View File

@@ -0,0 +1,122 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Hosting;
using System;
using System.IO;
namespace BlazorAppRadzenHtmlEditor.Controllers;
[DisableRequestSizeLimit]
public partial class UploadController : Controller
{
private readonly IWebHostEnvironment environment;
public UploadController(IWebHostEnvironment environment)
{
this.environment = environment;
}
//[HttpPost("upload/single")]
//public IActionResult Single(IFormFile file)
//{
// try
// {
// // Put your code here
// return Ok(new { Completed = true });
// }
// catch (Exception ex)
// {
// return StatusCode(500, ex.Message);
// }
//}
[HttpPost("upload/image")]
public IActionResult Image(IFormFile file)
{
try
{
// Used for demo purposes only
//DeleteOldFiles();
var fileName = $"upload-{DateTime.Today.ToString("yyyy-MM-dd")}-{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
var filePath = Path.Combine(environment.WebRootPath, fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
// Save the file
file.CopyTo(stream);
// Return the URL of the file
var url = Url.Content($"~/{fileName}");
return Ok(new { Url = url });
}
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
private void DeleteOldFiles()
{
foreach (var file in Directory.GetFiles(environment.WebRootPath))
{
var fileName = Path.GetFileName(file);
if (fileName.StartsWith("upload-") && !fileName.StartsWith($"upload-{DateTime.Today.ToString("yyyy-MM-dd")}"))
{
try
{
System.IO.File.Delete(file);
}
catch
{
}
}
}
}
//[HttpPost("upload/multiple")]
//public IActionResult Multiple(IFormFile[] files)
//{
// try
// {
// // Put your code here
// return StatusCode(200);
// }
// catch (Exception ex)
// {
// return StatusCode(500, ex.Message);
// }
//}
//[HttpPost("upload/{id}")]
//public IActionResult Post(IFormFile[] files, int id)
//{
// try
// {
// // Put your code here
// return StatusCode(200);
// }
// catch (Exception ex)
// {
// return StatusCode(500, ex.Message);
// }
//}
//[HttpPost("upload/specific")]
//public IActionResult Specific(IFormFile myName)
//{
// try
// {
// // Put your code here
// return Ok(new { Completed = true });
// }
// catch (Exception ex)
// {
// return StatusCode(500, ex.Message);
// }
//}
}

View File

@@ -0,0 +1,19 @@
using BlazorAppRadzenHtmlEditor.Models;
using Microsoft.EntityFrameworkCore;
namespace BlazorAppRadzenHtmlEditor.Data;
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<BlogPost> BlogPosts => Set<BlogPost>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}

View File

@@ -0,0 +1,104 @@
using BlazorAppRadzenHtmlEditor.Models;
namespace BlazorAppRadzenHtmlEditor.Data;
public class SeedData
{
private readonly ApplicationDbContext _context;
public SeedData(ApplicationDbContext context)
{
_context = context;
}
public async Task CreateInitialData()
{
var posts = GetAllBlogPosts();
await _context.BlogPosts.AddRangeAsync(posts);
await _context.SaveChangesAsync();
}
private static IEnumerable<BlogPost> GetAllBlogPosts()
{
List<BlogPost> posts = new();
for (int i = 0; i < 50; i++)
{
BlogPost post = new() { Id = i + 1, Title = titles[i], Content = contents[i % 6] };
posts.Add(post);
}
return posts;
}
private static readonly string[] titles = {
"Introduction to Object-Oriented Programming",
"Mastering Data Structures and Algorithms",
"Building Web Applications with ASP.NET",
"Creating Mobile Apps with Xamarin",
"Exploring Artificial Intelligence and Machine Learning",
"Understanding Functional Programming Concepts",
"Developing Games with Unity",
"Securing Web Applications from Cyber Attacks",
"Optimizing Code Performance for Better Efficiency",
"Implementing Design Patterns in Software Development",
"Testing and Debugging Strategies for Reliable Software",
"Working with Databases and SQL",
"Building Responsive User Interfaces with HTML and CSS",
"Exploring Cloud Computing and Serverless Architecture",
"Developing Cross-Platform Applications with React Native",
"Introduction to Internet of Things (IoT)",
"Creating Scalable Microservices with Docker and Kubernetes",
"Understanding Network Protocols and TCP/IP",
"Building RESTful APIs with Node.js and Express",
"Exploring Big Data Analytics and Apache Hadoop",
"Mastering Version Control with Git and GitHub",
"Developing Desktop Applications with WPF",
"Securing Mobile Applications from Malicious Attacks",
"Optimizing Database Performance with Indexing",
"Implementing Continuous Integration and Deployment",
"Testing Mobile Apps on Different Platforms",
"Working with NoSQL Databases like MongoDB",
"Building Progressive Web Apps with React",
"Exploring Quantum Computing and Quantum Algorithms",
"Introduction to Cybersecurity and Ethical Hacking",
"Creating Chatbots with Natural Language Processing",
"Understanding Software Development Life Cycle",
"Developing Augmented Reality (AR) Applications",
"Securing Web APIs with OAuth and JWT",
"Optimizing Front-End Performance for Better User Experience",
"Implementing Machine Learning Models with TensorFlow",
"Testing Web Applications for Cross-Browser Compatibility",
"Working with Blockchain Technology and Smart Contracts",
"Building Real-Time Applications with SignalR",
"Exploring Cryptography and Encryption Techniques",
"Introduction to Agile Software Development",
"Creating Voice User Interfaces with Amazon Alexa",
"Understanding Web Accessibility and Inclusive Design",
"Developing Natural Language Processing Applications",
"Securing Cloud Infrastructure and Services",
"Optimizing Backend Performance for Scalability",
"Implementing Continuous Monitoring and Alerting",
"Testing APIs with Postman and Swagger",
"Working with Data Visualization Libraries like D3.js",
"Building E-commerce Applications with Shopify",
"Exploring Robotic Process Automation (RPA)",
"Introduction to DevOps and CI/CD Pipelines"
};
private static readonly string[] contents = new string[]
{
"<div><img src=\"/upload-2024-05-12-3d2c9bc2-4ab6-4ede-acc9-4b6763081996.jpeg\" width=\"100\" style=\"border-radius: 25px;\"></div> \r\n<span style=\"font-weight: bold;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span> \r\nDuis ultricies posuere leo nec ornare. Etiam eu tempus tellus. Aliquam bibendum justo diam, et tincidunt ipsum tempus eu. Aenean vel sem ante. Vivamus maximus ornare imperdiet. Curabitur varius, arcu vitae pretium lobortis, dolor nisi facilisis orci, nec elementum nibh mi vel nisl. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed a venenatis nisi. Nullam sit amet ipsum maximus, tempus mi eget, sollicitudin diam.\r\n<div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-3d2c9bc2-4ab6-4ede-acc9-4b6763081996.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Sed faucibus, ex et pulvinar vulputate, erat diam sollicitudin odio, ut euismod enim odio nec nunc.</span> \r\nCurabitur nec dolor sem. Vivamus euismod mi risus, in bibendum erat cursus sed. Integer et nisi metus. In hac habitasse platea dictumst. Ut faucibus pretium sapien non ullamcorper. Etiam pretium mollis neque. Phasellus congue suscipit elit vitae porttitor. Mauris eget tellus non eros ultricies vehicula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Fusce sed tortor in enim luctus sagittis et consequat odio. Phasellus eu sapien quis turpis consequat tempus at quis mi. Aenean non justo lorem. Morbi tempus nec arcu a ullamcorper.\r\n<br></div><div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-b43d01df-f3d0-42d0-9fe2-1844c78ae414.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Vestibulum non eros risus.</span> \r\nSuspendisse orci diam, rhoncus vitae sollicitudin eu, porta at mauris. Mauris varius euismod dictum. Donec turpis diam, ultrices sed libero sit amet, tempor cursus urna. Pellentesque ut mattis nisi. Aenean luctus at sem id facilisis. Suspendisse tempus pellentesque sapien nec aliquam. Sed id mi bibendum, posuere elit in, viverra purus. Etiam id ultricies elit. Praesent a molestie felis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed vulputate ex id neque congue, id condimentum dui dignissim. Nulla pellentesque libero id leo fringilla bibendum. Suspendisse id aliquam quam.\r\n<br></div>",
"<div><img src=\"/upload-2024-05-12-6777339d-9aa3-4f6a-af0d-015e671fd67b.jpeg\" width=\"100\" style=\"border-radius: 25px;\"></div> \r\n<span style=\"font-weight: bold;\">Duis facilisis nisi at sapien mattis, eu tincidunt massa aliquam.</span> \r\nAenean eu erat pretium, tempus massa at, auctor lectus. Cras faucibus turpis ex, ut pretium diam condimentum vitae. Fusce blandit nisl at suscipit maximus. Donec tristique tellus dolor, et sollicitudin tellus porttitor ac. Suspendisse iaculis molestie congue. Mauris non mattis urna. Integer cursus orci arcu, non pellentesque felis porttitor sit amet. Nulla facilisi.\r\n<div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-6777339d-9aa3-4f6a-af0d-015e671fd67b.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Duis consequat mi nec diam faucibus tincidunt.</span> \r\nNunc ut feugiat dui. Proin malesuada nisl sit amet nulla scelerisque, in mollis mi rhoncus. Ut vel tristique lorem, ut feugiat enim. Pellentesque vel sapien feugiat, porttitor elit consequat, tristique eros. Aenean feugiat ex eget libero aliquet, a egestas dui ornare. Vestibulum dolor ligula, molestie eu lacus quis, pulvinar egestas turpis. Suspendisse a lobortis leo, at finibus magna. Nam nec tristique nisi, blandit dapibus neque. Aenean in velit a leo condimentum eleifend. Duis posuere urna lacus, id blandit nisl laoreet sed. Curabitur ligula turpis, pellentesque in purus eu, tincidunt posuere libero. Nulla finibus, erat ac ultrices posuere, tellus enim blandit nisl, at consectetur tellus nibh eget est. Donec gravida tortor quis fringilla pharetra. Pellentesque aliquam pellentesque risus nec rhoncus. Nunc mattis elit tellus, vel congue lectus mollis sed.\r\n<br></div><div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-5cb5f731-72ee-4b10-831c-f976eed6fef0.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Aenean tristique, nulla ac vehicula dignissim, turpis est accumsan urna, sed finibus lacus dui eget nulla.</span> \r\nSed finibus elit magna, vitae iaculis dui ornare a. Praesent tempor ligula sapien, id hendrerit diam cursus eget. Nam eu fermentum justo. Etiam lobortis lacinia erat, in dapibus est lacinia eu. Mauris sed quam sapien. Aenean maximus, nibh tincidunt tincidunt molestie, nisl arcu convallis mauris, at accumsan magna ex eu libero. Praesent vel vehicula massa. Suspendisse sodales augue id dui rutrum, at commodo lectus elementum. Nulla sed ante viverra, rhoncus erat ac, varius sem.\r\n<br></div>",
"<div><img src=\"/upload-2024-05-12-221b63e2-d7c8-4d7b-aa24-dd0c54461015.jpeg\" width=\"100\" style=\"border-radius: 25px;\"></div> \r\n<span style=\"font-weight: bold;\">Mauris scelerisque quam at posuere imperdiet.</span> \r\nMorbi porta vitae lacus ac hendrerit. Suspendisse posuere nulla ut finibus aliquet. Integer pretium pulvinar erat in efficitur. Proin justo ex, tincidunt eu congue non, mollis et nisl. Nunc nec rhoncus leo, eget eleifend diam. Phasellus feugiat nisl nec enim volutpat pulvinar. Maecenas ac urna in eros imperdiet dictum.\r\n<div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-221b63e2-d7c8-4d7b-aa24-dd0c54461015.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Duis varius in mauris id posuere.</span> \r\nEtiam lobortis purus augue, vitae ultricies purus consequat maximus. Mauris tincidunt molestie ligula, sit amet pulvinar nibh consectetur sit amet. Aliquam pellentesque malesuada orci id maximus. Curabitur lacinia, orci sed consectetur dignissim, ex dolor pulvinar orci, a consequat massa lectus vitae ipsum. Duis et augue vel odio placerat feugiat vitae id mauris. In mi turpis, consequat quis gravida sit amet, suscipit sit amet felis. Nam feugiat lobortis lectus ac bibendum. Aliquam id consequat sem, vel vehicula turpis. Pellentesque id dui malesuada, tincidunt felis id, faucibus mauris. Vestibulum ut ligula in enim vulputate euismod. Cras vel diam egestas, ultricies nisl ut, ornare magna.\r\n<br></div><div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-e627b879-a260-470d-b0fb-527ddc44af7d.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Vivamus erat diam, rutrum at nibh id, rhoncus tincidunt nunc.</span> \r\nMorbi sed libero condimentum, pharetra mauris id, facilisis felis. Pellentesque varius hendrerit rhoncus. In hac habitasse platea dictumst. Phasellus ex nisl, tincidunt quis lacus ac, suscipit sollicitudin mi. Nunc ultrices tempor mi. Aliquam ante urna, vestibulum in pellentesque nec, tempus sed sem. Nunc elementum arcu pretium nisl euismod condimentum sed eget urna. Praesent vitae tellus quis ante sollicitudin bibendum. Praesent a leo eros. Praesent pellentesque, odio sed consectetur maximus, diam nibh scelerisque nulla, et semper nisl libero consectetur ex. Vestibulum eget placerat nunc. Curabitur posuere justo quis massa venenatis, sit amet aliquam nisi scelerisque. Proin dignissim dui vulputate, dignissim nisl ac, mattis nibh.\r\n<br></div>",
"<div><img src=\"/upload-2024-05-12-ef3cb0a3-020e-4a00-8a79-f1706550f445.jpeg\" width=\"100\" style=\"border-radius: 25px;\"></div> \r\n<span style=\"font-weight: bold;\">Donec sed sapien in sapien imperdiet vestibulum in eget risus.</span> \r\nNulla metus purus, vehicula in neque id, ullamcorper finibus eros. Maecenas felis neque, molestie vitae urna at, lacinia sodales metus. Etiam cursus mi eget justo tincidunt, et cursus tortor efficitur. Aliquam fermentum lacinia ipsum, et maximus ante mattis vel. Cras rhoncus id dui sit amet finibus. Quisque ac tempus augue. Vestibulum at rhoncus velit. Nullam eget nulla bibendum, suscipit augue sed, accumsan nulla. Nam suscipit nunc id ipsum convallis, et egestas ipsum dictum. Integer at ante augue. Aenean sit amet cursus risus. Duis vel risus non mauris accumsan dictum.\r\n<div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-ef3cb0a3-020e-4a00-8a79-f1706550f445.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Cras semper eros a scelerisque vehicula.</span> \r\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Proin malesuada eros non sem fermentum, in auctor sem venenatis. Sed sollicitudin rhoncus orci, vitae fermentum leo condimentum eget. Mauris id lacinia neque. Etiam arcu purus, laoreet ac iaculis eu, sagittis a libero. Phasellus in nisl sit amet urna mattis bibendum. In condimentum pharetra est.\r\n<br></div><div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-35621ccb-ff3c-46c8-b510-405d9abb87ca.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Morbi posuere felis pretium, fringilla lacus a, ultricies urna.</span> \r\nCurabitur non diam vel dolor commodo dapibus ac vel urna. Duis dui ligula, sodales id lorem in, maximus molestie turpis. Donec vitae urna rutrum, cursus diam sed, vestibulum dui. Fusce id aliquet justo. Mauris lobortis, lacus ut eleifend lobortis, orci odio sollicitudin leo, ac sodales dui metus eget sapien. Etiam rutrum lorem id blandit vulputate. Vivamus suscipit augue sed augue efficitur commodo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec vitae orci non sem molestie fermentum. Nunc lobortis diam ligula. Phasellus imperdiet est vel sodales lacinia. Sed at dui eu magna lacinia fermentum.\r\n<br></div>",
"<div><img src=\"/upload-2024-05-12-6bb077bb-4a31-4f8c-b275-499ca2ec6e4b.jpeg\" width=\"100\" style=\"border-radius: 25px;\"></div> \r\n<span style=\"font-weight: bold;\">Nam ac massa vel ipsum consequat pretium et sit amet erat.</span> \r\nAenean risus lectus, varius a dolor quis, auctor accumsan felis. Donec dignissim quam in lacus auctor, a luctus mauris suscipit. Donec risus tellus, maximus nec odio vel, faucibus aliquet eros. Integer ut elit quis lacus vehicula consequat. Etiam porta ornare varius. Praesent non hendrerit neque. Vestibulum vehicula elementum libero quis interdum. Aliquam at nulla nisi. Ut vulputate lacus eget tortor pretium, vel luctus orci commodo.\r\n<div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-6bb077bb-4a31-4f8c-b275-499ca2ec6e4b.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Nunc ut tortor maximus, dapibus sem vel, tincidunt ligula.</span> \r\nSuspendisse sed tortor magna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec at urna metus. Integer at maximus sapien, vitae imperdiet tellus. Nunc mollis ultrices rhoncus. Nullam ullamcorper ante sit amet sollicitudin aliquam. Proin sit amet urna non nulla hendrerit tempus. Proin sodales enim at est interdum aliquam.\r\n<br></div><div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-bd2672cb-495e-41b9-aa3d-6e52189f5876.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Sed egestas blandit nisl, non gravida est.</span> \r\nPraesent aliquam auctor nunc, a blandit lorem varius at. Vestibulum dictum turpis sit amet lorem iaculis, posuere tempor turpis scelerisque. Pellentesque nec lacus mauris. Aenean tincidunt facilisis mauris sit amet sollicitudin. Vivamus et ligula eu metus tempus facilisis nec sed ligula. Nam ultricies urna non elit suscipit placerat. Etiam imperdiet egestas sem nec lacinia. Fusce malesuada neque in tortor sodales, vitae sagittis purus eleifend.\r\n<br></div>",
"<div><img src=\"/upload-2024-05-12-1d87cac3-25db-4ef2-9646-0f57d4ea3bcd.jpeg\" width=\"100\" style=\"border-radius: 25px;\"></div> \r\n<span style=\"font-weight: bold;\">Cras nec nulla sed nulla elementum imperdiet.</span> \r\nNulla dapibus eget augue id ullamcorper. Donec euismod fringilla ante, a scelerisque arcu. Nulla facilisi. Nullam dolor turpis, ornare nec molestie non, volutpat sed est. Sed nulla sapien, dignissim sit amet ex eu, tempor pharetra enim. Vestibulum ac augue vel mi egestas ullamcorper. Ut at lectus congue, viverra eros eu, placerat ex. Pellentesque iaculis venenatis sapien, vel congue tortor. In hac habitasse platea dictumst.\r\n<div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-1d87cac3-25db-4ef2-9646-0f57d4ea3bcd.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Ut porta venenatis nulla, eu laoreet nisl interdum dignissim.</span> \r\nPraesent in porttitor nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam et pellentesque turpis, non venenatis orci. Sed a bibendum odio. Pellentesque eget nisi nec nisl varius porta eget quis metus. Sed iaculis justo pharetra est interdum, sit amet dignissim diam auctor. Maecenas et augue velit. Maecenas nec varius mi. Vivamus viverra metus sed nulla tristique, vitae lacinia nunc commodo. Integer id lorem feugiat, auctor nibh sed, viverra mauris. Fusce ac luctus nisi. Aenean sit amet dolor laoreet, laoreet tortor eget, scelerisque nisi. Sed auctor rhoncus rutrum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris at mollis ligula.\r\n<br></div><div><br></div>\r\n<div style=\"text-align: center;\">\r\n<img src=\"/upload-2024-05-12-a8425c11-a159-40a1-92c4-d353d9ae068d.jpeg\" width=\"250\" style=\"border-radius: 25px;\">\r\n<br></div><div><br></div><div>\r\n<span style=\"font-weight: bold;\">Interdum et malesuada fames ac ante ipsum primis in faucibus.</span> \r\nUt ut tincidunt elit. Mauris ultrices nisi in eros varius, semper feugiat sapien euismod. Suspendisse laoreet molestie urna et tincidunt. Etiam commodo lacus a dignissim ornare. Aenean eget consequat purus. In hac habitasse platea dictumst. Integer dapibus nibh vel est gravida, congue tempor neque venenatis.\r\n<br></div>",
};
}

View File

@@ -0,0 +1,8 @@
namespace BlazorAppRadzenHtmlEditor.Models;
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,99 @@
using BlazorAppRadzenHtmlEditor.Components;
using BlazorAppRadzenHtmlEditor.Data;
using BlazorAppRadzenHtmlEditor.Services;
using BlazorAppRadzenHtmlEditor.ViewModels;
using Mapster;
using MapsterMapper;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.EntityFrameworkCore;
using Radzen;
using System.Reflection;
internal class Program
{
private static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase("ConnectionInMemory")
);
builder.Services.AddScoped<SeedData>();
builder.Services.AddScoped<BlogPostService>();
// Radzen Services
builder.Services.AddScoped<DialogService>();
builder.Services.AddScoped<NotificationService>();
builder.Services.AddScoped<TooltipService>();
builder.Services.AddScoped<ContextMenuService>();
// Add mapster mapper
builder.Services.AddMapster();
builder.Services.CreateDatabase().GetAwaiter().GetResult();
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.Configure<Microsoft.AspNetCore.Http.Features.FormOptions>(options =>
{
options.MultipartBodyLengthLimit = long.MaxValue;
});
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapControllers();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
}
}
public static class ServiceCollectionExtensions
{
public static async Task CreateDatabase(this IServiceCollection services)
{
using (IServiceScope tmp = services.BuildServiceProvider().CreateScope())
{
await using var _context = tmp.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var seedData = tmp.ServiceProvider.GetRequiredService<SeedData>();
await seedData.CreateInitialData();
}
}
}
public static class MapsterConfiguration
{
public static void AddMapster(this IServiceCollection services)
{
var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
Assembly applicationAssembly = typeof(BaseViewModel<,>).Assembly;
typeAdapterConfig.Scan(applicationAssembly);
var mapperConfig = new Mapper(typeAdapterConfig);
services.AddSingleton<IMapper>(mapperConfig);
}
}

View File

@@ -0,0 +1,14 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,90 @@
using BlazorAppRadzenHtmlEditor.Data;
using BlazorAppRadzenHtmlEditor.Models;
using Microsoft.EntityFrameworkCore;
using Radzen;
using System.Linq.Dynamic.Core;
namespace BlazorAppRadzenHtmlEditor.Services;
public class BlogPostService
{
private readonly ApplicationDbContext _context;
public BlogPostService(ApplicationDbContext context)
{
_context = context;
}
public Task<BlogPost?> GetbyId(int id)
{
return _context.BlogPosts.FirstOrDefaultAsync(x => x.Id == id);
}
public async Task<(IEnumerable<BlogPost> Result, int TotalCount)> GetBlogPostsAsync(string? filter = default, int? top = default, int? skip = default, string? orderby = default, string? expand = default, string? select = default, bool? count = default)
{
var query = _context.BlogPosts.AsQueryable();
if (!string.IsNullOrEmpty(filter))
query = query.Where(filter);
if (!string.IsNullOrEmpty(orderby))
query = query.OrderBy(orderby);
int totalCount = 0;
if (count == true)
totalCount = query.Count();
IEnumerable<BlogPost>? result;
if (skip == null || top == null)
result = await query.ToListAsync();
else
result = await query.Skip(skip.Value).Take(top.Value).ToListAsync();
return (result, totalCount);
}
public async Task<bool> AddBlogPostAsync(BlogPost blogPost)
{
try
{
await _context.BlogPosts.AddAsync(blogPost);
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
return false;
}
return true;
}
public async Task<bool> UpdateBlogPostAsync(int id, BlogPost blogPost)
{
try
{
var oldBlogPost = _context.BlogPosts.FirstOrDefault(x => x.Id == id);
if (oldBlogPost == null) return false;
oldBlogPost.Title = blogPost.Title;
oldBlogPost.Content = blogPost.Content;
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
return false;
}
return true;
}
public async Task<bool> DeletebyIdAsync(int id)
{
var blogPost = await _context.BlogPosts.FirstOrDefaultAsync(x => x.Id == id);
if (blogPost == null)
return false;
_context.BlogPosts.Remove(blogPost);
await _context.SaveChangesAsync();
return true;
}
}

View File

@@ -0,0 +1,46 @@
using Mapster;
namespace BlazorAppRadzenHtmlEditor.ViewModels;
/// <summary>
/// Model <-> ViewModel Mapping
/// </summary>
/// <typeparam name="TViewModel">ViewModel</typeparam>
/// <typeparam name="TModel">Model</typeparam>
public abstract class BaseViewModel<TViewModel, TModel> : IRegister
where TViewModel : class, new()
where TModel : class, new()
{
public TModel ToModel()
{
return this.Adapt<TModel>();
}
public TModel ToModel(TModel model)
{
return (this as TViewModel).Adapt(model);
}
public static TViewModel FromModel(TModel model)
{
return model.Adapt<TViewModel>();
}
private TypeAdapterConfig Config { get; set; }
public virtual void AddCustomMappings() { }
protected TypeAdapterSetter<TViewModel, TModel> SetCustomMappings()
=> Config.ForType<TViewModel, TModel>();
protected TypeAdapterSetter<TModel, TViewModel> SetCustomMappingsInverse()
=> Config.ForType<TModel, TViewModel>();
public void Register(TypeAdapterConfig config)
{
Config = config;
AddCustomMappings();
}
}

View File

@@ -0,0 +1,19 @@
using BlazorAppRadzenHtmlEditor.Models;
using System.ComponentModel.DataAnnotations;
namespace BlazorAppRadzenHtmlEditor.ViewModels;
public class BlogPostViewModel : BaseViewModel<BlogPostViewModel, BlogPost>
{
public int Id { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "Title can not be empty")]
public string Title { get; set; } = string.Empty;
[Required(AllowEmptyStrings = false, ErrorMessage = "Content can not be empty")]
public string Content { get; set; } = string.Empty;
public string TitleShort { get => this.Title.Length > 50 ? this.Title.Substring(0, 50) : this.Title; }
public string ContentShort { get => this.Content.Length > 500 ? this.Content.Substring(0, 500) : this.Content; }
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB