Skip to content

Commit

Permalink
Merge pull request #24 from intellifactory/userblogpage
Browse files Browse the repository at this point in the history
User blog pages and RSS/Atom feeds
  • Loading branch information
granicz authored Jan 22, 2021
2 parents dfb76b4 + a0225f9 commit 0bf8cae
Show file tree
Hide file tree
Showing 6 changed files with 420 additions and 46 deletions.
1 change: 1 addition & 0 deletions src/Hosted/Hosted.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<Content Include="404.html" />
<Content Include="assets\custom.css" />
<Content Include="assets\skin.css" />
<Content Include="userbloglist.html" />
<Content Include="contact.html" />
<Content Include="legal.html" />
<Content Include="trainings.html" />
Expand Down
142 changes: 97 additions & 45 deletions src/Hosted/Main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type EndPoint =
| [<EndPoint "GET /categories">] Categories
| [<EndPoint "GET /feed.atom">] AtomFeed
| [<EndPoint "GET /feed.rss">] RSSFeed
| [<EndPoint "GET /atom">] AtomFeedForUser of string
| [<EndPoint "GET /rss">] RSSFeedForUser of string
| [<EndPoint "GET /refresh">] Refresh
| [<EndPoint "GET /contact">] Contact
| [<EndPoint "GET /terms-of-use">] TermsOfUse
Expand Down Expand Up @@ -158,6 +160,11 @@ module Urls =
else
sprintf "/user/%s" user
let LANG (lang: string) = sprintf "/%s" lang
let RSS_URL user =
if String.IsNullOrEmpty user then
sprintf "/rss"
else
sprintf "/rss/%s.rss" user

module Helpers =
open System.IO
Expand Down Expand Up @@ -356,6 +363,7 @@ module Site =
type RedirectTemplate = Templating.Template<"../Hosted/redirect.html", serverLoad=Templating.ServerLoad.WhenChanged>
type TrainingsTemplate = Templating.Template<"../Hosted/trainings.html", serverLoad=Templating.ServerLoad.WhenChanged>
type BlogListTemplate = Templating.Template<"../Hosted/bloglist.html", serverLoad=Templating.ServerLoad.WhenChanged>
type UserBlogListTemplate = Templating.Template<"../Hosted/userbloglist.html", serverLoad=Templating.ServerLoad.WhenChanged>
type BlogPostTemplate = Templating.Template<"../Hosted/blogpost.html", serverLoad=Templating.ServerLoad.WhenChanged>
type ContactTemplate = Templating.Template<"../Hosted/contact.html", serverLoad=Templating.ServerLoad.WhenChanged>
type LegalTemplate = Templating.Template<"../Hosted/legal.html", serverLoad=Templating.ServerLoad.WhenChanged>
Expand Down Expand Up @@ -1006,11 +1014,78 @@ module Site =
.Cookie(Cookies.Banner false)
.Doc()
|> Content.Page
let USERBLOG_LISTING_NO_PAGING user f =
let templateFile = Path.Combine (__SOURCE_DIRECTORY__, sprintf @"../Hosted/userblog-%s.html" user)
if File.Exists templateFile then
UserBlogListTemplate(templateFile)
else
UserBlogListTemplate()
|> fun template ->
let name =
if String.IsNullOrEmpty(user) then
config.Value.MasterUserDisplayName
else
config.Value.Users.[user]
template
.Menubar(menubar config.Value)
.AuthorName(name)
.AuthorRSSUrl(Urls.RSS_URL user)
.ArticleList(Map.filter f articles.Value |> ARTICLES)
.Pagination(Doc.Empty)
.Footer(MainTemplate.Footer().Doc())
.Cookie(Cookies.Banner false)
.Doc()
|> Content.Page
let REDIRECT_TO (url: string) =
RedirectTemplate()
.Url(url)
.Doc()
|> Content.Page
let ARTICLES_BY_USEROPT (userOpt: string option) =
articles.Value |> Map.toList
// Filter by user, if given
|> List.filter (fun ((user, _), _) -> if userOpt.IsSome then user = userOpt.Value else true)
|> List.sortByDescending (fun (_, article: Article) -> article.Date.Ticks)
let ATOM_FEED userOpt =
let ns = XNamespace.Get "http://www.w3.org/2005/Atom"
let articles = ARTICLES_BY_USEROPT userOpt
X (ns + "feed") [] [
X (ns + "title") [] [TEXT config.Value.Title]
X (ns + "subtitle") [] [TEXT config.Value.Description]
X (ns + "link") ["href" => config.Value.ServerUrl] []
X (ns + "updated") [] [Helpers.ATOM_DATE DateTime.UtcNow]
for ((user, slug), article) in articles do
X (ns + "entry") [] [
X (ns + "title") [] [TEXT article.Title]
X (ns + "link") ["href" => config.Value.ServerUrl + Urls.POST_URL (user, slug)] []
X (ns + "id") [] [TEXT (user+slug)]
for category in article.Categories do
X (ns + "category") [] [TEXT category]
X (ns + "summary") [] [TEXT article.Abstract]
X (ns + "updated") [] [TEXT <| Helpers.ATOM_DATE article.Date]
]
]
let RSS_FEED userOpt =
let articles = ARTICLES_BY_USEROPT userOpt
X (N "rss") ["version" => "2.0"] [
X (N "channel") [] [
X (N "title") [] [TEXT config.Value.Title]
X (N "description") [] [TEXT config.Value.Description]
X (N "link") [] [TEXT config.Value.ServerUrl]
X (N "lastBuildDate") [] [Helpers.RSS_DATE DateTime.UtcNow]
for ((user, slug), article) in articles do
X (N "item") [] [
X (N "title") [] [TEXT article.Title]
X (N "link") [] [TEXT <| config.Value.ServerUrl + Urls.POST_URL (user, slug)]
X (N "guid") ["isPermaLink" => "false"] [TEXT (user+slug)]
for category in article.Categories do
X (N "category") [] [TEXT category]
X (N "description") [] [TEXT article.Abstract]
X (N "pubDate") [] [TEXT <| Helpers.RSS_DATE article.Date]
]
]
]

Application.MultiPage (fun (ctx: Context<_>) -> function
| Trainings ->
TRAININGS ()
Expand Down Expand Up @@ -1050,10 +1125,7 @@ module Site =
ARTICLE ("", p)
// All articles by a given user
| UserArticle (user, "") ->
BLOG_LISTING_NO_PAGING
<| BlogListTemplate.BlogCategoryBanner()
.Category(user)
.Doc()
USERBLOG_LISTING_NO_PAGING user
<| fun (u, _) _ -> user = u
| UserArticle (user, p) ->
ARTICLE (user, p)
Expand Down Expand Up @@ -1083,54 +1155,31 @@ module Site =
Status = Http.Status.Ok,
Headers = [Http.Header.Custom "content-type" "application/atom+xml"],
WriteBody = fun stream ->
let ns = XNamespace.Get "http://www.w3.org/2005/Atom"
let articles =
articles.Value |> Map.toList |> List.sortByDescending (fun (_, article: Article) -> article.Date.Ticks)
let doc =
X (ns + "feed") [] [
X (ns + "title") [] [TEXT config.Value.Title]
X (ns + "subtitle") [] [TEXT config.Value.Description]
X (ns + "link") ["href" => config.Value.ServerUrl] []
X (ns + "updated") [] [Helpers.ATOM_DATE DateTime.UtcNow]
for ((user, slug), article) in articles do
X (ns + "entry") [] [
X (ns + "title") [] [TEXT article.Title]
X (ns + "link") ["href" => config.Value.ServerUrl + Urls.POST_URL (user, slug)] []
X (ns + "id") [] [TEXT (user+slug)]
for category in article.Categories do
X (ns + "category") [] [TEXT category]
X (ns + "summary") [] [TEXT article.Abstract]
X (ns + "updated") [] [TEXT <| Helpers.ATOM_DATE article.Date]
]
]
let doc = ATOM_FEED None
doc.Save(stream)
)
| AtomFeedForUser user ->
Content.Custom (
Status = Http.Status.Ok,
Headers = [Http.Header.Custom "content-type" "application/atom+xml"],
WriteBody = fun stream ->
let doc = ATOM_FEED (Some user)
doc.Save(stream)
)
| RSSFeed ->
Content.Custom (
Status = Http.Status.Ok,
Headers = [Http.Header.Custom "content-type" "application/rss+xml"],
WriteBody = fun stream ->
let articles =
articles.Value |> Map.toList |> List.sortByDescending (fun (_, article: Article) -> article.Date.Ticks)
let doc =
X (N "rss") ["version" => "2.0"] [
X (N "channel") [] [
X (N "title") [] [TEXT config.Value.Title]
X (N "description") [] [TEXT config.Value.Description]
X (N "link") [] [TEXT config.Value.ServerUrl]
X (N "lastBuildDate") [] [Helpers.RSS_DATE DateTime.UtcNow]
for ((user, slug), article) in articles do
X (N "item") [] [
X (N "title") [] [TEXT article.Title]
X (N "link") [] [TEXT <| config.Value.ServerUrl + Urls.POST_URL (user, slug)]
X (N "guid") ["isPermaLink" => "false"] [TEXT (user+slug)]
for category in article.Categories do
X (N "category") [] [TEXT category]
X (N "description") [] [TEXT article.Abstract]
X (N "pubDate") [] [TEXT <| Helpers.RSS_DATE article.Date]
]
]
]
let doc = RSS_FEED None
doc.Save(stream)
)
| RSSFeedForUser user ->
Content.Custom (
Status = Http.Status.Ok,
Headers = [Http.Header.Custom "content-type" "application/rss+xml"],
WriteBody = fun stream ->
let doc = RSS_FEED (Some user)
doc.Save(stream)
)
| Refresh ->
Expand Down Expand Up @@ -1221,6 +1270,9 @@ type Website() =
// Generate the RSS/Atom feeds
RSSFeed
AtomFeed
for user in users do
RSSFeedForUser user
AtomFeedForUser user
// Generate 404 page
Error404
// Generate legal pages
Expand Down
35 changes: 35 additions & 0 deletions src/Hosted/assets/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -370,3 +370,38 @@ pre:not(.hljs) {
border-color: #464646;
color: #FFF;
}

.form-ajax .success-box, .form-ajax .error-box {
display: none;
margin-top: 20px;
}
.user-information {
text-align: center;
margin-top: 60px;
}

.user-name {
font-size: 400%;
margin-bottom: 20px;
line-height: 100%;
color: black;
}

.user-social i {
color: rgb(102, 102, 102);
}

.user-top a:not(:first-child) {
margin-left: 10px;
}

.user-profile-picture {
max-width: 200px;
margin-bottom: 20px;
margin-left: auto;
margin-right: auto;
}

.user-profile-picture img {
border-radius: 50%;
}
4 changes: 4 additions & 0 deletions src/Hosted/themekit/media/icons/fontawesome/icons.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,8 @@

.fa-spinner:before {
content: "\f110";
}

.fa-rss:before {
content: "\f09e";
}
Loading

0 comments on commit 0bf8cae

Please sign in to comment.