Skip to content

Commit 67acdfa

Browse files
authored
Merge pull request #35 from petrsvihlik/feature/project-tiles
Add project tiles, closes #11
2 parents 2c987fd + ebc5f34 commit 67acdfa

9 files changed

Lines changed: 257 additions & 35 deletions

File tree

Models/ContentTypes/Project.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace PetrSvihlik.Com.Models.ContentTypes
2+
{
3+
public class Project
4+
{
5+
public string Title { get; set; }
6+
public string Repo { get; set; }
7+
public string Logo { get; set; }
8+
public string Description { get; set; }
9+
public string ContentHtml { get; set; }
10+
public int Order { get; set; }
11+
12+
public string GitHubUrl => string.IsNullOrEmpty(Repo) ? null : $"https://github.com/{Repo}";
13+
}
14+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Collections.Generic;
2+
using PetrSvihlik.Com.Models.ContentTypes;
3+
4+
namespace PetrSvihlik.Com.Models.ViewModels
5+
{
6+
public class ProjectsViewModel : ViewModelBase
7+
{
8+
public IEnumerable<Project> Projects { get; }
9+
public SidebarViewModel Sidebar { get; }
10+
11+
public ProjectsViewModel(IEnumerable<Project> projects, SiteMetadata metadata, SidebarViewModel sidebar)
12+
: base(metadata)
13+
{
14+
Projects = projects;
15+
Sidebar = sidebar;
16+
}
17+
}
18+
}

Pipelines/ProjectsDataPipeline.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using PetrSvihlik.Com.Models.ContentTypes;
2+
using Statiq.Common;
3+
using Statiq.Core;
4+
using Statiq.Markdown;
5+
using Statiq.Yaml;
6+
7+
namespace PetrSvihlik.Com.Pipelines
8+
{
9+
public class ProjectsDataPipeline : Pipeline
10+
{
11+
public ProjectsDataPipeline()
12+
{
13+
InputModules = new ModuleList
14+
{
15+
new ReadFiles("projects/_*.md"),
16+
new ExtractFrontMatter(new ParseYaml()),
17+
new RenderMarkdown().UseExtensions(),
18+
new SetMetadata("ProjectModel", Config.FromDocument((doc, ctx) => new Project
19+
{
20+
Title = doc.GetString("title"),
21+
Repo = doc.GetString("repo"),
22+
Logo = doc.GetString("logo"),
23+
Description = doc.GetString("description"),
24+
ContentHtml = doc.GetContentStringAsync().GetAwaiter().GetResult(),
25+
Order = doc.GetInt("order", 999),
26+
})),
27+
};
28+
}
29+
}
30+
}

Pipelines/ProjectsPipeline.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using PetrSvihlik.Com.Models.ContentTypes;
2+
using PetrSvihlik.Com.Models.ViewModels;
3+
using Statiq.Common;
4+
using Statiq.Core;
5+
using Statiq.Razor;
6+
using System.Linq;
7+
8+
namespace PetrSvihlik.Com.Pipelines
9+
{
10+
public class ProjectsPipeline : Pipeline
11+
{
12+
public ProjectsPipeline()
13+
{
14+
Dependencies.AddRange(nameof(ProjectsDataPipeline), nameof(HomepagePipeline), nameof(SiteMetadataPipeline));
15+
16+
ProcessModules = new ModuleList
17+
{
18+
new ExecuteConfig(Config.FromContext(ctx =>
19+
{
20+
var projects = ctx.Outputs.FromPipeline(nameof(ProjectsDataPipeline))
21+
.OrderBy(d => d.GetInt("order", 999))
22+
.Select(d => d.Get<Project>("ProjectModel"))
23+
.ToList();
24+
var metadata = ctx.Outputs.FromPipeline(nameof(SiteMetadataPipeline))
25+
.Select(x => x.Get<SiteMetadata>("SiteMetadata")).FirstOrDefault();
26+
var homepage = ctx.Outputs.FromPipeline(nameof(HomepagePipeline))
27+
.Select(x => x.Get<Homepage>("Homepage")).FirstOrDefault();
28+
var sidebar = new SidebarViewModel(homepage, metadata, false, "projects");
29+
var viewModel = new ProjectsViewModel(projects, metadata, sidebar);
30+
return new[] { ctx.CreateDocument(new MetadataItems { { "ProjectsViewModel", viewModel } }) };
31+
})),
32+
new SetDestination(new NormalizedPath("pages/projects/index.html")),
33+
new MergeContent(new ReadFiles("_Projects.cshtml")),
34+
new RenderRazor().WithModel(Config.FromDocument((doc, ctx) =>
35+
doc.Get<ProjectsViewModel>("ProjectsViewModel"))),
36+
};
37+
38+
OutputModules = new ModuleList
39+
{
40+
new WriteFiles(),
41+
};
42+
}
43+
}
44+
}

input/_Projects.cshtml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
@using PetrSvihlik.Com.Models.ContentTypes
2+
@using PetrSvihlik.Com.Models.ViewModels
3+
@model ProjectsViewModel
4+
@{
5+
ViewData["SiteMetadata"] = Model.Metadata;
6+
ViewData["Author"] = Model.Author;
7+
ViewBag.Title = $"Projects - {Model.Metadata.Title}";
8+
ViewBag.PageType = "page";
9+
}
10+
@await Html.PartialAsync("Shared/Sidebar/_Sidebar", Model.Sidebar)
11+
<div class="content">
12+
<div class="content__inner">
13+
<div class="page">
14+
<h1 class="page__title">Projects</h1>
15+
<div class="page__body">
16+
<div class="projects">
17+
@foreach (var project in Model.Projects)
18+
{
19+
<div class="project-tile">
20+
<div class="project-tile__header">
21+
@if (!string.IsNullOrEmpty(project.Logo))
22+
{
23+
<img src="@project.Logo" alt="@project.Title logo" class="project-tile__logo">
24+
}
25+
<div>
26+
<h2 class="project-tile__name">
27+
<a href="@project.GitHubUrl" target="_blank" rel="noopener noreferrer">@project.Title</a>
28+
</h2>
29+
<p class="project-tile__description">@project.Description</p>
30+
</div>
31+
</div>
32+
@if (!string.IsNullOrEmpty(project.ContentHtml))
33+
{
34+
<div class="project-tile__highlights">
35+
@Html.Raw(project.ContentHtml)
36+
</div>
37+
}
38+
@if (!string.IsNullOrEmpty(project.Repo))
39+
{
40+
<div class="project-tile__actions">
41+
<a class="github-button" href="@project.GitHubUrl" data-icon="octicon-star" data-size="large" aria-label="Star @project.Repo on GitHub">Star</a>
42+
<a class="github-button" href="@project.GitHubUrl/fork" data-icon="octicon-repo-forked" data-size="large" aria-label="Fork @project.Repo on GitHub">Fork</a>
43+
<a href="@project.GitHubUrl" class="project-tile__link" target="_blank" rel="noopener noreferrer">View on GitHub →</a>
44+
</div>
45+
}
46+
</div>
47+
}
48+
</div>
49+
<script async defer src="https://buttons.github.io/buttons.js"></script>
50+
</div>
51+
</div>
52+
</div>
53+
</div>

input/assets/scss/_components.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
@import "components/links";
88
@import "components/layout";
99
@import "components/pagination";
10-
@import "components/switcher";
10+
@import "components/switcher";
11+
@import "components/projectTile";
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
.projects {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 2rem;
5+
}
6+
7+
.project-tile {
8+
border: 1px solid $color-gray-border;
9+
border-radius: 6px;
10+
padding: 1.5rem;
11+
12+
&__header {
13+
display: flex;
14+
align-items: flex-start;
15+
gap: 1rem;
16+
margin-bottom: 1rem;
17+
}
18+
19+
&__logo {
20+
width: 52px;
21+
height: 52px;
22+
object-fit: contain;
23+
flex-shrink: 0;
24+
border-radius: 4px;
25+
}
26+
27+
&__name {
28+
font-size: 1.25rem;
29+
margin: 0 0 0.25rem;
30+
31+
a {
32+
color: $color-base;
33+
text-decoration: none;
34+
35+
&:hover {
36+
color: $color-primary;
37+
}
38+
}
39+
}
40+
41+
&__description {
42+
margin: 0;
43+
color: $color-gray;
44+
font-size: $typographic-small-font-size;
45+
}
46+
47+
&__highlights {
48+
margin: 0 0 1.25rem;
49+
font-size: $typographic-small-font-size;
50+
51+
// Markdown renders a <ul> inside this div
52+
ul {
53+
margin: 0;
54+
padding-left: 1.25rem;
55+
}
56+
57+
li {
58+
margin-bottom: 0.35rem;
59+
}
60+
}
61+
62+
&__actions {
63+
display: flex;
64+
align-items: center;
65+
gap: 0.5rem;
66+
flex-wrap: wrap;
67+
}
68+
69+
&__link {
70+
font-size: $typographic-small-font-size;
71+
color: $color-primary;
72+
text-decoration: none;
73+
margin-left: auto;
74+
75+
&:hover {
76+
text-decoration: underline;
77+
}
78+
}
79+
}
80+
81+
.dark .project-tile {
82+
border-color: #444;
83+
}

input/pages/projects.md

Lines changed: 0 additions & 34 deletions
This file was deleted.

input/projects/_wopihost.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
title: WopiHost
3+
repo: petrsvihlik/WopiHost
4+
logo: /assets/img/wopihost-logo.png
5+
description: The most advanced open-source .NET implementation of the MS-WOPI protocol — enabling seamless integration with Office Online Server and Microsoft 365.
6+
order: 1
7+
---
8+
9+
- 🔌 **Pluggable storage** — implement `IWopiStorageProvider` to back WopiHost with any data source
10+
- ☁️ **.NET Aspire integration** — first-class cloud-native support with OpenTelemetry observability built in
11+
- 🔐 **Enterprise security** — WOPI proof key validation, origin checking, extensible JWT-based auth/authz
12+
- 📄 **Full WOPI compliance** — file & container operations, PutRelativeFile, rename, create, OneNote endpoints
13+
- 🎯 **Multi-targeting** — .NET 8, 9, and 10

0 commit comments

Comments
 (0)