|
1 | | -### Getting Started |
2 | | - |
3 | | -To start with new theme, create a folder, for example `Views/Themes/Custom` and add two files, |
4 | | -`List.cshtml` and `Post.cshtml`. |
5 | | - |
6 | | -The list declares a model and, for each post in the `Model.Posts` displays title and description. |
7 | | -The title is a link pointing to post detail and description is converted from Markdown to HTML. |
8 | | - |
9 | | -```html |
10 | | -<!-- Views/Themes/Custom/List.cshtml --> |
11 | | -@model ListModel |
12 | | -<!DOCTYPE html> |
13 | | -<html> |
14 | | -<head></head> |
15 | | -<body> |
16 | | - @if (Model.Posts != null) |
17 | | - { |
18 | | - foreach (var item in Model.Posts) |
19 | | - { |
20 | | - <h2><a href="~/posts/@item.Slug">@item.Title</a></h2> |
21 | | - <div>@Html.Raw(Markdig.Markdown.ToHtml(item.Description))</div> |
22 | | - } |
23 | | - } |
24 | | -</body> |
25 | | -</html> |
26 | | -``` |
| 1 | +### Quick Summary |
27 | 2 |
|
28 | | -The post detail just outputs content of the post on the page. |
29 | | - |
30 | | -```xml |
31 | | -<!-- Views/Themes/Custom/Post.cshtml --> |
32 | | -@model PostModel |
33 | | -<!DOCTYPE html> |
34 | | -<html> |
35 | | -<head></head> |
36 | | -<body> |
37 | | - <div>@Html.Raw(Markdig.Markdown.ToHtml(Model.Post.Content))</div> |
38 | | -</body> |
39 | | -</html> |
40 | | -``` |
| 3 | +- Any folder added to Blogifier under `src/App/wwwroot/themes` will show up in admin as a theme |
| 4 | +- When selected as active theme in admin, Blogifier will copy files from theme folder to `_active` |
| 5 | + |
| 6 | +Blogifier uses `_active` folder as Angular application root, so any Angular application deployed there will be available at "/". |
41 | 7 |
|
42 | | -Now load your blog, navigate to admin settings and select `Custom` theme. Save and view your blog. |
43 | | -It will use your newly created theme - congratulations, all done! |
44 | | - |
45 | | -### Improving on Design |
46 | | - |
47 | | -Here is another super simple example using Bootstrap stylesheet. |
48 | | - |
49 | | -```html |
50 | | -@model ListModel |
51 | | -<!DOCTYPE html> |
52 | | -<html> |
53 | | -<head> |
54 | | - <title>@Model.Blog.Title</title> |
55 | | - <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> |
56 | | -</head> |
57 | | -<body> |
58 | | - <div class="container"> |
59 | | - <div class="jumbotron"> |
60 | | - <h1>@Model.Blog.Title</h1> |
61 | | - <p>@Model.Blog.Description</p> |
62 | | - </div> |
63 | | - <div class="row"> |
64 | | - @if (Model.Posts != null) |
65 | | - { |
66 | | - foreach (var item in Model.Posts) |
67 | | - { |
68 | | - <div class="col-lg-4"> |
69 | | - <h2>@item.Title</h2> |
70 | | - @Html.Raw(Markdig.Markdown.ToHtml(item.Description)) |
71 | | - <p><a class="btn btn-primary" href="~/posts/@item.Slug" role="button">View details »</a></p> |
72 | | - </div> |
73 | | - } |
74 | | - } |
75 | | - </div> |
76 | | - <footer class="footer"> |
77 | | - <p>© @DateTime.Now.Year Company, Inc.</p> |
78 | | - </footer> |
79 | | - </div> |
80 | | -</body> |
81 | | -</html> |
| 8 | +```csharp |
| 9 | +services.AddSpaStaticFiles(configuration => |
| 10 | +{ |
| 11 | + configuration.RootPath = "wwwroot/themes/_active"; |
| 12 | +}); |
82 | 13 | ``` |
83 | 14 |
|
84 | | -For your own styles, create folder under `wwwroot/themes/theme-name` and reference it from the page header. |
85 | | -Same goes for JavaScript or any custom images you want to use in the custom theme. |
| 15 | +In the box Blogifier themes are prebuilt Angular CLI applications with source code hosted at `https://github.com/blogifierdotnet/themes`. |
| 16 | +Every time theme source updated, it is build to distribution folder and deployed to Blogifier. |
| 17 | + |
| 18 | +> [Angular Theme for Blogifier - Jump Start](http://rtur.net/posts/angular-theme-for-blogifier-jump-start) |
| 19 | +
|
| 20 | +### Routing |
| 21 | + |
| 22 | +Blogifier set up with both MVC and Angular routes, so theme can use normal Angular techniques |
| 23 | +to navigate within application as long as it does not clash with existing Blogifeir routes |
| 24 | +(like `/admin`, `/account` etc.). Here is simple example with `/` and `/posts/post-slug` routes. |
86 | 25 |
|
87 | | -```html |
88 | | -<link href="~/themes/custom/style.css" rel="stylesheet"> |
89 | | -<script src="~/themes/custom/script.js"></script> |
| 26 | +```javascript |
| 27 | +const routes: Routes = [ |
| 28 | + { path: '', component: HomeComponent }, |
| 29 | + { path: 'posts/:slug', component: PostsComponent } |
| 30 | +]; |
| 31 | + |
| 32 | +@NgModule({ |
| 33 | + imports: [ |
| 34 | + RouterModule.forRoot(routes) |
| 35 | + ] |
| 36 | +}) |
90 | 37 | ``` |
91 | 38 |
|
92 | | -Anything available in ASP.NET layouts can be used for the theme without any restrictions, like partial views for |
93 | | -headers and footers and so on. Please refer to `Standard` theme for examples on using some of the more complex elements. |
| 39 | +### Pulling the Data |
94 | 40 |
|
95 | | -### Understanding the Models |
| 41 | +Blogifier exposes data to the theme via APIs. Documentation available at `/swagger`. |
| 42 | +Some APIs require authentication or admin rights and can't be used in the theme. |
| 43 | +All public APIs, including CORS enabled, can be used in the theme to pull data. |
96 | 44 |
|
97 | | -Theme authors can use models inside post lists or single post. All properties from |
98 | | -the models can be accessed within theme, for example `Model.Blog.Title`. |
| 45 | +```javascript |
| 46 | +export class BlogService { |
| 47 | + constructor(private http: HttpClient) { } |
| 48 | + getPosts(): Observable<IPostList> { |
| 49 | + return this.http.get<IPostList>('/api/posts'); |
| 50 | + } |
| 51 | +} |
| 52 | +``` |
99 | 53 |
|
100 | | -#### ListModel |
| 54 | +Then client, like HomeController, can call this service. |
| 55 | + |
| 56 | +```javascript |
| 57 | +import { BlogService, IPostList } from '../core/blog.service'; |
| 58 | +export class HomeComponent implements OnInit { |
| 59 | + postList: IPostList; |
| 60 | + constructor(private blogService: BlogService) { } |
| 61 | + ngOnInit(): void { |
| 62 | + this.blogService.getPosts().subscribe( |
| 63 | + result => { this.postList = result; } |
| 64 | + ); |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +And output posts in the HTML page |
101 | 70 |
|
102 | | -Name | Data Type | Description |
103 | | ---- | --- | --- |
104 | | -Blog | [BlogItem](https://github.com/blogifierdotnet/Blogifier/blob/master/src/Core/Data/Models/AppModel.cs) | Blog settings (title, description etc.) |
105 | | -Author | [Author](https://github.com/blogifierdotnet/Blogifier/blob/master/src/Core/Data/Domain/Author.cs) | Author of the blog |
106 | | -Category | string | Category (when browse by category) |
107 | | -Posts | IEnumerable <PostItem> | List of blog posts |
108 | | -Pager | [Pager](https://github.com/blogifierdotnet/Blogifier/blob/master/src/Core/Helpers/Pager.cs) | Pager (older/newer links) |
109 | | -PostListType | PostListType | Posts type (blog, category, author, search) |
| 71 | +```javascript |
| 72 | +<div *ngFor="let post of postList.posts"> |
| 73 | + <h4>{{ post.title }}</h4> |
| 74 | +</div> |
| 75 | +``` |
110 | 76 |
|
111 | | -#### PostModel |
| 77 | +Because data service might be commonly used in many themes, |
| 78 | +it is [implemented as single file](https://github.com/blogifierdotnet/themes/blob/master/simple/src/app/core/blog.service.ts) |
| 79 | +and included in every built-in theme. It can be copied into new theme to speed up development. |
112 | 80 |
|
113 | | -Name | Data Type | Description |
114 | | ---- | --- | --- |
115 | | -Blog | BlogItem | Blog settings (title, description etc.) |
116 | | -Post | PostItem | Current post |
117 | | -Older | PostItem | Previous/older post |
118 | | -Newer | PostItem | Next/newer post |
| 81 | +(Later it can become external package distributed via `npm install`) |
0 commit comments