当前位置:网站首页 / .NET CORE / 正文

ay的C#8.0和net5高级编程笔记17-使用内容管理系统构建网站

时间:2021年05月09日 | 作者 : aaronyang | 分类 : .NET CORE | 浏览: 935次 | 评论 0

CMS这块,对于跨平台的C#和.NET Web开发人员来说,目前适合的是Piranha CMS

CMS将内容(数据值)与模板(布局,格式和样式)分离。大多数CMS能够生成像HTML这样的Web响应,使人们使用浏览器查看网站。

一些CMS还能够生成开放数据格式,比如Web服务要处理的JSON和XML,以及在浏览器中使用客户端技术(如Angular、React或Vue)呈现的JSON和XML。这类CMS通常被称为无头CMS。

开发人员使用内容类型类和内容模板定义CMS中存储的数据结构,这些内容类型类用于不同的目的(如产品页面),内容模板将内容数据呈现为HTML、JSON或其他格式。

非技术内容所有者可以登录CMS,使用简单的用户界面创建、编辑、删除和发布内容,这些内容符合内容类型类定义的结构,而不需要开发人员参与或使用VSCode等工具。


CMS基本特性

非技术人员登录和管理内容

内容交付系统将内容从简单数据转换为一种或多种格式(如Html和JSON)

企业级CMS特性

搜索引擎优化(SEO) URL、页面标题和相关元数据。站点地图等。

身份验证和授权,包括管理用户、组以及内容访问权限。

图像,视频,文档和其他文件的媒体资产管理。

共享和重用内容片段,通常成为块。

收集访客可输入的表单设计器

营销工具,如跟踪访问者行为和内容的A/B测试。

基于地理位置或跟踪访问者行为的机器学习处理规则的内容个性化。

保存网站到访问者隐藏的内容,直到发布。

保留内容的多个版本,并允许重新发布旧版本。

将内容翻译成多种认类语言,如英语,日语,汉语等


了解CMS平台

开发平台或者语言CMS
PHPWordPress,Drupal,Joomla!,Magento
PythonDjangoCMS
JavaAdobe Experience Manager,Hippo CMS
.NET FrameworkEpiserver CMS,Sitecore,Umbraco,KenticoCMS
.NET CorePiranha CMSOrchard Core CMS

关于OrchardCoreCMS更多信息 https://orchardcore.readthedocs.io/en/dev



了解Piranha CMS(我简称PCMS)

PCMS是学习开发CMS极佳选择,因为开源,简单,灵活。适用于.NET Stand2.0    可以用于向现有的应用程序添加CMS功能,从0开始构建新网站,甚至可以用作移动应用程序后端

三条设计原则:

    开放的、可扩展的平台

    简单、只管的内容管理

    对于开发人员来说快速、高效、有趣。

专注于中小型网站提供一个平台。

Piranha CMS不是WordPress,永远不会有成千上万个预定义的主题和插件需要安装。

官方文件: http://piranhacms.org


PCMS是采用一些开源库构建的,包括以下:

FontAwesome 图标集和工具包,https://fontawesome.com

AutoMapper:基于约定的对象-对象映射器, http://automapper.org

Markdig: 兼容 CommonMark且可扩展的MarkDown处理器 https://github.com/lunet-io/markdig

NewtonsoftJson.NET 高性能JSON框架,适用于https://www.newtonsoft/json


Piranha CMS有Empty,Web和Blog三个项目模板。Web和Blog项目模板包括Blog归档以及基本页面和博客帖子的模型、控制器和视图、

Web项目模板还包含一种起始页面类型用于登录页面。Blog项目模板则包含更高级的Blog归档,可以将分页、类别和标记过滤功能用于默认页面。

在创建PCMS项目之前你需要安装模板


在VSCODE中,安装PCMS模板

dotnet new -i Piranha.Templates

image.png

在终端窗口输入命令,列出Piranha CMS项目模板,如下所示

dotnet new "Piranha cms" --list

image.png



这里假设没有安装vscode,我们用vs2019

Ctrl+反引号  打开终端,输入

dotnet new -i Piranha.Templates

然后

dotnet new "Piranha cms" --list    

image.png

然后

dotnet new piranha.blog

但是我这没这个命令,

我们直接 dotnet new piranha.mvc回车

然后dotnet restore

然后dotnet run

浏览器输入


http://localhost:5000/

image.png

点击seed some data

image.png

浏览器输入http://localhost:5000/manager

输入用户名 admin 密码是 password

image.png

进入后

image.png

有3个站点,第2个是博客,第三个是文档


点击 添加站点

image.png

添加如下内容,内部Id 自己填写的

image.png

单击保存按钮

image.png

然后单击 Northwind CMS可以删除

image.png

,删除后,我们单击Default Site,修改为Northwind CMS

image.png

image.png


如果不在网页,先单击网页

image.png

(书中的about us 在新版piranha 已经没有了)

单击过后,添加页面

image.png

Standard page

image.png

修改内容,单击圆形+号可以添加块,上面那个图,我添加呢一个Content类型

image.png

这里面的内容自己测试和使用下就OK了,跟写博客一样。


现在编辑标题,单击设置

image.png


image.png

点击保存,然后点击发布

image.png

发布后访问者就可以看到了

image.png


创建一个新的子页面

现在要在页面层次结构中插入新的页面

向下箭头:在当前页面之后创建新页面,它们在同一级别。

向右箭头:在当前页面下方创建新页面,此时创建的是子页面

image.png

即使在错误位置创建错误了,也可以将这个页面拖放到页面层次结构中的正确位置。

很多人都喜欢先水平创建,然后再拖 层次。


单击About AY右侧的 向右箭头

image.png

单击Standard Page,页面标题改成 AY的位置

然后单击发布

image.png

如果在外侧拖动方式修改了层次结构,这里的网址标识不会修改,需要你手动修改。

image.png


回顾博客归档

单击导航栏-网页,然后单击Blog

image.png

单击 Archive   归档

image.png

单击添加,然后单击 Blog Post

标题输入Northwind has some cool new fish to sell you!

类别,在下面的下拉中的文本框 输入Fish,按下回车

image.png

右侧标签,输入完,按下回车就是一个标签。

单击顶部的发布,发布完成后,就会多出个(眼睛图标的)预览按钮

image.png



探索身份验证和授权

看看哪些设置可以保护内容

单击 导航栏-用户

image.png

单击导航栏-角色,单击SysAdmin角色,注意可以为一个角色分配几十种权限,如图

image.png

回到角色页面,单击添加

image.png

角色名称输入Editors,然后如下勾选 权限

image.png

然后单击保存

image.png

然后回到用户界面,添加个用户Eve,如下信息,密码是123123,单击保存

image.png

最佳实践,考虑不同的角色需要哪些权限,SystemAdmin角色应该具有所有权限,对于Editor角色,可能允许添加,删除编辑和保存页面,帖子,媒体等。但对于Publisher角色,我们只允许发布内容,因为只有发布者能够控制什么时候允许网站访问者查看内容。


探索配置

单击导航栏-配置,注意默认情况下,每个归档页面都显示了五个博客,并且为页面和帖子保留了10处修订。如果内容所有者创建页面的层次结构,那么URL路径将使用分层的段地址,如下所示

/aboutay/news-and-events/northwind-wins-award

image.png

将页面缓存从0分钟改为30分钟

开发过程中,可通过将缓存时间设置为0来关闭页面缓存,因为当更改代码时,这可能会导致混淆。

改完以后还是改回0,单击保存,因为后面我们要二次开发。


页面和子页面的地址

http://localhost:5000/aboutay

http://localhost:5000/aboutay/aylocation




了解路由

标准页面和 归档类型页面不需要自定义路由来工作,因为以下路由是默认配置的。

/page: 除归档类型页面外所有页面类型

/archive: 归档类型页面

/post: 存档中的帖子


因此传入的HTTP请求URL可转换成能让 ASP.NET Core以正常方式处理的路由。例如,以下请求

https://localhost:5000/aboutay

可由PCMS转化成

https://localhost:5000/page?id=a5a2a267-109a-4bb7-ad32-e1d8232d88ed

页面id是GUID,可用于在PCMS数据库中查找页面内容数据


打开PCMS的代码

image.png

打卡CmsController.cs,注意此类派生自Controller类。

向下滚动,找到Page操作方法。【Route】特性指明了Page操作方法用于响应HTTP请求的相对URL路径//page,你可以使用微软的模型绑定程序读取GUID,并使用Piranha的API从数据库中共查找页面的模型

image.png

Piranha CMS的高级路由-- http://piranhacms.org/docs/architecture/routing/advanced-routing



了解媒体

媒体文件用户可以在界面上传,也可以通过编程方式使用Piranha CMS提供的。带有流和字节数组的API进行上传。

http://piranhacmd.org/docs/basics/media-files


为了兼容,PCMS上传的内容格式限制了,以下支持的

.jpg jpeg png

.mp4,.pdf

如果需要上传其他类型,比如GIF,可以在Startup类注册

     App.MediaTypes.Images.Add(".gif","image/gif");

image.png


理解应用程序服务

应用程序服务可以简化对当前请求的公共对象的编程访问。应用程序服务通常可使用名为WebApp的_ViewImports.cshtml注入所有Razor文件

image.png

代码说明
@WebApp.PageId请求页面的GUID
@WebApp.Url浏览器请求在被中间件重写之前的原始URL
@WebApp.Api访问完整的Piranha API
@WebApp.Media.ResizeImage(ImageField image,int width?,int?height=null)将给定的ImageField调整为镇定的尺寸,并将生成的URL路径返回给调整大小后的文件。ImageField是一种可以引用已上传图片的Piranha类型




理解内容类型

PCMS允许开发者定义如下三种类型的内容:

    站点:用于可在所有其他之间共享的属性。如果不需要共享属性,则不需要站点内容类型。即使需要,每个站点通常也只需要一种站点内容类型。这种类必须用[SiteType]进行装饰。

    页面:用于信息页面(如About AY页面)和登录页面(如主页和类别页面),也可以其他页面作为子页面。由所有页面形成的层次结构提供了站点的URL路径结构,例如/aboutay/aylocation。每个站点通常有多种页面内容类型,比如标准页面、归档类型页面、类别页面、产品页面。这种类必须用[PageType]进行装饰。

    帖子:用于没有子页面且只能在归档类型页面中列出的页面。可以根据日期、类别和标记对帖子进行筛选和分组。每个站点通常只有一种或者两种帖子内容类型,比如NewsPost或者EventPost。这种类必须用[PostType]进行装饰


  1. 理解组件类型

    注册的内容类型在Piranha管理器中提供了对创建、编辑和删除操作的内置支持。内容类型的结构是通过将内容分为三类组件类型来提供的。

    => 字段:内容的最小组成部分。它们可以是简单的数字、日期或字符串值。字段类似于C#类中的属性。这种属性必须用[Field]进行装饰。

    =>区域:在开发人员的控制下,显示在页面上固定位置的小块内容或呈现给访问者的帖子。区域包括一个或多个字段,也可能包括字段的可排序集合。区域就像C#类中的复杂属性。这种属性必须用[Region]进行装饰。

       =>块:可以添加、重新排序和删除的小块内容。块为内容编辑器提供了强大的灵活性。默认情况下,所有页面和帖子可以包含任意数量的块,尽管对于具有[PageType]特性(用来设置UseBlocks为false)的特定页面或帖子内容类型,可以禁用块。标准块类型包括多列富文本、引用和图像。开发人员可以利用Piranha管理器中的自定义的编辑经验来自定义块类型。


    2 理解标准字段

    每个标准字段都有内置的编辑经验

    CheckBoxField    DateField    NumberField    StringField    TextField拥有简单的字段值。

    PageField和PostField:可使用GUID来引用页面或帖子

    DocumentField、ImageField、VideoField、MediaField:可使用GUID引用文档、图像、视频或任何媒体文件、默认情况下,DocumentField可以是.pdf, ImageField可以是.jpg,jpeg或png,VideoField可以是.mp4 ,MediaField可以是任何文件类型。

    HtmlField    MarkdownField 格式化的文本值。带有工具栏的可子帝国一的TinyMCE编辑器和Markdown编辑器。


    3 回顾一些内容类型

我下载了piranha的源码:https://github.com/PiranhaCMS/piranha.core   

image.png

先不管这个,还是以前那个项目,新建

using System.Collections.Generic;
using Piranha.AttributeBuilder;
using Piranha.Extend;
using Piranha.Extend.Fields;
using Piranha.Models;

namespace vsNorthwindCms.Models
{
    [SiteType(Title = "Default site")]
    public class BlogSite : SiteContent<BlogSite>
    {
        [Region] public SiteInfo Information { get; set; }
    }

    public class SiteInfo
    {
        [Field(Title = "Site Title", Options = FieldOption.HalfWidth)]
        public StringField SiteTitle { get; set; }
        [Field(Options = FieldOption.HalfWidth)]
        public StringField TagLine { get; set; }
        [Field(Title ="Site Logo")]
        public ImageField SiteLogo { get; set; }
    }


}

SiteInfo有3个属性,用来共享整个网站的标题,标语和徽标

(我暂时还不知道怎么用)


打开Models/StandardPage.cs

using Piranha.AttributeBuilder;
using Piranha.Models;

namespace vsNorthwindCms.Models
{
    [PageType(Title = "Standard page")]
    public class StandardPage  : Page<StandardPage>
    {
    }
}

继承了Page<T>,类被PageType修饰


打开Views/Cms/Page.cshtml

@model StandardPage
@{
    ViewData["Title"] = !string.IsNullOrEmpty(Model.MetaTitle) ? Model.MetaTitle : Model.Title;
    var hasImage = Model.PrimaryImage.HasValue;
}
@section head {
    @WebApp.MetaTags(Model)
}

<header @(hasImage ? "class=has-image" : "") @(hasImage ? $"style=background-image:url({ @Url.Content(WebApp.Media.ResizeImage(Model.PrimaryImage, 1920, 400)) })" : "")>
    <div class="dimmer"></div>
    <div class="container text-center">
        <h1>@Model.Title</h1>
        @if (!string.IsNullOrWhiteSpace(Model.Excerpt))
        {
            <div class="row justify-content-center">
                <div class="col-lg-8 lead">
                    @Html.Raw(Model.Excerpt)
                </div>
            </div>
        }
    </div>
</header>

<main>
    @foreach (var block in Model.Blocks)
    {
        <div class="block @block.CssName()">
            <div class="container">
                @Html.DisplayFor(m => block, block.GetType().Name)
            </div>
        </div>
    }
</main>

注意顶部的@model 是 StandardPage类型

============www.ayjs.net=================


由于书上的内容已经不符合教程,后续不写了,你自己对着piranha的文档可以学习

https://piranhacms.org/docs


下面我自己发挥,如果书上有,我会结合他的部分思路


打开vsNorthwindCms\Views\Cms\Post.cshtml

@model StandardPost
@{
    ViewData["Title"] = !string.IsNullOrEmpty(Model.MetaTitle) ? Model.MetaTitle : Model.Title;
    var hasImage = Model.PrimaryImage.HasValue;
    var archive = await WebApp.Api.Pages.GetByIdAsync(Model.BlogId);
}
@section head {
    @WebApp.MetaTags(Model)
}

<header @(hasImage ? "class=has-image" : "") @(hasImage ? $"style=background-image:url({ @Url.Content(WebApp.Media.ResizeImage(Model.PrimaryImage, 1920, 400)) })" : "")>
    <div class="dimmer"></div>
    <div class="container text-center">
        <h1>@Model.Title</h1>
        <p class="post-meta">
            <span>
                In <a href="@archive.Permalink/category/@Model.Category.Slug">@Model.Category.Title</a>
            </span>
            <span>
                Tags
                @foreach (var tag in Model.Tags)
                {
                    <a href="@archive.Permalink/tag/@tag.Slug">#@tag.Slug</a>
                }
            </span>
            @if (Model.IsPublished)
            {
                <span>
                    Published @Model.Published.Value.ToShortDateString()
                </span>
            }
            @if (Model.EnableComments && Model.CommentCount > 0)
            {
                <span>
                    <a href="#comments">@Model.CommentCount @(Model.CommentCount == 1 ? "comment" : "comments")</a>
                </span>
            }
        </p>
        @if (!string.IsNullOrWhiteSpace(Model.Excerpt))
        {
            <div class="row justify-content-center">
                <div class="col-lg-8 lead">
                    @Html.Raw(Model.Excerpt)
                </div>
            </div>
        }
    </div>
</header>

<main>
    @foreach (var block in Model.Blocks)
    {
        <div class="block @block.CssName()">
            <div class="container">
                @Html.DisplayFor(m => block, block.GetType().Name)
            </div>
        </div>
    }
    @if (Model.EnableComments)
    {
        <div id="comments" class="container comments">
            @if (Model.IsCommentsOpen)
            {
                <div class="alert alert-comment mt-2 mb-5">
                    <form action="@Model.Permalink/comment" method="post">
                        @Html.AntiForgeryToken()
                        @Html.Hidden("Id", Model.Id)

                        <h3 class="mb-3">Leave a comment</h3>
                        <p class="mb-4">
                            Please note that we won't show your email to others, or use it for sending
                            unwanted emails. We will only use it to render your Gravatar image and to
                            validate you as a real person.
                        </p>
                        <div class="row">
                            <div class="col-md-6">
                                <div class="form-group">
                                    <input name="CommentAuthor" type="text" class="form-control" placeholder="Your name">
                                </div>
                            </div>
                            <div class="col-md-6">
                                <input name="CommentEmail" type="text" class="form-control" placeholder="Your email address">
                            </div>
                        </div>
                        <div class="form-group">
                            <input name="CommentUrl" type="text" class="form-control" placeholder="An (optional) URL to your website">
                        </div>
                        <div class="form-group">
                            <textarea name="CommentBody" rows="6" class="form-control" placeholder="Your awesome comment"></textarea>
                        </div>
                        <div class="form-group text-left">
                            <button class="btn btn-success">Post Comment</button>
                        </div>
                    </form>
                </div>
            }
            @Html.DisplayFor(m => m.Comments)
        </div>
    }
</main>

这里有裁剪图片的代码

image.png

每个post只属于一种类别,但可以有多个标签。

image.png

如果发布了,显示发布时间,如果启用评论,评论数大于0,则显示评论



理解标准块

在Views/Cms/DisplayTemplates下有很多块的模板

image.png

标准块包括:

    列:这种标准块拥有Item属性,其中有一项或多项是Block实例。

image.png

    图像:这种标准块拥有Body属性,作为带视图的ImageField,可输出作为<img>元素的src属性值。

image.png

    引用:这种标准块拥有Body属性,作为带视图的TextField,可包装在<blockquote>元素块中并输出。

image.png

    文本:这种标准块拥有Body属性,是普通的TextField

image.png


每个模板头部的@model后面的类型F12,可以进行查看写法,如果还不清楚,可以下载源码

image.png


打开源码的HtmlBlock

/*
 * Copyright (c) .NET Foundation and Contributors
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 *
 * https://github.com/piranhacms/piranha.core
 *
 */

using System.Text.RegularExpressions;
using Piranha.Extend.Fields;

namespace Piranha.Extend.Blocks
{
    /// <summary>
    /// Single column HTML block.
    /// </summary>
    [BlockType(Name = "Content", Category = "Content", Icon = "fas fa-paragraph", Component = "html-block")]
    public class HtmlBlock : Block, ISearchable, ITranslatable
    {
        /// <summary>
        /// Gets/sets the HTML body.
        /// </summary>
        public HtmlField Body { get; set; }

        /// <summary>
        /// Gets the title of the block when used in a block group.
        /// </summary>
        /// <returns>The title</returns>
        public override string GetTitle()
        {
            if (Body?.Value != null)
            {
                var title = Regex.Replace(Body.Value, @"<[^>]*>", "");

                if (title.Length > 40)
                {
                    title = title.Substring(0, 40) + "...";
                }
                return title;
            }
            return "Empty";
        }

        /// <summary>
        /// Gets the content that should be indexed for searching.
        /// </summary>
        public string GetIndexedContent()
        {
            return !string.IsNullOrEmpty(Body.Value) ? Body.Value : "";
        }
    }
}

如果不熟悉Bootstrap的网格系统,可通过以下链接了解: https://getbootstrap.com/docs/4.1/layout/grid/


定义组件,内容类型和模板

打开StandardPage页面 F12看父类

image.pngimage.png

PageBase类,都有下面属性

image.png

SiteId,ParentId它们都是GUID类型

书上说下面两个有,但是我没找到,我在PageBase父类RoutedContentBase找到了

ContentType 值是字符串

Blocks值是Block实例的列表

现在继承了RoutedContentBase

image.png

如下红色方框,为了进行更好的SEO,定义了描述和关键字等关键字,slug

image.png

F12查看ContentBase类,定义如下

image.png

这里比作者写的多了一个 Permissions权限属性了。



由于书里的内容已经不适合最新版的 Piranha, 所以如果感兴趣,对着官方文档学怎么拓展吧。

















推荐您阅读更多有关于“C#8.0core3,”的文章

猜你喜欢

额 本文暂时没人评论 来添加一个吧

发表评论

必填

选填

选填

必填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

  查看权限

抖音:wpfui 工作wpf

目前在合肥企迈科技公司上班,加我QQ私聊

2023年11月网站停运,将搬到CSDN上

AYUI8全源码 Github地址:前往获取

杨洋(AaronYang简称AY,安徽六安人)AY唯一QQ:875556003和AY交流

高中学历,2010年开始web开发,2015年1月17日开始学习WPF

声明:AYUI7个人与商用免费,源码可购买。部分DEMO不免费

查看捐赠

AYUI7.X MVC教程 更新如下:

第一课 第二课 程序加密教程

标签列表