---
phase: 01-foundation-setup
plan: 02
type: execute
depends_on: []
files_modified: [includes/class-post-types.php]
---
Register custom post type `job_offer` with proper capabilities, labels, and support for Elementor templates.
Purpose: Create the data structure for job listings that providers will manage and mentors will browse.
Output: Functional `job_offer` CPT with correct capabilities and features.
~/.claude/get-shit-done/workflows/execute-plan.md
./summary.md
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
**From PROJECT.md:**
- Custom role `ddhh_provider` with restricted capabilities
- Providers can only edit their own posts
- No WP-Admin access for providers (except profile)
**Security requirement:** Use `map_meta_cap` filter to enforce ownership
Task 1: Register job_offer custom post type
includes/class-post-types.php
Create `DDHH_JM_Post_Types` class with static register() method.
Register CPT 'job_offer' with register_post_type():
- Labels: Singular "Jobangebot", Plural "Jobangebote", menu_name "Jobs" (German, as per PROJECT.md)
- public: true
- show_ui: true
- show_in_menu: true
- menu_position: 5 (below Posts)
- menu_icon: 'dashicons-businessperson'
- supports: ['title', 'editor', 'author', 'thumbnail']
- has_archive: true
- rewrite: ['slug' => 'jobangebote']
- capability_type: 'job_offer' (custom capabilities)
- map_meta_cap: true (enables per-post capability checks)
- show_in_rest: true (Gutenberg + Elementor support)
Custom capabilities:
- 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'
Hook register() to 'init' action in main class.
DO NOT use 'post' as capability_type — this would give providers access to regular posts.
CPT appears in WordPress admin menu, archive URL works (even if empty)
job_offer CPT registered with custom capabilities and Elementor support
Task 2: Add capability mapping filter for ownership enforcement
includes/class-post-types.php
Add static method `map_job_offer_capabilities($caps, $cap, $user_id, $args)` hooked to 'map_meta_cap' filter.
Logic:
- If $cap is 'edit_job_offer' or 'delete_job_offer':
- Check if post author == $user_id
- If yes: return ['edit_job_offers'] (allow)
- If no: return ['edit_others_job_offers'] (deny for providers, allow for admins)
- For all other caps: return $caps unchanged
This ensures providers can ONLY edit/delete their own job_offer posts, never others'.
WordPress will automatically check these capabilities against user roles.
Logic is sound: non-author providers cannot edit others' posts
Capability mapping enforces post ownership, providers restricted to own posts
Before declaring plan complete:
- [ ] CPT registered with correct labels (German)
- [ ] Custom capability_type prevents capability leakage to regular posts
- [ ] map_meta_cap filter enforces ownership
- [ ] show_in_rest enabled for Elementor compatibility
- All tasks completed
- CPT registered with custom capabilities
- Ownership enforcement via map_meta_cap
- Ready for ACF field registration in next plan