To check the block.json for eg or import component in edit.js :
https://github.com/WordPress/gutenberg/tree/trunk/packages/components
https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src
Notes about WordPress Gutenberg themes development.
.htaccess (1) ACF (7) admin (1) ajax (2) api (1) API interactivity (1) block (20) block_style (2) colors (2) constante (1) context (1) conventions (2) cron (1) css (5) custom post type (1) data (1) debug (2) define (1) file_API (1) functions.php (6) git (4) hook (7) i18n (2) js (2) layout (1) loop (1) media (1) media library (1) menu (2) navigation (1) patterns (1) performance (2) post (1) query (3) readmore (1) responsive (1) rest api (1) scss (1) security (7) spacing (1) sql (1) svg (1) taxonomy (1) theme (1) theme.json (11) typo (2) URL (1) wp-config.php (6) wp cli (3) wp function (7)
To check the block.json for eg or import component in edit.js :
https://github.com/WordPress/gutenberg/tree/trunk/packages/components
https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src
wp_enqueue_script_module
note: cannot be used with `wp_add_inline_script`
Source : https://developer.wordpress.org/reference/functions/wp_enqueue_script_module/
$ npx @wordpress/create-block@latest blocks --template @wordpress/create-block-interactive-template
$ cd blocks/src/
$ mkdir "blockname"
$ mv * in "blockname"
Duplicate the “whatever blockname” in the src folder to add new blocks.
In functions.php:
foreach ( glob( get_stylesheet_directory() . '/blocks/build/*' ) as $block_directory ) {
register_block_type( $block_directory );
}
To remove elements from the sitemap.xml :
add_filter(
'wp_sitemaps_add_provider',
function( $provider, $name ) {
if ( 'users' === $name ) {
return false;
}
return $provider;
},
10,
2
);
https://make.wordpress.org/core/2020/07/22/new-xml-sitemaps-functionality-in-wordpress-5-5/
sass --watch {src folder}:{dest folder} --style compressed --no-source-map
common.scss:
@mixin some-name {
css rules: css properties;
...
}
style.scss:
@use common.scss as common;
.some-element {
@include common.some-same;
}
Generate auth key and salt: https://api.wordpress.org/secret-key/1.1/salt
A convenient way to display the block in the editor :
import ServerSideRender from '@wordpress/server-side-render';
export default function Edit( { attributes, setAttributes } ) {
return (
<ServerSideRender
block="dev/some-block"
attributes={attributes}
/>
);
}
Without module : <wp.blockEditor.ServerSideRender block="custom/menu-display" attributes={attributes} />
Source: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-server-side-render/
To load pattern php files, create a patterns
folder, add a file.php with at least:
<?php
/*
* Title: required
* Slug: required
*/
Some content
Having a php file can be handy for translations.
Integration in a template file example :
<!-- wp:pattern {"slug":"df/content-404"} /-->
Note: for WordPress to be able to automatically look into the patterns folder, one need to add in wp-config.php
:
define( 'WP_ENVIRONMENT_TYPE', 'development' );
define( 'WP_DEVELOPMENT_MODE', 'theme' );
Sources:
<Files xmlrpc.php>
order deny,allow
deny from all
</Files>
<Files "wp-login.php">
Require ip 255.255.255.255
</Files>
Render precalculated static html files, e.g. plugin wp super cache
headers like expires cache-controls. In .htaccess:
<IfModule mod_headers.c>
Header set Cache-Control "public, max-age=31536000"
</IfModule>
Servers scattered geographically
using Redis
Transcient API
Keep the compiled PHP code in memory.
Save every new uploaded image as AVIF :
function filter_image_editor_output_format( $formats ) {
$formats['image/jpeg'] = 'image/avif';
return $formats;
}
add_filter( 'image_editor_output_format', 'filter_image_editor_output_format' );
To preserve gif animations : ffmpeg -i ./input.gif -vcodec webp -loop 0 -pix_fmt yuva420p ./output.webp
Source : https://make.wordpress.org/core/2024/02/23/wordpress-6-5-adds-avif-support/
Used to create a context of data, for example between parent / children blocks.
Example, making the post id accessible, in block.json:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
...
"usesContext": [ "postId" ]
}
In render.php :
$post_id = $block->context['postId'];
In edit.json :
export default function Edit( { attributes, setAttributes, context } ) {
// context.postId
}
How to fetch data from a block : in edit.js
import { useSelect } from '@wordpress/data';
const SomeComponent = ( { attributes, setAttributes } ) => {
const taxonomies = useSelect( select => select( 'core' ).getTaxonomies(), [] );
...
}
Sources :
<?php echo apply_filters( 'the_content', do_blocks( get_post_field( 'post_content', $post->ID ) ) ); ?>
In block.json : { supports: { interactivity: true }, viewScriptModule: file:./view.js }
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}
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:
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
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 } /> )
This allow more control on the editor site. In edit.js:
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
export default function Edit( { attributes, setAttributes } ) {
const MC_TEMPLATE = [
[ 'core/image', {} ],
[ 'core/paragraph', { content: 'Text content' } ]
];
const blockProps = useBlockProps( { className: 'sim-slide sim-slide-single-editor' } );
const innerBlocksProps = useInnerBlocksProps(
blockProps,
{ template: MC_TEMPLATE },
);
return (
<div { ...innerBlocksProps } />
);
}
Source:
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:
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:
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.
To register the block : register_block_type( ${path of directory containing the block.json} );
In case of several block, optional but perfomance improving : wp_register_block_metadata_collection( $path, $manifest );
To generate a manifest : wp-scripts build-blocks-manifest
Sources:
wp search-replace --dry-run 'oldprefix_' 'newprefix_'
wp db export
wp core update
Populate dynamically blocks (button, image, paragraph and heading), by setting the source, from meta field or php logic.
<!-- 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',
)
);
$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/
add_filter('acf/load_field/name=color', 'acf_load_color_field_choices');
Source: https://www.advancedcustomfields.com/resources/dynamically-populate-a-select-fields-choices/
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:
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"]
}
]
}
]
To set vim as text editor in git:
git config core.editor
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
$the_query = new WP_Query(array(
'post_type' => 'event',
'posts_per_page' => -1,
'meta_key' => 'featured',
'orderby' => 'meta_value',
'order' => 'DESC'
));
$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:
$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/
See:
wp_delete_post( $post_id );
See: https://developer.wordpress.org/reference/functions/wp_delete_post/
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
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
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 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:
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');
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:
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:
The hosting server doesn’t necessary allow WordPress to write files.
define( 'FS_METHOD', 'direct' );
Sources:
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() {
...
}
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:
$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:
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/
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 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
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/
Front + editor
enqueue_block_assets
Editor only
enqueue_block_editor_assets
Sources:
add_filter('upload_mimes', 'dw__upload_mimes');
function dw__upload_mimes( $mimes=array() ){
$mimes['svg'] = 'image/svg+xml';
return $mimes;
}
$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;
}
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:
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/
{
"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 :
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;
}
// 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
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"spacing": {
"spacingScale": {
"operator": "+",
"increment": 0.25,
"steps": 7,
"mediumStep": 1,
"unit": "rem"
}
}
}
}
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 utilisées exclusivement pour représenter des états et des statuts.
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 complémentaires de la charte.
from 0 (white) to 1000 (black)
–hover
–active
// 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
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/
# 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:
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/
function dev__dequeue_styles() {
wp_dequeue_style( 'some-handle' );
wp_deregister_style( 'some-handle' );
}
add_action( 'wp_print_styles', 'dev__dequeue_styles' );
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>
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:
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/
define( ‘FORCE_SSL_ADMIN’, true );
define( 'DISALLOW_FILE_EDIT', true );
With wp_get_environment_type(): string
one can get the current state of the project. It returns production
by default, otherwise local
, development
or staging
can be used.
It can be set in the wp-config.php file as :
define( 'WP_ENVIRONMENT_TYPE', 'development' );
Sources :
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;
});
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;
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));
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/
Note: check for updatedl10n.php
and wp 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/
wp i18n make-php languages/
It is possible to specify a specific Domain Path in style.css
: Defaults to /languages
.
The Text Domain
has to be specified in the style.css
file.
The function load_theme_textdomain( $text_domain, $path );
needs to be called with the after_setup_theme
hook.
Sources:
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/
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/
For e.g. on sub item of navigation blocks:
{
"styles": {
"blocks": {
"core/navigation": {
"css": "& a > .wp-block-navigation-item__label { opacity: .5 }"
}
}
}
}
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"
}
}
}
FSE by Carolina Nymark : https://fullsiteediting.com
Rich Tabor : https://richtabor.com/
Aurooba : https://aurooba.com/
WP Documentations : https://developer.wordpress.org/news/
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/
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"
}
]
}
}
...
}
assets/
- fonts/
templates/
- index.html
parts/
- header.html
styles/
- dark.json
functions.php
styles.css
screenshot.png
theme.json
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
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: