first draft of posting to a topic (including preview)

This commit is contained in:
2026-01-01 22:41:43 -07:00
parent c958bc5826
commit 3eb0f6a259
5 changed files with 302 additions and 5 deletions
+114
View File
@@ -553,3 +553,117 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
} }
return "framed_template", "posts.jet", nil return "framed_template", "posts.jet", nil
} }
func PostInTopic(ctxt ui.AmContext) (string, any, error) {
// Locate community, conference, and topic.
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
var topic *database.Topic = nil
if rawTopic, err := strconv.ParseInt(ctxt.URLParam("topic"), 10, 16); err == nil {
topic, err = database.AmGetTopicByNumber(ctxt.Ctx(), conf, int16(rawTopic))
}
if topic == nil {
ctxt.SetRC(http.StatusNotFound)
return ui.ErrorPage(ctxt, fmt.Errorf("topic not found: %s", ctxt.URLParam("topic")))
}
urlStem := fmt.Sprintf("/comm/%s/conf/%s/r/%d", comm.Alias, ctxt.URLParam("confid"), topic.Number)
if ctxt.FormFieldIsSet("cancel") {
return "redirect", urlStem, nil
}
if ctxt.FormFieldIsSet("preview") {
// Preview the post.
checker, err := htmlcheck.AmNewHTMLChecker(ctxt.Ctx(), "escaper")
if err != nil {
return ui.ErrorPage(ctxt, err)
}
checker.Append(ctxt.FormField("pseud"))
checker.Finish()
v, _ := checker.Value()
ctxt.VarMap().Set("pseud", v)
// escape the data
postdata := ctxt.FormField("pb")
checker.Reset()
checker.Append(postdata)
checker.Finish()
v, _ = checker.Value()
ctxt.VarMap().Set("pb", v)
// run the preview
checker, err = htmlcheck.AmNewHTMLChecker(ctxt.Ctx(), "preview")
if err != nil {
return ui.ErrorPage(ctxt, err)
}
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.URLParam("cid"), conf.TopTopic+1))
checker.Append(postdata)
checker.Finish()
v, _ = checker.Value()
ctxt.VarMap().Set("previewPb", v)
nErr, _ := checker.Counter("spelling")
ctxt.VarMap().Set("nError", nErr)
ctxt.VarMap().Set("maxPost", ctxt.FormField("xp"))
if ctxt.FormFieldIsSet("attach") {
ctxt.VarMap().Set("attachFile", true)
}
ctxt.VarMap().Set("urlStem", urlStem)
ctxt.VarMap().Set("amsterdam_pageTitle", "Previewing Message")
return "framed_template", "preview_post.jet", nil
}
// Figure out which URL to return to once this post is made.
var returnURL string
if ctxt.FormFieldIsSet("post") {
returnURL = urlStem
} else if ctxt.FormFieldIsSet("postnext") {
returnURL = urlStem // TODO
} else if ctxt.FormFieldIsSet("posttopics") {
returnURL = fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.URLParam("confid"))
} else {
return ui.ErrorPage(ctxt, errors.New("unknown post button"))
}
maxPost, err := ctxt.FormFieldInt("xp")
if err != nil {
return ui.ErrorPage(ctxt, err)
}
if int32(maxPost) < topic.TopMessage {
// Slippage detected! Display the slipped posts and another post box.
// TODO
return "framed_template", "???", nil
}
// start by checking the title and pseud
checker, err := htmlcheck.AmNewHTMLChecker(ctxt.Ctx(), "post-pseud")
if err != nil {
return ui.ErrorPage(ctxt, err)
}
checker.Append(ctxt.FormField("pseud"))
checker.Finish()
postPseud, _ := checker.Value()
// now check the post data itself
checker, err = htmlcheck.AmNewHTMLChecker(ctxt.Ctx(), "post-body")
if err != nil {
return ui.ErrorPage(ctxt, err)
}
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.URLParam("cid"), conf.TopTopic+1))
checker.Append(ctxt.FormField("pb"))
checker.Finish()
postText, _ := checker.Value()
lines, _ := checker.Lines()
// Add the post!
hdr, err := database.AmNewPost(ctxt.Ctx(), conf, topic, ctxt.CurrentUser(), postPseud, postText, int32(lines))
if err != nil {
return ui.ErrorPage(ctxt, err)
}
if !ctxt.FormFieldIsSet("attach") {
return "redirect", returnURL, nil // no attachment - just redisplay topic list
}
// go upload the attachment
ctxt.VarMap().Set("target", returnURL)
ctxt.VarMap().Set("post", hdr.PostId)
ctxt.VarMap().Set("amsterdam_pageTitle", "Upload Attachment")
return "framed_template", "attachment_upload.jet", nil
}
+83
View File
@@ -118,3 +118,86 @@ func AmGetPostRange(ctx context.Context, topic *Topic, first, last int32) ([]*Po
} }
return rc, nil return rc, nil
} }
/* AmNewPost adds a new post to a topic.
* Parameters:
* ctx - Standard Go context value.
* conf - Pointer to conference containing the topic.
* topic - Pointer to topic.
* user - Pointer to user posting the message.
* pseud - Pseud for the new post.
* post - New post text.
* postLines - Number of lines in the post text.
* Returns:
* New post header pointer.
* Standard Go error status.
*/
func AmNewPost(ctx context.Context, conf *Conference, topic *Topic, user *User, pseud string, post string, postLines int32) (*PostHeader, error) {
success := false
tx := amdb.MustBegin()
defer func() {
if !success {
tx.Rollback()
}
}()
unlock := true
tx.ExecContext(ctx, "LOCK TABLES topics WRITE, topicsettings WRITE, posts WRITE, postdata WRITE;")
defer func() {
if unlock {
tx.ExecContext(ctx, "UNLOCK TABLES;")
}
}()
// Add the post header information.
rs, err := tx.ExecContext(ctx, "INSERT INTO posts (topicid, num, linecount, creator_uid, posted, pseud) VALUES (?, ?, ?, ?, NOW(), ?)",
topic.TopicId, topic.TopMessage+1, postLines, user.Uid, pseud)
if err != nil {
return nil, err
}
xid, err := rs.LastInsertId()
if err != nil {
return nil, err
}
// Read back the post header.
var dbdata []PostHeader
if err := tx.SelectContext(ctx, &dbdata, "SELECT * FROM posts WHERE postid = ?", xid); err != nil {
return nil, err
}
if len(dbdata) == 0 {
return nil, errors.New("AmNewPost: new post not found")
}
if len(dbdata) > 1 {
return nil, fmt.Errorf("AmNewPost: too many entries (%d) for post ID %d", len(dbdata), xid)
}
hdr := &(dbdata[0])
// Add the post data.
_, err = tx.ExecContext(ctx, "INSERT INTO postdata (postid, data) VALUES (?, ?)", int32(xid), hdr.PostId)
if err != nil {
return nil, err
}
// Update the topic.
_, err = tx.ExecContext(ctx, "UPDATE topics SET top_message = ?, lastupdate = ? WHERE topicid = ?", hdr.Num, hdr.Posted, topic.TopicId)
if err != nil {
return nil, err
}
topic.TopMessage = hdr.Num
topic.LastUpdate = hdr.Posted
tx.ExecContext(ctx, "UNLOCK TABLES;")
unlock = false
// update the "last posted" date in the conference settings
_, err = conf.TouchPost(ctx, tx, user, hdr.Posted)
if err != nil {
return nil, err
}
if err = tx.Commit(); err != nil {
return nil, err
}
success = true
return hdr, nil
}
+1
View File
@@ -99,6 +99,7 @@ func setupEcho() *echo.Echo {
confGroup.GET("/new_topic", ui.AmWrap(NewTopicForm)) confGroup.GET("/new_topic", ui.AmWrap(NewTopicForm))
confGroup.POST("/new_topic", ui.AmWrap(NewTopic)) confGroup.POST("/new_topic", ui.AmWrap(NewTopic))
confGroup.GET("/r/:topic", ui.AmWrap(ReadPosts)) confGroup.GET("/r/:topic", ui.AmWrap(ReadPosts))
confGroup.POST("/r/:topic", ui.AmWrap(PostInTopic))
return e return e
} }
+3 -5
View File
@@ -106,11 +106,8 @@
<hr class="border-gray-400 mb-6"> <hr class="border-gray-400 mb-6">
<h2 class="text-2xl font-bold text-black mb-4">Post Message in "{{ topicName }}":</h2> <h2 class="text-2xl font-bold text-black mb-4">Post Message in "{{ topicName }}":</h2>
<form method="POST" action="/TODO"> <form method="POST" action="{{ post_stem }}">
<input type="hidden" name="sd" value="4"> <input type="hidden" name="xp" value="{{ post_max }}"/>
<input type="hidden" name="conf" value="2">
<input type="hidden" name="cc" value="2">
<input type="hidden" name="top" value="4">
<div class="bg-gray-50 p-6 rounded-lg space-y-4"> <div class="bg-gray-50 p-6 rounded-lg space-y-4">
<div> <div>
@@ -137,6 +134,7 @@
<div class="flex justify-center gap-4"> <div class="flex justify-center gap-4">
<button type="submit" name="preview" class="bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded font-medium transition-colors">Preview</button> <button type="submit" name="preview" class="bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded font-medium transition-colors">Preview</button>
<button type="submit" name="post" class="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded font-medium transition-colors">Post & Reload</button> <button type="submit" name="post" class="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded font-medium transition-colors">Post & Reload</button>
<button type="submit" name="postnext" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded font-medium transition-colors">Post & Go Next</button>
<button type="submit" name="posttopics" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded font-medium transition-colors">Post & Go Topics</button> <button type="submit" name="posttopics" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded font-medium transition-colors">Post & Go Topics</button>
</div> </div>
</div> </div>
+101
View File
@@ -0,0 +1,101 @@
{*
* Amsterdam Web Communities System
* Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*}
<div class="p-4">
<div class="mb-6">
<h1 class="text-blue-800 text-4xl font-bold inline">Previewing Post</h1>
<hr class="border-2 border-gray-400 w-4/5 mt-2 mb-6">
</div>
<!-- Preview Section -->
<div class="max-w-3xl mb-8">
<!-- Spelling Error Notice -->
{{ if nError == 0 }}
<div class="bg-blue-50 border-l-4 border-blue-400 p-4 mb-6">
<p class="text-sm font-bold text-blue-800">
Your post did not contain any spelling errors.
</p>
</div>
{{ else }}
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-6">
<p class="text-sm font-bold text-yellow-800">
{{ if nError == 1 }}
There was 1 spelling error in your post.
{{ else }}
There were {{ nError }} spelling errors in your post.
{{ end }}
</p>
</div>
{{ end }}
<!-- Preview Display -->
<div class="bg-white border-2 border-blue-300 rounded-lg p-6 mb-6">
<pre class="amsPost font-mono text-sm whitespace-pre-wrap break-words">{{ previewPb | postRewrite | raw }}</pre>
</div>
<hr class="border-gray-400 mb-6">
</div>
<!-- Posting Form -->
<div class="max-w-3xl">
<form method="POST" action="{{ urlStem }}">
<input type="hidden" name="xp" value="{{ maxPost }}"/>
<div class="bg-gray-50 p-6 rounded-lg space-y-4">
<!-- Your Name/Header and Attach File -->
<div>
<label for="pseud" class="block text-black text-sm font-medium mb-2">Your name/header:</label>
<div class="flex items-center gap-4">
<input type="text" id="pseud" name="pseud" size="37" maxlength="255" value="{{ pseud | raw }}"
class="flex-1 px-3 py-2 border border-gray-300 rounded font-mono focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<div class="flex items-center gap-2">
<input type="checkbox" id="attach" name="attach" value="Y"
{{ if isset(attachFile) }}checked{{ end }}
class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
<label for="attach" class="text-black text-sm">Attach a file</label>
</div>
</div>
</div>
<!-- Message -->
<div>
<div class="flex justify-between items-center mb-2">
<label for="pb" class="text-black text-sm font-medium">Message:</label>
<a href="/TODO/html-reference" target="_blank"
class="text-blue-700 hover:text-blue-900 text-sm">HTML Guide</a>
</div>
<textarea id="pb" name="pb" wrap="soft" rows="7" cols="51"
placeholder="Enter your message here. HTML tags are supported..."
class="w-full px-3 py-2 border border-gray-300 rounded font-mono text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">{{ pb | raw }}</textarea>
</div>
<!-- Action Buttons -->
<div class="flex justify-center gap-4 pt-4">
<button type="submit" name="preview" class="bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded font-medium transition-colors">Preview Again</button>
<button type="submit" name="post" class="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded font-medium transition-colors">Post & Reload</button>
<button type="submit" name="postnext" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded font-medium transition-colors">Post & Go Next</button>
<button type="submit" name="posttopics" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded font-medium transition-colors">Post & Go Topics</button>
<button type="submit" name="cancel" class="bg-red-600 hover:bg-red-700 text-white px-6 py-2 rounded font-medium transition-colors">Cancel</button>
</div>
</div>
</form>
<!-- Posting Guidelines -->
<div class="mt-6 p-4 bg-blue-50 border-l-4 border-blue-400">
<h3 class="text-sm font-bold text-blue-900 mb-2">Review Your Post:</h3>
<ul class="text-xs text-blue-800 space-y-1 list-disc list-inside">
<li>Check the preview above to see how your post will appear to others</li>
<li>Spelling errors are highlighted in red - you can edit your message to fix them</li>
<li>Click "Preview Again" to see updated changes before posting</li>
<li>When satisfied, click "Add Topic" to submit your message</li>
</ul>
</div>
</div>
</div>