Files
summercms/modules/towel
Jakub Zych 602e0412f3 docs: add a Role in the framework section to every module README
Each module README now says where the module sits in the dependency
graph, why it is its own module, and lists use cases for application
developers.
2026-10-11 00:58:17 +02:00
..

towel

Request-scoped actor, organization, collection and locale values carried through context.Context.

import "git.golem15.com/golem15/summercms/modules/towel"

Name: You carry it everywhere and it marks your spot; it carries the actor, organization and locale through each request.

Overview

towel replaces the request-global state that WinterCMS reads through facades (the current locale, the acting user) with explicit values on the request context. Middleware stores a value once, and any code further down the call chain that receives the context reads it back without a global lookup. surf sets the locale from Accept-Language (or the signed-in user's preferred locale) and the organization for every request, phrasebook reads the locale when it translates, and cabana sets it while localizing admin schemas.

Role in the framework

A dependency-free leaf for request-scoped values. surf sets them, and phrasebook, cabana and plugin code read them. It replaces WinterCMS facades for per-request state, so it has to be importable by any module without cycles.

Use cases:

  • Read the acting user or organization in a service without passing them through every call.
  • Translate in the request's locale from deep inside a plugin.
  • Set a locale explicitly in a job or a test.

Features

  • Four independent string values, each with a setter and a getter: actor (towel.WithActor, towel.Actor), organization (towel.WithOrganization, towel.Organization), collection (towel.WithCollection, towel.Collection) and locale (towel.WithLocale, towel.Locale).
  • Unexported context key types, so no other package can read or overwrite the values by accident.
  • Getters return (value, ok): a value that was set to an empty string is distinguishable from one that was never set.
  • Nil-safe: a setter given a nil context starts from context.Background(), and a getter given a nil context reports false.

Usage

package blog

import (
	"fmt"
	"net/http"

	"git.golem15.com/golem15/summercms/modules/towel"
)

// withAcmeScope tags every request with the organization and collection it serves.
func withAcmeScope(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := towel.WithOrganization(r.Context(), "acme")
		ctx = towel.WithCollection(ctx, "blog")
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func listPosts(w http.ResponseWriter, r *http.Request) {
	org, _ := towel.Organization(r.Context())
	locale, ok := towel.Locale(r.Context())
	if !ok {
		locale = "en"
	}
	fmt.Fprintf(w, "posts for %s in %s\n", org, locale)
}

API reference

Identifier Description
towel.WithActor / towel.Actor Store and read the acting user or client identifier.
towel.WithOrganization / towel.Organization Store and read the organization (tenant) the request belongs to.
towel.WithCollection / towel.Collection Store and read the collection the request operates on.
towel.WithLocale / towel.Locale Store and read the locale used for translations and localized responses.

Dependencies

  • SummerCMS modules: none.
  • Third-party: none.
  • Standard library: context.

Testing

go test ./modules/towel/...

The tests exercise plain contexts and need no external services.