├── languages ├── add-custom-header-images-sr_RS.mo ├── add-custom-header-images.pot └── add-custom-header-images-sr_RS.po ├── composer.json ├── CHANGES.md ├── readme.txt └── add-custom-header-images.php /languages/add-custom-header-images-sr_RS.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afragen/add-custom-header-images/develop/languages/add-custom-header-images-sr_RS.mo -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "afragen/add-custom-header-images", 3 | "description": "Remove default header images and load custom header images from 'The Headers' page. Allows for easy selection of random header images in your theme.", 4 | "type": "wordpress-plugin", 5 | "license": "GPL-2.0-or-later", 6 | "authors": [ 7 | { 8 | "name": "Andy Fragen", 9 | "email": "andy@thefragens.com", 10 | "homepage": "https://thefragens.com", 11 | "role": "Developer" 12 | } 13 | ], 14 | "support": { 15 | "issues": "https://github.com/afragen/add-custom-header-images/issues", 16 | "source": "https://github.com/afragen/add-custom-header-images" 17 | }, 18 | "prefer-stable": true, 19 | "require": { 20 | "php": ">=5.6" 21 | }, 22 | "scripts": { 23 | "make-pot": [ 24 | "wp i18n make-pot . languages/add-custom-header-images.pot" 25 | ] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /languages/add-custom-header-images.pot: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Andy Fragen 2 | # This file is distributed under the same license as the Add Custom Header Images plugin. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: Add Custom Header Images 2.0.3\n" 6 | "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/add-custom-header-images\n" 7 | "Last-Translator: FULL NAME \n" 8 | "Language-Team: LANGUAGE \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "POT-Creation-Date: 2020-08-01T18:20:53+00:00\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "X-Generator: WP-CLI 2.4.0\n" 15 | "X-Domain: add-custom-header-images\n" 16 | 17 | #. Plugin Name of the plugin 18 | msgid "Add Custom Header Images" 19 | msgstr "" 20 | 21 | #. Plugin URI of the plugin 22 | msgid "https://github.com/afragen/add-custom-header-images" 23 | msgstr "" 24 | 25 | #. Description of the plugin 26 | msgid "Remove default header images and add custom header images. Images must be added to new page titled The Headers. Based upon a post from Julio Biason." 27 | msgstr "" 28 | 29 | #. Author of the plugin 30 | msgid "Andy Fragen" 31 | msgstr "" 32 | 33 | #. Author URI of the plugin 34 | msgid "https://thefragens.com" 35 | msgstr "" 36 | 37 | #: add-custom-header-images.php:45 38 | msgid "The Headers" 39 | msgstr "" 40 | 41 | #: add-custom-header-images.php:78 42 | msgid "Add Custom Header Images requires a page titled The Headers." 43 | msgstr "" 44 | -------------------------------------------------------------------------------- /languages/add-custom-header-images-sr_RS.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: Add Custom Header Images\n" 4 | "POT-Creation-Date: 2017-05-23 10:32-0700\n" 5 | "PO-Revision-Date: 2017-05-23 10:32-0700\n" 6 | "Last-Translator: Andy Fragen \n" 7 | "Language-Team: Andy Fragen \n" 8 | "Language: en_US\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "X-Generator: Poedit 2.0.2\n" 13 | "X-Poedit-Basepath: ..\n" 14 | "X-Poedit-SourceCharset: UTF-8\n" 15 | "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;" 16 | "esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;" 17 | "_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "X-Poedit-SearchPath-0: .\n" 20 | "X-Poedit-SearchPathExcluded-0: *.js\n" 21 | 22 | #: add-custom-header-images.php:44 23 | msgid "The Headers" 24 | msgstr "Zaglavlja" 25 | 26 | #: add-custom-header-images.php:69 27 | #, fuzzy, php-format 28 | msgid "" 29 | "Add Custom Header Images requires a page titled %sThe Headers%s with images " 30 | "and WordPress v3.4 or greater." 31 | msgstr "" 32 | "Add Custom Header Images zahteva stranicu pod nazivom Zaglavlja sa slikama i WordPress v3.4 ili noviju." 34 | 35 | #. Plugin Name of the plugin/theme 36 | msgid "Add Custom Header Images" 37 | msgstr "Add Custom Header Images" 38 | 39 | #. Plugin URI of the plugin/theme 40 | msgid "https://github.com/afragen/add-custom-header-images" 41 | msgstr "" 42 | 43 | #. Description of the plugin/theme 44 | msgid "" 45 | "Remove default header images and add custom header images. Images must be " 46 | "added to new page titled The Headers. Based upon a post " 47 | "from Julio Biason." 49 | msgstr "" 50 | "Uklonite podrazumevane slike zaglavlja i dodajte prilagođene slike " 51 | "zaglavlja. Slike se moraju dodati na novu stranicu pod nazivom " 52 | "Zaglavlja. Na osnovu posta sa Julio " 54 | "Biason." 55 | 56 | #. Author of the plugin/theme 57 | msgid "Andy Fragen" 58 | msgstr "Andy Fragen" 59 | 60 | #. Author URI of the plugin/theme 61 | msgid "http://thefragens.com" 62 | msgstr "" 63 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | #### [unreleased] 2 | 3 | #### 2.3.5 / 2025-04-03 4 | * add GA to generate POT 5 | * change startup action hook to `after_setup_theme` for translations 6 | 7 | #### 2.3.4 / 2024-11-01 8 | * remove `load_plugin_textdomain()` 9 | 10 | #### 2.3.3 / 2023-07-21 11 | * use 'all' over 'any' in query, perhaps better 12 | * call `wp_get_attachment_image()` to populate specific attributes 13 | 14 | #### 2.3.2 / 2023-02-08 15 | * improve WP_Query for single call 16 | 17 | #### 2.3.1 / 2023-02-08 18 | * fix to use public or private pages 19 | 20 | #### 2.3.0 / 2023-02-05 21 | * PHP 8.1 compatibility changes 22 | * update for deprecated `get_page_by_title()` 23 | * start from `plugins_loaded` 24 | 25 | #### 2.2.0 / 2023-01-13 26 | * update GitHub Actions 27 | * update to add alt text to header image 28 | 29 | #### 2.1.0 / 2021-07-07 30 | * add @10up GitHub Actions for SVN 31 | 32 | #### 2.0.3 / 2020-08-01 33 | * add check for PHP warning 34 | 35 | #### 2.0.2 / 2020-03-28 36 | * initialize some variables 37 | 38 | #### 2.0.1 / 2020-03-03 39 | * add header image support to theme if none exists 40 | * parse images from page blocks 41 | * refactor class methods 42 | 43 | #### 1.9.0 44 | * always load `after_theme_setup` filter 45 | 46 | #### 1.8.1 47 | * correctly initialize `load_plugin_textdomain()` 48 | 49 | #### 1.8.0 50 | * simplify admin notice 51 | * WPCS compliant 52 | * fixed to use `wp_get_attachment_url()`, thanks @poulh 53 | 54 | #### 1.7.0 55 | * use WP_Query instead of `get_children()` 56 | * only load `after_theme_setup` hook on front end 57 | 58 | #### 1.6.1 59 | * update _Tested to_ 60 | * simplify conditional 61 | 62 | #### 1.6.0 63 | * don't run from constructor 64 | * requires PHP 5.3, sorta 65 | 66 | #### 1.5.2 67 | * use class variables to hold title and page data to reduce number of calls to database 68 | 69 | #### 1.5.1 70 | * set `after_theme_setup` hook to use later priority to ensure $_wp_default_headers is set, fixes removal of default images 71 | 72 | #### 1.5.0 73 | * removed specific srcset code as it was unnecessary and caused failures. `srcset` needs to be set correctly in `header.php` 74 | 75 | #### 1.4.2 76 | * fixed malformed closing `strong` tag in error message 77 | 78 | #### 1.4.1 79 | * escape translations of page name 80 | 81 | #### 1.4.0 82 | * added srcset for responsive image sizes 83 | * tested and updated for WP 4.4.0 84 | 85 | #### 1.3.3 86 | * tested to 4.3 87 | 88 | #### 1.3.2 89 | * load textdomain early so translations work. 90 | 91 | #### 1.3.1 92 | * fix readme.txt as plugin name generic 93 | * simplify warning, remove nested if statements 94 | * update .pot 95 | 96 | #### 1.3.0 97 | * better i18n strings, updated POT 98 | 99 | #### 1.2.0 100 | * move `remove_default_header_images` to run only if **The Headers** page is present. Should fix a PHP Notice too. 101 | 102 | #### 1.1.0 103 | * remove `deactivate_plugins` to and just display an error notice for better compatibility. 104 | 105 | #### 1.0.4 106 | * added some error checking 107 | 108 | #### 1.0.3 109 | * more graceful exit and return 110 | 111 | #### 1.0.2 112 | * exit after deactivating plugin when not able to be activated 113 | 114 | #### 1.0.1 115 | * Add .pot files 116 | * Fix short description by removing Markdown 117 | 118 | #### 1.0.0 119 | * Initial commit to WordPress repository 120 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | # Add Custom Header Images 2 | Contributors: afragen 3 | Plugin URI: https://github.com/afragen/add-custom-header-images 4 | Tags: headers, rotate headers, images 5 | License: GPLv2 or later 6 | License URI: https://www.gnu.org/licenses/gpl-2.0.html 7 | Requires at least: 5.2 8 | Requires PHP: 5.6 9 | Tested up to: 6.8 10 | Stable tag: 2.3.5 11 | 12 | ## Description 13 | 14 | Remove default header images and load custom header images from 'The Headers' page. Allows for easy selection of random header images in your theme. 15 | 16 | A plugin that should be able to remove default headers for a theme and add custom headers based upon the article written by Julio Biason who was inspired by wpti.ps. 17 | 18 | Create a Page named **The Headers**. Then upload header images (media files) to the the page. The page may have a visibility of private. All images displayed on the page will be parsed, those attached to the page and those added via image blocks. 19 | 20 | Once the custom header images are loaded, just go to `Appearance > Header` or `Customize > Header Image` and select `Randomize suggested headers`. 21 | 22 | The plugin will display an error notice if there is **not** a page titled, **The Headers**. 23 | 24 | ## Installation 25 | 26 | 1. Create a new page. It can be private. It must be titled `The Headers`. Add any images that you want to use as custom header images to this page. Header images should be cropped appropriately for the base theme. 27 | 2. Upload `add-custom-header-images` directory to the `/wp-content/plugins/` directory 28 | 3. Activate the plugin through the 'Plugins' menu in WordPress 29 | 4. Go to `Appearance > Header` or `Customize > Header Image` and select `Randomize` from the Default Images section. 30 | 31 | ## Changelog 32 | 33 | #### 2.3.5 / 2025-04-03 34 | * add GA to generate POT 35 | * change startup action hook to `after_setup_theme` for translations 36 | 37 | #### 2.3.4 / 2024-11-01 38 | * remove `load_plugin_textdomain()` 39 | 40 | #### 2.3.3 / 2023-07-21 41 | * use 'all' over 'any' in query, perhaps better 42 | * call `wp_get_attachment_image()` to populate specific attributes 43 | 44 | #### 2.3.2 / 2023-02-08 45 | * improve WP_Query for single call 46 | 47 | #### 2.3.1 / 2023-02-08 48 | * fix to use public or private pages 49 | 50 | #### 2.3.0 / 2023-02-05 51 | * PHP 8.1 compatibility changes 52 | * update for deprecated `get_page_by_title()` 53 | * start from `plugins_loaded` 54 | 55 | #### 2.2.0 / 2023-01-13 56 | * update GitHub Actions 57 | * update to add alt text to header image 58 | 59 | #### 2.1.0 / 2021-07-07 60 | * add @10up GitHub Actions for SVN 61 | 62 | #### 2.0.3 / 2020-08-01 63 | * add check for PHP warning 64 | 65 | #### 2.0.2 / 2020-03-28 66 | * initialize some variables 67 | 68 | #### 2.0.1 / 2020-03-03 69 | * add header image support to theme if none exists 70 | * parse images from page blocks 71 | * refactor class methods 72 | 73 | #### 1.9.0 74 | * always load `after_theme_setup` filter 75 | 76 | #### 1.8.1 77 | * correctly initialize `load_plugin_textdomain()` 78 | 79 | #### 1.8.0 80 | * simplify admin notice 81 | * WPCS compliant 82 | * fixed to use `wp_get_attachment_url()`, thanks @poulh 83 | 84 | #### 1.7.0 85 | * use WP_Query instead of `get_children()` 86 | * only load `after_theme_setup` hook on front end 87 | 88 | #### 1.6.1 89 | * update _Tested to_ 90 | * simplify conditional 91 | 92 | #### 1.6.0 93 | * don't run from constructor 94 | * requires PHP 5.3, sorta 95 | 96 | #### 1.5.2 97 | * use class variables to hold title and page data to reduce number of calls to database 98 | 99 | #### 1.5.1 100 | * set `after_theme_setup` hook to use later priority to ensure $_wp_default_headers is set, fixes removal of default images 101 | 102 | #### 1.5.0 103 | * removed specific srcset code as it was unnecessary and caused failures. `srcset` needs to be set correctly in `header.php` 104 | 105 | #### 1.4.2 106 | * fixed malformed closing `strong` tag in error message 107 | 108 | #### 1.4.1 109 | * escape translations of page name 110 | 111 | #### 1.4.0 112 | * added srcset for responsive image sizes 113 | * tested and updated for WP 4.4.0 114 | 115 | #### 1.3.3 116 | * tested to 4.3 117 | 118 | #### 1.3.2 119 | * load textdomain early so translations work. 120 | 121 | #### 1.3.1 122 | * fix readme.txt as plugin name generic 123 | * simplify warning, remove nested if statements 124 | * update .pot 125 | 126 | #### 1.3.0 127 | * better i18n strings, updated POT 128 | 129 | #### 1.2.0 130 | * move `remove_default_header_images` to run only if **The Headers** page is present. Should fix a PHP Notice too. 131 | 132 | #### 1.1.0 133 | * remove `deactivate_plugins` to and just display an error notice for better compatibility. 134 | 135 | #### 1.0.4 136 | * added some error checking 137 | 138 | #### 1.0.3 139 | * more graceful exit and return 140 | 141 | #### 1.0.2 142 | * exit after deactivating plugin when not able to be activated 143 | 144 | #### 1.0.1 145 | * Add .pot files 146 | * Fix short description by removing Markdown 147 | 148 | #### 1.0.0 149 | * Initial commit to WordPress repository 150 | -------------------------------------------------------------------------------- /add-custom-header-images.php: -------------------------------------------------------------------------------- 1 | The Headers. Based upon a post from Julio Biason. 11 | * Version: 2.3.5 12 | * Author: Andy Fragen 13 | * Author URI: https://thefragens.com 14 | * License: GNU General Public License v2 15 | * License URI: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html 16 | * Text Domain: add-custom-header-images 17 | * Domain Path: /languages 18 | * GitHub Plugin URI: https://github.com/afragen/add-custom-header-images 19 | * Requires at least: 5.2 20 | * Requires PHP: 5.6 21 | */ 22 | 23 | /** 24 | * Class Add_Custom_Header_Images 25 | */ 26 | class Add_Custom_Header_Images { 27 | /** 28 | * Variable to hold the data for `get_page_by_title()`. 29 | * 30 | * @var array|null|\WP_Post 31 | */ 32 | private $the_headers_page; 33 | 34 | /** 35 | * Variable to hold header image URL. 36 | * 37 | * @var string 38 | */ 39 | public $header_image; 40 | 41 | /** 42 | * Variable to hold header images. 43 | * 44 | * @var array 45 | */ 46 | public $header_images; 47 | 48 | /** 49 | * Variable to hold image attributes. 50 | * 51 | * @var array 52 | */ 53 | private $image_attr; 54 | 55 | /** 56 | * Constructor. 57 | */ 58 | public function __construct() { 59 | $the_headers_title = __( 'The Headers', 'add-custom-header-images' ); 60 | $query = new WP_Query( 61 | [ 62 | 'post_type' => 'page', 63 | 'title' => $the_headers_title, 64 | 'post_status' => 'all', 65 | ] 66 | ); 67 | 68 | $this->the_headers_page = $query->post; 69 | $this->run(); 70 | } 71 | 72 | /** 73 | * Let's get started. 74 | * 75 | * @return bool 76 | */ 77 | public function run() { 78 | if ( ( is_admin() && null === $this->the_headers_page ) 79 | ) { 80 | add_action( 'admin_notices', [ $this, 'headers_page_not_present' ] ); 81 | 82 | return false; 83 | } 84 | add_action( 'after_setup_theme', [ $this, 'new_default_header_images' ], 99 ); 85 | add_action( 'after_setup_theme', [ $this, 'setup_default_header_image' ], 100 ); 86 | add_filter( 'wp_get_attachment_image_attributes', [ $this, 'get_image_attributes' ], 100, 3 ); 87 | } 88 | 89 | /** 90 | * Disable plugin if 'The Headers' page does not exist. 91 | */ 92 | public function headers_page_not_present() { 93 | echo '

'; 94 | echo wp_kses_post( __( 'Add Custom Header Images requires a page titled The Headers.', 'add-custom-header-images' ) ); 95 | echo '

'; 96 | } 97 | 98 | /** 99 | * Remove default header images. 100 | */ 101 | public function remove_default_header_images() { 102 | global $_wp_default_headers; 103 | if ( empty( $_wp_default_headers ) ) { 104 | return false; 105 | } 106 | 107 | $header_ids = []; 108 | foreach ( (array) array_keys( $_wp_default_headers ) as $key ) { 109 | if ( ! is_int( $key ) ) { 110 | $header_ids[] = $key; 111 | } 112 | } 113 | 114 | unregister_default_headers( $header_ids ); 115 | } 116 | 117 | /** 118 | * Add new default header images. 119 | */ 120 | public function new_default_header_images() { 121 | if ( ! $this->the_headers_page instanceof \WP_Post ) { 122 | return false; 123 | } 124 | 125 | $this->remove_default_header_images(); 126 | $header_images = $this->get_images_from_post( $this->the_headers_page ); 127 | 128 | register_default_headers( $header_images ); 129 | } 130 | 131 | /** 132 | * Get images from attachments and blocks. 133 | * 134 | * @param \WP_Post $post Post object. 135 | * @return array $header_images 136 | */ 137 | private function get_images_from_post( \WP_Post $post ) { 138 | $images_query = new \WP_Query( 139 | [ 140 | 'post_parent' => $post->ID, 141 | 'post_status' => 'inherit', 142 | 'post_type' => 'attachment', 143 | 'post_mime_type' => 'image', 144 | 'order' => 'ASC', 145 | 'orderby' => 'menu_order ID', 146 | ] 147 | ); 148 | 149 | // Get images from blocks. 150 | $parsed_blocks = \parse_blocks( $post->post_content ); 151 | $parsed_images = array_filter( 152 | $parsed_blocks, 153 | function ( $block ) { 154 | return 'core/image' === $block['blockName']; 155 | } 156 | ); 157 | 158 | $blocks = []; 159 | foreach ( $parsed_images as $block ) { 160 | $id = $block['attrs']['id']; 161 | $blocks[] = (object) [ 162 | 'ID' => $id, 163 | 'post_title' => get_the_title( $id ), 164 | ]; 165 | } 166 | 167 | $images = array_merge( $images_query->posts, $blocks ); 168 | 169 | // Make default header image arrays. 170 | $header_images = []; 171 | $image_ids = []; 172 | foreach ( $images as $image ) { 173 | wp_get_attachment_image( $image->ID, 'medium' ); 174 | $header_images[] = [ 175 | 'url' => wp_get_attachment_url( $image->ID ), 176 | 'thumbnail_url' => $this->image_attr['src'], 177 | 'description' => $image->post_title, 178 | 'attachment_id' => $image->ID, 179 | 'alt_text' => $this->image_attr['alt'], 180 | ]; 181 | $image_ids[] = $image->ID; 182 | } 183 | 184 | $header_images = $this->filter_headers( $header_images, $image_ids ); 185 | $this->header_images = $header_images; 186 | 187 | return $header_images; 188 | } 189 | 190 | /** 191 | * Remove duplicate $headers. 192 | * 193 | * @param array $images Array of image data. 194 | * @param array $image_ids Array of image IDs. 195 | * @return array $images 196 | */ 197 | private function filter_headers( $images, $image_ids ) { 198 | $image_ids = array_flip( array_unique( $image_ids ) ); 199 | $images = array_filter( 200 | $images, 201 | function ( $id ) use ( &$image_ids ) { 202 | if ( array_key_exists( $id['attachment_id'], $image_ids ) ) { 203 | unset( $image_ids[ $id['attachment_id'] ] ); 204 | 205 | return $id; 206 | } 207 | } 208 | ); 209 | 210 | return (array) $images; 211 | } 212 | 213 | /** 214 | * Add default header image if theme doesn't support it. 215 | * 216 | * @return void 217 | */ 218 | public function setup_default_header_image() { 219 | if ( ! current_theme_supports( 'custom-header' ) ) { 220 | add_theme_support( 'custom-header' ); 221 | $this->header_image = get_header_image(); 222 | 223 | if ( ! function_exists( 'wp_body_open' ) ) { 224 | /** 225 | * Shim for wp_body_open, ensuring backward compatibility with versions of WordPress older than 5.2. 226 | */ 227 | function wp_body_open() { 228 | do_action( 'wp_body_open' ); 229 | } 230 | } 231 | 232 | $alt_text = array_map( 233 | function( $img_arr ) { 234 | if ( $this->header_image === $img_arr['url'] ) { 235 | return $img_arr['alt_text']; 236 | } 237 | }, 238 | (array) $this->header_images 239 | ); 240 | 241 | $alt_text = array_filter( $alt_text ); 242 | $alt_text = array_pop( $alt_text ); 243 | 244 | add_action( 245 | 'wp_body_open', 246 | function () use ( $alt_text ) { 247 | printf( 248 | /* translators: 1: image URL, 2: alt text */ 249 | '
%s
', 250 | esc_attr( $this->header_image ), 251 | esc_attr( $alt_text ) 252 | ); 253 | } 254 | ); 255 | add_action( 'wp_head', [ $this, 'header_image_style' ] ); 256 | } 257 | } 258 | 259 | /** 260 | * Get image attributes. 261 | * 262 | * @param string[] $attr Array of attribute values for the image markup, keyed by attribute name. 263 | * @param WP_Post $attachment Image attachment post. 264 | * @param string|int[] $size Requested image size. Can be any registered image size name, or 265 | * an array of width and height values in pixels (in that order). 266 | * 267 | * @return array 268 | */ 269 | public function get_image_attributes( $attr, $attachment, $size ) { 270 | $this->image_attr = $attr; 271 | 272 | return $attr; 273 | } 274 | 275 | /** 276 | * Header image CSS styling. 277 | * 278 | * @return void 279 | */ 280 | public function header_image_style() { 281 | ?> 282 | 290 |