Admin tweaks plus

- Version now reports the correct version
- Post edit properly renders existing selected categories as checked,
and uses the description in the title attribute if it is present
- Created light/dark transparent logos

Ready to test the functionality of the admin area next...
This commit is contained in:
Daniel J. Summers 2016-11-11 14:44:09 -06:00
parent ed9b8adc1c
commit 0d54fc2ec3
17 changed files with 81 additions and 65 deletions

View File

@ -130,7 +130,7 @@ type MyWebLogBootstrapper() =
let version =
let v = typeof<AppConfig>.GetType().GetTypeInfo().Assembly.GetName().Version
let v = typeof<AppConfig>.GetTypeInfo().Assembly.GetName().Version
match v.Build with
| 0 -> match v.Minor with 0 -> string v.Major | _ -> sprintf "%d.%d" v.Major v.Minor
| _ -> sprintf "%d.%d.%d" v.Major v.Minor v.Build

View File

@ -1,4 +1,4 @@
namespace myWebLog.Web.AssemblyInfo
namespace MyWebLog.AssemblyInfo
open System.Reflection
open System.Runtime.CompilerServices
@ -14,7 +14,7 @@ open System.Runtime.InteropServices
[<assembly: AssemblyCulture("")>]
[<assembly: ComVisible(false)>]
[<assembly: Guid("e6ee110a-27a6-4a19-b0cb-d24f48f71b53")>]
[<assembly: AssemblyVersion("0.9.1.0")>]
[<assembly: AssemblyVersion("0.9.2.0")>]
[<assembly: AssemblyFileVersion("1.0.0.0")>]
do

View File

@ -103,18 +103,18 @@ type PostModule(data : IMyWebLogData, clock : IClock) as this =
upcast this.Response.FromStream(stream, sprintf "application/%s+xml" format) *)
do
this.Get ("/", fun _ -> this.HomePage ())
this.Get ("/{permalink*}", fun parms -> this.CatchAll (downcast parms))
this.Get ("/posts/page/{page:int}", fun parms -> this.PublishedPostsPage (getPage <| downcast parms))
this.Get ("/category/{slug}", fun parms -> this.CategorizedPosts (downcast parms))
this.Get ("/category/{slug}/page/{page:int}", fun parms -> this.CategorizedPosts (downcast parms))
this.Get ("/tag/{tag}", fun parms -> this.TaggedPosts (downcast parms))
this.Get ("/tag/{tag}/page/{page:int}", fun parms -> this.TaggedPosts (downcast parms))
this.Get ("/feed", fun _ -> this.Feed ())
this.Get ("/posts/list", fun _ -> this.PostList 1)
this.Get ("/posts/list/page/{page:int}", fun parms -> this.PostList (getPage <| downcast parms))
this.Get ("/post/{postId}/edit", fun parms -> this.EditPost (downcast parms))
this.Post ("/post/{postId}/edit", fun parms -> this.SavePost (downcast parms))
this.Get ("/", fun _ -> this.HomePage ())
this.Get ("/{permalink*}", fun p -> this.CatchAll (downcast p))
this.Get ("/posts/page/{page:int}", fun p -> this.PublishedPostsPage (getPage <| downcast p))
this.Get ("/category/{slug}", fun p -> this.CategorizedPosts (downcast p))
this.Get ("/category/{slug}/page/{page:int}", fun p -> this.CategorizedPosts (downcast p))
this.Get ("/tag/{tag}", fun p -> this.TaggedPosts (downcast p))
this.Get ("/tag/{tag}/page/{page:int}", fun p -> this.TaggedPosts (downcast p))
this.Get ("/feed", fun _ -> this.Feed ())
this.Get ("/posts/list", fun _ -> this.PostList 1)
this.Get ("/posts/list/page/{page:int}", fun p -> this.PostList (getPage <| downcast p))
this.Get ("/post/{postId}/edit", fun p -> this.EditPost (downcast p))
this.Post ("/post/{postId}/edit", fun p -> this.SavePost (downcast p))
// ---- Display posts to users ----
@ -253,11 +253,10 @@ type PostModule(data : IMyWebLogData, clock : IClock) as this =
| None -> Revision.Empty
let model = EditPostModel (this.Context, this.WebLog, post, rev)
model.Categories <- findAllCategories data this.WebLog.Id
|> List.map (fun cat -> string (fst cat).Id,
sprintf "%s%s" (String.replicate (snd cat) " &nbsp; &nbsp; ")
(fst cat).Name)
|> List.map (fun cat ->
DisplayCategory.Create cat (post.CategoryIds |> List.contains (fst cat).Id))
model.PageTitle <- Strings.get <| match post.Id with "new" -> "AddNewPost" | _ -> "EditPost"
upcast this.View.["admin/post/edit"]
upcast this.View.["admin/post/edit", model]
| _ -> this.NotFound ()
/// Save a post

View File

@ -9,7 +9,7 @@ open Newtonsoft.Json
open NodaTime
open NodaTime.Text
open System
open System.Net
/// Levels for a user message
[<RequireQualifiedAccess>]
@ -90,7 +90,7 @@ module FormatDateTime =
/// Parent view model for all myWebLog views
type MyWebLogModel (ctx : NancyContext, webLog : WebLog) =
type MyWebLogModel (ctx : NancyContext, webLog : WebLog) as this =
/// Get the messages from the session
let getMessages () =
@ -100,6 +100,20 @@ type MyWebLogModel (ctx : NancyContext, webLog : WebLog) =
| _ -> ctx.Request.Session.Delete Keys.Messages
msg
/// Generate a footer logo with the given scheme
let footerLogo scheme =
seq {
yield sprintf "<img src=\"/content/logo-%s.png\" alt=\"myWebLog\" title=\"" scheme
yield sprintf "%s %s &bull; " (Strings.get "PoweredBy") this.Generator
yield Strings.get "LoadedIn"
yield " "
yield TimeSpan(System.DateTime.Now.Ticks - this.RequestStart).TotalSeconds.ToString "f3"
yield " "
yield (Strings.get "Seconds").ToLower ()
yield "\" height=\"30\" />"
}
|> Seq.reduce (+)
/// The web log for this request
member this.WebLog = webLog
/// The subtitle for the webLog (SSVE can't do IsSome that deep)
@ -134,19 +148,11 @@ type MyWebLogModel (ctx : NancyContext, webLog : WebLog) =
| None -> this.WebLog.Name
| pt -> sprintf "%s | %s" pt this.WebLog.Name
/// An image with the version and load time in the tool tip
member this.FooterLogo =
seq {
yield "<img src=\"/default/footer-logo.png\" alt=\"myWebLog\" title=\""
yield sprintf "%s %s &bull; " (Strings.get "PoweredBy") this.Generator
yield Strings.get "LoadedIn"
yield " "
yield TimeSpan(System.DateTime.Now.Ticks - this.RequestStart).TotalSeconds.ToString "f3"
yield " "
yield (Strings.get "Seconds").ToLower ()
yield "\" />"
}
|> Seq.reduce (+)
/// An image with the version and load time in the tool tip (using light text)
member this.FooterLogoLight = footerLogo "light"
/// An image with the version and load time in the tool tip (using dark text)
member this.FooterLogoDark = footerLogo "dark"
// ---- Admin models ----
@ -399,6 +405,27 @@ type EditPostForm () =
this.Text <- rev.Text
this
/// Category information for display
type DisplayCategory = {
Id : string
Indent : string
Name : string
Description : string
IsChecked : bool
}
with
/// Create a display category
static member Create (cat : Category, indent) isChecked =
{ Id = cat.Id
Indent = String.replicate indent " &nbsp; &nbsp; "
Name = WebUtility.HtmlEncode cat.Name
IsChecked = isChecked
Description = WebUtility.HtmlEncode (match cat.Description with Some d -> d | _ -> cat.Name)
}
/// The "checked" attribute for this category
member this.CheckedAttr
with get() = match this.IsChecked with true -> "checked=\"checked\"" | _ -> ""
/// View model for the edit post page
type EditPostModel (ctx, webLog, post, revision) =
inherit MyWebLogModel (ctx, webLog)
@ -408,7 +435,7 @@ type EditPostModel (ctx, webLog, post, revision) =
/// The post being edited
member val Post = post with get, set
/// The categories to which the post may be assigned
member val Categories : (string * string) list = [] with get, set
member val Categories : DisplayCategory list = [] with get, set
/// Whether the post is currently published
member this.IsPublished = PostStatus.Published = this.Post.Status
/// The published date

View File

@ -100,9 +100,13 @@ let tryFindPost conn webLogId postId : Post option =
let! p =
r.Table(Table.Post)
.Get(postId)
.Filter(ReqlFunction1 (fun p -> upcast p.["WebLogId"].Eq webLogId))
.RunAtomAsync<Post> conn
return match box p with null -> None | post -> Some <| unbox post
return
match box p with
| null -> None
| pst ->
let post : Post = unbox pst
match post.WebLogId = webLogId with true -> Some post | _ -> None
}
|> Async.RunSynchronously

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -2,7 +2,7 @@
"buildOptions": {
"emitEntryPoint": true,
"copyToOutput": {
"include": "views"
"include": [ "views", "content" ]
}
},
"dependencies": {

View File

@ -1,5 +0,0 @@
footer {
background-color: #808080;
border-top: solid 1px black;
color: white;
}

View File

@ -40,7 +40,7 @@
<footer>
<div class="container-fluid">
<div class="row">
<div class="col-xs-12 text-right">@Model.Generator</div>
<div class="col-xs-12 text-right">@Model.FooterLogoLight &nbsp;</div>
</div>
</div>
</footer>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -2,7 +2,7 @@
@Section['Content']
<div class="row">
<div class="col-xs-6">
<div class="col-xs-4 text-center">
<h3>@Translate.Posts &nbsp;<span class="badge">@Model.Posts</span></h3>
<p>
<a href="/posts/list"><i class="fa fa-list-ul"></i> @Translate.ListAll</a>
@ -10,7 +10,7 @@
<a href="/post/new/edit"><i class="fa fa-plus"></i> @Translate.AddNew</a>
</p>
</div>
<div class="col-xs-6">
<div class="col-xs-4 text-center">
<h3>@Translate.Pages &nbsp;<span class="badge">@Model.Pages</span></h3>
<p>
<a href="/pages"><i class="fa fa-list-ul"></i> @Translate.ListAll</a>
@ -18,9 +18,7 @@
<a href="/page/new/edit"><i class="fa fa-plus"></i> @Translate.AddNew</a>
</p>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="col-xs-4 text-center">
<h3>@Translate.Categories &nbsp;<span class="badge">@Model.Categories</span></h3>
<p>
<a href="/categories"><i class="fa fa-list-ul"></i> @Translate.ListAll</a>
@ -29,4 +27,5 @@
</p>
</div>
</div>
<br />
@EndSection

View File

@ -12,7 +12,7 @@
<div class="form-group">
<label class="control-label" for="Permalink">@Translate.Permalink</label>
<input type="text" name="Permalink" id="Permalink" class="form-control" value="@Model.Form.Permalink" />
<p class="form-hint"><em>@Translate.startingWith</em> http://@Model.webLog.urlBase/ </p>
<p class="form-hint"><em>@Translate.startingWith</em> http://@Model.WebLog.UrlBase/ </p>
</div>
<!-- // TODO: Markdown / HTML choice -->
<div class="form-group">
@ -48,7 +48,7 @@
@EndSection
@Section['Scripts']
<script type="text/javascript" src="/content/scripts/tinymce-init.js"></script>
<script type="text/javascript" src="/admin/content/tinymce-init.js"></script>
<script type="text/javascript">
/* <![CDATA[ */
$(document).ready(function () { $("#Title").focus() })

View File

@ -13,7 +13,7 @@
<label class="control-label" for="Permalink">@Translate.Permalink</label>
<input type="text" name="Permalink" id="Permalink" class="form-control" value="@Model.Form.Permalink" />
<!-- // FIXME: hard-coded "http" -->
<p class="form-hint"><em>@Translate.startingWith</em> http://@Model.webLog.urlBase/ </p>
<p class="form-hint"><em>@Translate.startingWith</em> http://@Model.WebLog.UrlBase/ </p>
</div>
<!-- // TODO: Markdown / HTML choice -->
<div class="form-group">
@ -47,19 +47,11 @@
<h4 class="panel-title">@Translate.Categories</h4>
</div>
<div class="panel-body" style="max-height:350px;overflow:scroll;">
<!-- // TODO: how to check the ones that are already selected? -->
@Each.Categories
<!-- - var tab = 0
while tab < item.indent
| &nbsp; &nbsp;
- tab++
- var attributes = {}
if -1 < currentCategories.indexOf(item.category.id)
- attributes.checked = 'checked' -->
<input type="checkbox" id="Category-@Current.Item1" name="Category", value="@Current.Item1" />
@Current.Indent
<input type="checkbox" id="Category-@Current.Id" name="Category" value="@Current.Id" @Current.CheckedAttr />
&nbsp;
<!-- // FIXME: the title should be the category description -->
<label for="Category-@Current.Item1" title="@Current.Item2">@Current.Item2</label>
<label for="Category-@Current.Id" title="@Current.Description">@Current.Name</label>
<br/>
@EndEach
</div>
@ -86,7 +78,7 @@
@EndSection
@Section['Scripts']
<script type="text/javascript" src="/content/scripts/tinymce-init.js"></script>
<script type="text/javascript" src="/admin/content/tinymce-init.js"></script>
<script type="text/javascript">
/** <![CDATA[ */
$(document).ready(function () { $("#Title").focus() })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -3,7 +3,7 @@
<div class="container-fluid">
<div class="row">
<div class="col-xs-12 text-right">
@Model.FooterLogo
@Model.FooterLogoDark
</div>
</div>
</div>