--- phase: 04-job-deactivation-system plan: 02 subsystem: notifications tags: [email, admin-notification, deactivation, wp-mail] # Dependency graph requires: - phase: 04-job-deactivation-system provides: [job-deactivation-form, deactivation-reason-capture] - phase: 03-job-management-core provides: [admin-notification-pattern, transition-post-status-pattern] provides: - admin-deactivation-notification - deactivation-reason-visibility affects: [] # Tech tracking tech-stack: added: [] patterns: [deactivation-notification-pattern] key-files: created: [] modified: [includes/class-notifications.php] key-decisions: - "Deactivation notification triggered only on publish→draft transitions" - "Email includes deactivation reason for business intelligence" - "Used current_time() for deactivation timestamp instead of post modified date" - "Fallback text 'Kein Grund angegeben' if reason field empty" patterns-established: - "Deactivation notification mirrors submission notification pattern" issues-created: [] # Metrics duration: 5 min completed: 2026-01-14 --- # Phase 4 Plan 2: Deactivation Notifications Summary **Admin receives German email on job deactivation with provider details and business intelligence reason** ## Performance - **Duration:** 5 min - **Started:** 2026-01-14T[start] - **Completed:** 2026-01-14T[end] - **Tasks:** 1 - **Files modified:** 1 ## Accomplishments - Added send_admin_job_deactivation_notification() method to notifications class - Configured hook to detect publish→draft status transitions only - Email captures complete job context: title, provider, organization, location, type - Email includes deactivation reason from ACF field for business intelligence - Deactivation timestamp shows current time (when deactivation occurred) - Error logging handles missing admin_email or wp_mail failures - Follows same pattern as existing submission notification method ## Task Commits Each task was committed atomically: 1. **Task 1: Add deactivation notification method to notifications class** - `4a1c05c` (feat) ## Files Created/Modified - `includes/class-notifications.php` - Added send_admin_job_deactivation_notification() method that hooks transition_post_status; guards ensure only publish→draft transitions trigger notification; extracts deactivation_reason from ACF field; sends German email to admin_email with job details, provider info, reason, and edit link; includes error logging for failures ## Email Template The email sent to admin includes: **Subject:** Stellenangebot deaktiviert: {job_title} **Body:** ``` Ein Stellenangebot wurde vom Anbieter deaktiviert. Titel: {job_title} Anbieter: {author_name} ({author_organization}) Standort: {job_location} Art: {job_type} Deaktiviert am: {deactivation_date} Grund für Deaktivierung: {deactivation_reason} Stelle ansehen: {edit_link} --- Diese E-Mail wurde automatisch gesendet. ``` ## Technical Details **Hook Implementation:** - Hook: `transition_post_status` with priority 10, accepts 3 params ($new_status, $old_status, $post) - Guards: Only trigger when post_type === 'job_offer' AND new_status === 'draft' AND old_status === 'publish' - This ensures only genuine deactivations trigger email (not initial drafts or draft saves) **Data Extraction:** - Admin email: `get_option('admin_email')` - Author org: `get_user_meta($post->post_author, 'ddhh_org_name', true)` - ACF fields: `get_field('job_location')`, `get_field('job_type')`, `get_field('job_deactivation_reason')` - Deactivation date: `current_time('d.m.Y H:i')` - shows when deactivation occurred - Edit link: `get_edit_post_link($post_id, '')` **Email Sending:** - Function: `wp_mail()` - Headers: 'Content-Type: text/html; charset=UTF-8' - Body: Plain text converted to HTML with `nl2br(esc_html())` - Error handling: Logs to error_log if admin_email missing or wp_mail() returns false **Business Intelligence:** The deactivation reason is the key addition - it allows admins to track patterns: - "Position filled" - Success metric - "Budget cut" - External factor - "Wrong category" - Platform usability issue - "Not enough applicants" - Quality/reach concern This data informs platform improvements and provider support strategies. ## Decisions Made **current_time() vs post modified date:** Used current_time() to capture the actual deactivation moment rather than post modified date. This provides accurate timing for admin records and distinguishes deactivation from other updates. **Fallback for empty reason:** If deactivation_reason field is empty (shouldn't happen with required field, but defensive), email shows "Kein Grund angegeben" rather than breaking or showing null. **Same pattern as submission notification:** Followed established pattern from send_admin_new_job_notification() for consistency. Makes code predictable and maintainable. **Email format matches submission:** Plain text with nl2br() conversion, German language, similar structure. Admin can process both notification types with same mental model. ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered None - implementation was straightforward following the established notification pattern from Phase 03-03. ## Next Step Phase 4 complete. Ready for Phase 5: Mentor Job Board. The job deactivation system is now fully functional end-to-end: - Providers can deactivate published jobs with required reason via dashboard form (04-01) - Admins receive immediate email notification with full context and business intelligence (04-02) - Jobs are removed from public view (draft status) while preserving admin access --- *Phase: 04-job-deactivation-system* *Completed: 2026-01-14*