• API interactivity

    Read more

    In block.json : { supports: { interactivity: true }, viewScriptModule: file:./view.js }

    1/ create directive

    data-wp-interactive="namespace"

    data-wp-context='{"foo": "bar"}' → local state

    data-wp-class--{class-name} the class presence will be tied to a value

    data-wp-bind--{attribute-name}

    data-wp-on--{event-name}

    2/ create a store

    • state
    • actions
    • callback
    • getElement()
    • getContext()

    Example

    view.js :

    import { store, getContext } from '@wordpress/interactivity';
    
    store( 'wpdsfr', {
    	actions: {
    		toggle: () => {
    			const context = getContext();
    			context.isOpen = ! context.isOpen;
    		},
    	}
    } );

    render.php

    <section
    	data-wp-interactive="wpdsfr"
    	data-wp-context='{ "isOpen": false}'
    	<?php echo get_block_wrapper_attributes(); ?>>
    
    	<h3	data-wp-on--click="actions.toggle">
    		item title
    	</h3>
    
    	<div data-wp-class--is-open="context.isOpen">
    		<?php echo $content; ?>
    	</div>
    </section>

    Sources:

  • block: RichText edition

    Read more

    With an attribute in block.json as attributes: { foo: { type: string, default: bar } }

    In edit.js :

    import { useBlockProps, RichText } from '@wordpress/block-editor';
    
    export default function Edit( { attributes, setAttributes } ) {
    	const blockProps = useBlockProps();
    
    	return (
    		<RichText
    			{ ...useBlockProps() }
    			tagName="p"
    			onChange={ val => setAttributes( { foo: val } ) }
    			value={ attributes.content }
    			placeholder="Enter some text here..."
    		/>
    	);
    }

    In render.php

    echo attributes['foo'];

    Sources:

  • block: setting sidebar options

    Read more

    in block.json:

    { ...
      "attributes": {
        "foo": {
          "type": "string",
          "default": "bar"
        }
      }
    }

    in render.php, the data is accessible simply with echo $attributes['foo'];

    In edit.js

    import { TextControl, Panel, PanelBody } from '@wordpress/components';
    import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
    
    export default function Edit( { attributes, setAttributes } ) {
    	const blockProps = useBlockProps();
    
    	return (
    		<p { ...blockProps }>
    			<InspectorControls key="setting">
    				<Panel>
    					<PanelBody title="Champs" initialOpen={true}>
    						<TextControl className="blocks-base-control__input"
    							label={"Foo"}
    							value={attributes.foo}
    							onChange={(val) => setAttributes({foo: val})}
    						/>
    					</PanelBody>
    				</Panel>
    			</InspectorControls>
    			{ attributes.heading }
    		</p>
    	);
    }

    Adding toggle button, for an attribute defined as { foo: { type: "boolean", default: true } }

     <ToggleControl
        label="Toggle Content"
        checked={ showContent }
        onChange={ (newValue) => setAttributes( { foo: newValue } )  }
    />

    A range button :

    <InspectorControls>
        <PanelBody>
            <RangeControl
                label="Columns"
                value={ columnCount }
                onChange={ onChangeColumnCount }
                min={ 2 }
                max={ 6 }
             />
       </PanelBody>
    </InspectorControls>

    NB: To extend useBlockProps hooks, just send new data as parameter:

    { ...useBlockProps( { style: columnStyles } ) }

    Sources:

  • API binding

    Read more

    Populate dynamically blocks (button, image, paragraph and heading), by setting the source, from meta field or php logic.

    meta field

    <!-- wp:paragraph {
    	"metadata":{
    		"bindings":{
    			"content":{
    				"source":"core/post-meta",
    				"args":{
    					"key":"book-genre"
    				}
    			}
    		}
    	}
    } -->
    <p></p>
    <!-- /wp:paragraph -->
    register_meta(
        'post',
        'book-genre',
        array(
            'show_in_rest' => true,
            'single'       => true,
            'type'         => 'string',
            'default'      => 'Default text field',
        )
    );

    php logic

    $source_name = 'namespace/name';
    $source_properties = array(
    	'label'	=> 'Name',
    	'get_value_callback' => 'fn',
    	'uses_context' => ['postId']
    );
    
    register_block_bindings_source(
    	string $source_name,
    	array $source_properties
    );
    
    projectslug_bindings_callback(
    	array $source_args, 
    	WP_Block $block_instance, 
    	string $attribute_name
    );

    Source: https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/

  • MYSQL cheat sheet

    Read more

    Explore :

    SHOW DATABASES;
    SHOW TABLES;
    
    USE database;
    
    DESCRIBE table;
    
    CREATE DATABASE database;
    DROP DATABASE database;

    User:

    CREATE USER 'user'@'localhost' IDENTIFIED BY 'pwd';
    GRANT ALL PRIVILEGES ON database.* TO 'user'@'localhost' IDENTIFIED BY 'pwd';
    FLUSH PRIVILEGES;

    Import:

    mysql -u username -p databasename < dump.sql

    Export:

    mysqldump -u username -p databasename > dump.sql

    Update user password

    Generate pwd: http://www.miraclesalad.com/webtools/md5.php

    sudo mysql
    
    SHOW DATABASES;
    USE database;
    SHOW TABLES;
    DESCRIBE usertable;
    SELECT ID, user_pass;
    UPDATE usertable SET user_pass="md5-pwd" WHERE ID = id;

    Sources:

  • Font variable

    Read more

    Reduce http requests with one font file handling variations.

    "fontFamilies": [
    				{
    					"fontFamily": "montserrat, sans-serif",
    					"name": "Montserrat",
    					"slug": "default",
    					"fontFace": [
    						{
    							"fontFamily": "montserrat",
    							"fontVariationSettings": "'wght' 400",
    							"fontStyle": "normal",
    							"fontWeight": "400",
    							"src": ["file:./assets/fonts/Montserrat-VariableFont_wght.ttf"]
    						},
    						{
    							"fontFamily": "montserrat",
    							"fontVariationSettings": "'wght' 500",
    							"fontStyle": "normal",
    							"fontWeight": "500",
    							"src": ["file:./assets/fonts/Montserrat-VariableFont_wght.ttf"]
    						},
    						{
    							"fontFamily": "montserrat",
    							"fontVariationSettings": "'wght' 600",
    							"fontStyle": "normal",
    							"fontWeight": "600",
    							"src": ["file:./assets/fonts/Montserrat-VariableFont_wght.ttf"]
    						},
    						{
    							"fontFamily": "montserrat",
    							"fontVariationSettings": "'wght' 400",
    							"fontStyle": "italic",
    							"fontWeight": "400",
    							"src": ["file:./assets/fonts/Montserrat-Italic-VariableFont_wght.ttf"]
    						}
    					]
    				}
    			]
  • git vim

    Read more

    To set vim as text editor in git:

    git config core.editor
  • git rebase

    Read more

    To squash a list of commits into a single one, for a better clarity of the tree:

    git rebase -i HEAD~<number of commits>
    git push origin <branch name> --force
  • ACF field filter query

    Read more

    Order by ACF field

    $the_query = new WP_Query(array(
        'post_type'         => 'event',
        'posts_per_page'    => -1,
        'meta_key'          => 'featured',
        'orderby'           => 'meta_value',
        'order'             => 'DESC'
    ));

    Filter by ACF field

    $posts = get_posts(array(
        'posts_per_page'    => -1,
        'post_type'     => 'post',
        'meta_query'    => array(
            'relation'      => 'AND',
            array(
                'key'       => 'color',
                'value'     => array('red', 'orange'),
                'compare'   => 'IN',
            ),
            array(
                'key'       => 'featured',
                'value'     => '1',
                'compare'   => '=',
            ),
        ),
    ));

    Sources:

    • https://www.advancedcustomfields.com/resources/query-posts-custom-fields/
    • https://www.advancedcustomfields.com/resources/order-posts-by-custom-fields/
  • Programmatically managing content

    Read more

    Creating

    $args = array(
    	'post_type'	=> 'eco__cpt_ressource',
    	'post_title'	=> get_the_title( $attachment_id ),
    	'post_status'	=> 'publish',
    	'post_author'	=> get_current_user_id(),
    	'post_date'	=> get_the_date( 'Y-m-d H:i:s', $attachment_id )
    );
    
    
    $new_postid = wp_insert_post( $args );

    See: https://developer.wordpress.org/reference/functions/wp_insert_post/

    Editing:

    See:

    • https://developer.wordpress.org/reference/functions/wp_update_post/
    • https://developer.wordpress.org/reference/functions/wp_set_object_terms/

    Deleting

    wp_delete_post( $post_id );

    See: https://developer.wordpress.org/reference/functions/wp_delete_post/

  • Adding an option page

    Read more

    With ACF:

    add_action( 'acf/init', 'dev__option_page' );
    
    function dev__option_page(){
    
    	acf_add_options_page( array(
    		'page_title' 	=> 'Transfert Ressources',
    		'menu_title'	=> 'Transfert de Ressources',
    		'icon_url'		=> 'dashicons-update',
    		'menu_slug' 	=> 'ator',
    		'capability'	=> 'edit_posts',
    		'redirect'		=> false
    	));
    
    }

    See: https://www.advancedcustomfields.com/resources/acf_add_options_page/

    Without ACF: https://codex.wordpress.org/Creating_Options_Pages

  • Menu: edit items

    Read more

    To edit individual items of a menu

    add_filter( 'wp_nav_menu_objects', 'dev__add_smiley', 11, 2 );
    
    function dev__add_smiley( $items, $args ) {
    
    	foreach ( $items as &$item ) {
    
    		// add class
    		array_push($item->classes, 'dw-submenu__parent');
    			
    		$item->title = ':D ' . $item->title;
    
    		// $item->title .= '</a>' . $subelement; note: close the link
    
    		}
    
    	}
    
    	return $items;
    
    }

    Sources:

    • https://developer.wordpress.org/reference/hooks/wp_nav_menu_items/
    • https://developer.wordpress.org/reference/hooks/wp_nav_menu_objects/
  • metadata customTemplates

    Read more

    A way to add data to the custom templates placed in the /templates folder:

    {
    	"$schema": "https://schemas.wp.org/trunk/theme.json",
    	"version": 2,
    	"customTemplates": [
    		{
    			"name": filename",
    			"title": "Template Name",
    			"postTypes": [
    				"page"
    			]
    		}
    	],
    	"settings": ...
    }

    Source: https://developer.wordpress.org/block-editor/how-to-guides/themes/global-settings-and-styles/#customtemplates

  • Parse args

    Read more

    Merge an array into a predefined one. Convenient to handle default configurations.

    wp_parse_args( string|array|object $args, array $defaults = array() ): array

    $args = wp_parse_args(
    
    	array(
    		'relation' => 'AND',
    		array(
    			'taxonomy' => 'product_type',
    			'field'    => 'slug',
    			'terms'    => 'variable',
    			'operator' => 'IN'
    		),
    	),
    
    	$query->get( 'tax_query' )
    
    );
    
    $query->set( 'tax_query',  $args );

    Source: https://developer.wordpress.org/reference/functions/wp_parse_args/

  • Block variations

    Read more

    Block styles: create specific look variations with CSS

    Block variations: preset a block configuration.

    Example:

    wp.blocks.registerBlockVariation(
    	'core/quote',
    	{
    		name: 'dev-name',
    		title: 'Title',
    		icon: 'format-status',
    		isDefault: true,
    		attributes: {
    			templateLock: 'all',
    			className: 'dev-classname'
    		},
    		innerBlocks: [
    			[
    				'core/image',
    				{
    					className: 'wp-block-image alignleft'
    				}
    			],
    			[
    				'core/paragraph',
    				{
    					className: 'dev-paragraph'
    				}
    			]
    		]
    	}
    );

    Source:

    • https://fullsiteediting.com/lessons/block-variations/
    • https://developer.wordpress.org/block-editor/reference-guides/block-api/block-variations/
    • https://developer.wordpress.org/news/2023/08/29/an-introduction-to-block-variations/
  • Hardening WordPress

    Read more
    • replace wp_ db prefix
    • rename folders wp-content, uploads, plugins, wp-admin, wp-include
    define('UPLOADS', 'media' ); // we renamed uploads and moved it level up
    define('WP_CONTENT_DIR', '/path/to/wordpress/dir/content'); // no host name, no trailing slash
    define('WP_CONTENT_URL', 'http://example.com/content');
    define('WP_PLUGIN_DIR', '/path/to/wordpress/dir/content/mod'); // no host name, no trailing slash
    define('WP_PLUGIN_URL', 'http://example.com/content/mod');
    • change cookie’s names
    define('USER_COOKIE', 'my_user_cookie' );
    define('PASS_COOKIE', 'my_pass_cookie' );
    define('AUTH_COOKIE', 'my_auth_cookie' );
    define('SECURE_AUTH_COOKIE', 'my_sec_cookie' );
    define('LOGGED_IN_COOKIE', 'my_logged_cookie' );
    define('TEST_COOKIE', 'my_test_cookie' );

    Sources:

    • https://wpcerber.com/turn-wordpress-into-fort-knox/
    • https://developer.wordpress.org/advanced-administration/security/hardening/
  • Redirections

    Read more

    Si on veut cacher à la fois les single et les archives, le plus simple est public à false. Préciser show_ui et show_in_rest à true pour pouvoir les gérer dans l’admin et dans Gutenberg. Ne pas préciser de rewrite du tout, ou alors à false. 

    => les urls ne sont pas du tout accessibles de nulle part (pas de lien “Voir” dans le BO), y compris en SEO : c’est le plus pratique, et pas d’erreurs/oublis possibles.

    Pour mémoire, si on veut uniquement cacher les archives, avoir public à true, mais has_archive à false permet de pouvoir gérer une Page au même nom que slug des single. Si on ne veut aucune page au slug des singles, laisser has_archive à true, et créer une redirection via le filter template_redirect. (cas des Rapports d’ecomaison).

    Si on veut cacher uniquement les singles mais conserver l’archive (si on n’a pas de page qui fait le job, ce qui est quand même le cas le plus fréquent), il faut passer par du code WP de redirection sur le filter template_redirect  et bloquer les singles dans Yoast SEO.

    Sylvie

    To handle redirections:

    add_actions( 'template_redirect', 'fn' );
    
    function fn() {
      wp_redirect( home_url( '/' ), 301 );
      exit;
    }

    Sources:

    • https://developer.wordpress.org/reference/functions/wp_redirect/
    • https://developer.wordpress.org/reference/hooks/template_redirect/
  • File system method

    Read more

    The hosting server doesn’t necessary allow WordPress to write files.

    define( 'FS_METHOD', 'direct' );

    Sources:

    • https://wordpress.stackexchange.com/questions/189554/what-security-concerns-should-i-have-when-setting-fs-method-to-direct-in-wp-co
    • https://kinsta.com/knowledgebase/constant-fs_method/
    • https://developer.wordpress.org/reference/functions/get_filesystem_method/
  • CRON: add a new task

    Read more

    Set a timing interval

    add_filter( 'cron_schedules', 'dev__set_interval' );
    function dev__set_interval( $schedules ) {
    	$schedules['dev__interval__5_minutes'] = array(
    		'interval'	=> 60 * 5,
    		'display'	=> 'Every 5 Minutes'
    	);
    	return $schedules;
    }

    Check if the CRON task already exists

    if ( ! wp_next_scheduled( 'dev__a_cron_thing' ) ) {
        wp_schedule_event( time(), 'dev__interval__5_minutes', 'dev__a_cron_thing' );
    }

    Add action to that new hook

    add_action( 'dev__a_cron_thing', 'dev__a_cron_thing__fallback' );
    
    function dev__a_cron_thing__fallback() {
    
    ...
    
    }
  • REST API: add new route

    Read more

    Create new routes likes https://wp_url/wp-json/dev/v1/some-endpoint

    add_action( 'rest_api_init', 'dev__rest_api_init');
    
    function dev__rest_api_init (){
    
    	register_rest_route(
    		'dev/v1',
    		'/some-endpoint/',
    		array(
    			'methods' => 'GET',
    			'callback' => 'dev__infos__get',
    		)
    	);
    
    }
    
    function dev__infos__get() {
    
    	$data = array(...);
    
    	$response = new WP_REST_Response( $data );
    	$response->set_status( 200 );
    
    	return $response;
    
    }

    Sources:

    • https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/
    • https://developer.wordpress.org/reference/functions/register_rest_route/
  • get_posts() vs WP_Query

    Read more

    $args are the same tho.

    get_posts is a function that will return an array, when WP_Query is a class which will be used with the loop related functions.

    Source:

    • https://wordpress.stackexchange.com/questions/1753/when-should-you-use-wp-query-vs-query-posts-vs-get-posts#answer-1755
    • https://developer.wordpress.org/reference/classes/wp_query/#parameters
  • DOING_AJAX, block admin access

    Read more

    To block the access to the admin page to users, but allowing the access to ajax at the same time:

    add_action( 'admin_init', 'dev__block_admin_access', 100 );
    
    function dev__block_admin_access() {
    
        if ( ! defined( 'DOING_AJAX' ) && ! current_user_can( 'edit_pages' ) ) {
            exit( wp_redirect( esc_url( home_url( '/' ) ) ) );
        }
    }

    source: https://developer.wordpress.org/reference/functions/wp_doing_ajax/

  • GIT del branch locally / remotely

    Read more

    Locally: git branch --delete --force <branch name>

    Remotely: git push <remote name> --delete <branch name>

    Source: https://stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-locally-and-remotely

  • GIT old / new filemode

    Read more
    git config core.filemode false

    Source: https://stackoverflow.com/questions/1257592/how-do-i-remove-files-saying-old-mode-100755-new-mode-100644-from-unstaged-cha

  • Override blocks style

    Read more

    To override block styles for rules that can’t be changed through theme.json (complexe rules or non core blocks) , one can use dev__blocks__enqueue_styles function:

    add_action( 'init', 'dev__blocks__enqueue_styles' );
    
    function dev__blocks__enqueue_styles() {
    	foreach ( glob( get_stylesheet_directory() . '/assets/css/blocks-overriding/*', GLOB_ONLYDIR ) as $directory) {
    		$namespace = substr( strrchr( $directory, '/' ), 1 );
    		foreach ( glob( $directory . '/*.css' ) as $file) {
    			$filename = pathinfo( $file, PATHINFO_FILENAME );
    			wp_enqueue_block_style(
    				$namespace . '/' . $filename,
    				array(
    					'handle'	=> 'dw-override--' . $namespace . '-' . $filename,
    					'src'		=> get_stylesheet_directory_uri() . '/assets/css/blocks-overriding/' . $namespace . '/' . $filename . '.css',
    					'path'		=> get_stylesheet_directory() . '/assets/css/blocks-overriding/' . $namespace . '/' . $filename . '.css',
    					'ver'		=> filemtime( get_stylesheet_directory() . '/assets/css/blocks-overriding/' . $namespace . '/' . $filename . '.css' )
    				)
    			);
    		}
    	}
    }

    Source : https://developer.wordpress.org/reference/functions/wp_enqueue_block_style/

  • Dynamically generate a block

    Read more
    $block = array(
    	'blockName'	=> 'dev/blockname',
    	'attrs' => array(
    		'name'	=> 'dev/blockname',
    		'data'	=> array(
    			'ACF__field' => get_field( 'ACF__field' )
    		)
    	),
    );
    $html_output = render_block($block);

    To know what data send as parameter for the $block object, one can use the render_block filter

    add_filter( 'render_block', 'dev__paragraph_add_block', 10, 2 );
    function dev__paragraph_add_block( $block_content, $block ){
    
    	if ( 'dw/icon' === $block['blockName'] ){
    		echo '<pre>';
    		var_dump($block);
    		echo '</pre>';
    	}
    
    	return $block_content;
    
    }

    Source : https://www.itsupportguides.com/knowledge-base/wordpress/using-wordpress-render_block-php-function/

  • Register navigation menu

    Read more

    To register a menu :

    add_action( 'init', 'dev__register_menu' );
    
    function dev__register_menu() {
    
    	register_nav_menus(array(
    		'primary' => __( 'Primary Navigation', 'dev' ),
    		'secondary' => __( 'Secondary Navigation', 'dev' ),
    	));
    }
  • block supports properties

    Read more

    To use the supports properties, first enable them in the block.json.

    {
    	"$schema": "https://schemas.wp.org/trunk/block.json",
    	"apiVersion": 3,
    	
     ...
    
    	"supports": {
    		"color": {
    			"text": true,
    			"background": true
    		}
    	},
    	"acf": {
    		"mode": "preview",
    		"renderTemplate": "./render.php"
    	}
    }

    Then in render.php :

    <?php
    
    $classes = array();
    
    if ( ! empty( $block['textColor'] ) ) {
    //	$classes = array_merge( $classes, explode( ' ', $block['className'] ) );
    	$classes[] = 'has-text-color';
    	$classes[] = 'has-' . $block['textColor'] . '-color';
    }
    
    echo '<div class="' . esc_attr( join( ' ', $classes ) ) . '"></div>';

    Or simply use the function get_block_wrapper_attributes( string[] $extra_attributes = array() ): string for e.g. to get the classes :

    preg_match( '/class="(.*?)"/', get_block_wrapper_attributes(), $matches);
    classes = $matches[1];

    Sources:

  • $_GET / query variables

    Read more

    To retrieve $_GET parameters in a secure way, one can use get_query_var

    get_query_var( string $query_var, mixed $default_value = '' ): mixed

    The parameters would have first to be added to the public query variables available to WP_Query.

    add_filter( 'query_vars', 'dev__pommes' );
    
    function dev__pomme( $qvars ) {
    	$qvars[] = 'pommes';
    	return $qvars;
    }

    Source : https://developer.wordpress.org/reference/functions/get_query_var/

  • file permission

    Read more
    # before setup
    chown www-data:www-data  -R * # Let Apache be owner
    find . -type d -exec chmod 755 {} \;  # rwxr-xr-x
    find . -type f -exec chmod 644 {} \;  # rw-r--r--
    
    # after setup
    chown <username>:<username>  -R * 
    chown www-data:www-data wp-content
    

    Source: