first draft of posting to a topic (including preview)
This commit is contained in:
+114
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
Reference in New Issue
Block a user