add_action( 'save_post_academiccrew_member', $this, 'on_member_saved', 20, 2 ); $loader->add_action( 'save_post_page', $this, 'on_linked_page_saved', 20, 2 ); $loader->add_action( 'trashed_post', $this, 'on_cpt_trashed' ); $loader->add_action( 'untrashed_post', $this, 'on_cpt_untrashed' ); $loader->add_action( 'before_delete_post', $this, 'on_cpt_before_delete', 10, 2 ); $loader->add_action( 'before_delete_post', $this, 'on_page_before_delete',10, 2 ); } // --------------------------------------------------------------- // Save hooks // --------------------------------------------------------------- public function on_member_saved( int $post_id, \WP_Post $post ): void { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; if ( wp_is_post_revision( $post_id ) ) return; if ( 'publish' !== $post->post_status ) return; if ( self::$syncing ) return; $existing_page_id = (int) get_post_meta( $post_id, '_academiccrew_linked_page_id', true ); if ( $existing_page_id && get_post( $existing_page_id ) ) { $this->sync_to_page( $post->post_content, $existing_page_id ); return; } $this->generate_page( $post_id ); } public function on_linked_page_saved( int $page_id, \WP_Post $page ): void { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; if ( wp_is_post_revision( $page_id ) ) return; if ( self::$syncing ) return; if ( 'publish' !== $page->post_status && 'private' !== $page->post_status ) return; $member_id = (int) get_post_meta( $page_id, '_academiccrew_member_id', true ); if ( ! $member_id ) return; self::$syncing = true; wp_update_post( [ 'ID' => $member_id, 'post_content' => $page->post_content ] ); self::$syncing = false; } private function sync_to_page( string $content, int $page_id ): void { if ( self::$syncing ) return; self::$syncing = true; wp_update_post( [ 'ID' => $page_id, 'post_content' => $content ] ); self::$syncing = false; } // --------------------------------------------------------------- // Lifecycle hooks — trash / delete // --------------------------------------------------------------- public function on_cpt_trashed( int $post_id ): void { if ( 'academiccrew_member' !== get_post_type( $post_id ) ) return; $page_id = (int) get_post_meta( $post_id, '_academiccrew_linked_page_id', true ); if ( ! $page_id || 'publish' !== get_post_status( $page_id ) ) return; self::$syncing = true; wp_trash_post( $page_id ); self::$syncing = false; } public function on_cpt_untrashed( int $post_id ): void { if ( 'academiccrew_member' !== get_post_type( $post_id ) ) return; $page_id = (int) get_post_meta( $post_id, '_academiccrew_linked_page_id', true ); if ( ! $page_id || 'trash' !== get_post_status( $page_id ) ) return; self::$syncing = true; wp_untrash_post( $page_id ); // wp_untrash_post may restore to 'draft' depending on WP config; ensure publish. if ( 'publish' !== get_post_status( $page_id ) ) { wp_update_post( [ 'ID' => $page_id, 'post_status' => 'publish' ] ); } self::$syncing = false; } public function on_cpt_before_delete( int $post_id, \WP_Post $post ): void { if ( 'academiccrew_member' !== $post->post_type ) return; // Delete the linked profile page. $page_id = (int) get_post_meta( $post_id, '_academiccrew_linked_page_id', true ); if ( $page_id && get_post( $page_id ) ) { self::$syncing = true; $this->remove_nav_menu_items_for_page( $page_id ); wp_delete_post( $page_id, true ); self::$syncing = false; } // Remove publication links and orphaned publications. $this->purge_member_publications( $post_id ); } /** * Removes the member's rows from academiccrew_member_publications, then * permanently deletes any publication that has no remaining member links. */ private function purge_member_publications( int $member_id ): void { global $wpdb; $rel_table = esc_sql( $wpdb->prefix . 'academiccrew_member_publications' ); $pub_table = esc_sql( $wpdb->prefix . 'academiccrew_publications' ); // Collect all publication IDs linked to this member. $pub_ids = $wpdb->get_col( $wpdb->prepare( // phpcs:ignore WordPress.DB "SELECT publication_id FROM {$rel_table} WHERE member_id = %d", $member_id ) ); // Remove the member's relation rows. $wpdb->delete( $rel_table, [ 'member_id' => $member_id ], [ '%d' ] ); // phpcs:ignore WordPress.DB if ( empty( $pub_ids ) ) { return; } // Delete publications that are now unlinked (no remaining member). $placeholders = implode( ',', array_fill( 0, count( $pub_ids ), '%d' ) ); $orphans = $wpdb->get_col( $wpdb->prepare( // phpcs:ignore WordPress.DB "SELECT p.id FROM {$pub_table} p WHERE p.id IN ({$placeholders}) AND NOT EXISTS ( SELECT 1 FROM {$rel_table} r WHERE r.publication_id = p.id )", ...$pub_ids ) ); foreach ( $orphans as $pub_id ) { $wpdb->delete( $pub_table, [ 'id' => (int) $pub_id ], [ '%d' ] ); // phpcs:ignore WordPress.DB } } /** * When the linked WP page is permanently deleted from the Pages screen, * clear the cross-reference so next CPT save regenerates it. */ public function on_page_before_delete( int $post_id, \WP_Post $post ): void { if ( 'page' !== $post->post_type ) return; if ( self::$syncing ) return; $member_id = (int) get_post_meta( $post_id, '_academiccrew_member_id', true ); if ( ! $member_id ) return; delete_post_meta( $member_id, '_academiccrew_linked_page_id' ); } // --------------------------------------------------------------- // Page generation // --------------------------------------------------------------- public function generate_page( int $member_id ): int { $member = get_post( $member_id ); if ( ! $member ) { return 0; } $content = ''; $archive_page_id = (int) get_option( 'academiccrew_members_archive_page_id', 0 ); if ( ! $archive_page_id ) { MembersArchivePageGenerator::generate_if_needed(); $archive_page_id = (int) get_option( 'academiccrew_members_archive_page_id', 0 ); } $role_key = (string) ( get_post_meta( $member_id, '_acc_member_role', true ) ?: '' ); $rank = (int) ( get_post_meta( $member_id, '_acc_role_rank', true ) ?: 0 ); $role_order = array_keys( RoleRegistry::all() ); $found = array_search( $role_key, $role_order, true ); $role_pos = $found !== false ? (int) $found : 99; $menu_order = $role_pos * 1000 + ( $rank > 0 ? min( $rank, 998 ) : 999 ); $page_id = $this->create_page( [ 'post_title' => $member->post_title, 'post_content' => $content, 'post_status' => 'publish', 'post_type' => 'page', 'post_name' => $member->post_name, 'post_parent' => $archive_page_id ?: 0, 'menu_order' => $menu_order, 'meta_input' => [ '_academiccrew_is_member_page' => 1, '_academiccrew_member_id' => $member_id, 'pyre_page_title' => 'no', 'pyre_sidebar_position' => 'none', ], ] ); if ( ! $page_id ) { return 0; } update_post_meta( $member_id, '_academiccrew_linked_page_id', $page_id ); return $page_id; } }