feat(07-01): implement complete job management workflow

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>
This commit is contained in:
2026-01-17 19:49:21 +09:00
parent 1ed164aed0
commit 737f3d6fe9
4 changed files with 695 additions and 7 deletions

View File

@@ -161,6 +161,9 @@ class DDHH_JM_Formidable {
// Hook into Formidable form submission
add_action( 'frm_after_create_entry', array( __CLASS__, 'handle_registration_submission' ), 30, 2 );
add_action( 'frm_after_create_entry', array( __CLASS__, 'handle_job_submission' ), 30, 2 );
add_action( 'frm_after_create_entry', array( __CLASS__, 'handle_job_edit_submission' ), 30, 2 );
add_action( 'frm_after_create_entry', array( __CLASS__, 'handle_job_deactivation_submission' ), 30, 2 );
// Add JavaScript redirect for registration form
add_action( 'wp_footer', array( __CLASS__, 'add_registration_redirect_script' ) );
@@ -168,6 +171,9 @@ class DDHH_JM_Formidable {
// Hook into Formidable form validation for ownership check
add_filter( 'frm_validate_entry', array( __CLASS__, 'validate_job_ownership' ), 10, 2 );
add_filter( 'frm_validate_entry', array( __CLASS__, 'validate_job_deactivation_ownership' ), 10, 2 );
// Hook to pre-populate edit form fields
add_filter( 'frm_get_default_value', array( __CLASS__, 'prepopulate_edit_form_fields' ), 10, 3 );
}
/**
@@ -437,6 +443,324 @@ class DDHH_JM_Formidable {
do_action( 'ddhh_provider_registered', $user_id, $organization_name );
}
/**
* Handle job submission form entry
*
* @param int $entry_id Entry ID.
* @param int $form_id Form ID.
*/
public static function handle_job_submission( $entry_id, $form_id ) {
// Only process our job submission form
if ( $form_id != self::get_job_submission_form_id() ) {
return;
}
error_log( 'DDHH Job Submission: Processing form entry ' . $entry_id );
// Get entry data
$entry = FrmEntry::getOne( $entry_id, true );
if ( ! $entry ) {
error_log( 'DDHH Job Submission: Failed to get entry' );
return;
}
// Extract field values
$job_title = '';
$job_description = '';
$job_location = '';
$job_type = '';
$job_deadline = '';
$job_contact_email = '';
$job_logo = '';
foreach ( $entry->metas as $field_id => $value ) {
$field = FrmField::getOne( $field_id );
if ( ! $field ) {
continue;
}
switch ( $field->field_key ) {
case 'job_title':
$job_title = sanitize_text_field( $value );
break;
case 'job_description':
$job_description = wp_kses_post( $value );
break;
case 'job_location':
$job_location = sanitize_text_field( $value );
break;
case 'job_type':
$job_type = sanitize_text_field( $value );
break;
case 'job_deadline':
$job_deadline = sanitize_text_field( $value );
break;
case 'job_contact_email':
$job_contact_email = sanitize_email( $value );
break;
case 'job_logo':
$job_logo = $value; // File ID
break;
}
}
// Validate required fields
if ( empty( $job_title ) || empty( $job_description ) || empty( $job_location ) || empty( $job_type ) || empty( $job_contact_email ) ) {
error_log( 'DDHH Job Submission: Validation failed - missing required fields' );
return;
}
// Create job_offer post
$post_data = array(
'post_title' => $job_title,
'post_content' => $job_description,
'post_type' => 'job_offer',
'post_status' => 'pending',
'post_author' => get_current_user_id(),
);
$post_id = wp_insert_post( $post_data );
// Check for errors
if ( is_wp_error( $post_id ) ) {
error_log( 'DDHH Job Submission: wp_insert_post failed - ' . $post_id->get_error_message() );
return;
}
error_log( 'DDHH Job Submission: Post created successfully with ID ' . $post_id );
// Save custom fields
update_post_meta( $post_id, 'job_location', $job_location );
update_post_meta( $post_id, 'job_type', $job_type );
update_post_meta( $post_id, 'job_contact_email', $job_contact_email );
if ( ! empty( $job_deadline ) ) {
update_post_meta( $post_id, 'job_deadline', $job_deadline );
}
// Handle logo upload if present
if ( ! empty( $job_logo ) && is_numeric( $job_logo ) ) {
set_post_thumbnail( $post_id, absint( $job_logo ) );
error_log( 'DDHH Job Submission: Logo set as featured image' );
}
error_log( 'DDHH Job Submission: Job offer created successfully' );
do_action( 'ddhh_job_submitted', $post_id, $entry_id );
}
/**
* Handle job edit form submission
*
* @param int $entry_id Entry ID.
* @param int $form_id Form ID.
*/
public static function handle_job_edit_submission( $entry_id, $form_id ) {
// Only process our job edit form
if ( $form_id != self::get_job_edit_form_id() ) {
return;
}
// Check if we have a job_id to update
if ( ! isset( $_GET['job_id'] ) ) {
error_log( 'DDHH Job Edit: No job_id provided' );
return;
}
$post_id = absint( $_GET['job_id'] );
error_log( 'DDHH Job Edit: Processing form entry ' . $entry_id . ' for post ' . $post_id );
// Verify post exists and user owns it
$post = get_post( $post_id );
if ( ! $post || 'job_offer' !== $post->post_type ) {
error_log( 'DDHH Job Edit: Invalid post' );
return;
}
if ( absint( $post->post_author ) !== get_current_user_id() ) {
error_log( 'DDHH Job Edit: User does not own this post' );
return;
}
// Get entry data
$entry = FrmEntry::getOne( $entry_id, true );
if ( ! $entry ) {
error_log( 'DDHH Job Edit: Failed to get entry' );
return;
}
// Extract field values (accounting for "2" suffix)
$job_title = '';
$job_description = '';
$job_location = '';
$job_type = '';
$job_deadline = '';
$job_contact_email = '';
$job_logo = '';
foreach ( $entry->metas as $field_id => $value ) {
$field = FrmField::getOne( $field_id );
if ( ! $field ) {
continue;
}
switch ( $field->field_key ) {
case 'job_title':
case 'job_title2':
$job_title = sanitize_text_field( $value );
break;
case 'job_description':
case 'job_description2':
$job_description = wp_kses_post( $value );
break;
case 'job_location':
case 'job_location2':
$job_location = sanitize_text_field( $value );
break;
case 'job_type':
case 'job_type2':
$job_type = sanitize_text_field( $value );
break;
case 'job_deadline':
case 'job_deadline2':
$job_deadline = sanitize_text_field( $value );
break;
case 'job_contact_email':
case 'job_contact_email2':
$job_contact_email = sanitize_email( $value );
break;
case 'job_logo':
case 'job_logo2':
$job_logo = $value; // File ID
break;
}
}
// Validate required fields
if ( empty( $job_title ) || empty( $job_description ) || empty( $job_location ) || empty( $job_type ) || empty( $job_contact_email ) ) {
error_log( 'DDHH Job Edit: Validation failed - missing required fields' );
return;
}
// Update job_offer post
$post_data = array(
'ID' => $post_id,
'post_title' => $job_title,
'post_content' => $job_description,
'post_status' => 'pending', // Reset to pending for admin review
);
$result = wp_update_post( $post_data );
// Check for errors
if ( is_wp_error( $result ) ) {
error_log( 'DDHH Job Edit: wp_update_post failed - ' . $result->get_error_message() );
return;
}
error_log( 'DDHH Job Edit: Post updated successfully with ID ' . $post_id );
// Update custom fields
update_post_meta( $post_id, 'job_location', $job_location );
update_post_meta( $post_id, 'job_type', $job_type );
update_post_meta( $post_id, 'job_contact_email', $job_contact_email );
if ( ! empty( $job_deadline ) ) {
update_post_meta( $post_id, 'job_deadline', $job_deadline );
}
// Handle logo upload if present
if ( ! empty( $job_logo ) && is_numeric( $job_logo ) ) {
set_post_thumbnail( $post_id, absint( $job_logo ) );
error_log( 'DDHH Job Edit: Logo updated' );
}
error_log( 'DDHH Job Edit: Job offer updated successfully' );
do_action( 'ddhh_job_edited', $post_id, $entry_id );
}
/**
* Handle job deactivation form submission
*
* @param int $entry_id Entry ID.
* @param int $form_id Form ID.
*/
public static function handle_job_deactivation_submission( $entry_id, $form_id ) {
// Only process our job deactivation form
if ( $form_id != self::get_job_deactivation_form_id() ) {
return;
}
// Check if we have a job_id to deactivate
if ( ! isset( $_GET['job_id'] ) ) {
error_log( 'DDHH Job Deactivation: No job_id provided' );
return;
}
$post_id = absint( $_GET['job_id'] );
error_log( 'DDHH Job Deactivation: Processing form entry ' . $entry_id . ' for post ' . $post_id );
// Verify post exists and user owns it
$post = get_post( $post_id );
if ( ! $post || 'job_offer' !== $post->post_type ) {
error_log( 'DDHH Job Deactivation: Invalid post' );
return;
}
if ( absint( $post->post_author ) !== get_current_user_id() ) {
error_log( 'DDHH Job Deactivation: User does not own this post' );
return;
}
// Get entry data
$entry = FrmEntry::getOne( $entry_id, true );
if ( ! $entry ) {
error_log( 'DDHH Job Deactivation: Failed to get entry' );
return;
}
// Extract deactivation reason
$deactivation_reason = '';
foreach ( $entry->metas as $field_id => $value ) {
$field = FrmField::getOne( $field_id );
if ( ! $field ) {
continue;
}
if ( 'deactivation_reason' === $field->field_key ) {
$deactivation_reason = sanitize_textarea_field( $value );
break;
}
}
// Update post status to draft (deactivated)
$result = wp_update_post( array(
'ID' => $post_id,
'post_status' => 'draft',
) );
// Check for errors
if ( is_wp_error( $result ) ) {
error_log( 'DDHH Job Deactivation: wp_update_post failed - ' . $result->get_error_message() );
return;
}
error_log( 'DDHH Job Deactivation: Post status updated to draft for post ' . $post_id );
// Save deactivation reason (using ACF field name)
if ( ! empty( $deactivation_reason ) ) {
update_post_meta( $post_id, 'job_deactivation_reason', $deactivation_reason );
update_post_meta( $post_id, 'job_deactivation_date', current_time( 'mysql' ) );
error_log( 'DDHH Job Deactivation: Saved deactivation reason' );
}
error_log( 'DDHH Job Deactivation: Job deactivated successfully' );
do_action( 'ddhh_job_deactivated', $post_id, $entry_id );
}
/**
* Create the job submission form programmatically if it doesn't exist
*/
@@ -777,6 +1101,71 @@ class DDHH_JM_Formidable {
}
}
/**
* Pre-populate edit form fields with existing post data
*
* @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_edit_form_fields( $default_value, $field, $dynamic_default ) {
// Only process for the job edit form
if ( absint( $field->form_id ) !== self::get_job_edit_form_id() ) {
return $default_value;
}
// Check if we're editing - look for job_id in URL
if ( ! isset( $_GET['job_id'] ) ) {
return $default_value;
}
$post_id = absint( $_GET['job_id'] );
$post = get_post( $post_id );
if ( ! $post || 'job_offer' !== $post->post_type ) {
return $default_value;
}
// Map field keys to post data (handle both with and without "2" suffix)
switch ( $field->field_key ) {
case 'job_title':
case 'job_title2':
return $post->post_title;
case 'job_description':
case 'job_description2':
return $post->post_content;
case 'job_location':
case 'job_location2':
$value = get_post_meta( $post_id, 'job_location', true );
return $value ? $value : $default_value;
case 'job_type':
case 'job_type2':
$value = get_post_meta( $post_id, 'job_type', true );
return $value ? $value : $default_value;
case 'job_deadline':
case 'job_deadline2':
$value = get_post_meta( $post_id, 'job_deadline', true );
return $value ? $value : $default_value;
case 'job_contact_email':
case 'job_contact_email2':
$value = get_post_meta( $post_id, 'job_contact_email', true );
return $value ? $value : $default_value;
case 'job_logo':
case 'job_logo2':
$value = get_post_thumbnail_id( $post_id );
return $value ? $value : $default_value;
}
return $default_value;
}
/**
* Validate job ownership before allowing edit
*

View File

@@ -50,6 +50,24 @@ class DDHH_JM_Roles {
'manage_options' => false,
)
);
// Grant job_offer capabilities to administrator
$admin_role = get_role( 'administrator' );
if ( $admin_role ) {
$admin_role->add_cap( 'edit_job_offer' );
$admin_role->add_cap( 'read_job_offer' );
$admin_role->add_cap( 'delete_job_offer' );
$admin_role->add_cap( 'edit_job_offers' );
$admin_role->add_cap( 'edit_others_job_offers' );
$admin_role->add_cap( 'publish_job_offers' );
$admin_role->add_cap( 'read_private_job_offers' );
$admin_role->add_cap( 'delete_job_offers' );
$admin_role->add_cap( 'delete_private_job_offers' );
$admin_role->add_cap( 'delete_published_job_offers' );
$admin_role->add_cap( 'delete_others_job_offers' );
$admin_role->add_cap( 'edit_private_job_offers' );
$admin_role->add_cap( 'edit_published_job_offers' );
}
}
/**
@@ -58,5 +76,23 @@ class DDHH_JM_Roles {
*/
public static function remove_roles() {
remove_role( 'ddhh_provider' );
// Remove job_offer capabilities from administrator
$admin_role = get_role( 'administrator' );
if ( $admin_role ) {
$admin_role->remove_cap( 'edit_job_offer' );
$admin_role->remove_cap( 'read_job_offer' );
$admin_role->remove_cap( 'delete_job_offer' );
$admin_role->remove_cap( 'edit_job_offers' );
$admin_role->remove_cap( 'edit_others_job_offers' );
$admin_role->remove_cap( 'publish_job_offers' );
$admin_role->remove_cap( 'read_private_job_offers' );
$admin_role->remove_cap( 'delete_job_offers' );
$admin_role->remove_cap( 'delete_private_job_offers' );
$admin_role->remove_cap( 'delete_published_job_offers' );
$admin_role->remove_cap( 'delete_others_job_offers' );
$admin_role->remove_cap( 'edit_private_job_offers' );
$admin_role->remove_cap( 'edit_published_job_offers' );
}
}
}