Compare commits
10 Commits
90525cd0ea
...
369870ef00
| Author | SHA1 | Date | |
|---|---|---|---|
| 369870ef00 | |||
| 1b41b72a3d | |||
| 907b5a9924 | |||
| a87b48df68 | |||
| f229af23f5 | |||
| 18ddcd5e0a | |||
| 0c9ebb9e89 | |||
| 9142b56a9f | |||
| 32a22e5fd6 | |||
| 7f2c5fa6a6 |
@@ -27,6 +27,41 @@
|
|||||||
5. Modify job detail template to display provider's company logo instead
|
5. Modify job detail template to display provider's company logo instead
|
||||||
6. Add logo display to provider dashboard (show their uploaded logo)
|
6. Add logo display to provider dashboard (show their uploaded logo)
|
||||||
|
|
||||||
|
## Date Field Format and Pre-population Issues (Discovered during 07-01 testing)
|
||||||
|
|
||||||
|
**Issue:** The deadline date field has multiple formatting and pre-population problems
|
||||||
|
|
||||||
|
**Current Behavior:**
|
||||||
|
1. **Initial display**: Shows as "2026-01-31" (YYYY-MM-DD) instead of German format
|
||||||
|
2. **Date picker bug**: Shows wrong date (current date instead of saved value)
|
||||||
|
- Saved: 2026-01-31
|
||||||
|
- Picker shows: 2026-01-17 (today)
|
||||||
|
3. **Post-selection format**: Changes to "13/02/2026" (DD/MM/YYYY with slashes)
|
||||||
|
|
||||||
|
**Expected Behavior:**
|
||||||
|
- Date should always display as "31.01.2026" (DD.MM.YYYY with dots)
|
||||||
|
- Date picker should show the saved date, not current date
|
||||||
|
- Format should remain consistent before and after selection
|
||||||
|
|
||||||
|
**Location:**
|
||||||
|
- Provider Dashboard edit form (`/anbieter-dashboard/?action=edit_job&job_id=XXX`)
|
||||||
|
- Specifically the "Bewerbungsfrist" (deadline) field
|
||||||
|
|
||||||
|
**Impact:** Medium - Confusing UX, users might select wrong dates
|
||||||
|
**Priority:** Medium
|
||||||
|
**Phase Discovered:** 07-01 (Provider flow testing)
|
||||||
|
|
||||||
|
**Root Cause:**
|
||||||
|
This is a Formidable Forms date field configuration issue, not a plugin code issue.
|
||||||
|
|
||||||
|
**Fix Required:**
|
||||||
|
1. Access Formidable Forms field settings for "job_deadline2" field
|
||||||
|
2. Set date format to: `d.m.Y` (PHP date format for DD.MM.YYYY)
|
||||||
|
3. Configure datepicker options to use dots instead of slashes
|
||||||
|
4. Ensure the JavaScript prepopulation correctly parses the saved YYYY-MM-DD value
|
||||||
|
5. May need custom JavaScript to convert saved value to picker-friendly format
|
||||||
|
6. Test thoroughly: initial load, picker display, post-selection format
|
||||||
|
|
||||||
## Empty "Art der Stelle" Dropdown (Fixed during 07-01 testing)
|
## Empty "Art der Stelle" Dropdown (Fixed during 07-01 testing)
|
||||||
|
|
||||||
**Issue:** The "Art der Stelle" dropdown was empty on job submission form
|
**Issue:** The "Art der Stelle" dropdown was empty on job submission form
|
||||||
|
|||||||
277
CLAUDE.md
Normal file
277
CLAUDE.md
Normal file
@@ -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
|
||||||
@@ -41,6 +41,7 @@ require_once DDHH_JM_PLUGIN_DIR . 'includes/class-dashboard.php';
|
|||||||
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-access-control.php';
|
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-access-control.php';
|
||||||
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-notifications.php';
|
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-notifications.php';
|
||||||
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-archive.php';
|
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-archive.php';
|
||||||
|
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-template.php';
|
||||||
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-admin-ui.php';
|
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-admin-ui.php';
|
||||||
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-user-preferences.php';
|
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-user-preferences.php';
|
||||||
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-scheduler.php';
|
require_once DDHH_JM_PLUGIN_DIR . 'includes/class-scheduler.php';
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ class DDHH_JM_Job_Manager {
|
|||||||
// Initialize post types
|
// Initialize post types
|
||||||
add_action( 'init', array( 'DDHH_JM_Post_Types', 'register' ) );
|
add_action( 'init', array( 'DDHH_JM_Post_Types', 'register' ) );
|
||||||
|
|
||||||
|
// Upgrade roles with any new capabilities
|
||||||
|
add_action( 'init', array( 'DDHH_JM_Roles', 'upgrade_roles' ) );
|
||||||
|
|
||||||
// Initialize ACF fields
|
// Initialize ACF fields
|
||||||
add_action( 'acf/init', array( 'DDHH_JM_ACF_Fields', 'register_fields' ) );
|
add_action( 'acf/init', array( 'DDHH_JM_ACF_Fields', 'register_fields' ) );
|
||||||
|
|
||||||
@@ -68,6 +71,9 @@ class DDHH_JM_Job_Manager {
|
|||||||
// Initialize archive query helper
|
// Initialize archive query helper
|
||||||
add_action( 'init', array( 'DDHH_JM_Archive', 'setup_hooks' ) );
|
add_action( 'init', array( 'DDHH_JM_Archive', 'setup_hooks' ) );
|
||||||
|
|
||||||
|
// Initialize template display
|
||||||
|
add_action( 'init', array( 'DDHH_JM_Template', 'setup_hooks' ) );
|
||||||
|
|
||||||
// Initialize admin UI enhancements (admin-only)
|
// Initialize admin UI enhancements (admin-only)
|
||||||
if ( is_admin() ) {
|
if ( is_admin() ) {
|
||||||
add_action( 'init', array( 'DDHH_JM_Admin_UI', 'setup_hooks' ) );
|
add_action( 'init', array( 'DDHH_JM_Admin_UI', 'setup_hooks' ) );
|
||||||
|
|||||||
@@ -174,6 +174,9 @@ class DDHH_JM_Formidable {
|
|||||||
|
|
||||||
// Hook to pre-populate edit form fields
|
// Hook to pre-populate edit form fields
|
||||||
add_filter( 'frm_get_default_value', array( __CLASS__, 'prepopulate_edit_form_fields' ), 10, 3 );
|
add_filter( 'frm_get_default_value', array( __CLASS__, 'prepopulate_edit_form_fields' ), 10, 3 );
|
||||||
|
|
||||||
|
// Hook to pre-populate job_id in application form
|
||||||
|
add_filter( 'frm_get_default_value', array( __CLASS__, 'prepopulate_application_job_id' ), 10, 3 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -619,7 +622,7 @@ class DDHH_JM_Formidable {
|
|||||||
break;
|
break;
|
||||||
case 'job_type':
|
case 'job_type':
|
||||||
case 'job_type2':
|
case 'job_type2':
|
||||||
$job_type = sanitize_text_field( $value );
|
$job_type = strtolower( sanitize_text_field( $value ) );
|
||||||
break;
|
break;
|
||||||
case 'job_deadline':
|
case 'job_deadline':
|
||||||
case 'job_deadline2':
|
case 'job_deadline2':
|
||||||
@@ -1425,4 +1428,46 @@ class DDHH_JM_Formidable {
|
|||||||
FrmField::create( $field );
|
FrmField::create( $field );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-populate job_id field in application form
|
||||||
|
*
|
||||||
|
* @param mixed $default_value The default value.
|
||||||
|
* @param object $field The field object.
|
||||||
|
* @param bool $dynamic_default Whether to use dynamic default.
|
||||||
|
* @return mixed The modified default value.
|
||||||
|
*/
|
||||||
|
public static function prepopulate_application_job_id( $default_value, $field, $dynamic_default ) {
|
||||||
|
// Only process for the job application form
|
||||||
|
if ( absint( $field->form_id ) !== self::get_job_application_form_id() ) {
|
||||||
|
return $default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only process the job_id field
|
||||||
|
if ( 'job_id' !== $field->field_key ) {
|
||||||
|
return $default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for job_id in shortcode attributes (passed from template)
|
||||||
|
// Formidable stores shortcode attributes in a global variable
|
||||||
|
global $frm_vars;
|
||||||
|
if ( isset( $frm_vars['job_id'] ) ) {
|
||||||
|
return absint( $frm_vars['job_id'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Check URL parameter
|
||||||
|
if ( isset( $_GET['job_id'] ) ) {
|
||||||
|
return absint( $_GET['job_id'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Try to get from current post if we're on a single job page
|
||||||
|
if ( is_singular( 'job_offer' ) ) {
|
||||||
|
global $post;
|
||||||
|
if ( $post && 'job_offer' === $post->post_type ) {
|
||||||
|
return absint( $post->ID );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default_value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ class DDHH_JM_Notifications {
|
|||||||
// Get post meta fields
|
// Get post meta fields
|
||||||
$job_location = get_post_meta( $post->ID, 'job_location', true );
|
$job_location = get_post_meta( $post->ID, 'job_location', true );
|
||||||
$job_type = get_post_meta( $post->ID, 'job_type', true );
|
$job_type = get_post_meta( $post->ID, 'job_type', true );
|
||||||
|
$job_deadline = get_post_meta( $post->ID, 'job_deadline', true );
|
||||||
|
$job_contact_email = get_post_meta( $post->ID, 'job_contact_email', true );
|
||||||
|
|
||||||
|
// Format deadline if present
|
||||||
|
$deadline_formatted = 'Nicht angegeben';
|
||||||
|
if ( ! empty( $job_deadline ) ) {
|
||||||
|
$deadline_formatted = date( 'd.m.Y', strtotime( $job_deadline ) );
|
||||||
|
}
|
||||||
|
|
||||||
// Get submission date
|
// Get submission date
|
||||||
$submission_date = get_the_date( 'd.m.Y H:i', $post->ID );
|
$submission_date = get_the_date( 'd.m.Y H:i', $post->ID );
|
||||||
@@ -81,6 +89,8 @@ class DDHH_JM_Notifications {
|
|||||||
"Anbieter: %s (%s)\n" .
|
"Anbieter: %s (%s)\n" .
|
||||||
"Standort: %s\n" .
|
"Standort: %s\n" .
|
||||||
"Art: %s\n" .
|
"Art: %s\n" .
|
||||||
|
"Bewerbungsfrist: %s\n" .
|
||||||
|
"Kontakt-E-Mail: %s\n" .
|
||||||
"Eingereicht am: %s\n\n" .
|
"Eingereicht am: %s\n\n" .
|
||||||
"Prüfen Sie das Stellenangebot hier:\n%s\n\n" .
|
"Prüfen Sie das Stellenangebot hier:\n%s\n\n" .
|
||||||
"---\n" .
|
"---\n" .
|
||||||
@@ -90,6 +100,8 @@ class DDHH_JM_Notifications {
|
|||||||
$author_org,
|
$author_org,
|
||||||
$job_location ? $job_location : 'Nicht angegeben',
|
$job_location ? $job_location : 'Nicht angegeben',
|
||||||
$job_type ? $job_type : 'Nicht angegeben',
|
$job_type ? $job_type : 'Nicht angegeben',
|
||||||
|
$deadline_formatted,
|
||||||
|
$job_contact_email ? $job_contact_email : 'Nicht angegeben',
|
||||||
$submission_date,
|
$submission_date,
|
||||||
$edit_link
|
$edit_link
|
||||||
);
|
);
|
||||||
@@ -146,6 +158,14 @@ class DDHH_JM_Notifications {
|
|||||||
// Get post meta fields
|
// Get post meta fields
|
||||||
$job_location = get_post_meta( $post->ID, 'job_location', true );
|
$job_location = get_post_meta( $post->ID, 'job_location', true );
|
||||||
$job_type = get_post_meta( $post->ID, 'job_type', true );
|
$job_type = get_post_meta( $post->ID, 'job_type', true );
|
||||||
|
$job_deadline = get_post_meta( $post->ID, 'job_deadline', true );
|
||||||
|
$job_contact_email = get_post_meta( $post->ID, 'job_contact_email', true );
|
||||||
|
|
||||||
|
// Format deadline if present
|
||||||
|
$deadline_formatted = 'Nicht angegeben';
|
||||||
|
if ( ! empty( $job_deadline ) ) {
|
||||||
|
$deadline_formatted = date( 'd.m.Y', strtotime( $job_deadline ) );
|
||||||
|
}
|
||||||
|
|
||||||
// Get submission date
|
// Get submission date
|
||||||
$submission_date = current_time( 'd.m.Y H:i' );
|
$submission_date = current_time( 'd.m.Y H:i' );
|
||||||
@@ -163,6 +183,8 @@ class DDHH_JM_Notifications {
|
|||||||
"Anbieter: %s (%s)\n" .
|
"Anbieter: %s (%s)\n" .
|
||||||
"Standort: %s\n" .
|
"Standort: %s\n" .
|
||||||
"Art: %s\n" .
|
"Art: %s\n" .
|
||||||
|
"Bewerbungsfrist: %s\n" .
|
||||||
|
"Kontakt-E-Mail: %s\n" .
|
||||||
"Bearbeitet am: %s\n\n" .
|
"Bearbeitet am: %s\n\n" .
|
||||||
"Prüfen Sie das Stellenangebot hier:\n%s\n\n" .
|
"Prüfen Sie das Stellenangebot hier:\n%s\n\n" .
|
||||||
"---\n" .
|
"---\n" .
|
||||||
@@ -172,6 +194,8 @@ class DDHH_JM_Notifications {
|
|||||||
$author_org,
|
$author_org,
|
||||||
$job_location ? $job_location : 'Nicht angegeben',
|
$job_location ? $job_location : 'Nicht angegeben',
|
||||||
$job_type ? $job_type : 'Nicht angegeben',
|
$job_type ? $job_type : 'Nicht angegeben',
|
||||||
|
$deadline_formatted,
|
||||||
|
$job_contact_email ? $job_contact_email : 'Nicht angegeben',
|
||||||
$submission_date,
|
$submission_date,
|
||||||
$edit_link
|
$edit_link
|
||||||
);
|
);
|
||||||
@@ -235,6 +259,14 @@ class DDHH_JM_Notifications {
|
|||||||
// Get post meta fields
|
// Get post meta fields
|
||||||
$job_location = get_post_meta( $post->ID, 'job_location', true );
|
$job_location = get_post_meta( $post->ID, 'job_location', true );
|
||||||
$job_type = get_post_meta( $post->ID, 'job_type', true );
|
$job_type = get_post_meta( $post->ID, 'job_type', true );
|
||||||
|
$job_deadline = get_post_meta( $post->ID, 'job_deadline', true );
|
||||||
|
$job_contact_email = get_post_meta( $post->ID, 'job_contact_email', true );
|
||||||
|
|
||||||
|
// Format deadline if present
|
||||||
|
$deadline_formatted = 'Nicht angegeben';
|
||||||
|
if ( ! empty( $job_deadline ) ) {
|
||||||
|
$deadline_formatted = date( 'd.m.Y', strtotime( $job_deadline ) );
|
||||||
|
}
|
||||||
|
|
||||||
// Get deactivation reason from post meta
|
// Get deactivation reason from post meta
|
||||||
$deactivation_reason = get_post_meta( $post->ID, 'job_deactivation_reason', true );
|
$deactivation_reason = get_post_meta( $post->ID, 'job_deactivation_reason', true );
|
||||||
@@ -258,6 +290,8 @@ class DDHH_JM_Notifications {
|
|||||||
"Anbieter: %s (%s)\n" .
|
"Anbieter: %s (%s)\n" .
|
||||||
"Standort: %s\n" .
|
"Standort: %s\n" .
|
||||||
"Art: %s\n" .
|
"Art: %s\n" .
|
||||||
|
"Bewerbungsfrist: %s\n" .
|
||||||
|
"Kontakt-E-Mail: %s\n" .
|
||||||
"Deaktiviert am: %s\n\n" .
|
"Deaktiviert am: %s\n\n" .
|
||||||
"Grund für Deaktivierung:\n%s\n\n" .
|
"Grund für Deaktivierung:\n%s\n\n" .
|
||||||
"Stelle ansehen:\n%s\n\n" .
|
"Stelle ansehen:\n%s\n\n" .
|
||||||
@@ -268,6 +302,8 @@ class DDHH_JM_Notifications {
|
|||||||
$author_org,
|
$author_org,
|
||||||
$job_location ? $job_location : 'Nicht angegeben',
|
$job_location ? $job_location : 'Nicht angegeben',
|
||||||
$job_type ? $job_type : 'Nicht angegeben',
|
$job_type ? $job_type : 'Nicht angegeben',
|
||||||
|
$deadline_formatted,
|
||||||
|
$job_contact_email ? $job_contact_email : 'Nicht angegeben',
|
||||||
$deactivation_date,
|
$deactivation_date,
|
||||||
$deactivation_reason ? $deactivation_reason : 'Nicht angegeben',
|
$deactivation_reason ? $deactivation_reason : 'Nicht angegeben',
|
||||||
$edit_link
|
$edit_link
|
||||||
@@ -303,7 +339,7 @@ class DDHH_JM_Notifications {
|
|||||||
public static function send_provider_application_notification( $entry_id, $form_id ) {
|
public static function send_provider_application_notification( $entry_id, $form_id ) {
|
||||||
// Only process job application form submissions
|
// Only process job application form submissions
|
||||||
$application_form_id = DDHH_JM_Formidable::get_job_application_form_id();
|
$application_form_id = DDHH_JM_Formidable::get_job_application_form_id();
|
||||||
if ( $form_id !== $application_form_id ) {
|
if ( $form_id != $application_form_id ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +372,7 @@ class DDHH_JM_Notifications {
|
|||||||
$applicant_message = sanitize_textarea_field( $value );
|
$applicant_message = sanitize_textarea_field( $value );
|
||||||
break;
|
break;
|
||||||
case 'job_id':
|
case 'job_id':
|
||||||
|
case 'job_id2':
|
||||||
$job_id = absint( $value );
|
$job_id = absint( $value );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class DDHH_JM_Roles {
|
|||||||
|
|
||||||
// Job offer capabilities (own only)
|
// Job offer capabilities (own only)
|
||||||
'edit_job_offers' => true,
|
'edit_job_offers' => true,
|
||||||
|
'edit_published_job_offers' => true,
|
||||||
'delete_job_offers' => true,
|
'delete_job_offers' => true,
|
||||||
'upload_files' => true,
|
'upload_files' => true,
|
||||||
|
|
||||||
@@ -70,11 +71,28 @@ class DDHH_JM_Roles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade existing roles with new capabilities
|
||||||
|
* Called on plugin init to ensure all capabilities are present
|
||||||
|
*/
|
||||||
|
public static function upgrade_roles() {
|
||||||
|
$provider_role = get_role( 'ddhh_provider' );
|
||||||
|
if ( $provider_role && ! $provider_role->has_cap( 'edit_published_job_offers' ) ) {
|
||||||
|
$provider_role->add_cap( 'edit_published_job_offers' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove custom roles
|
* Remove custom roles
|
||||||
* Called on plugin deactivation
|
* Called on plugin deactivation
|
||||||
*/
|
*/
|
||||||
public static function remove_roles() {
|
public static function remove_roles() {
|
||||||
|
// Remove provider role capabilities before removing role
|
||||||
|
$provider_role = get_role( 'ddhh_provider' );
|
||||||
|
if ( $provider_role ) {
|
||||||
|
$provider_role->remove_cap( 'edit_published_job_offers' );
|
||||||
|
}
|
||||||
|
|
||||||
remove_role( 'ddhh_provider' );
|
remove_role( 'ddhh_provider' );
|
||||||
|
|
||||||
// Remove job_offer capabilities from administrator
|
// Remove job_offer capabilities from administrator
|
||||||
|
|||||||
325
includes/class-template.php
Normal file
325
includes/class-template.php
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Template functionality for job offers
|
||||||
|
*
|
||||||
|
* @package DDHH_Job_Manager
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Exit if accessed directly.
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles template display for job offers
|
||||||
|
*/
|
||||||
|
class DDHH_JM_Template {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup template hooks
|
||||||
|
*/
|
||||||
|
public static function setup_hooks() {
|
||||||
|
// Filter the content to add job details
|
||||||
|
add_filter( 'the_content', array( __CLASS__, 'add_job_details_to_content' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add job details to the content for single job_offer posts
|
||||||
|
*
|
||||||
|
* @param string $content Post content.
|
||||||
|
* @return string Modified content.
|
||||||
|
*/
|
||||||
|
public static function add_job_details_to_content( $content ) {
|
||||||
|
// Only modify content on single job_offer posts
|
||||||
|
if ( ! is_singular( 'job_offer' ) || ! in_the_loop() || ! is_main_query() ) {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $post;
|
||||||
|
|
||||||
|
// Get job meta data
|
||||||
|
$job_location = get_post_meta( $post->ID, 'job_location', true );
|
||||||
|
$job_type = get_post_meta( $post->ID, 'job_type', true );
|
||||||
|
$job_deadline = get_post_meta( $post->ID, 'job_deadline', true );
|
||||||
|
$job_contact_email = get_post_meta( $post->ID, 'job_contact_email', true );
|
||||||
|
$job_logo = get_the_post_thumbnail( $post->ID, 'job-logo' );
|
||||||
|
|
||||||
|
// Get author/organization info
|
||||||
|
$author = get_userdata( $post->post_author );
|
||||||
|
$author_name = $author ? $author->display_name : '';
|
||||||
|
$author_org = get_user_meta( $post->post_author, 'ddhh_org_name', true );
|
||||||
|
|
||||||
|
// Build job details HTML
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<div class="ddhh-job-offer-details">
|
||||||
|
<?php if ( $job_logo ) : ?>
|
||||||
|
<div class="job-logo">
|
||||||
|
<?php echo $job_logo; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="job-meta">
|
||||||
|
<?php if ( $author_org ) : ?>
|
||||||
|
<div class="job-meta-item">
|
||||||
|
<strong>Anbieter:</strong> <?php echo esc_html( $author_org ); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ( $job_location ) : ?>
|
||||||
|
<div class="job-meta-item">
|
||||||
|
<strong>Standort:</strong> <?php echo esc_html( $job_location ); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ( $job_type ) : ?>
|
||||||
|
<div class="job-meta-item">
|
||||||
|
<strong>Art:</strong> <?php echo esc_html( $job_type ); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ( $job_deadline ) : ?>
|
||||||
|
<div class="job-meta-item">
|
||||||
|
<strong>Bewerbungsfrist:</strong> <?php echo esc_html( date( 'd.m.Y', strtotime( $job_deadline ) ) ); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="job-description">
|
||||||
|
<h3>Stellenbeschreibung</h3>
|
||||||
|
<?php echo wp_kses_post( $content ); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ( $job_contact_email && is_user_logged_in() && current_user_can( 'read' ) ) : ?>
|
||||||
|
<div class="job-application-section">
|
||||||
|
<h3>Interesse?</h3>
|
||||||
|
<button type="button" class="ddhh-contact-button" onclick="ddhhOpenContactModal(<?php echo absint( $post->ID ); ?>)">
|
||||||
|
Jetzt Kontakt aufnehmen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Contact Form Modal -->
|
||||||
|
<div id="ddhh-contact-modal-<?php echo absint( $post->ID ); ?>" class="ddhh-modal" style="display: none;">
|
||||||
|
<div class="ddhh-modal-content">
|
||||||
|
<span class="ddhh-modal-close" onclick="ddhhCloseContactModal(<?php echo absint( $post->ID ); ?>)">×</span>
|
||||||
|
<h3>Kontakt aufnehmen</h3>
|
||||||
|
<div class="ddhh-modal-body">
|
||||||
|
<?php
|
||||||
|
// Get the job application form ID
|
||||||
|
$form_id = DDHH_JM_Formidable::get_job_application_form_id();
|
||||||
|
if ( $form_id ) {
|
||||||
|
// Render the Formidable form with AJAX enabled to prevent page reload
|
||||||
|
echo do_shortcode( '[formidable id="' . absint( $form_id ) . '" job_id="' . absint( $post->ID ) . '" ajax="true"]' );
|
||||||
|
} else {
|
||||||
|
echo '<p>Formular konnte nicht geladen werden. Bitte kontaktieren Sie uns unter: <a href="mailto:' . esc_attr( $job_contact_email ) . '">' . esc_html( $job_contact_email ) . '</a></p>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.ddhh-job-offer-details {
|
||||||
|
margin: 2em 0;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .job-logo {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .job-logo img {
|
||||||
|
max-width: 200px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .job-meta {
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 1.5em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .job-meta-item {
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .job-meta-item:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .job-meta-item strong {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .job-description {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .job-application-section {
|
||||||
|
background: #e8f4f8;
|
||||||
|
padding: 1.5em;
|
||||||
|
border-left: 4px solid #0073aa;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .job-application-section h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .ddhh-contact-button {
|
||||||
|
background: #0073aa;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 30px;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
.ddhh-job-offer-details .ddhh-contact-button:hover {
|
||||||
|
background: #005a87;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal Styles */
|
||||||
|
.ddhh-modal {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 9999;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: rgba(0,0,0,0.6);
|
||||||
|
}
|
||||||
|
.ddhh-modal-content {
|
||||||
|
background-color: #fefefe;
|
||||||
|
margin: 5% auto;
|
||||||
|
padding: 30px;
|
||||||
|
border: 1px solid #888;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600px;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.ddhh-modal-close {
|
||||||
|
color: #aaa;
|
||||||
|
float: right;
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
.ddhh-modal-close:hover,
|
||||||
|
.ddhh-modal-close:focus {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.ddhh-modal-body {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.ddhh-modal h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function ddhhOpenContactModal(jobId) {
|
||||||
|
var modal = document.getElementById('ddhh-contact-modal-' + jobId);
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'block';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
|
||||||
|
// Set the job_id in the hidden field
|
||||||
|
var jobIdField = modal.querySelector('input[id*="job_id"]');
|
||||||
|
if (jobIdField) {
|
||||||
|
jobIdField.value = jobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent form from redirecting on submit
|
||||||
|
var form = modal.querySelector('form.frm-show-form');
|
||||||
|
if (form && !form.dataset.ddhhHandled) {
|
||||||
|
form.dataset.ddhhHandled = 'true';
|
||||||
|
|
||||||
|
jQuery(form).on('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log('Form submit intercepted - preventing page reload');
|
||||||
|
|
||||||
|
var $form = jQuery(this);
|
||||||
|
var formData = new FormData(this);
|
||||||
|
|
||||||
|
// Submit form via AJAX
|
||||||
|
jQuery.ajax({
|
||||||
|
url: $form.attr('action') || window.location.href,
|
||||||
|
type: 'POST',
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
success: function(response) {
|
||||||
|
console.log('Form submitted successfully via AJAX');
|
||||||
|
// Replace form content with response
|
||||||
|
var formContainer = modal.querySelector('.ddhh-modal-body');
|
||||||
|
if (formContainer) {
|
||||||
|
var tempDiv = document.createElement('div');
|
||||||
|
tempDiv.innerHTML = response;
|
||||||
|
var newForm = tempDiv.querySelector('form.frm-show-form');
|
||||||
|
if (newForm) {
|
||||||
|
formContainer.innerHTML = newForm.outerHTML;
|
||||||
|
} else {
|
||||||
|
// Show success message if no form in response
|
||||||
|
var successMsg = tempDiv.querySelector('.frm_message');
|
||||||
|
if (successMsg) {
|
||||||
|
formContainer.innerHTML = successMsg.outerHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
console.error('Form submission failed');
|
||||||
|
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ddhhCloseContactModal(jobId) {
|
||||||
|
var modal = document.getElementById('ddhh-contact-modal-' + jobId);
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close modal when clicking outside of it
|
||||||
|
window.onclick = function(event) {
|
||||||
|
if (event.target.classList.contains('ddhh-modal')) {
|
||||||
|
event.target.style.display = 'none';
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle successful form submission - keep modal open to show success message
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
// Prevent redirect after form submission
|
||||||
|
$(document).on('frmBeforeFormRedirect', function(event, form, response) {
|
||||||
|
var formKey = $(form).find('input[name="form_key"]').val();
|
||||||
|
if (formKey === 'job_application') {
|
||||||
|
// Prevent the default redirect behavior
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('frmFormComplete', function(event, form, response) {
|
||||||
|
// Check if this is the job application form
|
||||||
|
var formKey = $(form).find('input[name="form_key"]').val();
|
||||||
|
if (formKey === 'job_application') {
|
||||||
|
// Modal stays open so user can see success message
|
||||||
|
// User can close it manually by clicking X or outside the modal
|
||||||
|
console.log('Form submitted successfully, modal staying open');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -98,16 +98,77 @@ if ( $is_edit_mode ) {
|
|||||||
|
|
||||||
if (field.length) {
|
if (field.length) {
|
||||||
if (field.is('select')) {
|
if (field.is('select')) {
|
||||||
field.val(value).trigger('change');
|
// For select fields, try case-insensitive matching
|
||||||
|
var options = field.find('option');
|
||||||
|
var matched = false;
|
||||||
|
|
||||||
|
options.each(function() {
|
||||||
|
if ($(this).val().toLowerCase() === value.toLowerCase()) {
|
||||||
|
field.val($(this).val()).trigger('change');
|
||||||
|
matched = true;
|
||||||
|
console.log('Matched select option:', $(this).val(), 'for value:', value);
|
||||||
|
return false; // break
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!matched) {
|
||||||
|
console.warn('No matching option found for field', fieldId, 'value:', value, 'Available options:', options.map(function() { return $(this).val(); }).get());
|
||||||
|
}
|
||||||
} else if (field.is(':checkbox') || field.is(':radio')) {
|
} else if (field.is(':checkbox') || field.is(':radio')) {
|
||||||
field.filter('[value="' + value + '"]').prop('checked', true);
|
field.filter('[value="' + value + '"]').prop('checked', true);
|
||||||
|
} else {
|
||||||
|
// For date fields, convert YYYY-MM-DD to DD.MM.YYYY format
|
||||||
|
if (field.attr('type') === 'date' || field.hasClass('frm_date')) {
|
||||||
|
// Check if value is in YYYY-MM-DD format
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
||||||
|
var parts = value.split('-');
|
||||||
|
var formattedDate = parts[2] + '.' + parts[1] + '.' + parts[0];
|
||||||
|
field.val(formattedDate).trigger('change');
|
||||||
|
console.log('Converted date from', value, 'to', formattedDate);
|
||||||
} else {
|
} else {
|
||||||
field.val(value);
|
field.val(value);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
field.val(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
console.log('Populated field ' + fieldId + ' with:', value);
|
console.log('Populated field ' + fieldId + ' with:', value);
|
||||||
|
} else {
|
||||||
|
console.warn('Field not found:', fieldId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
|
// Fix date format after user selects a date - replace slashes with dots for display
|
||||||
|
$(document).on('change', 'input[type="date"], input.frm_date', function() {
|
||||||
|
var field = $(this);
|
||||||
|
var value = field.val();
|
||||||
|
|
||||||
|
// If the value contains slashes, replace them with dots
|
||||||
|
if (value && value.includes('/')) {
|
||||||
|
var fixedValue = value.replace(/\//g, '.');
|
||||||
|
field.val(fixedValue);
|
||||||
|
// Store the original format in a data attribute for form submission
|
||||||
|
field.data('original-format', value.replace(/\//g, '-'));
|
||||||
|
console.log('Fixed date format from', value, 'to', fixedValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Before form submission, convert dates back to YYYY-MM-DD format
|
||||||
|
$('form').on('submit', function() {
|
||||||
|
$(this).find('input[type="date"], input.frm_date').each(function() {
|
||||||
|
var field = $(this);
|
||||||
|
var value = field.val();
|
||||||
|
|
||||||
|
// Convert DD.MM.YYYY or DD/MM/YYYY back to YYYY-MM-DD
|
||||||
|
if (value && /^\d{2}[./]\d{2}[./]\d{4}$/.test(value)) {
|
||||||
|
var parts = value.split(/[./]/);
|
||||||
|
var isoDate = parts[2] + '-' + parts[1] + '-' + parts[0];
|
||||||
|
field.val(isoDate);
|
||||||
|
console.log('Converted date for submission from', value, 'to', isoDate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user