% npŦߨƅDS2 6K.q1o~:wV~zeBwTLR\'d@s_~OO߶jrb#tZ[w9]Cl]ҎQTG`;S}ޣ SvS|hQpq$04bn ,loܗ?h'C<B:~]mP}L EJ)71{gEa&AS D:IK DgZeZI]n2A~xɋRb`nFYN63pU6!&"%b 7O ؕXWDkb%ސ澐nňB9w7U3U/D"w Nl,ķMs]} oRj%bC볉ys TGT'5t\Pq^IIlko8 GJ_-t.D33줾uY Zᆩ|ItvJu+~M?^;µHW||rFS⎓\lBzkSҩGb2-ND!9 jR~BZ ȑP^!) m()EZ4!)#5F O[UE3VgW pY%3$/K~h=pcB[&7_@nh.Um4 f.3v2b>[rmt&&VJrf Nf.ŭMŘ$VB0r ퟠxZQ=Pn:zb,S->4?s|f` [SW(a_A0N]Ը*THnxPҬ< h7LuEF}>yqvrɵ z+da-h;pd^s~&e[faFL[m!]T@朁sD 1*J:"u|CL#%7"Pj |aE Ip>"vwhY6Le˃@-%^gG,v=N{ªޠ:1gk<9w\.^Z~XWF!R. ʭkl11>Oa+1V4 v+:TxĮ1FRq#rRqyd23Oܻ\7˽ C{![o f@X}og.=|" ̥݇yXf~T.y}Cc3"Oov  % Yֿ X.DhѸ8@$>V!%l{AZFngWxHvN~;PF I1?P V?M5Ao SR(sI8I&F#J5ekam/Gě+f3I_+I| #_hB "y+B BYvr! o~J`{b| P+tғm+3%;jJL 49vI GO*tUS+>?mCuu:g3\날{ϔ3d68FH~ye6d"v-GwQ_jXw ;UB+<\,K s5P:Bf˒Īl٩vuWkmFSzYpȐE~ÄTuE^yˌGHܬ 9<2>wC[%vY`'RzV#TGu_tJv% 7^tjY[pa09RzY88ujTѩ=$ó0!{q[ AJtFo'B*˩2rZ'h2-- c~%Aڤg%/|Kge3㠮Txp=#[ t8t7yP<34|VRwAl#B++{{0NFR=g-rEM0*fՍ5sg85$f?;8 l\좤~XxAP@֟oE[df3< ,[lPw]V92A1ޞwJ*كeً[? 4c))GGK?R ݀U~(lLos`"̵ (u0S,^>F!"oe3Y(-'rU4L$;,Rݮ)V@g-f7!^6X.u\6nni7R %#]EQ gvZʿ$_Ţ=(Hy!Qpl7G}=-Y'/bm3xFz土#FdIJ۴:r-nTpt'|@cW| ůr4vxmj"ƿ}VV<4py؈jzN mc5!Pf^7!> 8L D9o%Cd:|{Ap"2k:wjI$;!"3t/R6>^ZLl%Xˀ2~ų5R6ffN% 0@*jlȁ"Ľg?dX8*HZ>O7[(=|j;Uøk ɮm3d΋㤀ȤZXk' $lyߊ&GFY\6tD a-4OY1q>BPAYuyK[^o2삘谦W~(,L%Fd|{LZ i|%"mG"*zP`]KY (zHD,6 >xAС$[l+?n)Dф1(rۭ$#5dRFM"?"*͓ۼ4"H>eh/0]Pm(i@W[DImqfZ{޹Gzyt*J"(mF)yR϶!14CUˉpcɳ=^1G3u was returned. if ( $graph_pieces !== false && \array_key_exists( '@type', $graph_pieces ) ) { $graph_pieces = [ $graph_pieces ]; } if ( ! \is_array( $graph_pieces ) ) { continue; } foreach ( $graph_pieces as $graph_piece ) { /** * Filter: 'wpseo_schema_' - Allows changing graph piece output. * This filter can be called with either an identifier or a block type (see `add_schema_blocks_graph_pieces()`). * * @api array $graph_piece The graph piece to filter. * * @param Meta_Tags_Context $context A value object with context variables. * @param Abstract_Schema_Piece $graph_piece_generator A value object with context variables. * @param Abstract_Schema_Piece[] $graph_piece_generators A value object with context variables. */ $graph_piece = \apply_filters( 'wpseo_schema_' . $identifier, $graph_piece, $context, $graph_piece_generator, $graph_piece_generators ); $graph_piece = $this->type_filter( $graph_piece, $identifier, $context, $graph_piece_generator, $graph_piece_generators ); $graph_piece = $this->validate_type( $graph_piece ); if ( \is_array( $graph_piece ) ) { $graph[] = $graph_piece; } } } /** * Filter: 'wpseo_schema_graph' - Allows changing graph output. * * @api array $graph The graph to filter. * * @param Meta_Tags_Context $context A value object with context variables. */ $graph = \apply_filters( 'wpseo_schema_graph', $graph, $context ); return $graph; } /** * Adds schema graph pieces from Gutenberg blocks on the current page to * the given schema graph. * * Think of blocks like the Yoast FAQ block or the How To block. * * @param array $graph The current schema graph. * @param Meta_Tags_Context $context The meta tags context. * * @return array The graph with the schema blocks graph pieces added. */ protected function add_schema_blocks_graph_pieces( $graph, $context ) { foreach ( $context->blocks as $block_type => $blocks ) { foreach ( $blocks as $block ) { $block_type = \strtolower( $block['blockName'] ); /** * Filter: 'wpseo_schema_block_'. * This filter is documented in the `generate_graph()` function in this class. */ $graph = \apply_filters( 'wpseo_schema_block_' . $block_type, $graph, $block, $context ); if ( isset( $block['attrs']['yoast-schema'] ) ) { $graph[] = $this->schema_replace_vars_helper->replace( $block['attrs']['yoast-schema'], $context->presentation ); } } } return $graph; } /** * Finalizes the schema graph after all filtering is done. * * @param array $graph The current schema graph. * @param Meta_Tags_Context $context The meta tags context. * * @return array The schema graph. */ protected function finalize_graph( $graph, $context ) { $graph = $this->remove_empty_breadcrumb( $graph, $context ); return $graph; } /** * Removes the breadcrumb schema if empty. * * @param array $graph The current schema graph. * @param Meta_Tags_Context $context The meta tags context. * * @return array The schema graph with empty breadcrumbs taken out. */ protected function remove_empty_breadcrumb( $graph, $context ) { if ( $this->helpers->current_page->is_home_static_page() || $this->helpers->current_page->is_home_posts_page() ) { return $graph; } // Remove the breadcrumb piece, if it's empty. $index_to_remove = 0; foreach ( $graph as $key => $piece ) { if ( \in_array( 'BreadcrumbList', $this->get_type_from_piece( $piece ), true ) ) { if ( isset( $piece['itemListElement'] ) && \is_array( $piece['itemListElement'] ) && \count( $piece['itemListElement'] ) === 1 ) { $index_to_remove = $key; break; } } } // If the breadcrumb piece has been removed, we should remove its reference from the WebPage node. if ( $index_to_remove !== 0 ) { \array_splice( $graph, $index_to_remove, 1 ); // Get the type of the WebPage node. $webpage_types = \is_array( $context->schema_page_type ) ? $context->schema_page_type : [ $context->schema_page_type ]; foreach ( $graph as $key => $piece ) { if ( ! empty( \array_intersect( $webpage_types, $this->get_type_from_piece( $piece ) ) ) && isset( $piece['breadcrumb'] ) ) { unset( $piece['breadcrumb'] ); $graph[ $key ] = $piece; } } } return $graph; } /** * Adapts the WebPage graph piece for password-protected posts. * * It should only have certain whitelisted properties. * The type should always be WebPage. * * @param array $graph_piece The WebPage graph piece that should be adapted for password-protected posts. * * @return array The WebPage graph piece that has been adapted for password-protected posts. */ public function protected_webpage_schema( $graph_piece ) { $properties_to_show = \array_flip( [ '@type', '@id', 'url', 'name', 'isPartOf', 'inLanguage', 'datePublished', 'dateModified', 'breadcrumb', ] ); $graph_piece = \array_intersect_key( $graph_piece, $properties_to_show ); $graph_piece['@type'] = 'WebPage'; return $graph_piece; } /** * Gets all the graph pieces we need. * * @param Meta_Tags_Context $context The meta tags context. * * @return Abstract_Schema_Piece[] A filtered array of graph pieces. */ protected function get_graph_pieces( $context ) { if ( $context->indexable->object_type === 'post' && \post_password_required( $context->post ) ) { $schema_pieces = [ new Schema\WebPage(), new Schema\Website(), new Schema\Organization(), ]; \add_filter( 'wpseo_schema_webpage', [ $this, 'protected_webpage_schema' ], 1 ); } else { $schema_pieces = [ new Schema\Article(), new Schema\WebPage(), new Schema\Main_Image(), new Schema\Breadcrumb(), new Schema\Website(), new Schema\Organization(), new Schema\Person(), new Schema\Author(), new Schema\FAQ(), new Schema\HowTo(), ]; } /** * Filter: 'wpseo_schema_graph_pieces' - Allows adding pieces to the graph. * * @param Meta_Tags_Context $context An object with context variables. * * @api array $pieces The schema pieces. */ return \apply_filters( 'wpseo_schema_graph_pieces', $schema_pieces, $context ); } /** * Allows filtering the graph piece by its schema type. * * Note: We removed the Abstract_Schema_Piece type-hint from the $graph_piece_generator argument, because * it caused conflicts with old code, Yoast SEO Video specifically. * * @param array $graph_piece The graph piece we're filtering. * @param string $identifier The identifier of the graph piece that is being filtered. * @param Meta_Tags_Context $context The meta tags context. * @param Abstract_Schema_Piece $graph_piece_generator A value object with context variables. * @param Abstract_Schema_Piece[] $graph_piece_generators A value object with context variables. * * @return array The filtered graph piece. */ private function type_filter( $graph_piece, $identifier, Meta_Tags_Context $context, $graph_piece_generator, array $graph_piece_generators ) { $types = $this->get_type_from_piece( $graph_piece ); foreach ( $types as $type ) { $type = \strtolower( $type ); // Prevent running the same filter twice. This makes sure we run f/i. for 'author' and for 'person'. if ( $type && $type !== $identifier ) { /** * Filter: 'wpseo_schema_' - Allows changing graph piece output by @type. * * @api array $graph_piece The graph piece to filter. * * @param Meta_Tags_Context $context A value object with context variables. * @param Abstract_Schema_Piece $graph_piece_generator A value object with context variables. * @param Abstract_Schema_Piece[] $graph_piece_generators A value object with context variables. */ $graph_piece = \apply_filters( 'wpseo_schema_' . $type, $graph_piece, $context, $graph_piece_generator, $graph_piece_generators ); } } return $graph_piece; } /** * Retrieves the type from a graph piece. * * @param array $piece The graph piece. * * @return array An array of the piece's types. */ private function get_type_from_piece( $piece ) { if ( isset( $piece['@type'] ) ) { if ( \is_array( $piece['@type'] ) ) { // Return as-is, but remove unusable values, like sub-arrays, objects, null. return \array_filter( $piece['@type'], 'is_string' ); } return [ $piece['@type'] ]; } return []; } /** * Validates a graph piece's type. * * When the type is an array: * - Ensure the values are unique. * - Only 1 value? Use that value without the array wrapping. * * @param array $piece The graph piece. * * @return array The graph piece. */ private function validate_type( $piece ) { if ( ! isset( $piece['@type'] ) ) { // No type to validate. return $piece; } // If it is not an array, we can return immediately. if ( ! \is_array( $piece['@type'] ) ) { return $piece; } /* * Ensure the types are unique. * Use array_values to reset the indices (e.g. no 0, 2 because 1 was a duplicate). */ $piece['@type'] = \array_values( \array_unique( $piece['@type'] ) ); // Use the first value if there is only 1 type. if ( \count( $piece['@type'] ) === 1 ) { $piece['@type'] = \reset( $piece['@type'] ); } return $piece; } }