diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c9ff9e4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,277 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Digital Dabei Job Manager is a WordPress plugin providing a closed job board for "digital dabei Hamburg". External organizations (providers) self-register and manage job listings. Mentors (existing subscribers) view and apply to jobs. All jobs require admin moderation before publication. + +**Core principle**: Every job goes through admin approval before mentors see it. The moderation flow is the trust layer protecting mentors from spam. + +## Development Environment + +- **Platform**: Local WP (WordPress development environment) +- **WordPress**: 6.0+, PHP 7.4+ +- **Required Plugins**: ACF Pro, Formidable Forms Pro, Elementor Pro +- **Email**: WP Mail SMTP on production; disabled in Local WP dev environment + +## Development Workflow with GSD + +**IMPORTANT**: Use `/gsd` commands for all code-related work in this repository. + +The GSD (Get Stuff Done) workflow provides: +- Structured planning and execution phases +- Automatic verification of changes +- State management across sessions +- Parallel execution when possible + +Common commands: +- `/gsd:progress` - Check current state and next steps +- `/gsd:plan-phase` - Plan implementation for a phase +- `/gsd:execute-phase` - Execute all plans in a phase +- `/gsd:help` - View all available GSD commands + +## Architecture + +### Plugin Entry Point + +The plugin follows WordPress singleton pattern with class-based initialization: + +1. `ddhh-job-manager.php` - Main plugin file that: + - Loads Action Scheduler vendor library **first** (required for async operations) + - Defines constants (DDHH_JM_VERSION, DDHH_JM_PLUGIN_DIR, etc.) + - Requires all class files from `includes/` + - Initializes via `DDHH_JM_Job_Manager::get_instance()` on `plugins_loaded` hook + +2. `includes/class-ddhh-job-manager.php` - Singleton orchestrator that initializes all subsystems via `init_hooks()` + +### Core Subsystems + +The plugin is organized into focused class files in `includes/`: + +**Foundation Layer**: +- `class-post-types.php` - Registers `job_offer` CPT with custom capabilities +- `class-roles.php` - Manages `ddhh_provider` role with restricted capabilities +- `class-acf-fields.php` - Programmatically registers ACF field groups for job metadata +- `class-activator.php` / `class-deactivator.php` - Plugin lifecycle hooks + +**User Management**: +- `class-formidable.php` - Integration layer for all Formidable Forms (registration, job submission, edit, deactivation, application). Uses form keys like 'provider_registration', 'job_submission', etc. +- `class-access-control.php` - Blocks providers from WP-Admin, enforces login requirements on job archives +- `class-user-preferences.php` - Manages mentor opt-in preferences for job notifications + +**Job Workflows**: +- `class-dashboard.php` - Provides `[ddhh_provider_dashboard]` shortcode displaying provider's jobs +- `class-pages.php` - Creates/manages plugin pages (dashboard, login) via option storage +- `class-archive.php` - Filters job queries to show only published jobs to appropriate users +- `class-template.php` - Handles single job display and contact form modal + +**Notifications & Async Processing**: +- `class-notifications.php` - Email system triggered by WordPress hooks (`ddhh_job_submitted`, `transition_post_status`, `frm_after_create_entry`) +- `class-scheduler.php` - Action Scheduler wrapper for async batch email processing (chunks of 50 users) + +**Admin Experience**: +- `class-admin-ui.php` - Enhances admin screens with custom columns, quick links, status indicators + +### Custom Roles & Capabilities + +**ddhh_provider role**: Restricted role for external organizations +- Can create/edit/delete only their own `job_offer` posts +- Cannot access WP-Admin (redirected to frontend dashboard) +- Can edit their own profile (`profile.php` access allowed) + +**Capability mapping** (in `class-post-types.php`): +- `edit_job_offer` - Owner can edit their own jobs +- `delete_job_offer` - Owner can delete their own jobs +- `read_job_offer` - Users can read published jobs +- Standard WordPress `map_meta_cap` filter enforces ownership + +### Formidable Forms Integration + +The plugin uses 5 Formidable forms, identified by form keys: +1. **provider_registration** (F1) - Creates user with `ddhh_provider` role, auto-login +2. **job_submission** (F2) - Creates `job_offer` post with `pending` status, maps ACF fields +3. **job_edit** (F3) - Updates existing job (ownership validated), preserves submission date +4. **job_deactivation** (F4) - Sets status to `draft`, captures deactivation reason +5. **job_application** (F5) - Mentor applies to job, emails provider contact address + +**Form hooks** (`class-formidable.php`): +- Registration: `frm_after_create_entry` → auto-login → redirect to dashboard +- Job submission: `frm_after_create_entry` → fires `ddhh_job_submitted` action → admin notification +- Job edit: `frm_after_update_entry` → fires `ddhh_job_edited` action → admin notification +- Application: `frm_after_create_entry` → emails provider's contact email from ACF field + +### Email Notification System + +**Immediate notifications** (`class-notifications.php`): +- Admin receives email on job submission with edit/view links +- Admin receives email on job edit with change summary +- Admin receives email on deactivation with reason +- Provider receives email when mentor applies (via Formidable) + +**Async batch notifications** (`class-scheduler.php`): +- Mentor notification on job publish uses Action Scheduler +- Users are queried for `ddhh_jm_notification_optin` meta +- Batched into groups of 50 to prevent email provider limits +- Each batch scheduled as separate `ddhh_jm_send_mentor_notification_batch` action + +### Access Control Layers + +1. **WP-Admin lockout**: Providers redirected to dashboard unless accessing `profile.php` or AJAX +2. **Dashboard protection**: `template_redirect` hook checks user role, redirects non-providers +3. **Archive protection**: Jobs require login; only `publish` status shown to mentors +4. **Single job protection**: Login required to view job details +5. **Ownership validation**: Forms validate current user owns the job before editing/deactivation + +### Template System + +**Frontend templates** (`templates/`): +- `provider-dashboard.php` - Displays provider's jobs in table format with status badges and action links + +**Elementor integration**: +- Job archive uses Elementor Loop Grid (no custom template file) +- Job detail page uses Elementor single post template with ACF dynamic tags +- Contact form modal injected via `the_content` filter in `class-template.php` + +### Job Lifecycle + +``` +Provider submits → pending → Admin reviews → publish → Mentors see + notified + ↓ + Admin can reject → trash +Provider deactivates → draft (with reason stored in meta) +``` + +**Status hooks**: +- `ddhh_job_submitted` - Fired after new job metadata saved (form entry ID passed) +- `ddhh_job_edited` - Fired after job update metadata saved (form entry ID passed) +- `transition_post_status` - Used for publish detection and deactivation detection + +### ACF Field Groups + +Programmatically registered in `class-acf-fields.php`: +- **Job Details**: `job_location`, `job_type`, `job_deadline` (date picker), `contact_email` +- **Deactivation**: `deactivation_reason`, `deactivation_date` +- **Metadata**: `submission_date` (preserved during edits) + +All fields support German labels matching site language. + +## Development Workflow + +### Testing Locally + +Since email is disabled in Local WP, test email functionality by: +1. Checking Action Scheduler status: WP Admin → Tools → Scheduled Actions +2. Monitoring logs: `error_log()` calls throughout notification classes +3. Using a plugin like WP Mail Logging to capture emails during dev + +### Working with Forms + +Formidable forms are managed through the Formidable UI. When referencing forms in code: +- Use form keys (e.g., 'job_submission') not IDs +- Form ID lookup happens via `FrmForm::getOne('form_key')` +- Field IDs are stored in form configuration, not hardcoded + +### Modifying Email Templates + +Email templates are inline in `class-notifications.php`: +- Admin emails use `wp_mail()` with HTML content +- Mentor notification emails use `wp_mail()` with plain text +- All emails include job title, provider name, and relevant links +- Date formatting: Use `get_field()` for ACF dates, format with German locale + +### Adding New Notifications + +1. Identify triggering event (form submission, status change, custom action) +2. Add notification method to `class-notifications.php` +3. Hook into appropriate action in `setup_hooks()` +4. For batch processing, use `DDHH_JM_Scheduler::schedule_mentor_notification_batch()` + +## Common Tasks + +### Debugging Action Scheduler + +```bash +# View scheduled actions in WP CLI +wp action-scheduler list --status=pending +wp action-scheduler list --status=failed + +# Run pending actions manually +wp action-scheduler run --batch-size=10 + +# Clean up old actions +wp action-scheduler clean --days=90 +``` + +### Checking Provider Permissions + +Providers should NOT be able to: +- Access WP-Admin (except profile.php) +- Edit other providers' jobs +- Publish jobs directly (must be pending) +- View unpublished jobs by other providers + +### Verifying Job Moderation Flow + +1. Log in as provider → Submit job → Should create `pending` post +2. Admin receives email with job details and moderation links +3. Admin publishes job → Opted-in mentors receive notification (async batches) +4. Provider sees published job in dashboard +5. Mentors can view and apply to job + +### Date Field Handling + +Job deadlines use ACF date picker: +- Format conversion in Formidable forms: Slashes auto-converted to dots for display +- Before submission: Dots converted back to ISO format (YYYY-MM-DD) +- Display: Use `get_field('job_deadline')` returns 'Ymd', format with `date_i18n()` +- Validation: Date fields are optional (empty string is valid) + +### Logo Auto-Cropping + +Featured images (logos) auto-crop to 200×200px on upload: +- Handled by `class-post-types.php` registering 'job-logo' image size +- Uses WordPress `add_image_size()` with hard crop enabled +- Images uploaded via Formidable forms trigger standard WP media processing + +## Project State + +**Currently**: Phase 6 (Email Notifications) is mostly complete. Phase 7 (Testing & Polish) is next. + +**What's working**: +- Provider registration and authentication +- Job submission, editing, deactivation workflows +- Admin moderation and notifications +- Mentor job browsing and applications +- Opt-in preferences for mentor notifications +- Action Scheduler integration for async processing + +**What's pending**: +- Phase 6.3: Async batch email processing on job publish (implementation in progress) +- Phase 7: End-to-end testing and production deployment prep + +**Documentation**: See `.planning/` for detailed project documentation: +- `PROJECT.md` - Requirements and context +- `ROADMAP.md` - Phase breakdown and progress +- `STATE.md` - Current work and blockers +- `phases/` - Detailed plans and summaries for each phase + +## Code Standards + +- **WordPress Coding Standards**: Follow WordPress PHP coding standards +- **Text Domain**: Use `ddhh-job-manager` for all translatable strings +- **German Language**: All user-facing text in German +- **Security**: Validate ownership, escape output, verify nonces in forms +- **Hook Naming**: Use `ddhh_jm_` prefix for custom actions/filters +- **Class Naming**: Use `DDHH_JM_` prefix, one class per file +- **Static Methods**: Use static methods for classes that don't maintain state +- **Direct Exit**: Always include `defined( 'ABSPATH' ) || exit;` at top of PHP files + +## Critical Files to Review Before Major Changes + +- `ddhh-job-manager.php` - Plugin initialization order matters (Action Scheduler first) +- `class-ddhh-job-manager.php` - Hook registration sequence +- `class-formidable.php` - Form IDs and field mappings +- `class-notifications.php` - Email templates and triggering logic +- `class-access-control.php` - Security boundaries for providers +- `class-post-types.php` - Capability mapping for `job_offer` posts