put the ad (quote) banners in - last major feature in MISSINGFUNCS
@@ -110,6 +110,7 @@ type AmConfig struct {
|
||||
WorkerTasks int `yaml:"workerTasks"`
|
||||
} `yaml:"queues"`
|
||||
Caches struct {
|
||||
Ads int `yaml:"ads"`
|
||||
Communities int `yaml:"communities"`
|
||||
CommunityProps int `yaml:"communityProps"`
|
||||
Conferences int `yaml:"conferences"`
|
||||
@@ -254,6 +255,7 @@ func overlayConfig(dest *AmConfig, loaded *AmConfig, defaults *AmConfig) {
|
||||
dest.Tuning.Queues.EmailSend = overlayInt(loaded.Tuning.Queues.EmailSend, defaults.Tuning.Queues.EmailSend)
|
||||
dest.Tuning.Queues.IPBans = overlayInt(loaded.Tuning.Queues.IPBans, defaults.Tuning.Queues.IPBans)
|
||||
dest.Tuning.Queues.WorkerTasks = overlayInt(loaded.Tuning.Queues.WorkerTasks, defaults.Tuning.Queues.WorkerTasks)
|
||||
dest.Tuning.Caches.Ads = overlayInt(loaded.Tuning.Caches.Ads, defaults.Tuning.Caches.Ads)
|
||||
dest.Tuning.Caches.Communities = overlayInt(loaded.Tuning.Caches.Communities, defaults.Tuning.Caches.Communities)
|
||||
dest.Tuning.Caches.CommunityProps = overlayInt(loaded.Tuning.Caches.CommunityProps, defaults.Tuning.Caches.CommunityProps)
|
||||
dest.Tuning.Caches.Conferences = overlayInt(loaded.Tuning.Caches.Conferences, defaults.Tuning.Caches.Conferences)
|
||||
|
||||
@@ -64,6 +64,7 @@ tuning:
|
||||
ipBans: 32
|
||||
workerTasks: 128
|
||||
caches:
|
||||
ads: 30
|
||||
communities: 50
|
||||
communityProps: 100
|
||||
conferences: 100
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
// The database package contains database management and storage logic.
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
crand "crypto/rand"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"git.erbosoft.com/amy/amsterdam/config"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// Advert represents an advertising banner.
|
||||
type Advert struct {
|
||||
AdId int32 `db:"adid"` // ID of the ad
|
||||
ImagePath string `db:"imagepath"` // path to the ad image
|
||||
PathStyle int16 `db:"pathstyle"` // path style
|
||||
Caption *string `db:"caption"` // caption
|
||||
LinkURL *string `db:"linkurl"` // link URL
|
||||
}
|
||||
|
||||
// Values for PathStyle.
|
||||
const (
|
||||
AdPathStyleContextRelative int16 = 0 // indicates context-relative image path
|
||||
)
|
||||
|
||||
// adCache is the cache for advertisements.
|
||||
var adCache *lru.Cache = nil
|
||||
|
||||
// adCacheMutex synchronizes access to adCache.
|
||||
var adCacheMutex sync.Mutex
|
||||
|
||||
// setupAdCache sets up the ad cache.
|
||||
func setupAdCache() {
|
||||
var err error
|
||||
adCache, err = lru.New(config.GlobalConfig.Tuning.Caches.Ads)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ResolvePath creates an absolute image path based on the stored path data.
|
||||
func (ad *Advert) ResolvePath() string {
|
||||
if ad.PathStyle == AdPathStyleContextRelative {
|
||||
return "/" + ad.ImagePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// AmGetAd gets an ad by ID.
|
||||
func AmGetAd(ctx context.Context, adid int32) (*Advert, error) {
|
||||
adCacheMutex.Lock()
|
||||
defer adCacheMutex.Unlock()
|
||||
rc, ok := adCache.Get(adid)
|
||||
if ok {
|
||||
return rc.(*Advert), nil
|
||||
}
|
||||
var theAd Advert
|
||||
err := amdb.GetContext(ctx, &theAd, "SELECT * FROM adverts WHERE adid = ?", adid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adCache.Add(adid, &theAd)
|
||||
return &theAd, nil
|
||||
}
|
||||
|
||||
// AmGetRandomAd gets a random ad from the
|
||||
func AmGetRandomAd(ctx context.Context) (*Advert, error) {
|
||||
var num int
|
||||
err := amdb.GetContext(ctx, &num, "SELECT COUNT(*) FROM adverts")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v1, err := crand.Int(crand.Reader, big.NewInt(int64(num)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AmGetAd(ctx, int32(v1.Int64())+1)
|
||||
}
|
||||
@@ -28,6 +28,7 @@ func SetupDb() (func(), error) {
|
||||
db, err := sqlx.Open(config.GlobalConfig.Database.Driver, config.GlobalConfig.Database.Dsn)
|
||||
if err == nil {
|
||||
amdb = db
|
||||
setupAdCache()
|
||||
setupUserCache()
|
||||
setupContactsCache()
|
||||
setupCommunityCache()
|
||||
|
||||
@@ -11,7 +11,7 @@ _(italicized items can be deferred)_
|
||||
- _Calendar (top menu link)_
|
||||
- _Chat (top menu link)_
|
||||
- _Documentation (top menu link)_
|
||||
- Quote banner rotation
|
||||
- ~~Quote banner rotation~~
|
||||
- ~~Sysadmin Menu:~~
|
||||
- ~~Edit Global Properties~~
|
||||
- ~~View/Edit IP Address Bans~~
|
||||
|
||||
@@ -64,6 +64,7 @@ func setupEcho() *echo.Echo {
|
||||
}
|
||||
e.Match(GetAndPost, "/TODO/*", ui.AmWrap(NotImplPage), uiset...)
|
||||
e.GET("/img/*", ui.AmServeImage)
|
||||
e.GET("/images/*", ui.AmServeImage)
|
||||
if config.GlobalConfig.Rendering.VeniceCompatibleImageURLs {
|
||||
e.GET("/venice/imagedata/:id", ui.AmServeVeniceCompatibleImage)
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
@@ -34,6 +34,9 @@ import (
|
||||
//go:embed static_images/*
|
||||
var static_images embed.FS
|
||||
|
||||
//go:embed adbanners/*
|
||||
var ad_banners embed.FS
|
||||
|
||||
// Constants for default photo sizes.
|
||||
const (
|
||||
UserPhotoWidth = 100
|
||||
@@ -71,6 +74,12 @@ func AmServeImage(c echo.Context) error {
|
||||
if err == nil {
|
||||
return c.Blob(http.StatusOK, mimeTypeFromFilename(components[3]), b)
|
||||
}
|
||||
case "ads/":
|
||||
var b []byte
|
||||
b, err = ad_banners.ReadFile(filepath.Join("adbanners", components[3]))
|
||||
if err == nil {
|
||||
return c.Blob(http.StatusOK, mimeTypeFromFilename(components[3]), b)
|
||||
}
|
||||
case "store/":
|
||||
var id int
|
||||
id, err = strconv.Atoi(components[3])
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.erbosoft.com/amy/amsterdam/database"
|
||||
"github.com/klauspost/lctime"
|
||||
"github.com/labstack/echo/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -129,6 +130,17 @@ func AmSendPageData(ctxt echo.Context, amctxt AmContext, command string, data an
|
||||
}
|
||||
menus[1] = AmMenu("fixed")
|
||||
amctxt.VarMap().Set("__leftMenus", menus)
|
||||
ad, err := database.AmGetRandomAd(ctxt.Request().Context())
|
||||
if err != nil {
|
||||
ad = &database.Advert{
|
||||
AdId: -1,
|
||||
ImagePath: "",
|
||||
PathStyle: -1,
|
||||
Caption: nil,
|
||||
LinkURL: nil,
|
||||
}
|
||||
}
|
||||
amctxt.VarMap().Set("__bannerad", ad)
|
||||
if tmp := amctxt.GetScratch("frame_suppressLogin"); tmp != nil {
|
||||
amctxt.VarMap().Set("__suppressLogin", true)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,16 @@
|
||||
|
||||
<!-- Banner Ad -->
|
||||
<div class="w-2/5 flex justify-end">
|
||||
<img src="/img/builtin/placeholder_ad.gif" alt="" class="w-96 h-15 mx-2 my-2">
|
||||
{{ p := __bannerad.ResolvePath() }}
|
||||
{{ if p != "" }}
|
||||
{{ if isset(__bannerad.LinkURL) }}
|
||||
<a href="{{ __bannerad.LinkURL }}"><img src="{{ p }}" {{ if isset(__bannerad.Caption) }}alt="{{ __bannerad.Caption }}"{{ end }} class="w-96 h-15 mx-2 my-2"></a>
|
||||
{{ else }}
|
||||
<img src="{{ p }}" {{ if isset(__bannerad.Caption) }}alt="{{ __bannerad.Caption }}"{{ end }} class="w-96 h-15 mx-2 my-2">
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||