Adds comprehensive job submission, editing, and deactivation functionality with proper form handling and permissions. Includes administrator capabilities for job_offer management and fixed dashboard navigation.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added ddhhRedirectTriggered flag to ensure only one redirect
- Clear interval immediately when success message detected
- Removed MutationObserver which was causing duplicate events
- Simplified JavaScript to single interval-based check
- Changed from event listener to MutationObserver
- Watches for success message text to appear in form
- Monitors form DOM for changes and redirects when success appears
- More reliable than waiting for custom events
- Added set_registration_redirect() filter on frm_json_response
- Modifies Formidable's JSON response to include redirect_url
- Formidable JavaScript will then redirect to /anbieter-dashboard/
- Fixes registration redirect issue
- Registration form now redirects to /anbieter-dashboard/ after successful submission
- Configured via form options redirect_url setting
- Fixes missing redirect after registration
- Changed !== to != to allow type coercion
- Fixes registration not working due to string vs int comparison
- Added type debugging to help diagnose similar issues
- Changed FrmFormAction::create() to FrmFormActionsController::create_action()
- Updated class_exists checks to use FrmFormActionsController
- Fixes fatal error on plugin activation with modern Formidable Forms
- Discovered during provider flow testing (Plan 07-01)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add process_mentor_notification_batch() method to process Action
Scheduler callbacks. Sends German email notifications with job details
(title, location, type, permalink) to batches of opted-in mentors.
Includes rate limit delay (0.1s between emails), error logging for
failures, and unsubscribe hint in email body. Registered callback for
'ddhh_jm_send_mentor_notification_batch' hook.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add notify_mentors_on_job_publish() method to trigger async batch
notifications when jobs transition to publish status. Only triggers on
initial publish (not updates). Queries opted-in mentors via User
Preferences class and schedules batches via Scheduler class. Logs
notification scheduling with mentor count and batch count.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created DDHH_JM_Scheduler class with static setup_hooks() method
- Added schedule_mentor_notification_batch() method with 50-user batching
- Uses as_enqueue_async_action() with unique flag and email-notifications group
- Initialized in main plugin file and job manager class
- Ready for Phase 06-03 to register async action callbacks
Add protect_single_job() method to access control class following the
same pattern as job archive protection. Non-logged-in users are
redirected to /anbieter-login/ when attempting to access individual
job_offer posts. Logged-in users (any role) can view job details.
Completes backend infrastructure for Phase 5 mentor job board. All ACF
fields and application form ready for Elementor template integration.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add send_provider_application_notification() method:
- Hooks to frm_after_create_entry for job application form submissions
- Extracts applicant details (name, email, message) and job_id
- Fetches job details (title, location, type) from post and ACF fields
- Sends email to provider contact email (job_contact_email ACF field)
- Email includes full applicant info and job context
- Provider can reply directly to applicant email
- Error logging for missing contact email or wp_mail failures
Hook registered in setup_hooks() method
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add DDHH_JM_Archive class to modify job archive queries. Ensures
Elementor Loop Grid displays only published jobs sorted by date
(newest first) with no pagination limit.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add create_job_application_form() method with 4 fields:
- applicant_name (text, required)
- applicant_email (email, required, pre-filled for logged-in users)
- applicant_message (textarea, required)
- job_id (hidden)
Form title: "Jetzt bewerben"
Submit button: "Bewerbung absenden"
Success message: "Ihre Bewerbung wurde versendet. Der Anbieter wird sich bei Ihnen melden."
Added get_job_application_form_id() helper method
Registered form creation in setup_registration_hooks()
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add register_image_sizes() method to DDHH_JM_Post_Types class that
registers 'job-logo' image size (200x200px with hard crop). Hook
method to after_setup_theme action for proper WordPress timing.
This enables Elementor templates to request consistent logo sizing
via wp_get_attachment_image() using size 'job-logo'. Auto-generates
cropped version when logos uploaded via ACF field.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Protect job archive from public access by redirecting non-logged-in users
to /anbieter-login/. Only authenticated users (mentors/subscribers) can
browse the /jobangebote/ archive.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added send_admin_job_deactivation_notification() method
- Hooks transition_post_status with guards: draft status AND old publish
- Extracts deactivation reason from ACF field job_deactivation_reason
- Email subject: "Stellenangebot deaktiviert: {job_title}"
- Email includes job details, provider info, and deactivation reason
- Sends to admin_email with edit link for review
- Error logging for missing admin_email or wp_mail failure
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add create_job_deactivation_form() method following established pattern from create_job_edit_form(). Form includes deactivation_reason textarea (required, German label) and hidden job_id field. Configure Update Post action to set post_status='draft' removing job from public view. Map deactivation_reason to ACF meta field 'job_deactivation_reason'. Add ownership validation hook validate_job_deactivation_ownership() following same pattern as validate_job_ownership() to prevent URL tampering. Submit button: "Stellenangebot deaktivieren". Success message: "Ihr Stellenangebot wurde deaktiviert." Redirect to /anbieter-dashboard/. Add get_job_deactivation_form_id() helper following established pattern.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add admin UI class to main plugin bootstrap and initialize hooks in admin context only:
- Require class-admin-ui.php in main plugin file
- Initialize Admin_UI::setup_hooks() only when is_admin() is true
- Performance optimization: admin hooks don't load on frontend
Admin moderation interface now provides efficient job listing with custom columns and status filters.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Create DDHH_JM_Admin_UI class with custom columns for job_offer admin listing:
- Anbieter (provider org name + contact person from user meta)
- Standort (location from ACF field)
- Art (job type from ACF field)
- Eingereicht am (submission date)
Removed default author/date columns to reduce clutter. Made columns sortable for efficient moderation workflow.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added require statement for class-notifications.php in main plugin file
- Initialized notification hooks via init action in main class
- Pattern consistent with existing class initializations (Access_Control, Dashboard, etc.)
- Notifications will trigger on transition_post_status after WordPress fully loads
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add create_job_edit_form() method to programmatically create edit form
- Form uses 'job_edit' key and matches submission form fields exactly
- Configure Update Post action to update existing job_offer posts
- Add validate_job_ownership() method with frm_validate_entry hook
- Security: Validates job_id parameter, post_type, and post_author match
- Prevents URL parameter tampering by malicious providers
- Add get_job_edit_form_id() helper method
- Form pre-populates from post ID via URL parameter
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created DDHH_JM_Notifications class with email notification system
- Hook into transition_post_status to detect new pending job submissions
- Send German email to admin with job details and edit link
- Email includes: title, author, org, location, type, date, edit link
- Only trigger on new → pending transition to avoid spam on updates
- Log errors if wp_mail() fails (common in Local WP)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add create_job_submission_form() method to DDHH_JM_Formidable class
- Create Formidable form with key 'job_submission'
- Add 7 fields with German labels: title, description, location, type, deadline, email, logo
- Configure Create Post action: post_type='job_offer', status='pending'
- Map form fields to ACF fields: job_location, job_type, job_deadline, job_contact_email, job_logo
- Add get_job_submission_form_id() helper method
- Hook form creation into init action
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create DDHH_JM_Access_Control class with redirect logic
- Redirect providers from WP-Admin to dashboard page
- Preserve access to profile.php for password/email changes
- Preserve access to admin-ajax.php for AJAX requests
- Integrate access control hooks in main plugin class
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add create_login_page() method to DDHH_JM_Pages class
- Combined view with registration form (Formidable shortcode) and login form (wp_login_form)
- German headings: "Neu registrieren" and "Bereits registriert?"
- Responsive two-column layout (desktop) with flexbox
- Stacked layout on mobile (< 768px)
- Inline CSS styling for visual separation and consistency
- Background colors and borders for section distinction
- Page accessible at /anbieter-login/
- Duplicate prevention: checks for existing page by slug
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Phase 2 Plan 1 execution summary:
- Created Formidable registration form with 5 German-labeled fields
- Implemented auto-login and ddhh_provider role assignment
- Email uniqueness and password validation enforced
- Organization name stored as user meta
- All tasks completed successfully with 2 commits
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created templates/provider-dashboard.php with:
- User role check (ddhh_provider)
- WP_Query for current user's job_offer posts
- Table display with German column headings
- Status badges (Veröffentlicht/Ausstehend/Entwurf)
- Edit and View action links
- Empty state message
- Responsive CSS styling
- Created includes/class-dashboard.php with:
- Template loader method
- Shortcode registration [ddhh_provider_dashboard]
- Output buffering for shortcode content
Dashboard queries only current user's posts with proper capability checking.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added class-formidable.php to autoload in main plugin file
- Hooked setup_registration_hooks() to init action
- Added missing class-roles.php and class-acf-fields.php to autoload
- Ensures Formidable integration runs after Formidable Forms loads
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created DDHH_JM_Formidable class with registration form setup
- Programmatic form creation with 5 German-labeled fields
- Email uniqueness validation enforced
- Auto-login after successful registration
- ddhh_provider role assignment on user creation
- Organization name stored as user meta (ddhh_org_name)
- Duplicate submission prevention via email_exists() check
- Password validation (min 8 chars, confirmation match)
- Username generation from email prefix with uniqueness check
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add DDHH_JM_ACF_Fields class with register_fields() method
- Field group 'Job Details' for post_type job_offer
- 6 fields: job_location (text, required), job_type (select, required),
job_deadline (date_picker), job_contact_email (email, required),
job_logo (image), job_deactivation_reason (textarea, conditional)
- German labels: Standort, Art, Bewerbungsfrist, Kontakt-E-Mail, Logo
- Logo returns ID for media library integration
- Deactivation reason field shows only when status != publish
- Hooked to 'acf/init' action
- Add DDHH_JM_Roles class with add_roles() and remove_roles() methods
- Role 'ddhh_provider' (Anbieter) can edit/delete own job_offers only
- Jobs go to pending status (publish_job_offers: false)
- No access to regular posts/pages (edit_posts/edit_pages: false)
- Can upload files for logos
- Integrated with Activator and Deactivator hooks
- Create main singleton class
- Create activator with version checks
- Create deactivator with rewrite flush
- Update main file to include core classes
This is a blocking fix (Rule 3) - Plan 01-01 was not completed but
these files are required for Plan 01-02 to execute.
- Add main singleton class DDHH_JM_Job_Manager
- Implement activation handler with WP/PHP version checks
- Implement deactivation handler with rewrite flush
- Use transient-based rewrite flush to avoid multiple flushes
- Add comprehensive security checks and documentation
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>