WIP on saving uploads (#2)
This commit is contained in:
parent
feada6f11f
commit
9307ace24a
|
@ -878,6 +878,15 @@ type SettingsModel =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// View model for uploading a file
|
||||||
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
|
type UploadFileModel =
|
||||||
|
{ /// The upload destination
|
||||||
|
destination : string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// A message displayed to the user
|
||||||
[<CLIMutable; NoComparison; NoEquality>]
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
type UserMessage =
|
type UserMessage =
|
||||||
{ /// The level of the message
|
{ /// The level of the message
|
||||||
|
|
|
@ -223,8 +223,8 @@ let register () =
|
||||||
Template.RegisterTag<UserLinksTag> "user_links"
|
Template.RegisterTag<UserLinksTag> "user_links"
|
||||||
|
|
||||||
[ // Domain types
|
[ // Domain types
|
||||||
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>; typeof<RssOptions>
|
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>
|
||||||
typeof<TagMap>; typeof<WebLog>
|
typeof<RssOptions>; typeof<TagMap>; typeof<UploadDestination>; typeof<WebLog>
|
||||||
// View models
|
// View models
|
||||||
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
|
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
|
||||||
typeof<DisplayUpload>; typeof<EditCategoryModel>; typeof<EditCustomFeedModel>; typeof<EditPageModel>
|
typeof<DisplayUpload>; typeof<EditCategoryModel>; typeof<EditCustomFeedModel>; typeof<EditPageModel>
|
||||||
|
|
|
@ -107,11 +107,11 @@ module Asset =
|
||||||
|
|
||||||
/// The primary myWebLog router
|
/// The primary myWebLog router
|
||||||
let router : HttpHandler = choose [
|
let router : HttpHandler = choose [
|
||||||
GET >=> choose [
|
GET_HEAD >=> choose [
|
||||||
route "/" >=> Post.home
|
route "/" >=> Post.home
|
||||||
]
|
]
|
||||||
subRoute "/admin" (requireUser >=> choose [
|
subRoute "/admin" (requireUser >=> choose [
|
||||||
GET >=> choose [
|
GET_HEAD >=> choose [
|
||||||
subRoute "/categor" (choose [
|
subRoute "/categor" (choose [
|
||||||
route "ies" >=> Admin.listCategories
|
route "ies" >=> Admin.listCategories
|
||||||
route "ies/bare" >=> Admin.listCategoriesBare
|
route "ies/bare" >=> Admin.listCategoriesBare
|
||||||
|
@ -145,6 +145,7 @@ let router : HttpHandler = choose [
|
||||||
route "/theme/update" >=> Admin.themeUpdatePage
|
route "/theme/update" >=> Admin.themeUpdatePage
|
||||||
subRoute "/upload" (choose [
|
subRoute "/upload" (choose [
|
||||||
route "s" >=> Upload.list
|
route "s" >=> Upload.list
|
||||||
|
route "/new" >=> Upload.showNew
|
||||||
])
|
])
|
||||||
route "/user/edit" >=> User.edit
|
route "/user/edit" >=> User.edit
|
||||||
]
|
]
|
||||||
|
@ -176,6 +177,9 @@ let router : HttpHandler = choose [
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
route "/theme/update" >=> Admin.updateTheme
|
route "/theme/update" >=> Admin.updateTheme
|
||||||
|
subRoute "/upload" (choose [
|
||||||
|
route "/save" >=> Upload.save
|
||||||
|
])
|
||||||
route "/user/save" >=> User.save
|
route "/user/save" >=> User.save
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
|
|
|
@ -71,9 +71,13 @@ let serve (urlParts : string seq) : HttpHandler = fun next ctx -> task {
|
||||||
|
|
||||||
// ADMIN
|
// ADMIN
|
||||||
|
|
||||||
|
open System.Text.RegularExpressions
|
||||||
open DotLiquid
|
open DotLiquid
|
||||||
open MyWebLog.ViewModels
|
open MyWebLog.ViewModels
|
||||||
|
|
||||||
|
/// Turn a string into a lowercase URL-safe slug
|
||||||
|
let makeSlug it = ((Regex """\s+""").Replace ((Regex "[^A-z0-9 ]").Replace (it, ""), "-")).ToLowerInvariant ()
|
||||||
|
|
||||||
// GET /admin/uploads
|
// GET /admin/uploads
|
||||||
let list : HttpHandler = fun next ctx -> task {
|
let list : HttpHandler = fun next ctx -> task {
|
||||||
let webLog = ctx.WebLog
|
let webLog = ctx.WebLog
|
||||||
|
@ -114,3 +118,50 @@ let list : HttpHandler = fun next ctx -> task {
|
||||||
|}
|
|}
|
||||||
|> viewForTheme "admin" "upload-list" next ctx
|
|> viewForTheme "admin" "upload-list" next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET /admin/upload/new
|
||||||
|
let showNew : HttpHandler = fun next ctx -> task {
|
||||||
|
return!
|
||||||
|
Hash.FromAnonymousObject {|
|
||||||
|
csrf = csrfToken ctx
|
||||||
|
destination = UploadDestination.toString ctx.WebLog.uploads
|
||||||
|
page_title = "Upload a File"
|
||||||
|
|}
|
||||||
|
|> viewForTheme "admin" "upload-new" next ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /admin/upload/save
|
||||||
|
let save : HttpHandler = fun next ctx -> task {
|
||||||
|
if ctx.Request.HasFormContentType && ctx.Request.Form.Files.Count > 0 then
|
||||||
|
let upload = Seq.head ctx.Request.Form.Files
|
||||||
|
let fileName = String.Join ('.', makeSlug (Path.GetFileNameWithoutExtension upload.FileName),
|
||||||
|
Path.GetExtension(upload.FileName).ToLowerInvariant ())
|
||||||
|
let webLog = ctx.WebLog
|
||||||
|
let localNow = WebLog.localTime webLog DateTime.Now
|
||||||
|
let year = localNow.ToString "yyyy"
|
||||||
|
let month = localNow.ToString "MM"
|
||||||
|
let! form = ctx.BindFormAsync<UploadFileModel> ()
|
||||||
|
|
||||||
|
match UploadDestination.parse form.destination with
|
||||||
|
| Database ->
|
||||||
|
use stream = new MemoryStream ()
|
||||||
|
do! upload.CopyToAsync stream
|
||||||
|
let file =
|
||||||
|
{ id = UploadId.create ()
|
||||||
|
webLogId = webLog.id
|
||||||
|
path = Permalink $"{year}/{month}/{fileName}"
|
||||||
|
updatedOn = DateTime.UtcNow
|
||||||
|
data = stream.ToArray ()
|
||||||
|
}
|
||||||
|
do! ctx.Data.Upload.add file
|
||||||
|
| Disk ->
|
||||||
|
let fullPath = Path.Combine ("wwwroot", webLog.slug, year, month)
|
||||||
|
let _ = Directory.CreateDirectory fullPath
|
||||||
|
use stream = new FileStream (Path.Combine (fullPath, fileName), FileMode.Create)
|
||||||
|
do! upload.CopyToAsync stream
|
||||||
|
|
||||||
|
do! addMessage ctx { UserMessage.success with message = $"File uploaded to {form.destination} successfully" }
|
||||||
|
return! redirectToGet (WebLog.relativeUrl ctx.WebLog (Permalink "admin/uploads")) next ctx
|
||||||
|
else
|
||||||
|
return! RequestErrors.BAD_REQUEST "Bad request; no file present" next ctx
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ module MyWebLog.Maintenance
|
||||||
|
|
||||||
open System
|
open System
|
||||||
open System.IO
|
open System.IO
|
||||||
open System.Text.RegularExpressions
|
|
||||||
open Microsoft.Extensions.DependencyInjection
|
open Microsoft.Extensions.DependencyInjection
|
||||||
open MyWebLog.Data
|
open MyWebLog.Data
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ let private doCreateWebLog (args : string[]) (sp : IServiceProvider) = task {
|
||||||
let webLogId = WebLogId.create ()
|
let webLogId = WebLogId.create ()
|
||||||
let userId = WebLogUserId.create ()
|
let userId = WebLogUserId.create ()
|
||||||
let homePageId = PageId.create ()
|
let homePageId = PageId.create ()
|
||||||
let slug = ((Regex """\s+""").Replace ((Regex "[^A-z0-9 ]").Replace (args[2], ""), "-")).ToLowerInvariant ()
|
let slug = Handlers.Upload.makeSlug args[2]
|
||||||
|
|
||||||
do! data.WebLog.add
|
do! data.WebLog.add
|
||||||
{ WebLog.empty with
|
{ WebLog.empty with
|
||||||
|
|
31
src/admin-theme/upload-new.liquid
Normal file
31
src/admin-theme/upload-new.liquid
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<h2>{{ page_title }}</h2>
|
||||||
|
<article>
|
||||||
|
<form action="{{ "admin/upload/save" | relative_link }}"
|
||||||
|
method="post" class="container" enctype="multipart/form-data" hx-boost="false">
|
||||||
|
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-6 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="file" id="file" name="file" class="form-control" placeholder="File" required>
|
||||||
|
<label for="file">File to Upload</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 pb-3 d-flex align-self-center justify-content-around">
|
||||||
|
Destination<br>
|
||||||
|
<div class="btn-group" role="group" aria-label="Upload destination button group">
|
||||||
|
<input type="radio" name="destination" id="destination_db" class="btn-check" value="database"
|
||||||
|
{%- if destination == "database" %} checked="checked"{% endif %}>
|
||||||
|
<label class="btn btn-outline-primary" for="destination_db">Database</label>
|
||||||
|
<input type="radio" name="destination" id="destination_disk" class="btn-check" value="disk"
|
||||||
|
{%- if destination == "disk" %} checked="checked"{% endif %}>
|
||||||
|
<label class="btn btn-outline-secondary" for="destination_disk">Disk</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col text-center">
|
||||||
|
<button type="submit" class="btn btn-primary">Upload File</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</article>
|
Loading…
Reference in New Issue
Block a user