Files
Digital-Dabei-Hamburg-Job-M…/.planning/phases/01-foundation-setup/01-02-PLAN.md
Viktor Miller 8b296cefbe docs(01): create phase 1 plans
Phase 01: Foundation & Setup
- 3 plans created (all independent, can run parallel)
- 7 total tasks defined
- Ready for execution

Plans:
- 01-01: Plugin structure and activation hooks
- 01-02: Register job_offer CPT with capabilities
- 01-03: Register ddhh_provider role and ACF fields

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-14 18:47:23 +09:00

4.3 KiB

phase, plan, type, depends_on, files_modified
phase plan type depends_on files_modified
01-foundation-setup 02 execute
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.

<execution_context> ~/.claude/get-shit-done/workflows/execute-plan.md ./summary.md </execution_context>

@.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

<success_criteria>

  • All tasks completed
  • CPT registered with custom capabilities
  • Ownership enforcement via map_meta_cap
  • Ready for ACF field registration in next plan </success_criteria>
After completion, create `.planning/phases/01-foundation-setup/01-02-SUMMARY.md`:

Phase 1 Plan 2: Custom Post Type Summary

job_offer CPT registered with ownership-enforced capabilities

Accomplishments

  • Custom post type 'job_offer' with German labels
  • Custom capability type prevents access to regular posts
  • map_meta_cap filter enforces per-post ownership
  • Elementor support via show_in_rest

Files Created/Modified

  • includes/class-post-types.php - CPT registration and capability mapping

Decisions Made

  • Used custom capability_type 'job_offer' (not 'post') for security
  • German labels: "Jobangebot" / "Jobangebote"
  • Archive slug: 'jobangebote'

Issues Encountered

None

Next Step

Ready for 01-03-PLAN.md (can run in parallel)