wired up all the hotlist functionality
This commit is contained in:
@@ -98,6 +98,11 @@ func Topics(ctxt ui.AmContext) (string, any, error) {
|
||||
return ui.ErrorPage(ctxt, err)
|
||||
}
|
||||
|
||||
hotlistTest, err := database.AmIsInHotlist(ctxt.Ctx(), ctxt.CurrentUser(), comm.Id, conf.ConfId)
|
||||
if err != nil {
|
||||
return ui.ErrorPage(ctxt, err)
|
||||
}
|
||||
|
||||
traverser := ui.NewTopicTraverser(topics)
|
||||
ctxt.SetSession("topic.traverser", traverser)
|
||||
|
||||
@@ -116,6 +121,7 @@ func Topics(ctxt ui.AmContext) (string, any, error) {
|
||||
}
|
||||
|
||||
ctxt.VarMap().Set("canCreate", conf.TestPermission("Conference.Create", myLevel))
|
||||
ctxt.VarMap().Set("showHotlist", !hotlistTest)
|
||||
ctxt.VarMap().Set("conferenceName", conf.Name)
|
||||
ctxt.VarMap().Set("urlBack", fmt.Sprintf("/comm/%s/conf", comm.Alias))
|
||||
ctxt.VarMap().Set("urlStem", urlStem)
|
||||
|
||||
+19
-1
@@ -122,7 +122,25 @@ func AttachmentSend(ctxt ui.AmContext) (string, any, error) {
|
||||
return "bytes", data, nil
|
||||
}
|
||||
|
||||
/* HideTopic hides or shows rthe current topic for the current user.
|
||||
/* AddToHotlist adds the current community and conference to the user's hotlist..
|
||||
* Parameters:
|
||||
* ctxt - The AmContext for the request.
|
||||
* Returns:
|
||||
* Command string dictating what to be rendered.
|
||||
* Data as a parameter for the command string.
|
||||
* Standard Go error status.
|
||||
*/
|
||||
func AddToHotlist(ctxt ui.AmContext) (string, any, error) {
|
||||
comm := ctxt.CurrentCommunity()
|
||||
conf := ctxt.GetScratch("currentConference").(*database.Conference)
|
||||
err := database.AmAppendToHotlist(ctxt.Ctx(), ctxt.CurrentUser(), comm.Id, conf.ConfId)
|
||||
if err != nil {
|
||||
return ui.ErrorPage(ctxt, err)
|
||||
}
|
||||
return "redirect", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias")), nil
|
||||
}
|
||||
|
||||
/* HideTopic hides or shows the current topic for the current user.
|
||||
* Parameters:
|
||||
* ctxt - The AmContext for the request.
|
||||
* Returns:
|
||||
|
||||
@@ -383,7 +383,7 @@ func AmGetConferenceByAliasInCommunity(ctx context.Context, cid int32, alias str
|
||||
err := row.Scan(&confid)
|
||||
switch err {
|
||||
case nil:
|
||||
AmGetConference(ctx, confid)
|
||||
return AmGetConference(ctx, confid)
|
||||
case sql.ErrNoRows:
|
||||
return nil, errors.New("conference not found")
|
||||
}
|
||||
|
||||
+109
-1
@@ -9,7 +9,11 @@
|
||||
// The database package contains database management and storage logic.
|
||||
package database
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ConferenceHotlist represents a user's conference hotlist.
|
||||
type ConferenceHotlist struct {
|
||||
@@ -19,6 +23,8 @@ type ConferenceHotlist struct {
|
||||
ConfId int32 `db:"confid"`
|
||||
}
|
||||
|
||||
const HOTLIST_SEQUENCE_SPACING = 100
|
||||
|
||||
// Community gets the community pointer from the hotlist.
|
||||
func (h *ConferenceHotlist) Community(ctx context.Context) (*Community, error) {
|
||||
return AmGetCommunity(ctx, h.CommId)
|
||||
@@ -62,3 +68,105 @@ func AmCopyConferenceHotlist(ctx context.Context, from, to *User) error {
|
||||
success = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// AmReorderHotlist exchanges the position of two items on the user's hotlist.
|
||||
func AmReorderHotlist(ctx context.Context, u *User, seq1, seq2 int16) error {
|
||||
success := false
|
||||
tx := amdb.MustBegin()
|
||||
defer func() {
|
||||
if !success {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
|
||||
_, err := tx.ExecContext(ctx, "UPDATE confhotlist SET sequence = -1 WHERE uid = ? AND sequence = ?", u.Uid, seq1)
|
||||
if err == nil {
|
||||
_, err = tx.ExecContext(ctx, "UPDATE confhotlist SET sequence = ? WHERE uid = ? AND sequence = ?", seq1, u.Uid, seq2)
|
||||
if err == nil {
|
||||
_, err = tx.ExecContext(ctx, "UPDATE confhotlist SET sequence = ? WHERE uid = ? AND sequence = -1", seq2, u.Uid)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
success = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// AmRemoveEntryFromHotlist removes an entry from the user's hotlist.
|
||||
func AmRemoveEntryFromHotlist(ctx context.Context, u *User, seq int16) error {
|
||||
success := false
|
||||
tx := amdb.MustBegin()
|
||||
defer func() {
|
||||
if !success {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
|
||||
_, err := tx.ExecContext(ctx, "DELETE FROM confhotlist WHERE uid = ? AND sequence = ?", u.Uid, seq)
|
||||
if err == nil {
|
||||
_, err = tx.ExecContext(ctx, "UPDATE confhotlist SET sequence = sequence - ? WHERE uid = ? AND sequence > ?", HOTLIST_SEQUENCE_SPACING, u.Uid, seq)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
success = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// AmAppendToHotlist adds a community/conference ID to the end of the user's hotlist.
|
||||
func AmAppendToHotlist(ctx context.Context, u *User, commid, confid int32) error {
|
||||
success := false
|
||||
tx := amdb.MustBegin()
|
||||
defer func() {
|
||||
if !success {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
|
||||
var newseq int16
|
||||
row := tx.QueryRowContext(ctx, "SELECT sequence FROM confhotlist WHERE uid = ? AND commid = ? AND confid = ?", u.Uid, commid, confid)
|
||||
err := row.Scan(&newseq)
|
||||
if err == nil {
|
||||
return errors.New("community/conference already exist in hotlist")
|
||||
} else if err != sql.ErrNoRows {
|
||||
return err
|
||||
}
|
||||
row = tx.QueryRowContext(ctx, "SELECT MAX(sequence) FROM confhotlist WHERE uid = ?", u.Uid)
|
||||
err = row.Scan(&newseq)
|
||||
if err == sql.ErrNoRows {
|
||||
newseq = 0
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, "INSERT INTO confhotlist (uid, sequence, commid, confid) VALUES (?, ?, ?, ?)",
|
||||
u.Uid, newseq+HOTLIST_SEQUENCE_SPACING, commid, confid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
success = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// AmIsInHotlist returns true if the community/conference pair is in the hotlist.
|
||||
func AmIsInHotlist(ctx context.Context, u *User, commid, confid int32) (bool, error) {
|
||||
row := amdb.QueryRowContext(ctx, "SELECT sequence FROM confhotlist WHERE uid = ? AND commid = ? AND confid = ?", u.Uid, commid, confid)
|
||||
var tmp int16
|
||||
err := row.Scan(&tmp)
|
||||
switch err {
|
||||
case nil:
|
||||
return true, nil
|
||||
case sql.ErrNoRows:
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@
|
||||
- Manage Communities on communities sidebox
|
||||
- ~~Conference Hotlist sidebox~~
|
||||
- ~~"New" flag on Conference Hotlist sidebox~~
|
||||
- Manage on Conference Hotlist sidebox
|
||||
- ~~Manage on Conference Hotlist sidebox~~
|
||||
- Sidebox configuration
|
||||
- Topics view:
|
||||
- Find
|
||||
- Manage
|
||||
- Add to Hotlist/Remove from Hotlist
|
||||
- ~~Add to Hotlist/Remove from Hotlist~~
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ func setupEcho() *echo.Echo {
|
||||
e.POST("/find", ui.AmWrap(Find))
|
||||
e.GET("/user/:uname", ui.AmWrap(ShowProfile))
|
||||
e.POST("/quick_email", ui.AmWrap(QuickEMail))
|
||||
e.GET("/hotlist", ui.AmWrap(Hotlist))
|
||||
e.GET("/sysadmin", ui.AmWrap(SysAdminMenu))
|
||||
e.GET("/create_comm", ui.AmWrap(CreateCommunityForm))
|
||||
e.POST("/create_comm", ui.AmWrap(CreateCommunity))
|
||||
@@ -100,6 +101,7 @@ func setupEcho() *echo.Echo {
|
||||
confGroup.GET("", ui.AmWrap(Topics))
|
||||
confGroup.GET("/new_topic", ui.AmWrap(NewTopicForm))
|
||||
confGroup.POST("/new_topic", ui.AmWrap(NewTopic))
|
||||
confGroup.GET("/hotlist", ui.AmWrap(AddToHotlist))
|
||||
confGroup.GET("/r/:topic", ui.AmWrap(ReadPosts), ui.SetTopic)
|
||||
confGroup.POST("/r/:topic", ui.AmWrap(PostInTopic), ui.SetTopic)
|
||||
opsGroup := confGroup.Group("/op/:topic", ui.SetTopic)
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
{*
|
||||
* 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">
|
||||
<!-- Page Title -->
|
||||
<div class="mb-6">
|
||||
<h1 class="text-blue-800 text-4xl font-bold mb-2">Your Conference Hotlist</h1>
|
||||
<hr class="border-2 border-gray-400 w-4/5 mb-6">
|
||||
</div>
|
||||
|
||||
<!-- Hotlist Table -->
|
||||
<div class="max-w-4xl mb-8">
|
||||
<div class="bg-white border border-gray-300 rounded-lg overflow-hidden">
|
||||
<table class="w-full">
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
{{ range i, hl := hotlist }}
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-4 py-3 whitespace-nowrap text-center w-12">
|
||||
{{ if i < (len(hotlist) - 1) }}
|
||||
<a href="/hotlist?m={{ i }}&n=1" class="text-2xl hover:scale-125 inline-block transition-transform"
|
||||
title="Move Down">⬇️</a>
|
||||
{{ else }}
|
||||
<span class="text-2xl text-gray-300"> </span>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-center w-12">
|
||||
{{ if i > 0 }}
|
||||
<a href="/hotlist?m={{ i }}&n=-1" class="text-2xl hover:scale-125 inline-block transition-transform"
|
||||
title="Move Up">⬆️</a>
|
||||
{{ else }}
|
||||
<span class="text-2xl text-gray-300"> </span>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-center w-12">
|
||||
<a href="/hotlist?d={{ i }}" class="text-2xl hover:scale-125 inline-block transition-transform"
|
||||
title="Remove">❌</a>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm">
|
||||
<div class="font-bold text-black">{{ conferences[i] }}</div>
|
||||
<div class="text-gray-600">({{ communities[i] }})</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Legend -->
|
||||
<div class="max-w-4xl">
|
||||
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
||||
<h2 class="text-blue-800 font-bold text-lg mb-3">How to update the hotlist:</h2>
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-2xl">⬇️</span>
|
||||
<span class="text-gray-700">Click this symbol to move the specified conference down in your hotlist.</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-2xl">⬆️</span>
|
||||
<span class="text-gray-700">Click this symbol to move the specified conference up in your hotlist.</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-2xl">❌</span>
|
||||
<span class="text-gray-700">Click this symbol to remove the specified conference from your hotlist.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,7 +32,7 @@
|
||||
{{ if sb.Flags["canManage"] }}
|
||||
<div class="mt-3 text-center">
|
||||
<span class="text-black text-xs font-bold">
|
||||
[ <a href="/TODO/manage_hotlist" class="text-blue-700 hover:text-blue-900">Manage</a> ]
|
||||
[ <a href="/hotlist" class="text-blue-700 hover:text-blue-900">Manage</a> ]
|
||||
</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded text-sm font-medium transition-colors">
|
||||
Manage
|
||||
</a>
|
||||
{{ if false }}{* TODO *}
|
||||
<a href="/TODO{{ urlStem }}/hotlist"
|
||||
{{ if showHotlist }}
|
||||
<a href="{{ urlStem }}/hotlist"
|
||||
class="bg-orange-600 hover:bg-orange-700 text-black px-4 py-2 rounded text-sm font-medium transition-colors">
|
||||
Add to Hotlist
|
||||
</a>
|
||||
|
||||
+66
@@ -476,3 +476,69 @@ func QuickEMail(ctxt ui.AmContext) (string, any, error) {
|
||||
msg.Send()
|
||||
return "redirect", "/user/" + user.Username, nil
|
||||
}
|
||||
|
||||
/* Hotlist displays and edits the user's conference hotlist.
|
||||
* Parameters:
|
||||
* ctxt - The AmContext for the request.
|
||||
* Returns:
|
||||
* Command string dictating what to be rendered.
|
||||
* Data as a parameter for the command string.
|
||||
* Standard Go error status.
|
||||
*/
|
||||
func Hotlist(ctxt ui.AmContext) (string, any, error) {
|
||||
me := ctxt.CurrentUser()
|
||||
if me.IsAnon {
|
||||
return ui.ErrorPage(ctxt, errors.New("you are not logged in"))
|
||||
}
|
||||
hotlist, err := database.AmGetConferenceHotlist(ctxt.Ctx(), me)
|
||||
if err != nil {
|
||||
return ui.ErrorPage(ctxt, err)
|
||||
}
|
||||
|
||||
if ctxt.HasParameter("m") {
|
||||
index := ctxt.QueryParamInt("m", -1)
|
||||
dir := ctxt.QueryParamInt("n", 0)
|
||||
if index >= 0 && (index+dir) != index {
|
||||
err := database.AmReorderHotlist(ctxt.Ctx(), me, hotlist[index].Sequence, hotlist[index+dir].Sequence)
|
||||
if err != nil {
|
||||
return ui.ErrorPage(ctxt, err)
|
||||
}
|
||||
tmp := hotlist[index].CommId
|
||||
hotlist[index].CommId = hotlist[index+dir].CommId
|
||||
hotlist[index+dir].CommId = tmp
|
||||
tmp = hotlist[index].ConfId
|
||||
hotlist[index].ConfId = hotlist[index+dir].ConfId
|
||||
hotlist[index+dir].ConfId = tmp
|
||||
}
|
||||
} else if ctxt.HasParameter("d") {
|
||||
index := ctxt.QueryParamInt("d", -1)
|
||||
if index >= 0 {
|
||||
err := database.AmRemoveEntryFromHotlist(ctxt.Ctx(), me, hotlist[index].Sequence)
|
||||
if err != nil {
|
||||
return ui.ErrorPage(ctxt, err)
|
||||
}
|
||||
hotlist = append(hotlist[:index], hotlist[index+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
communities := make([]string, len(hotlist))
|
||||
conferences := make([]string, len(hotlist))
|
||||
for i := range hotlist {
|
||||
comm, err := hotlist[i].Community(ctxt.Ctx())
|
||||
if err != nil {
|
||||
return ui.ErrorPage(ctxt, err)
|
||||
}
|
||||
communities[i] = comm.Name
|
||||
conf, err := hotlist[i].Conference(ctxt.Ctx())
|
||||
if err != nil {
|
||||
return ui.ErrorPage(ctxt, err)
|
||||
}
|
||||
conferences[i] = conf.Name
|
||||
}
|
||||
|
||||
ctxt.VarMap().Set("hotlist", hotlist)
|
||||
ctxt.VarMap().Set("communities", communities)
|
||||
ctxt.VarMap().Set("conferences", conferences)
|
||||
ctxt.VarMap().Set("amsterdam_pageTitle", "Your Conference Hotlist")
|
||||
return "framed_template", "hotlist.jet", nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user