OrchardCMS / OrchardCore

Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.
https://orchardcore.net
BSD 3-Clause "New" or "Revised" License
7.43k stars 2.4k forks source link

Implementation notes for traditional CMS #4121

Closed dodyg closed 1 year ago

dodyg commented 5 years ago

In the vein of https://github.com/OrchardCMS/OrchardCore/issues/3553, I am starting another website using the traditional CMS model.

hishamco commented 5 years ago

Multi Language website (English and Arabic)

If you need any kind of help in Arabic, ping me :)

dodyg commented 5 years ago

svg aren't cached https://github.com/OrchardCMS/OrchardCore/issues/3807

dodyg commented 5 years ago

zone-for-layers

dodyg commented 5 years ago

rule

What rule?

dodyg commented 5 years ago

widget-layers

agriffard commented 5 years ago

This is a condition to display the layer, a javascript boolean expression interpreted by Jint.

ex: true if always displayed, isHomepage() for the home, url('~/blog*') for a url starting by blog. We still need to create the corresponding documentation: #3111

When you use other recipes, some layers are created during the setup.

agriffard commented 5 years ago

A Widget is a content type that has a Widget stereotype.

Some widgets are created when you use other recipes, like Paragraph, ...

Widgets can also be added in a FlowPage and a WidgetListPart.

agriffard commented 5 years ago

Then, you will ask: What is a zone?

You can define them in the admin Zones setting and they have to be the equivalent sections in your theme.

So, to summarize: the Layers page allows you to put widgets in zones for a specific Layer. Ex: Add a paragraph Widget in the Footer zone on all the pages (= the Always layer).

dodyg commented 5 years ago

So, to summarize: the Layers page allows you to put widgets in zones for a specific Layer.

So let me validate my understanding

Does zone comes first then layers?

eg There's Template

<html>
<body>
   <zone header>
      <Layer One>
      <Layer Two>
   </zone header >

   <zone footer>
       <Layer One>
   </zone footer>
</body>
</html>

or Layer first then Zone

<html>
<body>
     <Layer One>
          <zone header>
          <zone footer>
      </Layer One>
     <Layer Two>
           <zone header>
     </Layer Two>
</body>
</html>
agriffard commented 5 years ago

There are only zone sections in the theme. Ex: {% render_section "Footer", required: false %}

You need to set them in the Zones settings You declare Layers (which are displayed based on their Rule, the interpreted condition).

It allows you to drag and drop and edit widgets inside the zones for a specified layer. Ex: You add a paragraph Widget in the Footer zone on all the pages (= the Always layer).

dodyg commented 5 years ago

OK I think I get it now. Let me study the theme. Thank you for your explanation.

dodyg commented 5 years ago

https://orchardcore.readthedocs.io/en/dev/docs/getting-started/theme/ needs more details. At the moment it is good enough to start an empty theme project but anything useful people will have to study existing themes.

dodyg commented 5 years ago

There needs to be a gentler version introduction to theming other than this document. This is hard mode tutorial https://orchardcore.readthedocs.io/en/dev/OrchardCore.Modules/OrchardCore.Themes/.

dodyg commented 5 years ago

I think a better theme tutorial would start from this layout.liquid and start expanding from here. This layout.liquid is generated by dotnet new octheme.

<!DOCTYPE html>
<html lang="{{ Culture.Name }}">
<head>
    <meta charset="utf-8">
    <title>{% page_title Site.SiteName, position: "before", separator: " - " %}</title>

    {% resources type: "Meta" %}

    <!-- INSERT REQUIRED RESOURCES HERE -->

    {% resources type: "HeadLink" %}
    {% resources type: "Stylesheet" %}
    {% resources type: "HeadScript" %}
</head>
<body dir="{{ Culture.Dir }}">

    {% render_body %}

    <footer>
    {% render_section "Footer", required: false %}
    </footer>

    {% script name:"jquery", use_cdn:"true", at:"Foot" %}

    {% resources type: "FootScript" %}
</body>
</html>
dodyg commented 5 years ago

In the traditional CMS, existing theme isn't really a solution because you cannot alter the files of existing theme. You can't modify a theme and switch it from using bootstrap to using bulma for example.

You have to create a new theme.

AmrTealeb commented 5 years ago

@hishamco How i can make my CMS web project localized I tried to add .po files to Localization folder but it didn't work

dodyg commented 5 years ago

SVG cache problem #3807 is fixed.

dodyg commented 5 years ago

instant-preview

This instant preview feature while modifying template is super sweet.

dodyg commented 5 years ago

{% resources type: "HeadLink" %}

It's not clear how this tag behaves. If the type is "HeadLink", where do you define the resources associated with it?

I mean the themes have these statements usually

    {% resources type: "HeadLink" %}
    {% resources type: "Stylesheet" %}
    {% resources type: "HeadScript" %}

And it's not clear how to interact with them.

dodyg commented 5 years ago

{% meta name:"description", content:"This is a website" %}

Call this to be able to set a meta information from your liquid template

dodyg commented 5 years ago

This is the relationship between the "zone" in the template and "render_section" in theme.

relationship

dodyg commented 5 years ago

Orchard Core will pick up any changes on your theme (just refresh your browser). Just make sure you make your new custom theme as the default theme for your website.

auto-refresh

dodyg commented 5 years ago

Visual Guide to Zone, Widget and Layers

Name the zones where you can attach widgets zone


You can add Widgets to each specified Zone and you can change the order of appearance for the Widget

zone-2


Each Widget in your zone is associated wit a Layer. A Layer is pretty much a condition where your Widget can appear

zone-3


In this layer, the condition always evaluate true so it will show in every page. There are a bunch of possible rules you can use as mentioned here https://github.com/OrchardCMS/OrchardCore/issues/4121#issuecomment-526829675

zone-4


What is a widget? A widget is Content Type that has a stereotype value 'Widget'

zone-5


It's literally just a string that you type in this stereotype field "Widget" :laughing:

zone-6

dodyg commented 5 years ago

This autoroute description is tricky. You'd assume that just by adding the part you can define a custom path for your Content Type. Nope, you have to adjust more settings. If you don't, autoroute will generate the path for you.

autoroute autoroute-2

dodyg commented 5 years ago

This is new. I haven't checked it out yet but it can potentially solves the issue of sorting, filtering, etc.

https://orchardcore.readthedocs.io/en/dev/docs/reference/modules/SQL/

dodyg commented 5 years ago

This thread is important regarding ASP.NET Core 3.0 upgrade (https://github.com/OrchardCMS/OrchardCore/issues/4261)

hishamco commented 5 years ago

@hishamco How i can make my CMS web project localized I tried to add .po files to Localization folder but it didn't work

Sorry I didn't notice your thread, to enable the localization you need to enable Localization Module after that the tenant will restart and you will be able to find a new sub menu Settings -> Localization then add your favorites cultures

dodyg commented 5 years ago

This is interesting Liquid Custom Tag (https://github.com/OrchardCMS/OrchardCore/issues/4384)

dodyg commented 5 years ago

The default layout for a theme is called either "Layout.liquid" or "Layout.cshtml".

dodyg commented 5 years ago

You can create an alternative layout e.g. "LayoutModern.liquid" and then call it from the view

{% layout "LayoutModern" %}

dodyg commented 5 years ago

Using {{ Model.ContentItem.Content }} in your Content Item template will expose the Json structure of the content. It's great for accessing individual properties of the content.

dodyg commented 5 years ago

Shape remains a mystery. I've watched the video twice. It looks like it's not relevant for just implementing a website (vs module developer)

dodyg commented 5 years ago

Getting extra content in your liquid template:

jptissot commented 5 years ago

@dodyg What made it click for me with shapes is then I compared them to React Components. Coming from doing javascript development for 2 years, when I understood that, everything clicked. The OC display manager is simply rendering a component (shape) and uses a bunch of conventions to find the right template to render. It also uses other metadata for placement of this shape. Hope this helps in some way.

dodyg commented 5 years ago

What I understand is that Model.Content is a shape. OK got it.

Then what? It's not clear whether you can create a shape within a layout using liquid. I tried all the documented shape filters (https://orchardcore.readthedocs.io/en/latest/OrchardCore.Modules/OrchardCore.Liquid/README/#shape-filters) but they either don't work or I misunderstood their purpose, e.g. 'shape_new'.

Skrypt commented 5 years ago

What you are looking for is something like this : {{ "Footer__Before" | shape_new | shape_render }} Which would work with a Footer-Before.liquid template. Then the only matter missing here is passing an actual model to that sub-template.

jptissot commented 5 years ago

You can build a shape (component), pass properties, and assign a template.

https://orchardcore.readthedocs.io/en/dev/OrchardCore.Modules/OrchardCore.Liquid/#shape_add_properties

dodyg commented 5 years ago

You can build a shape (component),

I am lost this part.

jptissot commented 5 years ago

Example:

{% shape_add_properties customShape my_string: "String Test 3", my_int: 1 %}
{{ customShape | shape_render }}

{% shape_remove_property customShape "my_string" %}
{{ customShape | shape_render }}

{% assign customShape = "MyTestShape" | shape_new | shape_properties: my_int: 3, my_string: "String Test 3" %}
{{ customShape | shape_render }}

{% assign customShape = "MyTestShape" | shape_new: my_int: 4, my_string: "String Test 4" %}
{{ customShape | shape_render }}

dodyg commented 5 years ago

Ah so a Shape depends on a template file (either liquid or razor). Does it always have to rely on the template in the theme or can it be created in the db?

jptissot commented 5 years ago

I am not sure of that one. I assume there must be some extension point to OC to be able to resolve liquid files from a database.

dodyg commented 5 years ago

I found it. The template file can be created in the DB!

template template-2

Skrypt commented 5 years ago

A shape doesn't necessarly depends on a template. Most of shapes are built by the DisplayManager for use with a template but some other shapes are also used for simply transforming a value from a context like a datetime displayed in the proper culture format. So a shape is basically a dynamic container used as a dynamic Viewmodel, but it's also used for all kind of things.

I think you can see Shapes visual representation in the Layout admin UI where you can add widgets in a layout template dynamically. That's the basic usage of shapes.

dodyg commented 5 years ago

As a website implementor, a shape is a template file you can include in other template and pass some parameters to it. It is simpler to start understanding from this angle.

dodyg commented 5 years ago

I am going to write "50 shades of Shapes" guide, which will contains various aspect of shapes and it will start from the simplest usage of Shapes. I really had a hard time understanding Shapes until just minutes ago so I think this guide can save others time.

dodyg commented 5 years ago

/OrchardCore.Templates/Template/Index is the place to manage all your templates. The corresponding documentation is here

Skrypt commented 5 years ago

This template naming convention hell is not for faint of hearths 😉 I'm still trying to figure out what works or not still. So don't be discouraged when trying different names 😄

sebastienros commented 5 years ago

I love to talk about shapes. They are very easy to understand actually, let me try.

A Shape is an object that holds some data, like a view model, and also some metadata about how to render it. Any class that implements IShape is a shape.

Shapes are used to as a way to render some html, and to do so you need to call the IDisplayHelper.DisplayAsync(shape) that returns Task<IHmlContent>.

When calling this method you don't need to specify a template, it will trigger an event to allow any component in the system to chose what template is best suited to render this specific shape. This is why a shape can be rendered using a cshtml file, a liquid file, some template from the database, some code, ... it's completely extensible, but from your point of view you just ask it to be rendered. This is an advantage over Views in MVC where when return a ViewResult from an action the template is strongly associated with the action.

This indirection is very useful for theming, such that templates can come from different modules, or any module can redefine what template to use for a shape. So as a developer you can provide one default template with your shape that represents a Menu, but allow any other module or theme to redefine it.

Then to allow for better granularity in ways to select a template, the shape has a Metadata.Alternates property that contains which templates names are better suited for this shape, in order of priority. Like a shape representing an Article, which is a Content item, could accept template for an with a specific name, or a template for any article, or a template for any content item. This allows the themes to be able to customize the templates for very specific instances of shapes, or group of shapes.

When working in the decoupled CMS mode, you are removing theming altogether, and not using shapes at all, because you implement each view and route handler, and don't expect anything else to be able to override these. But most modules that expose front-end views need to use shapes in order to allow a theme to customizes parts of the system without having to re-implement all handlers and views.

dodyg commented 5 years ago

Thank you for the explanation. I think Shapes conceptually is very elegant however I think for OrchardCore CMS beginners like me it is much easier to grasp the concept from the practical POV.

Shapes allows me to modularize my template. Wow. That's super cool and very practical.

sectors

A collection of samples on the practical usages of Shapes in the CMS will be very helpful for everyone.

dodyg commented 5 years ago

Rendering a sub Shapes using template

sliders