ACWE Members Portal

Technical Implementation Plan (Revised)

Built-to-last architecture with Drupal 11: Registration workflow, section groups, forums, and member directory

Status

Core Complete

Platform

Drupal 11.2.7

Security

Multi-Layer

Approach

Drupal-Native

Table of Contents

Our Approach: Why We Changed Direction

Key Decision: Drupal-Native Solution

Original Plan: Integrate existing Python/FileMaker-based part distribution software with Drupal via APIs. View the entire contents of the original plan here.

Revised Plan: Build everything natively in Drupal 11 using proven contrib modules.

Why: Better long-term maintainability, leverages Drupal's strengths (access control, user management, content workflows), eliminates complex API integration layer, and provides more flexibility for future enhancements.

Benefits of Drupal-Native Approach
  • Single Platform: Everything in Drupal (no external systems)
  • Proven Modules: Using well-maintained contrib modules
  • Better Security: Drupal's built-in access control
  • Easier Maintenance: One system to update
  • More Flexible: Easy to extend with new features
  • Community Support: Large Drupal community
What We Built Instead
  • Custom Registration: Keyword-protected with approval workflow
  • Seasonal Management: Self-service keyword reactivation
  • Section Groups: Using Groups module (battle-tested)
  • Forums: Drupal Forum module (members-only)
  • Member Directory: Views-based table display
  • Anti-Spam: Multi-layer protection (reCAPTCHA, Honeypot, etc.)
  • Future Music Management: Will use Group content + File entity
Implementation Timeline (Actual)

November 7-9, 2025: Built core registration, groups, forums, directory (3 days)

November 10, 2025: Documentation and testing preparation. Added seasonal membership management planning (self-service keyword reactivation).

Next: Small group testing, then phased rollout

Result: Went from concept to working system in 3 days vs. original 12-week plan. Drupal-native approach is much faster.

System Architecture

Component Overview

Drupal 11.2.7 Core
User Management • Access Control • Content System
Registration
Custom Module
Groups
drupal/group 3.3.5
Forums
drupal/forum
Directory
Drupal Views
Anti-Spam
reCAPTCHA • Honeypot • Antibot
Theme
acwe2025_bootstrap
Database
MariaDB/MySQL
Environment:
• Drupal 11.2.7
• PHP 8.3.27
• Path: /var/www/acwe
• Custom Theme: acwe2025_bootstrap
• Database: MySQL-compatible

Registration & Approval System

The Problem We Solved

Traditional open registration leads to spam. Email-only approval is slow and burdensome. We needed a system that:

  • Prevents automated spam bots
  • Verifies in-person rehearsal attendance
  • Allows fast approvals (even at rehearsal)
  • Distributes approval workload to section leaders
  • Automatically assigns users to their section groups
Registration Form

Path: /user/register

Fields:

  • Username
  • Email address
  • Password
  • Primary Instrument/Section (dropdown)
  • Rehearsal Keyword (validation required)

Validation:

  • Keyword must match current rehearsal keyword
  • Case-insensitive comparison
  • Account created as "Blocked" (pending approval)
Approval Dashboard

Path: /admin/people/pending

Access: Registration Approver role

Features:

  • List of pending registrations
  • Shows: username, email, time waiting
  • Actions: Approve, Deny, View
  • Mobile-friendly for on-site approvals

Approval Actions:

  • Activates user account
  • Auto-assigns to section group
  • Sends welcome email
  • Logs action for accountability
Rehearsal Keyword System

Management: /admin/config/acwe/rehearsal-keyword

How It Works:

  1. Admin sets a simple keyword (e.g., "November", "Mozart", "Austin")
  2. Keyword is announced at rehearsals
  3. New members must enter it during registration
  4. Proves they attended a rehearsal in person
  5. Changed monthly or as needed for security

Storage: Drupal state API (acwe.rehearsal_keyword), tracks who updated it and when

Implementation: Custom Module (acwe_registration)
hook_form_alter() - Adds keyword field to registration form
• Custom validation handler - Checks keyword against state value
• Custom ApproveUserForm - Handles approval with group assignment
• KeywordSettingsForm - Admin UI for keyword management
• Routing: Custom routes for approval dashboard and keyword config

Seasonal Membership Management

The Challenge

ACWE has 4 seasons per year. Members may skip one or more seasons due to work, travel, health, or personal reasons. They should be able to take a break without losing their account and return seamlessly without re-registering.

Key Requirements: On-break members should not access Group content (especially music files), but can stay connected via forums. Members should be able to self-reactivate when they return.

Self-Service Keyword Reactivation

The Solution: Leverage the existing rehearsal keyword system for seasonal reactivation.

  1. Between Seasons: Section leader updates rehearsal keyword
  2. Season End: Admin clicks "End Season" - all active members → on break
  3. Member Returns: Logs in, enters current keyword at /user/reactivate
  4. Auto-Reactivation: Valid keyword → Status restored → Group access granted

Benefits: Self-service (no admin overhead), proves attendance, reuses existing infrastructure, forces keyword refresh each season

Selective Access Control

On-Break Members CAN Access:

  • Forums - Read and post (stay connected)
  • Member directory (see who's active)
  • Their user profile
  • General announcements

On-Break Members CANNOT Access:

  • Group content (section materials)
  • Sheet music / music library
  • Section-specific resources
Technical Implementation

New Field: field_member_status

  • active_season: Currently playing (default for new members)
  • on_break: Not currently participating

Reactivation Form: /user/reactivate

  • Keyword validation against current rehearsal keyword
  • Updates status to active_season
  • Re-adds to section Group based on field_primary_section
  • Flood control (same as registration)

Season End Control:

  • Admin button: "End Season" (confirmation required)
  • Bulk transitions all active_season → on_break
  • Removes members from all Section Groups
  • Logs transition with timestamp

Access Control Logic:

  • hook_user_update() - Toggles Group membership
  • Forums: Controlled by authenticated user role
  • Group content: Controlled by Group membership
  • Clean separation - no extra complexity
Edge Cases Considered
  • Section changes: Reactivation uses existing field_primary_section - admin can edit if member switches instruments
  • Mid-season breaks: Section leader can manually set member to on_break via user edit form
  • Multiple seasons off: Status persists as on_break until member reactivates - no time limit
  • Password reset: Works normally - reactivation is separate step after login
  • Invalid keyword attempts: Flood control prevents abuse (3 attempts per hour)
  • New members mid-season: Set to active_season by default during approval
  • Section leaders: Also go on_break between seasons (ensures current keyword)
  • Bulk performance: Season-end transition monitored - can add queue if needed
User Experience

For Returning Members:

  1. Member logs in after being on break
  2. Sees persistent notification banner: "Welcome back! Enter the current rehearsal keyword to reactivate your account"
  3. Clicks link or navigates to /user/reactivate
  4. Enters keyword heard at rehearsal
  5. Valid keyword → Access instantly restored, can see section content
  6. Invalid keyword → Message: "Please see your section leader at the next rehearsal"

For Active Members at Season End: Account automatically set to on_break. Simply reactivate with new keyword when next season begins. This maintains tight access control and ensures everyone has the current keyword.

Implementation Status: Planning Complete
field_member_status - User field (list_string, 2 values)
• ReactivateUserForm - Keyword validation, status update, Group re-join
• EndSeasonForm - Bulk status transition with confirmation
hook_user_update() - Auto Group membership toggle
• Notification block - Persistent banner for on_break members
• Member directory updated - Status column and filter
• All changes logged via Drupal logger service

Section Groups

Groups Module: The Foundation

We use the Groups module (v3.3.5) - one of Drupal's most mature and well-maintained contrib modules. It provides flexible group-based access control.

Why Groups Module: Battle-tested, supports complex permissions, integrates with all Drupal entities, will easily extend to music file management in Phase 2.

11 Section Groups Created
  • Conductor (Group ID: 1)
  • Flutes/Piccolos (ID: 2)
  • Oboes/Bassoons/English Horns (ID: 3)
  • Clarinets (ID: 4)
  • Bass Clarinets (ID: 5)
  • Saxophones (ID: 6)
  • French Horns (ID: 7)
  • Cornets/Trumpets (ID: 8)
  • Trombones (ID: 9)
  • Euphoniums/Tubas (ID: 10)
  • Percussion (ID: 11)
Auto-Assignment on Approval

Process:

  1. User selects section during registration
  2. Value stored in field_primary_section
  3. On approval, system reads section field
  4. Maps section to group ID
  5. Calls $group->addMember($user)
  6. User immediately has section access

Flexibility:

  • Section field and group membership are independent
  • Admins can manually adjust groups later
  • Supports multi-instrument players
  • Handles guest musicians
Important Architectural Decision

field_primary_section and Group Membership are Separate

The section field is for display/categorization. Group membership controls access. They're only linked automatically at registration. This provides flexibility:

  • Admins can change groups without changing display field
  • User can show "Trumpets" but be in both Trumpets and Trombones groups
  • Allows temporary access for substitutes
  • Groups can be used for other purposes (e.g., board members group)
Implementation:
• Groups 3.3.5 module
• 11 groups created via programmatic script
• Section field: list_string type with 11 allowed values
• Auto-assignment in ApproveUserForm::addUserToSectionGroup()
• Hardcoded mapping array (section keys → group IDs)

Forum System

Replacing Freelists.org

Forums serve as the replacement for the old Freelists.org email list. They provide threaded discussions, better spam control, and integrated access with the rest of the portal.

Key Feature: Members-only access. Anonymous users cannot see any forum content.

Forum Structure (3 Containers, 15 Forums)

1. Announcements Container

  • Official Announcements - Director/President posts only

2. General Container

  • General Discussion - Open topics for all members
  • Repertoire Discussion - Discuss pieces we're playing
  • Off-Topic & Social - Non-music related chat

3. Section Forums Container

  • 11 section-specific forums (one for each section group)
  • All members can view all section forums
  • Not restricted by group (fosters collaboration across sections)
Permissions

Authenticated Users:

  • View all forums
  • Create forum topics
  • Post replies
  • Edit own posts
  • Delete own posts

Registration Approvers (Section Leaders):

  • All authenticated user permissions
  • Edit ANY post
  • Delete ANY post
  • Administer forums
  • Moderate all forums (not just their section)
Members-Only Implementation

Two-Layer Protection:

1. Node Access (Content Level)

  • hook_node_access()
  • Returns NODE_ACCESS_DENY for anonymous users
  • Applies to all forum content nodes

2. Page Redirect (Forum Pages)

  • hook_preprocess_page()
  • Detects /forum paths
  • Redirects anonymous to homepage
  • Shows "Please login" message
Implementation:
• Forum module (drupal/forum) - Contrib module for D11
• Created via programmatic script (create_forums.php)
• Access control: Custom hooks in acwe_registration.module
• Permissions: Configured via Drupal permissions system
• Created: November 9, 2025

Member Directory

Privacy-Focused Directory

Shows only essential information: Name, Section, Member Since. No email addresses or phone numbers to prevent spam harvesting.

View Configuration

Path: /members/directory

Display Format: Table

Columns:

  • Name (username) - Sortable
  • Primary Section - Sortable
  • Member Since (creation date) - Sortable

Filters:

  • Status = Active (blocked users hidden)
  • Excludes admin accounts (uid 0, 1)
  • Exposed filter: Section dropdown

Other Features:

  • 50 members per page
  • Pager with page numbers
  • Default sort: Name (A-Z)
Custom Styling

Issue Solved: Default Views table had poor contrast (white text on light gray)

Solution: Custom CSS in theme

Styling Features:

  • Dark header background (ACWE primary color)
  • White text in headers (high contrast)
  • Alternating row colors
  • Hover effects
  • Mobile-responsive
  • Proper spacing and padding

Location:

  • acwe2025_bootstrap/css/style.css
  • Lines ~350-440
  • Uses CSS variables for ACWE colors
Implementation:
• Drupal Views (member_directory)
• Created via programmatic script (create_member_directory.php)
• Custom CSS library attached via hook_preprocess_views_view()
• Configuration saved to database (views.view.member_directory)
• Created: November 9, 2025

Security & Anti-Spam

Multi-Layer Protection Strategy

We implemented a defense-in-depth approach with four independent layers. Each layer catches different types of threats.

Layer 1: reCAPTCHA v3

Type: Invisible background scoring

Configuration:

  • Site Key: 6LfDogcsAAAAABGXPMA6YKSsCSoWVAPAHrWuYdpq
  • Secret Key: (configured, not displayed)

Enabled On:

  • Registration form
  • Password reset form
  • Contact forms

How It Works: Google scores user behavior (mouse movement, timing, browser fingerprint). Scores below threshold are blocked.

Layer 2: Honeypot + Antibot

Honeypot:

  • Hidden field that bots auto-fill
  • 5-second time limit (bots submit too fast)
  • All forms protected

Antibot:

  • JavaScript-based detection
  • 6-second delay before form is enabled
  • Requires human interaction

Effect: Catches automated bots that don't execute JavaScript or fill forms too quickly.

Layer 3: Rehearsal Keyword

Custom Verification Question

Concept:

  • Only announced at in-person rehearsals
  • Changed monthly or as needed
  • Not posted publicly online

Effect:

  • Proves physical attendance at rehearsal
  • Defeats all automated registration
  • Prevents random spam signups
  • Creates trusted initial registration pool
Layer 4: Manual Approval

Human Review (Final Barrier)

Process:

  • Section leaders review registrations
  • Verify they recognize the person
  • Check for suspicious patterns
  • Approve or deny with logging

Effect:

  • 100% effective against spam
  • Builds community (leaders know members)
  • Catches any edge cases
  • Accountability via approval logs
Result: Extremely Robust Protection

These four layers work together:

  1. Bots eliminated by reCAPTCHA, Honeypot, Antibot
  2. Random spammers blocked by keyword requirement
  3. Sophisticated attempts caught by human review
  4. Community protected without being burdensome to legitimate users

User Experience: Legitimate members experience minimal friction (keyword + invisible reCAPTCHA), while spam is effectively eliminated.

Modules Used:
• drupal/recaptcha - Google reCAPTCHA v3 integration
• drupal/honeypot - Hidden field and time-based protection
• drupal/antibot - JavaScript-based bot detection
• drupal/flood_control - Rate limiting for additional protection
• Custom: Rehearsal keyword validation in acwe_registration module

Module Stack

Installed & Configured

Core Functionality:

  • drupal/group (v3.3.5) - Section groups
  • drupal/gnode - Group Node integration
  • drupal/forum - Forum system

Anti-Spam & Security:

  • drupal/recaptcha - reCAPTCHA v3
  • drupal/honeypot - Bot trap
  • drupal/antibot - Bot detection
  • drupal/flood_control - Rate limiting

User Management:

  • drupal/pathauto - URL aliases
  • drupal/token - Token system
  • drupal/legal - Terms of Use
  • drupal/field_group - Field organization

Admin Tools:

  • drupal/admin_toolbar - Enhanced menu

Custom:

  • acwe_registration - Our custom module
Pending Installation

High Priority:

  • drupal/message - Notification system
  • drupal/message_notify - Email notifications
  • drupal/symfony_mailer_lite - Email handling
  • drupal/easy_email - Email templates
  • drupal/mailsystem - Mail configuration
  • drupal/contact_storage - Contact forms

Optional:

  • drupal/pwa - Progressive Web App features
NOT Used (D11 Incompatible)
  • drupal/advanced_forum - No D11 support
  • drupal/mailhandler - Not supported (reply-via-email)
  • drupal/message_subscribe - Not D11.2.7 compatible
  • drupal/subscribe - Not security covered

Future Features (Phase 2+)

High Priority (Before Full Launch)
  • Seasonal Membership Implementation
    • field_member_status field (planning complete)
    • Self-service reactivation form
    • Season-end bulk transition control
    • Group membership toggle logic
    • Notification banner for on-break members
  • Email Template System
    • Branded welcome emails
    • ACWE styling and logo
    • Consistent footer with links
  • Approver Notifications
    • Email when new registration arrives
    • Smart timing (immediate during rehearsal hours)
    • Daily digest for off-hours
  • Contact Forms
    • "Contact [Name]" on profiles
    • Email relay through Drupal
    • Protects email privacy
    • Rate limiting (5/hour)
Medium Priority (First Month)
  • Forum Enhancements
    • First-post moderation queue
    • "Report spam" button
    • Auto-queue after 3 reports
  • User Notification Preferences
    • Three-tier system (Critical, Important, Optional)
    • Per-user settings page
    • Control digest frequency
  • Admin Dashboard
    • Pending registrations widget
    • Moderation queue summary
    • Activity statistics
Low Priority (Future Enhancements)
  • Forum Digest Emails - Daily/weekly summaries of forum activity
  • PWA Features - Offline capability, push notifications, add-to-home-screen
  • Advanced Analytics - Detailed usage statistics and reporting
  • Social Features - Member profiles, avatars, private messaging

Music Management (Phase 2)

Architecture Decision: Drupal-Native Music Management

Original Plan: Integrate existing Python/FileMaker system via API

Revised Plan: Build music management natively in Drupal using Group content

Rationale: Groups module is already in place, provides perfect access control foundation, eliminates API complexity, and offers more flexibility for future features.

Planned Architecture

Content Type: Sheet Music

Fields:

  • Title (e.g., "March Militaire")
  • Composer
  • Arranger
  • Part/Instrument (e.g., "1st Trumpet")
  • File Upload (PDF)
  • Performance Date
  • Copyright Info
  • Retention Date (auto-calculated)

Group Integration:

  • Each music file is Group content
  • Assigned to one or more section groups
  • Only group members can access files
  • Uses Drupal's private file system
Copyright Compliance

Automatic Retention Management:

  • Retention date = Performance date + 2 weeks
  • Calculated automatically on save
  • Cron job checks daily for expired files
  • Email warnings 7 days before deletion
  • Auto-deletion after retention date

Admin Override:

  • Can mark files as "Keep indefinitely"
  • For public domain or owned music
  • Requires justification note

Audit Trail:

  • Log all uploads (who, when, what)
  • Log all deletions
  • Track who downloaded files
Implementation Plan
  1. Create Content Type - Sheet Music with all fields
  2. Configure Group Content Plugin - Make Sheet Music group-able
  3. Set Up Private Files - Secure file storage configuration
  4. Build Upload Form - For directors/section leaders
  5. Create Views - Music library by section, by concert
  6. Implement Retention System - Cron job for auto-deletion
  7. Add Notification System - "New music available" emails
  8. Build Admin Dashboard - Overview of all music, upcoming deletions

Estimated Time: 2-3 weeks (after core features are fully tested)

Technology Stack for Phase 2:
• Group content plugin (part of Groups module)
• Drupal private file system
• Custom content type (sheet_music)
• Computed field for retention date
• Cron hooks for automated deletion
• Message module for notifications
• Views for music library displays
• Custom module: acwe_music (to be created)

Technical Specifications

Server Environment
  • Drupal Version: 11.2.7
  • PHP Version: 8.3.27
  • Database: MySQL/MariaDB compatible
  • Web Server: Apache (assumed)
  • Base Path: /var/www/acwe
Key Components
  • Custom Module: acwe_registration
  • Theme: acwe2025_bootstrap (custom)
  • Groups: 11 Section Groups (IDs 1-11)
  • Forums: 3 containers, 15 forums
  • Views: member_directory
  • Roles: registration_approver
  • Field: field_primary_section (list_string)
Security Configuration
  • reCAPTCHA v3: Site key 6LfDogcsAAAAABGXPMA6YKSsCSoWVAPAHrWuYdpq
  • Honeypot: 5-sec time limit, all forms
  • Antibot: 6-sec delay
  • Forum Access: Members-only via hooks
  • Keyword Storage: Drupal state API
Data Storage
  • Users: Drupal user entity + field_primary_section
  • Groups: Group module tables
  • Forums: Taxonomy vocabulary + Forum nodes
  • Directory: View configuration (database)
  • Keyword: State API (acwe.rehearsal_keyword)
  • Logs: Drupal logger + watchdog table
Custom Module Structure: acwe_registration
modules/custom/acwe_registration/
├── acwe_registration.info.yml          (Module metadata)
├── acwe_registration.module            (Hooks: form_alter, node_access, preprocess)
├── acwe_registration.routing.yml       (Routes for approval dashboard, keyword config)
├── acwe_registration.permissions.yml   (Custom permissions)
├── src/
│   ├── Controller/
│   │   └── PendingRegistrationsController.php  (Approval dashboard)
│   └── Form/
│       ├── ApproveUserForm.php        (Approval confirmation with group assignment)
│       ├── DenyUserForm.php           (Denial confirmation)
│       └── KeywordSettingsForm.php    (Keyword management)
└── config/
    └── install/
        └── acwe_registration.settings.yml  (Default config)

Summary: What Makes This Architecture Solid

Key Strengths of Our Approach

1. Drupal-Native
  • Single platform (no external integrations)
  • Leverages Drupal's strengths
  • Easier to maintain long-term
  • No API complexity
2. Proven Modules
  • Groups 3.3.5 (battle-tested)
  • Forum (official contrib)
  • reCAPTCHA, Honeypot (widely used)
  • Active security support
3. Fast Development
  • 3 days vs. 12 weeks
  • Minimal custom code
  • Configuration over code
  • Rapid testing possible
4. Flexible
  • Easy to add features
  • Groups extend to music mgmt
  • Permissions finely tunable
  • Scales with ensemble
5. Secure
  • Four-layer spam protection
  • Human approval workflow
  • Members-only content
  • Audit logging throughout
6. User-Friendly
  • Mobile-optimized
  • Fast approval at rehearsal
  • Intuitive navigation
  • Comprehensive docs

Ready for Testing

All core features are complete and tested. The system is ready for a small group pilot (5-10 users) to validate the workflow before broader rollout.

Next Step: Set the rehearsal keyword, identify test users, and run through the complete registration → approval → forum posting → directory workflow.