category / part of page
Category editing pages done; page list and delete are done
This commit is contained in:
parent
2e8d002e30
commit
08ee8990d3
@ -1,7 +1,15 @@
|
||||
module myWebLog.Data.Category
|
||||
|
||||
open FSharp.Interop.Dynamic
|
||||
open myWebLog.Entities
|
||||
open Rethink
|
||||
open System.Dynamic
|
||||
|
||||
/// Shorthand to get a category by Id and filter by web log Id
|
||||
let private category webLogId catId =
|
||||
table Table.Category
|
||||
|> get catId
|
||||
|> filter (fun c -> upcast c.["webLogId"].Eq(webLogId))
|
||||
|
||||
/// Sort categories by their name, with their children sorted below them, including an indent level
|
||||
let sortCategories categories =
|
||||
@ -34,3 +42,91 @@ let countCategories conn webLogId =
|
||||
|> optArg "index" "webLogId"
|
||||
|> count
|
||||
|> runAtomAsync<int> conn
|
||||
|
||||
/// Get a specific category by its Id
|
||||
let tryFindCategory conn webLogId catId : Category option =
|
||||
match category webLogId catId
|
||||
|> runAtomAsync<Category> conn
|
||||
|> box with
|
||||
| null -> None
|
||||
| cat -> Some <| unbox cat
|
||||
|
||||
/// Save a category
|
||||
let saveCategory conn webLogId (cat : Category) =
|
||||
match cat.id with
|
||||
| "new" -> let newCat = { cat with id = string <| System.Guid.NewGuid()
|
||||
webLogId = webLogId }
|
||||
table Table.Category
|
||||
|> insert newCat
|
||||
|> runResultAsync conn
|
||||
|> ignore
|
||||
newCat.id
|
||||
| _ -> let upd8 = ExpandoObject()
|
||||
upd8?name <- cat.name
|
||||
upd8?slug <- cat.slug
|
||||
upd8?description <- cat.description
|
||||
upd8?parentId <- cat.parentId
|
||||
category webLogId cat.id
|
||||
|> update upd8
|
||||
|> runResultAsync conn
|
||||
|> ignore
|
||||
cat.id
|
||||
|
||||
/// Remove a category from a given parent
|
||||
let removeCategoryFromParent conn webLogId parentId catId =
|
||||
match tryFindCategory conn webLogId parentId with
|
||||
| Some parent -> let upd8 = ExpandoObject()
|
||||
upd8?children <- parent.children
|
||||
|> List.filter (fun ch -> ch <> catId)
|
||||
category webLogId parentId
|
||||
|> update upd8
|
||||
|> runResultAsync conn
|
||||
|> ignore
|
||||
| None -> ()
|
||||
|
||||
/// Add a category to a given parent
|
||||
let addCategoryToParent conn webLogId parentId catId =
|
||||
match tryFindCategory conn webLogId parentId with
|
||||
| Some parent -> let upd8 = ExpandoObject()
|
||||
upd8?children <- catId :: parent.children
|
||||
category webLogId parentId
|
||||
|> update upd8
|
||||
|> runResultAsync conn
|
||||
|> ignore
|
||||
| None -> ()
|
||||
|
||||
/// Delete a category
|
||||
let deleteCategory conn cat =
|
||||
// Remove the category from its parent
|
||||
match cat.parentId with
|
||||
| Some parentId -> removeCategoryFromParent conn cat.webLogId parentId cat.id
|
||||
| None -> ()
|
||||
// Move this category's children to its parent
|
||||
let newParent = ExpandoObject()
|
||||
newParent?parentId <- cat.parentId
|
||||
cat.children
|
||||
|> List.iter (fun childId -> category cat.webLogId childId
|
||||
|> update newParent
|
||||
|> runResultAsync conn
|
||||
|> ignore)
|
||||
// Remove the category from posts where it is assigned
|
||||
table Table.Post
|
||||
|> getAll [| cat.webLogId |]
|
||||
|> optArg "index" "webLogId"
|
||||
|> filter (fun p -> upcast p.["categoryIds"].Contains(cat.id))
|
||||
|> runCursorAsync<Post> conn
|
||||
|> Seq.toList
|
||||
|> List.iter (fun post -> let newCats = ExpandoObject()
|
||||
newCats?categoryIds <- post.categoryIds
|
||||
|> List.filter (fun c -> c <> cat.id)
|
||||
table Table.Post
|
||||
|> get post.id
|
||||
|> update newCats
|
||||
|> runResultAsync conn
|
||||
|> ignore)
|
||||
// Now, delete the category
|
||||
table Table.Category
|
||||
|> get cat.id
|
||||
|> delete
|
||||
|> runResultAsync conn
|
||||
|> ignore
|
||||
|
@ -166,6 +166,8 @@ with
|
||||
type Category = {
|
||||
/// The Id
|
||||
id : string
|
||||
/// The Id of the web log to which this category belongs
|
||||
webLogId : string
|
||||
/// The displayed name
|
||||
name : string
|
||||
/// The slug (used in category URLs)
|
||||
@ -180,7 +182,8 @@ type Category = {
|
||||
with
|
||||
/// An empty category
|
||||
static member empty =
|
||||
{ id = ""
|
||||
{ id = "new"
|
||||
webLogId = ""
|
||||
name = ""
|
||||
slug = ""
|
||||
description = None
|
||||
|
@ -3,11 +3,15 @@
|
||||
open myWebLog.Entities
|
||||
open Rethink
|
||||
|
||||
/// Shorthand to get the page by its Id, filtering on web log Id
|
||||
let private page webLogId pageId =
|
||||
table Table.Page
|
||||
|> get pageId
|
||||
|> filter (fun p -> upcast p.["webLogId"].Eq(webLogId))
|
||||
|
||||
/// Get a page by its Id
|
||||
let tryFindPage conn webLogId pageId : Page option =
|
||||
match table Table.Page
|
||||
|> get pageId
|
||||
|> filter (fun p -> upcast p.["webLogId"].Eq(webLogId))
|
||||
match page webLogId pageId
|
||||
|> runAtomAsync<Page> conn
|
||||
|> box with
|
||||
| null -> None
|
||||
@ -15,9 +19,7 @@ let tryFindPage conn webLogId pageId : Page option =
|
||||
|
||||
/// Get a page by its Id (excluding revisions)
|
||||
let tryFindPageWithoutRevisions conn webLogId pageId : Page option =
|
||||
match table Table.Page
|
||||
|> get pageId
|
||||
|> filter (fun p -> upcast p.["webLogId"].Eq(webLogId))
|
||||
match page webLogId pageId
|
||||
|> without [| "revisions" |]
|
||||
|> runAtomAsync<Page> conn
|
||||
|> box with
|
||||
@ -40,3 +42,19 @@ let countPages conn webLogId =
|
||||
|> optArg "index" "webLogId"
|
||||
|> count
|
||||
|> runAtomAsync<int> conn
|
||||
|
||||
/// Get a list of all pages (excludes page text and revisions)
|
||||
let findAllPages conn webLogId =
|
||||
table Table.Page
|
||||
|> getAll [| webLogId |]
|
||||
|> orderBy (fun p -> upcast p.["title"])
|
||||
|> without [| "text"; "revisions" |]
|
||||
|> runCursorAsync<Page> conn
|
||||
|> Seq.toList
|
||||
|
||||
/// Delete a page
|
||||
let deletePage conn webLogId pageId =
|
||||
page webLogId pageId
|
||||
|> delete
|
||||
|> runResultAsync conn
|
||||
|> ignore
|
||||
|
117
src/myWebLog.Resources/Resources.Designer.cs
generated
117
src/myWebLog.Resources/Resources.Designer.cs
generated
@ -60,6 +60,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Action.
|
||||
/// </summary>
|
||||
public static string Action {
|
||||
get {
|
||||
return ResourceManager.GetString("Action", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Added.
|
||||
/// </summary>
|
||||
@ -114,6 +123,24 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Category.
|
||||
/// </summary>
|
||||
public static string Category {
|
||||
get {
|
||||
return ResourceManager.GetString("Category", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure you wish to delete the category.
|
||||
/// </summary>
|
||||
public static string CategoryDeleteWarning {
|
||||
get {
|
||||
return ResourceManager.GetString("CategoryDeleteWarning", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Dashboard.
|
||||
/// </summary>
|
||||
@ -141,6 +168,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Description.
|
||||
/// </summary>
|
||||
public static string Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit.
|
||||
/// </summary>
|
||||
@ -177,6 +213,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Last Updated.
|
||||
/// </summary>
|
||||
public static string LastUpdated {
|
||||
get {
|
||||
return ResourceManager.GetString("LastUpdated", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to List All.
|
||||
/// </summary>
|
||||
@ -204,6 +249,33 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Deleted category {0} successfully.
|
||||
/// </summary>
|
||||
public static string MsgCategoryDeleted {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgCategoryDeleted", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} category successfully.
|
||||
/// </summary>
|
||||
public static string MsgCategoryEditSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgCategoryEditSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Deleted page successfully.
|
||||
/// </summary>
|
||||
public static string MsgPageDeleted {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgPageDeleted", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0}{1} post successfully.
|
||||
/// </summary>
|
||||
@ -213,6 +285,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name.
|
||||
/// </summary>
|
||||
public static string Name {
|
||||
get {
|
||||
return ResourceManager.GetString("Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Newer Posts.
|
||||
/// </summary>
|
||||
@ -231,6 +312,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No Parent.
|
||||
/// </summary>
|
||||
public static string NoParent {
|
||||
get {
|
||||
return ResourceManager.GetString("NoParent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Older Posts.
|
||||
/// </summary>
|
||||
@ -240,6 +330,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure you wish to delete the page.
|
||||
/// </summary>
|
||||
public static string PageDeleteWarning {
|
||||
get {
|
||||
return ResourceManager.GetString("PageDeleteWarning", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Page #.
|
||||
/// </summary>
|
||||
@ -258,6 +357,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Parent Category.
|
||||
/// </summary>
|
||||
public static string ParentCategory {
|
||||
get {
|
||||
return ResourceManager.GetString("ParentCategory", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Permalink.
|
||||
/// </summary>
|
||||
@ -348,6 +456,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Slug.
|
||||
/// </summary>
|
||||
public static string Slug {
|
||||
get {
|
||||
return ResourceManager.GetString("Slug", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to starting with.
|
||||
/// </summary>
|
||||
|
@ -234,4 +234,43 @@
|
||||
<data name="View" xml:space="preserve">
|
||||
<value>View</value>
|
||||
</data>
|
||||
<data name="Action" xml:space="preserve">
|
||||
<value>Action</value>
|
||||
</data>
|
||||
<data name="Category" xml:space="preserve">
|
||||
<value>Category</value>
|
||||
</data>
|
||||
<data name="CategoryDeleteWarning" xml:space="preserve">
|
||||
<value>Are you sure you wish to delete the category</value>
|
||||
</data>
|
||||
<data name="Description" xml:space="preserve">
|
||||
<value>Description</value>
|
||||
</data>
|
||||
<data name="LastUpdated" xml:space="preserve">
|
||||
<value>Last Updated</value>
|
||||
</data>
|
||||
<data name="MsgCategoryDeleted" xml:space="preserve">
|
||||
<value>Deleted category {0} successfully</value>
|
||||
</data>
|
||||
<data name="MsgCategoryEditSuccess" xml:space="preserve">
|
||||
<value>{0} category successfully</value>
|
||||
</data>
|
||||
<data name="MsgPageDeleted" xml:space="preserve">
|
||||
<value>Deleted page successfully</value>
|
||||
</data>
|
||||
<data name="Name" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="NoParent" xml:space="preserve">
|
||||
<value>No Parent</value>
|
||||
</data>
|
||||
<data name="PageDeleteWarning" xml:space="preserve">
|
||||
<value>Are you sure you wish to delete the page</value>
|
||||
</data>
|
||||
<data name="ParentCategory" xml:space="preserve">
|
||||
<value>Parent Category</value>
|
||||
</data>
|
||||
<data name="Slug" xml:space="preserve">
|
||||
<value>Slug</value>
|
||||
</data>
|
||||
</root>
|
91
src/myWebLog.Web/CategoryModule.fs
Normal file
91
src/myWebLog.Web/CategoryModule.fs
Normal file
@ -0,0 +1,91 @@
|
||||
namespace myWebLog
|
||||
|
||||
open myWebLog.Data.Category
|
||||
open myWebLog.Entities
|
||||
open Nancy
|
||||
open Nancy.ModelBinding
|
||||
open Nancy.Security
|
||||
open RethinkDb.Driver.Net
|
||||
|
||||
/// Handle /category and /categories URLs
|
||||
type CategoryModule(conn : IConnection) as this =
|
||||
inherit NancyModule()
|
||||
|
||||
do
|
||||
this.Get .["/categories" ] <- fun _ -> upcast this.CategoryList ()
|
||||
this.Get .["/category/{id}/edit" ] <- fun parms -> upcast this.EditCategory (downcast parms)
|
||||
this.Post .["/category/{id}/edit" ] <- fun parms -> upcast this.SaveCategory (downcast parms)
|
||||
this.Delete.["/category/{id}/delete"] <- fun parms -> upcast this.DeleteCategory (downcast parms)
|
||||
|
||||
/// Display a list of categories
|
||||
member this.CategoryList () =
|
||||
this.RequiresAccessLevel AuthorizationLevel.Administrator
|
||||
let model = CategoryListModel(this.Context, this.WebLog,
|
||||
(getAllCategories conn this.WebLog.id
|
||||
|> List.map (fun cat -> IndentedCategory.create cat (fun _ -> false))))
|
||||
this.View.["/admin/category/list", model]
|
||||
|
||||
/// Edit a category
|
||||
member this.EditCategory (parameters : DynamicDictionary) =
|
||||
this.RequiresAccessLevel AuthorizationLevel.Administrator
|
||||
let catId : string = downcast parameters.["id"]
|
||||
match (match catId with
|
||||
| "new" -> Some Category.empty
|
||||
| _ -> tryFindCategory conn this.WebLog.id catId) with
|
||||
| Some cat -> let model = CategoryEditModel(this.Context, this.WebLog, cat)
|
||||
let cats = getAllCategories conn this.WebLog.id
|
||||
|> List.map (fun cat -> IndentedCategory.create cat
|
||||
(fun c -> c = defaultArg (fst cat).parentId ""))
|
||||
model.categories <- getAllCategories conn this.WebLog.id
|
||||
|> List.map (fun cat -> IndentedCategory.create cat
|
||||
(fun c -> c = defaultArg (fst cat).parentId ""))
|
||||
this.View.["admin/category/edit", model]
|
||||
| None -> this.NotFound ()
|
||||
|
||||
/// Save a category
|
||||
member this.SaveCategory (parameters : DynamicDictionary) =
|
||||
this.ValidateCsrfToken ()
|
||||
this.RequiresAccessLevel AuthorizationLevel.Administrator
|
||||
let catId : string = downcast parameters.["id"]
|
||||
let form = this.Bind<CategoryForm> ()
|
||||
let oldCat = match catId with
|
||||
| "new" -> Some Category.empty
|
||||
| _ -> tryFindCategory conn this.WebLog.id catId
|
||||
match oldCat with
|
||||
| Some old -> let cat = { old with name = form.name
|
||||
slug = form.slug
|
||||
description = match form.description with | "" -> None | d -> Some d
|
||||
parentId = match form.parentId with | "" -> None | p -> Some p }
|
||||
let newCatId = saveCategory conn this.WebLog.id cat
|
||||
match old.parentId = cat.parentId with
|
||||
| true -> ()
|
||||
| _ -> match old.parentId with
|
||||
| Some parentId -> removeCategoryFromParent conn this.WebLog.id parentId newCatId
|
||||
| None -> ()
|
||||
match cat.parentId with
|
||||
| Some parentId -> addCategoryToParent conn this.WebLog.id parentId newCatId
|
||||
| None -> ()
|
||||
let model = MyWebLogModel(this.Context, this.WebLog)
|
||||
{ level = Level.Info
|
||||
message = System.String.Format
|
||||
(Resources.MsgCategoryEditSuccess,
|
||||
(match catId with | "new" -> Resources.Added | _ -> Resources.Updated))
|
||||
details = None }
|
||||
|> model.addMessage
|
||||
this.Redirect (sprintf "/category/%s/edit" newCatId) model
|
||||
| None -> this.NotFound ()
|
||||
|
||||
/// Delete a category
|
||||
member this.DeleteCategory (parameters : DynamicDictionary) =
|
||||
this.ValidateCsrfToken ()
|
||||
this.RequiresAccessLevel AuthorizationLevel.Administrator
|
||||
let catId : string = downcast parameters.["id"]
|
||||
match tryFindCategory conn this.WebLog.id catId with
|
||||
| Some cat -> deleteCategory conn cat
|
||||
let model = MyWebLogModel(this.Context, this.WebLog)
|
||||
{ level = Level.Info
|
||||
message = System.String.Format(Resources.MsgCategoryDeleted, cat.name)
|
||||
details = None }
|
||||
|> model.addMessage
|
||||
this.Redirect "/categories" model
|
||||
| None -> this.NotFound ()
|
39
src/myWebLog.Web/PageModule.fs
Normal file
39
src/myWebLog.Web/PageModule.fs
Normal file
@ -0,0 +1,39 @@
|
||||
namespace myWebLog
|
||||
|
||||
open myWebLog.Data.Page
|
||||
open myWebLog.Entities
|
||||
open Nancy
|
||||
open Nancy.Security
|
||||
open RethinkDb.Driver.Net
|
||||
|
||||
/// Handle /pages and /page URLs
|
||||
type PageModule(conn : IConnection) as this =
|
||||
inherit NancyModule()
|
||||
|
||||
do
|
||||
this.Get .["/pages" ] <- fun _ -> upcast this.PageList ()
|
||||
this.Delete.["/page/{id}/delete"] <- fun parms -> upcast this.DeletePage (downcast parms)
|
||||
|
||||
/// List all pages
|
||||
member this.PageList () =
|
||||
this.RequiresAccessLevel AuthorizationLevel.Administrator
|
||||
let model = PagesModel(this.Context, this.WebLog, findAllPages conn this.WebLog.id)
|
||||
model.pageTitle <- Resources.Pages
|
||||
this.View.["admin/page/list", model]
|
||||
|
||||
// TODO: edit goes here!
|
||||
|
||||
/// Delete a page
|
||||
member this.DeletePage (parameters : DynamicDictionary) =
|
||||
this.ValidateCsrfToken ()
|
||||
this.RequiresAccessLevel AuthorizationLevel.Administrator
|
||||
let pageId : string = downcast parameters.["id"]
|
||||
match tryFindPageWithoutRevisions conn this.WebLog.id pageId with
|
||||
| Some page -> deletePage conn page.webLogId page.id
|
||||
let model = MyWebLogModel(this.Context, this.WebLog)
|
||||
{ level = Level.Info
|
||||
message = Resources.MsgPageDeleted
|
||||
details = None }
|
||||
|> model.addMessage
|
||||
this.Redirect "/pages" model
|
||||
| None -> this.NotFound ()
|
@ -92,6 +92,60 @@ type DashboardModel(ctx, webLog) =
|
||||
member val categories = 0 with get, set
|
||||
|
||||
|
||||
// ---- Category models ----
|
||||
|
||||
type IndentedCategory = {
|
||||
category : Category
|
||||
indent : int
|
||||
selected : bool
|
||||
}
|
||||
with
|
||||
/// Create an indented category
|
||||
static member create (cat : Category * int) (isSelected : string -> bool) =
|
||||
{ category = fst cat
|
||||
indent = snd cat
|
||||
selected = isSelected (fst cat).id }
|
||||
/// Display name for a category on the list page, complete with indents
|
||||
member this.listName = sprintf "%s%s" (String.replicate this.indent " ઻ ") this.category.name
|
||||
/// Display for this category as an option within a select box
|
||||
member this.option =
|
||||
seq {
|
||||
yield sprintf "<option value=\"%s\"" this.category.id
|
||||
yield (match this.selected with | true -> """ selected="selected">""" | _ -> ">")
|
||||
yield String.replicate this.indent " "
|
||||
yield this.category.name
|
||||
yield "</option>"
|
||||
}
|
||||
|> String.concat ""
|
||||
|
||||
/// Model for the list of categories
|
||||
type CategoryListModel(ctx, webLog, categories) =
|
||||
inherit MyWebLogModel(ctx, webLog)
|
||||
/// The categories
|
||||
member this.categories : IndentedCategory list = categories
|
||||
|
||||
|
||||
/// Form for editing a category
|
||||
type CategoryForm(category : Category) =
|
||||
new() = CategoryForm(Category.empty)
|
||||
/// The name of the category
|
||||
member val name = category.name with get, set
|
||||
/// The slug of the category (used in category URLs)
|
||||
member val slug = category.slug with get, set
|
||||
/// The description of the category
|
||||
member val description = defaultArg category.description "" with get, set
|
||||
/// The parent category for this one
|
||||
member val parentId = defaultArg category.parentId "" with get, set
|
||||
|
||||
/// Model for editing a category
|
||||
type CategoryEditModel(ctx, webLog, category) =
|
||||
inherit MyWebLogModel(ctx, webLog)
|
||||
/// The form with the category information
|
||||
member val form = CategoryForm(category) with get, set
|
||||
/// The categories
|
||||
member val categories : IndentedCategory list = List.empty with get, set
|
||||
|
||||
|
||||
// ---- Page models ----
|
||||
|
||||
/// Model for page display
|
||||
@ -102,6 +156,12 @@ type PageModel(ctx, webLog, page) =
|
||||
member this.page : Page = page
|
||||
|
||||
|
||||
/// Model for page list display
|
||||
type PagesModel(ctx, webLog, pages) =
|
||||
inherit MyWebLogModel(ctx, webLog)
|
||||
/// The pages
|
||||
member this.pages : Page list = pages
|
||||
|
||||
// ---- Post models ----
|
||||
|
||||
/// Model for post display
|
||||
|
@ -55,6 +55,8 @@
|
||||
<Compile Include="ViewModels.fs" />
|
||||
<Compile Include="ModuleExtensions.fs" />
|
||||
<Compile Include="AdminModule.fs" />
|
||||
<Compile Include="CategoryModule.fs" />
|
||||
<Compile Include="PageModule.fs" />
|
||||
<Compile Include="PostModule.fs" />
|
||||
<Compile Include="App.fs" />
|
||||
<Content Include="packages.config" />
|
||||
|
@ -69,7 +69,10 @@
|
||||
<Content Include="content\scripts\tinymce-init.js" />
|
||||
<Content Include="content\styles\admin.css" />
|
||||
<Content Include="views\admin\admin-layout.html" />
|
||||
<Content Include="views\admin\category\edit.html" />
|
||||
<Content Include="views\admin\category\list.html" />
|
||||
<Content Include="views\admin\dashboard.html" />
|
||||
<Content Include="views\admin\page\list.html" />
|
||||
<Content Include="views\admin\post\edit.html" />
|
||||
<Content Include="views\admin\post\list.html" />
|
||||
<Content Include="views\default\index-content.html" />
|
||||
|
52
src/myWebLog/views/admin/category/edit.html
Normal file
52
src/myWebLog/views/admin/category/edit.html
Normal file
@ -0,0 +1,52 @@
|
||||
@Master['admin/admin-layout']
|
||||
|
||||
@Section['Content']
|
||||
<form action="/category/@Model.category.id/edit" method="post">
|
||||
@AntiForgeryToken
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="name">@Translate.Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="@Model.form.name" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="slug">@Translate.Slug</label>
|
||||
<input type="text" class="form-control" id="slug" name="slug" value="@Model.form.slug}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="description">@Translate.Description</label>
|
||||
<textarea class="form-control" rows="4" id="description" name="description">@Model.form.description</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="parentId">@Translate.ParentCategory</label>
|
||||
<select class="form-control" id="parentId" name="parentId">
|
||||
<option value="">— @Translate.NoParent —</option>
|
||||
@Each.categories
|
||||
@Current.option
|
||||
@EndEach
|
||||
</select>
|
||||
</div>
|
||||
<br />
|
||||
<p class="text-center">
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<i class="fa fa-floppy-o"></i> @Translate.Save
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@EndSection
|
||||
|
||||
@Section['Scripts']
|
||||
<script type="text/javascript">
|
||||
/* <![CDATA[ */
|
||||
$(document).ready(function () { $("#name").focus() })
|
||||
/* ]] */
|
||||
</script>
|
||||
@EndSection
|
44
src/myWebLog/views/admin/category/list.html
Normal file
44
src/myWebLog/views/admin/category/list.html
Normal file
@ -0,0 +1,44 @@
|
||||
@Master['admin/admin-layout']
|
||||
|
||||
@Section['Content']
|
||||
<div class="row">
|
||||
<p><a class="btn btn-primary" href="/category/new/edit"><i class="fa fa-plus"></i> @Translate.AddNew"</a></p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>@Translate.Action</th>
|
||||
<th>@Translate.Category</th>
|
||||
<th>@Translate.Description</th>
|
||||
</tr>
|
||||
@Each.categories
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/category/@Current.category.id/edit">@Translate.Edit</a>
|
||||
<a href="javascript:void(0)" onclick="deleteCategory('@Current.category.id', '@Current.category.name')">
|
||||
@Translate.Delete
|
||||
</a>
|
||||
</td>
|
||||
<td>@Current.listName</td>
|
||||
<td>@Current.category.description</td>
|
||||
</tr>
|
||||
@EndEach
|
||||
</table>
|
||||
</div>
|
||||
<form method="delete" id="deleteForm">
|
||||
@AntiForgeryToken
|
||||
</form>
|
||||
@EndSection
|
||||
|
||||
@Section['Scripts']
|
||||
<script type="text/javascript">
|
||||
/* <![CDATA[ */
|
||||
function deleteCategory(id, title) {
|
||||
if (confirm('@Translate.CategoryDeleteWarning "' + title + '"?')) {
|
||||
document.getElementById("deleteForm").action = "/category/" + id + "/delete"
|
||||
document.getElementById("deleteForm").submit()
|
||||
}
|
||||
}
|
||||
/* ]] */
|
||||
</script>
|
||||
@EndSection
|
@ -13,7 +13,7 @@
|
||||
<div class="col-xs-6">
|
||||
<h3>@Translate.Pages <span class="badge">@Model.pages</span></h3>
|
||||
<p>
|
||||
<a href="/pages/list"><i class="fa fa-list-ul"></i> @Translate.ListAll</a>
|
||||
<a href="/pages"><i class="fa fa-list-ul"></i> @Translate.ListAll</a>
|
||||
|
||||
<a href="/page/new/edit"><i class="fa fa-plus"></i> @Translate.AddNew</a>
|
||||
</p>
|
||||
@ -23,7 +23,7 @@
|
||||
<div class="col-xs-6">
|
||||
<h3>@Translate.Categories <span class="badge">@Model.categories</span></h3>
|
||||
<p>
|
||||
<a href="/categories/list"><i class="fa fa-list.ul"></i> @Translate.ListAll</a>
|
||||
<a href="/categories"><i class="fa fa-list.ul"></i> @Translate.ListAll</a>
|
||||
|
||||
<a href="/category/new/edit"><i class="fa fa-plus"></i> @Translate.AddNew</a>
|
||||
</p>
|
||||
|
47
src/myWebLog/views/admin/page/list.html
Normal file
47
src/myWebLog/views/admin/page/list.html
Normal file
@ -0,0 +1,47 @@
|
||||
@Master['admin/admin-layout']
|
||||
|
||||
@Section['Content']
|
||||
<div class="row">
|
||||
<p><a class="btn btn-primary" href="/page/new/edit"><i class="fa fa-plus"></i> @Translate.AddNew</a></p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>@Translate.Title</th>
|
||||
<th>@Translate.LastUpdated</th>
|
||||
</tr>
|
||||
@Each.pages
|
||||
<tr>
|
||||
<td>
|
||||
@Current.title<br />
|
||||
<a href="/@Current.permalink">@Translate.View</a>
|
||||
<a href="/page/@Current.id}/edit">@Translate.Edit</a>
|
||||
<a href="javascript:void(0)" onclick="deletePage('@Current.id', '@Current.title')">@Translate.Delete</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- // TODO: make the formatting stuff work in a loop
|
||||
=theDate.format('MMM D, YYYY')
|
||||
br
|
||||
#{__("at")} #{theDate.format('h:mma')} -->
|
||||
</td>
|
||||
</tr>
|
||||
@EndEach
|
||||
</table>
|
||||
</div>
|
||||
<form method="delete" id="deleteForm">
|
||||
@AntiForgeryToken
|
||||
</form>
|
||||
@EndSection
|
||||
|
||||
@Section['Scripts']
|
||||
<script type="text/javascript">
|
||||
/* <![CDATA[ */
|
||||
function deletePage(id, title) {
|
||||
if (confirm('@Translate.PageDeleteWarning "' + title + '"?')) {
|
||||
document.getElementById("deleteForm").action = "/page/" + id + "/delete"
|
||||
document.getElementById("deleteForm").submit()
|
||||
}
|
||||
}
|
||||
/* ]] */
|
||||
</script>
|
||||
@EndSection
|
Loading…
Reference in New Issue
Block a user