a mini social media app for small communities

add post editing

Changed files
+119 -11
doc
src
+1
doc/todo.md
··· 26 - [x] post:likes/dislikes 27 - [x] post:mentioning ('tagging') other users in posts 28 - [x] post:mentioning:who mentioned you (send notifications when a user mentions you) 29 - [ ] ~~site:stylesheet (and a toggle for html-only mode)~~ 30 - replaced with per-user optional stylesheets 31 - [x] site:message of the day (admins can add a welcome message displayed on index.html)
··· 26 - [x] post:likes/dislikes 27 - [x] post:mentioning ('tagging') other users in posts 28 - [x] post:mentioning:who mentioned you (send notifications when a user mentions you) 29 + - [x] post:editing 30 - [ ] ~~site:stylesheet (and a toggle for html-only mode)~~ 31 - replaced with per-user optional stylesheets 32 - [x] site:message of the day (admins can add a welcome message displayed on index.html)
+26
src/api.v
··· 490 return ctx.text(post.title) 491 } 492 493 ////// site ////// 494 495 @['/api/site/set_motd'; post]
··· 490 return ctx.text(post.title) 491 } 492 493 + @['/api/post/edit'; post] 494 + fn (mut app App) api_post_edit(mut ctx Context, id int, title string, body string) veb.Result { 495 + user := app.whoami(mut ctx) or { 496 + ctx.error('not logged in!') 497 + return ctx.redirect('/login') 498 + } 499 + post := app.get_post_by_id(id) or { 500 + ctx.error('no such post') 501 + return ctx.redirect('/') 502 + } 503 + if post.author_id != user.id { 504 + ctx.error('insufficient permissions') 505 + return ctx.redirect('/') 506 + } 507 + 508 + sql app.db { 509 + update Post set body = body, title = title where id == id 510 + } or { 511 + eprintln('failed to update post') 512 + ctx.error('failed to update post') 513 + return ctx.redirect('/') 514 + } 515 + 516 + return ctx.redirect('/post/${id}') 517 + } 518 + 519 ////// site ////// 520 521 @['/api/site/set_motd'; post]
+12 -6
src/app.v
··· 61 return posts 62 } 63 64 - // pub fn (app &App) get_popular_posts() []Post { 65 - // posts := sql app.db { 66 - // select from Post order by likes desc limit 10 67 - // } or { [] } 68 - // return posts 69 - // } 70 71 pub fn (app &App) get_posts_from_user(user_id int) []Post { 72 posts := sql app.db {
··· 61 return posts 62 } 63 64 + pub fn (app &App) get_popular_posts() []Post { 65 + cached_likes := sql app.db { 66 + select from LikeCache order by likes desc limit 10 67 + } or { [] } 68 + posts := cached_likes.map(fn [app] (it LikeCache) Post { 69 + return app.get_post_by_id(it.post_id) or { 70 + eprintln('cached like ${it} does not have a post related to it (from get_popular_posts)') 71 + return Post{} 72 + } 73 + }).filter(it.id != 0) 74 + return posts 75 + } 76 77 pub fn (app &App) get_posts_from_user(user_id int) []Post { 78 posts := sql app.db {
+18
src/pages.v
··· 70 user := app.whoami(mut ctx) or { User{} } 71 return $veb.html() 72 }
··· 70 user := app.whoami(mut ctx) or { User{} } 71 return $veb.html() 72 } 73 + 74 + @['/post/:post_id/edit'] 75 + fn (mut app App) edit(mut ctx Context, post_id int) veb.Result { 76 + user := app.whoami(mut ctx) or { 77 + ctx.error('not logged in') 78 + return ctx.redirect('/login') 79 + } 80 + post := app.get_post_by_id(post_id) or { 81 + ctx.error('no such post') 82 + return ctx.redirect('/') 83 + } 84 + if post.author_id != user.id { 85 + ctx.error('insufficient permissions') 86 + return ctx.redirect('/post/${post_id}') 87 + } 88 + ctx.title = '${app.config.instance.name} - editing ${post.title}' 89 + return $veb.html() 90 + }
+48
src/templates/edit.html
···
··· 1 + @include 'partial/header.html' 2 + 3 + <script src="/static/js/post.js"></script> 4 + <script src="/static/js/render_body.js"></script> 5 + 6 + <h1>edit post</h1> 7 + 8 + <div class="post post-full"> 9 + <form action="/api/post/edit" method="post"> 10 + <input 11 + type="number" 12 + name="id" 13 + id="id" 14 + placeholder="post id" 15 + value="@post.id" 16 + required 17 + readonly 18 + hidden 19 + aria-hidden 20 + > 21 + <input 22 + type="text" 23 + name="title" 24 + id="title" 25 + minlength="@app.config.post.title_min_len" 26 + maxlength="@app.config.post.title_max_len" 27 + pattern="@app.config.post.title_pattern" 28 + placeholder="title" 29 + value="@post.title" 30 + required 31 + > 32 + <br> 33 + <textarea 34 + name="body" 35 + id="body" 36 + minlength="@app.config.post.body_min_len" 37 + maxlength="@app.config.post.body_max_len" 38 + rows="10" 39 + cols="30" 40 + placeholder="body" 41 + required 42 + >@post.body</textarea> 43 + <br> 44 + <input type="submit" value="save"> 45 + </form> 46 + </div> 47 + 48 + @include 'partial/footer.html'
+14 -5
src/templates/post.html
··· 9 <p><em>likes: @{app.get_net_likes_for_post(post.id)}</em></p> 10 <p><em>posted at: @post.posted_at</em></p> 11 12 @if ctx.is_logged_in() 13 <br> 14 <div> ··· 29 </div> 30 @end 31 32 - @if (ctx.is_logged_in() && user.admin) || (ctx.is_logged_in() && post.author_id == user.id) 33 - <hr> 34 - @if user.admin 35 <h4>admin powers:</h4> 36 - @else if post.author_id == user.id 37 - <h4>manage post:</h4> 38 @end 39 <form action="/api/post/delete" method="post"> 40 <input 41 type="number" ··· 50 > 51 <input type="submit" value="delete"> 52 </form> 53 @end 54 </div> 55
··· 9 <p><em>likes: @{app.get_net_likes_for_post(post.id)}</em></p> 10 <p><em>posted at: @post.posted_at</em></p> 11 12 + @if ctx.is_logged_in() && post.author_id == user.id 13 + <p><a href="/post/@{post.id}/edit">edit post</a></p> 14 + @end 15 + 16 @if ctx.is_logged_in() 17 <br> 18 <div> ··· 33 </div> 34 @end 35 36 + @if ctx.is_logged_in() && (post.author_id == user.id || user.admin) 37 + <br> 38 + <div> 39 + 40 + @if post.author_id == user.id 41 + <h4>manage post:</h4> 42 + @else if user.admin 43 <h4>admin powers:</h4> 44 @end 45 + 46 <form action="/api/post/delete" method="post"> 47 <input 48 type="number" ··· 57 > 58 <input type="submit" value="delete"> 59 </form> 60 + 61 + </div> 62 @end 63 </div> 64