From 693974b5614f9904a9aa9b58cb41f77eedbfa5ef Mon Sep 17 00:00:00 2001 From: Viktor Miller Date: Wed, 14 Jan 2026 18:57:29 +0900 Subject: [PATCH] feat(01-02): register job_offer CPT with ownership-enforced capabilities - Register job_offer custom post type with German labels - Custom capability_type 'job_offer' prevents access to regular posts - map_meta_cap filter enforces per-post ownership - Elementor support via show_in_rest - Menu icon: dashicons-businessperson at position 5 - Supports: title, editor, author, thumbnail - Archive slug: jobangebote - Custom capabilities prevent providers from editing others' posts Tasks completed: - Task 1: Register job_offer custom post type - Task 2: Add capability mapping filter for ownership enforcement Co-Authored-By: Claude Sonnet 4.5 --- ddhh-job-manager.php | 1 + includes/class-post-types.php | 125 ++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 includes/class-post-types.php diff --git a/ddhh-job-manager.php b/ddhh-job-manager.php index 6645e5a..c0df530 100644 --- a/ddhh-job-manager.php +++ b/ddhh-job-manager.php @@ -28,6 +28,7 @@ define( 'DDHH_JM_PLUGIN_FILE', __FILE__ ); // Include core classes. require_once DDHH_JM_PLUGIN_DIR . 'includes/class-activator.php'; require_once DDHH_JM_PLUGIN_DIR . 'includes/class-deactivator.php'; +require_once DDHH_JM_PLUGIN_DIR . 'includes/class-post-types.php'; require_once DDHH_JM_PLUGIN_DIR . 'includes/class-ddhh-job-manager.php'; /** diff --git a/includes/class-post-types.php b/includes/class-post-types.php new file mode 100644 index 0000000..dae2784 --- /dev/null +++ b/includes/class-post-types.php @@ -0,0 +1,125 @@ + _x( 'Jobangebote', 'Post type general name', 'ddhh-job-manager' ), + 'singular_name' => _x( 'Jobangebot', 'Post type singular name', 'ddhh-job-manager' ), + 'menu_name' => _x( 'Jobs', 'Admin Menu text', 'ddhh-job-manager' ), + 'name_admin_bar' => _x( 'Jobangebot', 'Add New on Toolbar', 'ddhh-job-manager' ), + 'add_new' => __( 'Neu hinzufügen', 'ddhh-job-manager' ), + 'add_new_item' => __( 'Neues Jobangebot', 'ddhh-job-manager' ), + 'new_item' => __( 'Neues Jobangebot', 'ddhh-job-manager' ), + 'edit_item' => __( 'Jobangebot bearbeiten', 'ddhh-job-manager' ), + 'view_item' => __( 'Jobangebot ansehen', 'ddhh-job-manager' ), + 'all_items' => __( 'Alle Jobangebote', 'ddhh-job-manager' ), + 'search_items' => __( 'Jobangebote durchsuchen', 'ddhh-job-manager' ), + 'parent_item_colon' => __( 'Übergeordnetes Jobangebot:', 'ddhh-job-manager' ), + 'not_found' => __( 'Keine Jobangebote gefunden.', 'ddhh-job-manager' ), + 'not_found_in_trash' => __( 'Keine Jobangebote im Papierkorb gefunden.', 'ddhh-job-manager' ), + 'featured_image' => _x( 'Logo', 'Overrides the "Featured Image" phrase', 'ddhh-job-manager' ), + 'set_featured_image' => _x( 'Logo festlegen', 'Overrides the "Set featured image" phrase', 'ddhh-job-manager' ), + 'remove_featured_image' => _x( 'Logo entfernen', 'Overrides the "Remove featured image" phrase', 'ddhh-job-manager' ), + 'use_featured_image' => _x( 'Als Logo verwenden', 'Overrides the "Use as featured image" phrase', 'ddhh-job-manager' ), + 'archives' => _x( 'Jobangebote Archiv', 'The post type archive label used in nav menus', 'ddhh-job-manager' ), + 'insert_into_item' => _x( 'In Jobangebot einfügen', 'Overrides the "Insert into post" phrase', 'ddhh-job-manager' ), + 'uploaded_to_this_item' => _x( 'Zu diesem Jobangebot hochgeladen', 'Overrides the "Uploaded to this post" phrase', 'ddhh-job-manager' ), + 'filter_items_list' => _x( 'Jobangebote Liste filtern', 'Screen reader text for the filter links', 'ddhh-job-manager' ), + 'items_list_navigation' => _x( 'Jobangebote Navigation', 'Screen reader text for the pagination', 'ddhh-job-manager' ), + 'items_list' => _x( 'Jobangebote Liste', 'Screen reader text for the items list', 'ddhh-job-manager' ), + ); + + $args = array( + 'labels' => $labels, + 'public' => true, + 'publicly_queryable' => true, + 'show_ui' => true, + 'show_in_menu' => true, + 'query_var' => true, + 'rewrite' => array( 'slug' => 'jobangebote' ), + 'capability_type' => 'job_offer', + 'map_meta_cap' => true, + 'has_archive' => true, + 'hierarchical' => false, + 'menu_position' => 5, + 'menu_icon' => 'dashicons-businessperson', + 'supports' => array( 'title', 'editor', 'author', 'thumbnail' ), + 'show_in_rest' => true, + 'capabilities' => array( + 'edit_post' => 'edit_job_offer', + 'read_post' => 'read_job_offer', + 'delete_post' => 'delete_job_offer', + 'edit_posts' => 'edit_job_offers', + 'edit_others_posts' => 'edit_others_job_offers', + 'publish_posts' => 'publish_job_offers', + 'read_private_posts' => 'read_private_job_offers', + 'delete_posts' => 'delete_job_offers', + 'delete_private_posts' => 'delete_private_job_offers', + 'delete_published_posts' => 'delete_published_job_offers', + 'delete_others_posts' => 'delete_others_job_offers', + 'edit_private_posts' => 'edit_private_job_offers', + 'edit_published_posts' => 'edit_published_job_offers', + 'create_posts' => 'edit_job_offers', + ), + ); + + register_post_type( 'job_offer', $args ); + } + + /** + * Map job_offer capabilities to enforce ownership + * + * @param array $caps Required capabilities. + * @param string $cap Capability being checked. + * @param int $user_id User ID. + * @param array $args Additional arguments. + * @return array Modified capabilities. + */ + public static function map_job_offer_capabilities( $caps, $cap, $user_id, $args ) { + // Check if this is a job_offer capability we need to map + if ( 'edit_job_offer' === $cap || 'delete_job_offer' === $cap ) { + // Get the post + $post = get_post( $args[0] ); + + if ( ! $post || 'job_offer' !== $post->post_type ) { + return $caps; + } + + // Check if user is the post author + if ( (int) $user_id === (int) $post->post_author ) { + // User is the author - allow with base capability + $caps = array( 'edit_job_offers' ); + } else { + // User is NOT the author - require elevated capability + $caps = array( 'edit_others_job_offers' ); + } + } + + return $caps; + } +}