Skip to main content

Guild Settings - Usage Guide

Overview

Guild settings are now editable through a new Settings tab in the guild page, restricted to wowRank 0-1 (Guild Master and top officers only).

Permission Level

  • wowRank 0 (Guild Master): Full access to all settings ✓
  • wowRank 1 (Top Officer): Full access to all settings ✓
  • wowRank 2 (Officer): Can manage members/events, cannot edit guild settings
  • wowRank 3+ (Members): Read-only access ✗

This ensures only the most trusted leadership can modify important guild configuration.

Available Settings

1. Guild Logo Management

Upload and manage custom guild logos with professional image editing, or toggle between synced and custom logos for Battle.net guilds.

Features:

  • Advanced Image Editor: Professional crop, zoom, and rotate interface
  • Drag to Reposition: Click and drag to position your logo
  • Zoom Control: 1x-3x zoom with slider or scroll/pinch gestures
  • Rotation Support: 0-360° rotation for perfect alignment
  • Visual Crop Preview: Grid overlay shows exactly what will be saved
  • WYSIWYG Output: What you see in the preview is what gets saved (512x512 PNG)
  • Logo Selection (Blizzard guilds): Choose between Battle.net crest or custom upload via radio cards
  • Touch Support: Optimized for mobile and tablet devices
  • File Validation: JPEG, PNG, GIF, WebP (max 2MB) with client-side validation
  • Instant Updates: Logo changes visible immediately across all pages
  • Cache Management: Automatic cache invalidation on upload

API Endpoints:

# Upload custom logo
POST /api/v1/guilds/:guildId/logo
Authorization: Bearer <token>
Content-Type: multipart/form-data

Form Data:
logo: <image file>

# Toggle between synced/custom logo (Blizzard guilds)
PATCH /api/v1/guilds/:guildId/logo/toggle
Authorization: Bearer <token>

{
"useCustomLogo": true
}

# Get logo information
GET /api/v1/guilds/:guildId/logo

Response Format:

{
"currentLogo": "/uploads/guild-logos/xyz-123456789.png",
"syncedLogo": "/api/v1/blizzard/guild-logo/...",
"customLogo": "/uploads/guild-logos/xyz-123456789.png",
"emblemData": {
"icon": 12,
"iconColor": "rgba(255,255,255,1)",
"border": 5,
"borderColor": "rgba(30,30,30,1)",
"backgroundColor": "rgba(200,50,50,1)"
},
"useCustomLogo": true,
"syncSource": "blizzard",
"hasCustomLogo": true,
"hasSyncedLogo": true
}

Note: Battle.net guilds may have emblemData (SVG-like crest structure) instead of syncedLogo URL. The frontend GuildCrest component renders this structured data.

Cache Management: Logo updates trigger comprehensive cache clearing:

  • Backend Redis cache (guild detail + list endpoints)
  • Frontend TanStack Query cache
  • Both authenticated user and guest caches
  • Pattern-based clearing ensures all query param variations are cleared

2. Roster Visibility

Control who can view your guild roster:

  • 🌍 Public - Anyone can view the roster (great for recruiting)
  • 👥 Members Only - Only guild members can view (default, balanced)
  • 🔒 Officers Only - Only ranks 0-2 can view (maximum privacy)

API Endpoint:

PATCH /api/v1/guilds/:guildId/settings
Authorization: Bearer <token>

{
"rosterPrivacy": "officers"
}

3. Public Listing

Control whether your guild appears in the public guild directory for unauthenticated users. When enabled, basic guild info (name, realm, faction, logo) is visible to anyone, allowing potential recruits to discover and apply without logging in first.

This is separate from roster privacy — a guild can be publicly listed while keeping its roster restricted to members or officers.

{
"publicListing": true
}

Default: true (guilds are publicly listed)

4. Show Character Details

Toggle display of character level, class, and gear info in roster.

{
"showCharacterDetails": true
}

5. Allow Guild Applications

Enable/disable the ability for users to submit applications to join.

{
"allowApplications": false
}

How It Works

Backend: CASL Enforces Restrictions

The AbilityFactory checks wowRank and only grants Action.Update on Organization to ranks 0-1:

// Only ranks 0-1 can update guild settings
if (wowRank !== null && wowRank <= 1) {
can([Action.Read, Action.Update], 'Organization', { id: membership.guildId });
}
// Rank 2 can manage members/events but NOT guild settings
else if (wowRank === 2) {
can(Action.Read, 'Organization', { id: membership.guildId });
// Note: can NOT update Organization
}

The Settings sidebar link and its sub-pages only appear for users with canManageGuild permission. The settings are split across dedicated routes:

RoutePage
/guilds/:id/settings/basicName, alias (URL slug)
/guilds/:id/settings/infoMOTD and guild description
/guilds/:id/settings/privacyPublic listing, roster visibility, feature toggles
/guilds/:id/settings/appearanceLogo management
/guilds/:id/settings/integrationsDiscord server connection
/guilds/:id/settings/rosterBattle.net sync (Blizzard guilds only)
/guilds/:id/settings/advancedArchive / delete (standalone guilds only)

Navigation for all these pages is driven by a single shared list in -settingsLinks.ts (see Adding a Settings Page below), so both the desktop sidebar and the mobile tab bar always stay in sync.

UI Features

Layout

  • Two-Column Responsive Design: Settings organized in main content (left) and sidebar (right)
  • Privacy & Access Section: Roster visibility and feature toggles in main column
  • Appearance Section: Logo management in sidebar
  • Roster Sync Section: Battle.net sync tools for connected guilds
  • Advanced Section: Danger zone actions (archive/delete)
  • Mobile Responsive: Stacks vertically on small screens

Interactions

  • Real-time updates: Settings apply instantly
  • Auto-save: Changes save automatically when toggled
  • Success/Error feedback: Visual confirmation of setting changes
  • Helpful descriptions: Each setting explains what it does
  • Permission notice: Warning that only ranks 0-1 can edit
  • Logo Radio Cards: Visual selection between Battle.net and custom logos
  • Professional Upload Modal: Industry-standard crop/zoom/rotate interface

Testing the Feature

As Guild Master (rank 0)

  1. Navigate to your guild page
  2. See the "Settings" tab appear
  3. Click Settings tab
  4. Change roster privacy setting
  5. See success message
  6. Setting is now enforced guild-wide

As Rank 2 Officer

  1. Navigate to guild page
  2. Settings tab does NOT appear
  3. Cannot access /guilds/:id/settings endpoint
  4. API returns 403 Forbidden if attempted

As Regular Member

  1. Navigate to guild page
  2. No Settings tab visible
  3. Cannot modify guild configuration

Privacy Enforcement Example

Guild A sets rosterPrivacy: "officers"

  • Guild Master (rank 0): ✓ Can see full roster
  • Top Officer (rank 1): ✓ Can see full roster
  • Officer (rank 2): ✓ Can see full roster
  • Regular Member (rank 4): ✗ Cannot see roster (CASL blocks it)
  • Guest (not logged in): ✗ Cannot see roster

Guild B sets rosterPrivacy: "public"

  • Everyone: ✓ Can see roster (including guests)

The AbilityFactory reads the guild's settings.rosterPrivacy and dynamically generates abilities accordingly.

Adding a Settings Page

To add a new settings section:

  1. Register the route — create apps/web/src/routes/guilds/$guildId/settings.<name>.tsx following the pattern of the existing settings routes.

  2. Add to the shared nav list — open apps/web/src/routes/guilds/$guildId/-settingsLinks.ts and append an entry:

    { segment: 'my-section', label: 'My Section' },
    // or conditionally:
    { segment: 'my-section', label: 'My Section', condition: (s) => s === 'standalone' },

    This single change updates both the desktop sidebar sub-links (OrganizationLayout) and the mobile horizontal tab bar (settings.tsx) simultaneously.

  3. Update the backend DTO if new persisted settings are needed (update-guild-settings.dto.ts):

    @IsOptional()
    @IsBoolean()
    newSetting?: boolean;
  4. (Optional) Add CASL logic (ability.factory.ts):

    if (guildSettings.newSetting) {
    can(Action.SomeAction, 'SomeSubject');
    }

API Response Format

When fetching a guild with settings:

{
"id": "guild-123",
"name": "My Guild",
"realm": "Area 52",
"faction": "horde",
"settings": {
"publicListing": true,
"rosterPrivacy": "members",
"showCharacterDetails": true,
"allowApplications": false
},
"userPermissions": {
"abilityRules": [...] // Includes privacy enforcement rules
}
}

The abilityRules are automatically generated based on the guild's settings and the user's rank.

Public Access (Unauthenticated Users)

When publicListing is enabled, the guild appears in the public directory and its overview page is accessible without login. Unauthenticated users can:

  • ✓ See guild name, realm, faction, logo
  • ✓ View published/active/completed events
  • ✓ Submit an application (if applications are enabled)
  • ✗ Cannot see draft events (organizer-only)
  • ✗ Cannot see the roster (controlled by rosterPrivacy)

The @OptionalAuth() decorator on the organization endpoints allows both authenticated and unauthenticated requests. The backend filters results based on publicListing for guests.

Security

Backend-enforced: Even if frontend is bypassed, AbilitiesGuard checks CASL rules ✅ Rank-based: Only wowRank 0-1 can update ✅ Per-guild: Each guild has independent settings ✅ Type-safe: Settings validated with class-validator DTOs ✅ Audit trail: All setting changes go through the API (loggable)

Migration

If adding default settings to existing guilds:

-- Set default roster privacy for guilds without it
UPDATE guilds
SET settings = jsonb_set(
COALESCE(settings, '{}'::jsonb),
'{rosterPrivacy}',
'"members"'
)
WHERE settings IS NULL
OR settings->>'rosterPrivacy' IS NULL;