// ========================================================= // WISSENSGRAPH – DEFINITION CPT // ========================================================= add_action( 'init', function() { register_post_type( 'bvg_definition', [ 'labels' => [ 'name' => 'Definitionen', 'singular_name' => 'Definition', 'add_new_item' => 'Neue Definition', 'edit_item' => 'Definition bearbeiten', ], 'public' => true, 'has_archive' => false, 'rewrite' => [ 'slug' => 'definition', 'with_front' => false, ], 'supports' => [ 'title', 'editor', 'excerpt' ], 'show_in_rest' => true, 'menu_icon' => 'dashicons-networking', 'menu_position'=> 6, ] ); } ); // ========================================================= // WISSENSGRAPH – META-FELDER // ========================================================= add_action( 'init', function() { $auth = function() { return current_user_can( 'edit_posts' ); }; $definition_meta = [ '_entity_uri', '_entity_type', '_entity_schema_type', '_definition_url', '_source_url', '_sr_nummer', '_stand_am', '_version', '_status', '_airtable_record_id', ]; foreach ( $definition_meta as $key ) { register_post_meta( 'bvg_definition', $key, [ 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_text_field', 'auth_callback' => $auth, ] ); } register_post_meta( 'bvg_frage', '_bvg_mentions_json', [ 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'wp_kses_post', 'auth_callback' => $auth, ] ); } ); // ========================================================= // WISSENSGRAPH – AIRTABLE CONFIG // In wp-config.php setzen: // define( 'DPK_AIRTABLE_TOKEN', 'xxx' ); // define( 'DPK_AIRTABLE_BASE_ID', 'xxx' ); // ========================================================= function dpk_airtable_is_configured() { return defined( 'DPK_AIRTABLE_TOKEN' ) && defined( 'DPK_AIRTABLE_BASE_ID' ); } function dpk_airtable_get_records( $table ) { if ( ! dpk_airtable_is_configured() ) { return []; } $records = []; $offset = null; do { $url = 'https://api.airtable.com/v0/' . rawurlencode( DPK_AIRTABLE_BASE_ID ) . '/' . rawurlencode( $table ); if ( $offset ) { $url .= '?offset=' . rawurlencode( $offset ); } $response = wp_remote_get( $url, [ 'headers' => [ 'Authorization' => 'Bearer ' . DPK_AIRTABLE_TOKEN, ], 'timeout' => 30, ] ); if ( is_wp_error( $response ) ) { break; } $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( empty( $body['records'] ) || ! is_array( $body['records'] ) ) { break; } $records = array_merge( $records, $body['records'] ); $offset = $body['offset'] ?? null; } while ( $offset ); return $records; } // ========================================================= // WISSENSGRAPH – ENTITY UPSERT // ========================================================= function dpk_find_definition_by_entity_uri( $entity_uri ) { $posts = get_posts( [ 'post_type' => 'bvg_definition', 'post_status' => 'any', 'meta_key' => '_entity_uri', 'meta_value' => $entity_uri, 'posts_per_page' => 1, 'fields' => 'ids', ] ); return $posts ? (int) $posts[0] : 0; } function dpk_upsert_definition_entity( array $record ) { $fields = $record['fields'] ?? []; $entity_uri = $fields['URI'] ?? ''; $name = $fields['Name'] ?? ''; if ( ! $entity_uri || ! $name ) { return 0; } $existing_id = dpk_find_definition_by_entity_uri( $entity_uri ); $post_data = [ 'post_type' => 'bvg_definition', 'post_status' => 'publish', 'post_title' => sanitize_text_field( $name ), 'post_excerpt' => sanitize_textarea_field( $fields['Kurzdefinition'] ?? '' ), 'post_content' => wp_kses_post( $fields['Kurzdefinition'] ?? '' ), ]; if ( $existing_id ) { $post_data['ID'] = $existing_id; $post_id = wp_update_post( $post_data, true ); } else { $post_data['post_name'] = sanitize_title( $fields['Slug'] ?? $name ); $post_id = wp_insert_post( $post_data, true ); } if ( is_wp_error( $post_id ) || ! $post_id ) { return 0; } update_post_meta( $post_id, '_airtable_record_id', sanitize_text_field( $record['id'] ?? '' ) ); update_post_meta( $post_id, '_entity_uri', esc_url_raw( $entity_uri ) ); update_post_meta( $post_id, '_definition_url', esc_url_raw( $fields['Definition URL'] ?? '' ) ); update_post_meta( $post_id, '_entity_type', sanitize_text_field( $fields['Entity Type'] ?? '' ) ); update_post_meta( $post_id, '_entity_schema_type', sanitize_text_field( $fields['Schema Type'] ?? 'DefinedTerm' ) ); update_post_meta( $post_id, '_source_url', esc_url_raw( $fields['Quelle URL'] ?? '' ) ); update_post_meta( $post_id, '_sr_nummer', sanitize_text_field( $fields['SR Nummer'] ?? '' ) ); update_post_meta( $post_id, '_stand_am', sanitize_text_field( $fields['Stand am'] ?? '' ) ); update_post_meta( $post_id, '_status', sanitize_text_field( $fields['Status'] ?? '' ) ); return (int) $post_id; } // ========================================================= // WISSENSGRAPH – REST SYNC ENDPOINT // Aufruf im eingeloggten Admin: // POST /wp-json/dpk/v1/sync-entities // ========================================================= add_action( 'rest_api_init', function() { register_rest_route( 'dpk/v1', '/sync-entities', [ 'methods' => 'POST', 'callback' => 'dpk_sync_entities_from_airtable', 'permission_callback' => function() { return current_user_can( 'manage_options' ); }, ] ); } ); function dpk_sync_entities_from_airtable() { $records = dpk_airtable_get_records( 'Entitäten' ); $created_or_updated = []; foreach ( $records as $record ) { $fields = $record['fields'] ?? []; if ( empty( $fields['Publish Definition'] ) ) { continue; } $post_id = dpk_upsert_definition_entity( $record ); if ( $post_id ) { $created_or_updated[] = [ 'post_id' => $post_id, 'name' => $fields['Name'] ?? '', 'uri' => $fields['URI'] ?? '', ]; } } flush_rewrite_rules( false ); return rest_ensure_response( [ 'success' => true, 'count' => count( $created_or_updated ), 'items' => $created_or_updated, ] ); } // ========================================================= // WISSENSGRAPH – ENTITY REGISTRY HELPERS // ========================================================= function dpk_get_all_definition_entities() { $posts = get_posts( [ 'post_type' => 'bvg_definition', 'post_status' => 'publish', 'posts_per_page' => -1, ] ); $entities = []; foreach ( $posts as $post ) { $uri = get_post_meta( $post->ID, '_entity_uri', true ); if ( ! $uri ) { continue; } $entities[] = [ 'post_id' => $post->ID, 'name' => get_the_title( $post->ID ), 'uri' => $uri, 'url' => get_permalink( $post->ID ), 'type' => get_post_meta( $post->ID, '_entity_schema_type', true ) ?: 'DefinedTerm', 'entity_type' => get_post_meta( $post->ID, '_entity_type', true ), ]; } return $entities; } function dpk_extract_mentions_from_article_text( $post_id ) { $text = wp_strip_all_tags( get_the_title( $post_id ) . "\n" . get_post_meta( $post_id, 'bvg_direktantwort', true ) . "\n" . get_post_field( 'post_content', $post_id ) . "\n" . get_post_meta( $post_id, 'bvg_fakten', true ) ); $entities = dpk_get_all_definition_entities(); $mentions = []; foreach ( $entities as $entity ) { $name = $entity['name']; if ( ! $name ) { continue; } if ( mb_stripos( $text, $name ) !== false ) { $mentions[ $entity['uri'] ] = [ '@type' => $entity['type'], '@id' => $entity['uri'], 'name' => $entity['name'], 'url' => $entity['url'], ]; } } return array_values( $mentions ); } // ========================================================= // WISSENSGRAPH – JSON-LD FÜR DEFINITIONEN // ========================================================= add_action( 'wp_head', function() { if ( ! is_singular( 'bvg_definition' ) ) { return; } $post_id = get_the_ID(); $entity_uri = get_post_meta( $post_id, '_entity_uri', true ); $schema_type = get_post_meta( $post_id, '_entity_schema_type', true ) ?: 'DefinedTerm'; $source_url = get_post_meta( $post_id, '_source_url', true ); $sr_nummer = get_post_meta( $post_id, '_sr_nummer', true ); $stand_am = get_post_meta( $post_id, '_stand_am', true ); $description = get_the_excerpt( $post_id ); if ( ! $entity_uri ) { return; } $jsonld = [ '@context' => 'https://schema.org', '@type' => $schema_type, '@id' => esc_url_raw( $entity_uri ), 'name' => get_the_title( $post_id ), 'description' => wp_strip_all_tags( $description ), 'url' => get_permalink( $post_id ), 'inLanguage' => 'de-CH', ]; if ( $source_url ) { $jsonld['sameAs'] = esc_url_raw( $source_url ); } if ( $sr_nummer && $schema_type === 'Legislation' ) { $jsonld['legislationIdentifier'] = 'SR ' . $sr_nummer; $jsonld['legislationJurisdiction'] = [ '@type' => 'Country', 'name' => 'Schweiz', ]; } if ( $stand_am ) { $jsonld['dateModified'] = $stand_am; } echo "\n\n"; }, 30 ); // ========================================================= // WISSENSGRAPH – JSON-LD FÜR WISSENSARTIKEL // ersetzt lokale #term-, #legislation-, #kennzahl-IDs // ========================================================= add_action( 'wp_head', function() { if ( ! is_singular( 'bvg_frage' ) ) { return; } $post_id = get_the_ID(); $url = get_permalink( $post_id ); $title = get_the_title( $post_id ); $published = get_the_date( 'c', $post_id ); $modified = get_the_modified_date( 'c', $post_id ); $direktantwort = get_post_meta( $post_id, 'bvg_direktantwort', true ); $quelle = get_post_meta( $post_id, 'bvg_quelle', true ); $stand = get_post_meta( $post_id, 'bvg_stand', true ); $article_id = $url . '#article'; $page_id = $url . '#webpage'; $org_id = home_url( '/' ) . '#organization'; $website_id = home_url( '/' ) . '#website'; $mentions = dpk_extract_mentions_from_article_text( $post_id ); $about = []; foreach ( $mentions as $mention ) { if ( in_array( $mention['@type'], [ 'DefinedTerm', 'Legislation' ], true ) ) { $about[] = [ '@id' => $mention['@id'], ]; } } $graph = []; $graph[] = [ '@type' => 'Organization', '@id' => $org_id, 'name' => 'Die Pensionskasse', 'url' => home_url( '/' ), ]; $graph[] = [ '@type' => 'WebSite', '@id' => $website_id, 'name' => 'Die Pensionskasse', 'url' => home_url( '/' ), 'publisher' => [ '@id' => $org_id ], 'inLanguage' => 'de-CH', ]; $graph[] = [ '@type' => 'WebPage', '@id' => $page_id, 'url' => $url, 'name' => $title, 'inLanguage' => 'de-CH', 'isPartOf' => [ '@id' => $website_id ], 'publisher' => [ '@id' => $org_id ], 'mainEntity' => [ '@id' => $article_id ], 'dateModified' => $modified, ]; $article = [ '@type' => 'Article', '@id' => $article_id, 'headline' => $title, 'url' => $url, 'datePublished' => $published, 'dateModified' => $modified, 'inLanguage' => 'de-CH', 'mainEntityOfPage' => [ '@id' => $page_id ], 'publisher' => [ '@id' => $org_id ], 'mentions' => array_map( function( $entity ) { return [ '@id' => $entity['@id'] ]; }, $mentions ), ]; if ( $about ) { $article['about'] = $about; } if ( $direktantwort ) { $article['description'] = wp_strip_all_tags( $direktantwort ); } if ( $quelle ) { $article['citation'] = $quelle; $article['isBasedOn'] = $quelle; } if ( $stand ) { $article['version'] = $stand; } $graph[] = $article; foreach ( $mentions as $entity ) { $graph[] = $entity; } $jsonld = [ '@context' => 'https://schema.org', '@graph' => $graph, ]; echo "\n\n"; }, 30 );