- $content
- $attributes (defined in block.json)
- $block
Notes about WordPress Gutenberg themes development.
ACF (6) admin (1) ajax (2) API interactivity (1) block (15) block_style (2) colors (2) constante (1) conventions (2) cron (1) css (5) custom post type (1) debug (2) define (1) file_API (1) functions.php (5) git (4) hook (6) i18n (2) js (2) layout (1) loop (1) media library (1) menu (2) navigation (1) post (1) query (3) readmore (1) responsive (1) rest api (1) security (6) spacing (1) sql (1) svg (1) taxonomy (1) theme (1) theme.json (11) typo (2) URL (1) wp-config.php (5) wp cli (3) wp function (7)
-
# block
-
Mixing Gutenberg’s blocks within an old theme
Read more
<?php echo apply_filters( 'the_content', do_blocks( get_post_field( 'post_content', $post->ID ) ) ); ?>
-
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 statedata-wp-class--{class-name}
the class presence will be tied to a valuedata-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:
- https://make.wordpress.org/core/2024/03/04/interactivity-api-dev-note/
- https://developer.wordpress.org/news/2024/04/11/a-first-look-at-the-interactivity-api/
- https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/api-reference/#list-of-directives (*)
- https://www.youtube.com/watch?v=49_XlQJYztA&list=LL&index=1
-
block: custom icon
# blockRead more
in index.js :
registerBlockType( metadata, { icon: { src: <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" enable-background="new 0 0 512 512"> <g> <g> <path d="... </g> </g> </svg> }, edit: Edit, save: Save } )
Source: https://rudrastyh.com/gutenberg/custom-svg-icons.html
-
block: innerBlocks
# blockRead more
index.js:
import { InnerBlocks } from '@wordpress/block-editor'; registerBlockType( metadata.name, { edit: Edit, save: props => { return <InnerBlocks.Content /> } ):
render.php:
<?php echo $content; ?>
in edit.js:
return ( <InnerBlocks { ...blockProps } /> )
Source: https://learn.wordpress.org/lesson/work-with-the-innerblocks-component/
-
block: RichText edition
# blockRead 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
# blockRead 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:
-
create a new block
# blockRead more
npx @wordpress/create-block@latest blockname --template @wordpress/create-block-interactive-template
Some other options :
npx @wordpress/create-block@latest blockname --variant dynamic
npx @wordpress/create-block@latest blockname --variant dynamic --template es5
→ no node module, great for simple block, remove –variant for a static one.Sources: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-create-block/
-
wp cli cheat sheet
# wp cliRead more
wp core update
-
API binding
# blockRead 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/
-
Auto populate select option field
# ACFRead more
add_filter('acf/load_field/name=color', 'acf_load_color_field_choices');
Source: https://www.advancedcustomfields.com/resources/dynamically-populate-a-select-fields-choices/
-
MYSQL cheat sheet
# sqlRead 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
# gitRead more
To set vim as text editor in git:
git config core.editor
-
git rebase
# gitRead 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
# ACFRead 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
-
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
# securityRead 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
# hookRead 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.
SylvieTo 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
# cronRead 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
# rest apiRead 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, whenWP_Query
is a class which will be used with theloop
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
# gitRead 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
# gitRead 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/
-
Enqueuing styles hooks
Read more
Front + editor
enqueue_block_assets
Editor only
enqueue_block_editor_assets
Sources:
-
Allow .svg import
Read more
add_filter('upload_mimes', 'dw__upload_mimes'); function dw__upload_mimes( $mimes=array() ){ $mimes['svg'] = 'image/svg+xml'; return $mimes; }
-
Dynamically generate a block
# blockRead 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
filteradd_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; }
-
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:
-
update_field
# ACFRead more
Prototype:
update_field($selector, $value, [$post_id]);
Note: to update a date field, the data has to be formatted first (even in case of copying the data from a date field).
$date_from = get_field( 'old_field', $old_post_id ); $date = DateTime::createFromFormat( 'd/m/Y', $date_from); $date = $date->format( 'Ymd' ); update_field('new_field', $date, $new_post_id);
Source : https://www.advancedcustomfields.com/resources/update_field/
-
Custom css variables
Read more
{ "version": 2, "settings": { "custom": { "line-height": { "body": 1.7, "heading": 1.3 } } } }
will be accessible in css
--wp--custom--line-height--body
Note: the custom property can be add into a block, thus enabling overriding.
Sources :
-
Block rendering filter
Read more
add_filter( 'render_block', 'dev__paragraph_add_block', 10, 2 ); function dev__paragraph_add_block( $block_content, $block ){ if ( 'core/paragraph' === $block['blockName'] ){ $block_content = new WP_HTML_Tag_Processor( $block_content ); $block_content->next_tag(); /* first tag should always be ul or ol */ $block_content->add_class( 'wp-block-paragraph' ); $block_content->get_updated_html(); } return $block_content; }
-
breakpoints
Read more
// Most used breakpoints $break-xhuge: 1920px; $break-huge: 1440px; $break-wide: 1280px; $break-xlarge: 1080px; $break-large: 960px; // admin sidebar auto folds $break-medium: 782px; // adminbar goes big $break-small: 600px; $break-mobile: 480px; $break-zoomed-in: 280px;
Source : https://github.com/WordPress/gutenberg/blob/trunk/packages/base-styles/_breakpoints.scss
-
Spacing
Read more
{ "$schema": "https://schemas.wp.org/trunk/theme.json", "version": 2, "settings": { "spacing": { "spacingScale": { "operator": "+", "increment": 0.25, "steps": 7, "mediumStep": 1, "unit": "rem" } } } }
-
Colors conventions
Read more
Couleurs primaires
Construites à partir des deux / trois grandes couleurs de la marque, elles sont utilisées pour marquer l’identité dans des composants qui véhiculent l’image de la marque ou sur lesquels il est nécessaire d’attirer l’attention de l’utilisateur, tels que les éléments cliquables ou les états actifs.
Couleurs système
Couleurs utilisées exclusivement pour représenter des états et des statuts.
Couleurs neutres
Couleurs de base utilisées dans les typographies, fonds, contours et séparateurs dans la majorité des composants. Elles sont notamment utilisées dans les éléments non cliquables et pour représenter les états inactifs.
Couleurs illustratives
Couleurs complémentaires de la charte.
–variations
lightness
from 0 (white) to 1000 (black)
state
–hover
–activeExamples:
// couleurs primaires primary : primary--525 primary--100 primary--200 ... secondary tertiary // couleurs neutres neutral neutral--100 : #fff ... neutral--1000 : #000 // couleurs système info success warning error text : neutral--100 text-inverted : neutral--1000 background : neutral--950 // already outdated accent ? highlight ? base ? -> background contrast ? -> main text color
- https://github.com/WordPress/gutenberg/issues/29568
- https://richtabor.com/standardizing-theme-json-colors/
- https://www.systeme-de-design.gouv.fr/a-propos/articles/refonte-du-systeme-de-couleur/
- https://www.systeme-de-design.gouv.fr/elements-d-interface/fondamentaux-de-l-identite-de-l-etat/couleurs-palette/
- https://spectrum.adobe.com/page/color-system/
- https://bootcamp.uxdesign.cc/simple-design-tokens-with-css-custom-properties-7ab69b71d8ad
-
$_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
# securityRead 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:
- https://wordpress.org/documentation/article/hardening-wordpress/#file-permissions
- https://stackoverflow.com/questions/18352682/correct-file-permissions-for-wordpress
- https://wordpress.org/documentation/article/changing-file-permissions/
- https://httpd.apache.org/docs/2.2/misc/security_tips.html
-
Debug
Read more
define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true ); // or define( 'WP_DEBUG_LOG', '/tmp/wp-errors.log' ) define( 'WP_DEBUG_DISPLAY', true );
Source: https://wordpress.org/documentation/article/debugging-in-wordpress/
-
Dequeue styles & scripts
Read more
function dev__dequeue_styles() { wp_dequeue_style( 'some-handle' ); wp_deregister_style( 'some-handle' ); } add_action( 'wp_print_styles', 'dev__dequeue_styles' );
-
InnerBlock
Read more
Interesting attributes :
allowedBlocks
,template
,InnerBlocks
.Example of a devblock/render.php
<?php $classes_container = array( 'block__container' ); $classes_innerblock = array( 'block__innerblock' ); if ( !empty( $block['className'] ) ){ $classes_root = array_merge( $classes_root, explode( ' ', $block['className'] ) ); } $allowed_blocks = array( 'namespace/blockname' ); $template = array( array( 'namespace/blockname', array( 'className' => 'some-class' ) ) ); ?> <div class="<?php echo join( ' ', $classes_container ); ?>"> <InnerBlocks class="<?php echo join( ' ', $classes_innerblock ); ?>" allowedBlocks="<?php echo esc_attr( wp_json_encode( $allowed_blocks ) ); ?>" template="<?php echo esc_attr( wp_json_encode( $template ) ); ?>" /> </div>
-
Creating a block
# blockRead more
Prototype:
register_block_type( string|WP_Block_Type $block_type, array $args = array() ): WP_Block_Type|false
add_action( 'init', 'dev__register_blocks', 5 ); function dev__register_blocks() { register_block_type( __DIR__ . '/some_block_dir' ); }
The block.json file :
{ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "namespace/blockname", "title": "Notice", "category": "text", "parent": [ "core/group" ], "icon": "star", "description": "Shows warning, error or success notices...", "keywords": [ "alert", "message" ], "version": "1.0.3", "textdomain": "my-plugin", "editorScript": "file:./index.js", "script": "file:./script.js", "viewScript": [ "file:./view.js", "example-shared-view-script" ], "editorStyle": "file:./index.css", "style": [ "file:./style.css", "example-shared-style" ], "render": "file:./render.php", "supports": { "className": true, "customClassName": true, "jsx": true // to use innerBlocks, "inserter": true // will be accessible only programatically }, // ACF options: "acf": { "mode": "preview", "renderTemplate": "render.php" }, "example": { "attributes": { "mode": "preview" } } }
Sources:
- https://developer.wordpress.org/reference/functions/register_block_type/
- https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/
- https://www.advancedcustomfields.com/resources/acf-blocks-with-block-json/#acf-configuration-key
- https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/
-
Input / output
# securityRead more
Never trust user’s inputs, even from admin or database.
Validation checks an input over a list of requirements.
in_array( mixed $needle, array $haystack, bool $strict = false ): bool ctype_alnum( mixed $text ): bool strlen( string $string ): int ...
https://developer.wordpress.org/apis/security/data-validation/
Sanitizing cleans a input, removing / transforming all unwanted piece of data.
// For text inputs: sanitize_text_field( string $str ): string // Use specific sanitazing functions like for e.g.: sanitize_email( string $email ): string
https://developer.wordpress.org/apis/security/sanitizing/
Escaping ouputs is the process of securing output data by stripping out unwanted data. It is best to do the output escaping as late as possible, ideally as data is being outputted.
esc_html( string $text ): string // will remove HTML tags esc_url( string $url, string[] $protocols = null, string $_context = 'display' ): string esc_attr( string $text ): string esc_js( string $text ): string // escaping with localization esc_html_e( 'Hello World', 'text_domain' ); // can become -> echo esc_html( __( 'Hello World', 'text_domain' ) );
https://developer.wordpress.org/apis/security/escaping/
Source: https://developer.wordpress.org/apis/security/common-vulnerabilities/
-
Force SSL admin connection
Read more
define( ‘FORCE_SSL_ADMIN’, true );
-
Disallow file editing
Read more
define( 'DISALLOW_FILE_EDIT', true );
-
Get and set the environment state
Read more
With
wp_get_environment_type(): string
one can get the current state of the project. It returnsproduction
by default, otherwiselocal
,development
orstaging
can be used.It can be set in the wp-config.php file as :
define( 'WP_ENVIRONMENT_TYPE', 'development' );
Sources :
-
REST API /users/
Read more
To enumerate users :
curl -L http://$1/wp-json/wp/v2/users
To prevent this :
add_filter( 'rest_endpoints', function( $endpoints ) { if ( isset( $endpoints['/wp/v2/users'] ) ) { unset( $endpoints['/wp/v2/users'] ); } if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) { unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ); } return $endpoints; });
-
Variables from WP to JS files
Read more
Server side :
add_action('wp_enqueue_scripts', 'load_scripts'); function load_scripts (){ $data = array( 'siteURL' => site_url() ); $data_inline = 'const wpData = ' . json_encode( $data ); $handle = 'jsfile'; wp_enqueue_script( $handle, get_stylesheet_directory_uri() . '/assets/js/script.js', array(), filemtime( get_stylesheet_directory() . '/assets/js/script.js' ), true ); wp_add_inline_script( $handle, $data_inline ); }
Client side :
const siteURL = wpData.siteURL;
-
Using ajax
# ajaxRead more
Server side :
function fn(){ header( 'Content-Type: application/json; charset=utf-8' ); echo json_encode( 'some response' ); // don't forget this one below : exit(); } add_action( 'wp_ajax_actionname', 'fn' ); // add below for unlogged user add add_action( 'wp_ajax_nopriv_actionname', 'fn' );
Client side :
const url = siteURL + '/wp-admin/admin-ajax.php?action=actionname'; fetch(url) .then(response => response.json()) .then(result => console.log(result));
-
Context in translation
# i18nRead more
Prototype:
_x( string $text, string $context, string $domain = 'default' ): string
Quite a few times, there will be collisions with similar translatable text found in more than two places, but with different translated context.
By including the context in the pot file, translators can translate the two strings differently.Example:
_x( 'T', 'Tuesday', 'text-domain'); _x( 'T', 'Thursday', 'text-domain');
source : https://developer.wordpress.org/reference/functions/_x/
-
wp i18n
Read more
Note: check for updated
l10n.php
andwp i18n make-php
command → https://make.wordpress.org/core/2024/02/27/i18n-improvements-6-5-performant-translations/Used to generate translations from
__('string to translate', 'text-domain')
functions.MO files: MO, or Machine Object is a binary data file that contains object data referenced by a program. It is typically used to translate program code, and may be loaded or imported into the GNU gettext program.
PO files: PO files are the files which contain the actual translations. Each language will have its own PO file, for example, for French there would be a fr.po file, for german there would be a de.po, for American English there might be en-US.po.
POT file: POT files are the template files for PO files. They will have all the translation strings left empty. A POT file is essentially an empty PO file without the translations, with just the original strings.
wp i18n make-pot . languages/<file-name>.pot --domain=<domain-name> wp i18n update-po languages/<file-name>.pot languages/fr_FR.po wp i18n make-mo languages/<file-name>.po languages/
Sources:
-
wp cli install
# wp cliRead more
To install wp cli
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar chmod +x wp-cli.phar sudo mv wp-cli.phar /usr/local/bin/wp # test wp --info
source : https://make.wordpress.org/cli/handbook/guides/installing/
-
Block styles variations
Read more
To add a new block style :
<?php function dsfr_register_block_styles(){ register_block_style( 'core/paragraph', array( 'name' => 'highlight', 'label' => 'Mise en avant', ) ); } add_action( 'init', 'dsfr_register_block_styles' );
The core styles are editable in the theme.json, but not the custom ones.
{ "styles": { "blocks": { "core/image": { "variations": { "rounded" : { "border": { "radius": ".5rem" } } } } }
https://fullsiteediting.com/lessons/modifying-block-style-variations-via-theme-json/
-
css in theme.json
Read more
For e.g. on sub item of navigation blocks:
{ "styles": { "blocks": { "core/navigation": { "css": "& a > .wp-block-navigation-item__label { opacity: .5 }" } } } }
-
Layout
Read more
To set the –wp–style–root–padding
{ "settings": { "useRootPaddingAwareAlignments": true }, "styles": { "spacing": { "padding": { "top": "1rem", "right": "1rem", "bottom": "1rem", "left": "1rem" } } } }
To set the layout :
{ "settings": { "layout": { "contentSize": "840px", "wideSize": "1100px" } } }
-
Sources
Read more
FSE by Carolina Nymark : https://fullsiteediting.com
Rich Tabor : https://richtabor.com/
Aurooba : https://aurooba.com/
WP Documentations : https://developer.wordpress.org/news/ -
filter theme.json
Read more
There is a way to intervene on the generation of the css based on the theme.json file.
function update_with_dark_theme( $theme_json ){ $dark_theme = json_decode( file_get_contents( get_template_directory() . '/styles/dark.json' ), true ); return $theme_json->update_with( $dark_theme ); } add_filter( 'wp_theme_json_data_user', 'dsfr_update_with_dark_theme' );
It can be used to switch the theme scheme for e.g.
function dsfr_reload_styles(){ if ( isset( $_GET['scheme'] ) && $_GET['scheme'] == 'dark' ){ add_filter( 'wp_theme_json_data_user', 'dsfr_update_with_dark_theme' ); } wp_enqueue_global_styles(); remove_action( 'wp_print_styles', 'print_emoji_styles' ); header( 'Content-type: text/css' ); wp_print_styles(); exit(); } add_action( 'wp_ajax_dsfr_reload_styles', 'dsfr_reload_styles' ); add_action( 'wp_ajax_nopriv_dsfr_reload_styles', 'dsfr_reload_styles' );
Source : https://fullsiteediting.com/lessons/how-to-filter-theme-json-with-php/
-
Set color palette
Read more
In theme.json, hide default color pickers and set a controled ranged colors.
{ ... "settings": { "color": { "custom": false, "defaultPalette": false, "customDuotone": false, "customGradient": false, "palette": [ { "slug": "dark", "name": "Dark", "color": "#19191A" }, { "slug": "bright", "name": "Bright", "color": "#FFFFFF" } ] } } ... }
-
Minimal start up
Read more
Hierarchy
assets/ - fonts/ templates/ - index.html parts/ - header.html styles/ - dark.json functions.php styles.css screenshot.png theme.json
-
Font weights
Read more
To set the font weights :
{ "settings": { "typography": { "fontFamilies": [ { "fontFamily": "\"Barlow\", sans-serif", "name": "Barlow", "slug": "barlow", "fontFace" : [ { "fontFamily": "Barlow", "fontWeight": "400", "src": "file:./assets/fonts/Barlow-Regular-webfont.woff" }, { "fontFamily": "Barlow", "fontWeight": "500", "fontStyle": "normal", "src": "file:./assets/fonts/Barlow-Medium-webfont.woff" }, { "fontFamily": "Barlow", "fontWeight": "700", "fontStyle": "normal", "src": "file:./assets/fonts/Barlow-Bold-webfont.woff" } ] } ] } } }
Note : reduce a variable font to the required variations:
fonttools varLib.instancer ./MyAwesomeVariableFont.ttf wdth=drop opsz=drop wght=300:700
-
Custom post type
Read more
To enable block editing on custom post type :
function create_post_type (){ $args = array( ... 'support' => array('title', 'thumbnail'), 'show_in_rest' => true, ... ); register_post_type('new-cpt-slug', $args); } add_action('init', 'create_post_type', 9);
Custom taxonomies :
$args = array( ... 'rewrite' => array('slug' => 'profil'), 'show_in_rest' => true, ); register_taxonomy( 'custom_tax_slug', ['cpt_slug'], [$args] );
Sources: