WP Manifestindependent plugin directory
manifest / themes / wp-bootstrap-hooks

Bootstrap Hooks WP Registry grade D+. High-severity findings · 3 findingsWP RegistryHigh-severity findings · 3 findingsOpen the reportD+

A collection of filters and actions for Bootstrap-based themes

★ 7stars
v1.1.21latest · 1 year ago

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/benignware/wp-bootstrap-hooks/archive/refs/heads/master.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

A collection of filters and actions for bootstrap-based themes

When integrating Bootstrap with Wordpress, it is not sufficient to just include assets and add some css-classes to templates. You will also need to inject bootstrap-compatible markup into programmatically generated sections, such as menus, widgets, comments etc. Bootstrap Hooks aims to cover most of these cases to make us immediately start implementing the individual application rather than getting hassled by markup incompatibilities.

Bootstrap Hooks consists of six separate modules for Navigation, Pagination, Forms, Widgets, Comments, Gallery and Blocks which can be used altogether or independently from each other. Every module is customizable by passing options to a central filter method.

Bootstrap Hooks has been optimized for Bootstrap 5, though it can be adjusted to also support older versions. See Recipes for more details on how to do this.

Install

Install preferrably as a must-use plugin. It should be enabled by themes programmatically and updated by theme developers who maintain Bootstrap inside a certain themes.

Initialize

Bootstrap Hooks is made aware that your theme actually supports Bootstrap by calling add_theme_support in your functions.php:

add_theme_support( 'bootstrap' );

Usage

With few exceptions, Bootstrap Hooks works out-of-the-box by injecting its magic via actions and filters. Read further to learn more about the distinct modules.

Content

The Content-Hook takes care of your post content primarily. It sets proper markup and classes to images, captions, blockquotes and tables. It also handles the post thumbnail to add the responsive image class.

In Bootstrap 4, the Tag-component may break Wordpress` default taxonomy styles. See here for reference. To avoid undesired effects, the tag class is replaced with post-tag in body_class- or post_class-methods and also when it's found in the content.

This module also provides a custom method to handle the edit-post-link. Search your theme for occurrences of edit_post_link and replace with wp_bootstrap_edit_post_link:

// Edit post link
wp_bootstrap_edit_post_link(
  sprintf(
    /* translators: %s: Name of current post */
    __( 'Edit<span class="screen-reader-text"> "%s"</span>', 'textdomain' ),
    get_the_title()
  ),
  '<span class="edit-link">',
  '</span>'
) );

When using Bootstrap Hooks as a plugin, you should definitely check if the method has been loaded by wrapping function call into a conditional function_exists-check. See the Recipes section for an example.

Forms

This module handles search- and password-forms.

A search-form is rendered as input-group. You can customize the button's icon by passing arguments from a filter. This example shows how to integrate font-awesome:

// Show Font-Awesome search icon in Searchform
function my_bootstrap_options($options) {
  return array_merge($options, array(
    'search_submit_label' => '<i class="fa fa-search"></i>'
  ));
}
add_filter( 'bootstrap_options', 'my_bootstrap_options' );

Gallery

The gallery hook uses a grid of thumbnails in combination with a carousel inside a modal for zoom view.

In typical Bootstrap-driven layout, column sizes may differ from Wordpress default thumbnail size. You may update thumbnail size to your needs in order to fit thumbnail images into at least three columns:

// Adjust thumbnail size
update_option( 'thumbnail_size_w', 230 );
update_option( 'thumbnail_size_h', 230 );
update_option( 'thumbnail_crop', 1 );

The implementation does not handle different zoom-image heights. An easy way to fix this, is to register a custom image size with cropping enabled and apply to the gallery hook:

// Register custom gallery image size
add_image_size( 'gallery-zoom', 900, 500, true );

// Apply custom image size to gallery zoom
function my_bootstrap_options($options) {
  return array_merge($options, array(
    'gallery_zoom_size' => 'gallery-zoom'
  ));
}
add_filter( 'bootstrap_options', 'my_bootstrap_options' );

Menu

Bootstrap Hooks provides a custom MenuWalker based on the work by Edward McIntyre which is injected into menus per default. For the menus being in primary or top location, the navbar-nav-class will automatically be added.

Bootstrap Hooks also adds a script that handles links on dropdown-toggles which are prevented by default from Bootstrap.

Pagination

Since there's no existing hook for posts-pagination as well as for post-navigation, we need to call custom methods from our templates:

Search your theme for occurrences of the_posts_pagination and replace with wp_bootstrap_posts_pagination:

// Previous/next page navigation.
wp_bootstrap_posts_pagination( array(
  'prev_text'          => __( 'Previous page', 'textdomain' ),
  'next_text'          => __( 'Next page', 'textdomain' ),
  'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'textdomain' ) . ' </span>'
) );

Search your theme for occurrences of the_post_navigation and replace with wp_bootstrap_post_navigation:

// Previous/next post navigation.
wp_bootstrap_post_navigation( array(
  'next_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Next', 'textdomain' ) . '</span> ' .
    '<span class="screen-reader-text">' . __( 'Next post:', 'textdomain' ) . '</span> ' .
    '<span class="post-title">%title</span>',
  'prev_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Previous', 'textdomain' ) . '</span> ' .
    '<span class="screen-reader-text">' . __( 'Previous post:', 'textdomain' ) . '</span> ' .
    '<span class="post-title">%title</span>'
) );

Widgets

Widgets are rendered as cards. Some manipulations take care of content that's common to widgets, such as applying list-groups to unordered lists. Make sure that you registered any widget areas in your functions.php:

// Register widget area.
register_sidebar( array(
  'name'          => __( 'Widget Area', 'textdomain' ),
  'id'            => 'sidebar-1',
  'description'   => __( 'Add widgets here to appear in your sidebar.', 'textdomain' )
) );

Comments

Comments are rendered as nested media-objects.

You can customize the label 'Comment' by making use of the bootstrap_options-filter:

// Customize Comment Label
function my_bootstrap_options($args) {
  return array_merge($args, array(
    'comment_label' => ('Comment' , 'textdomain');
  ));
}
add_filter( 'bootstrap_options', 'my_bootstrap_options' );

Recipes

Use as Plugin

When intended to use as plugin or must-use plugin, you should also take care of the situation where the plugin is unistalled and check if any of the functions exist first:

// Previous/next page navigation.
call_user_func_array(function_exists('wp_bootstrap_posts_pagination') ? 'wp_bootstrap_posts_pagination' : 'the_posts_pagination', array( array(
  'prev_text'          => __( 'Previous page', 'textdomain' ),
  'next_text'          => __( 'Next page', 'textdomain' ),
  'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'textdomain' ) . ' </span>'
) ) );

// Previous/next post navigation.
call_user_func_array(function_exists('wp_bootstrap_post_navigation') ? 'wp_bootstrap_post_navigation' : 'the_post_navigation', array( array(
  'next_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Next', 'textdomain' ) . '</span> ' .
    '<span class="screen-reader-text">' . __( 'Next post:', 'textdomain' ) . '</span> ' .
    '<span class="post-title">%title</span>',
  'prev_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Previous', 'textdomain' ) . '</span> ' .
    '<span class="screen-reader-text">' . __( 'Previous post:', 'textdomain' ) . '</span> ' .
    '<span class="post-title">%title</span>'
) ) );

// Edit post link
call_user_func_array(function_exists('wp_bootstrap_edit_post_link') ? 'wp_bootstrap_edit_post_link' : 'edit_post_link', array(
  sprintf(
    /* translators: %s: Name of current post */
    __( 'Edit<span class="screen-reader-text"> "%s"</span>', 'textdomain' ),
    get_the_title()
  ),
  '<span class="edit-link">',
  '</span>'
) );

Bootstrap 3

If you're still working with Bootstrap 3, you need to override at least some options.

// Bootstrap 3 Options
function my_bootstrap_options($options) {
  return array_merge($options, array(
    'img_class' => 'img-responsive',
    'align_center_class' => 'center-block',
    'edit_post_link_class' => 'btn btn-default btn-xs',
    'search_submit_label' => '<i class="glyphicon glyphicon-search"></i>',
    'gallery_thumbnail_class' => '',
    'gallery_thumbnail_img_class' => 'img-thumbnail',
    'close_button_class' => 'btn btn-default',
    'carousel_item_class' => 'item',
    'widget_class' => 'panel',
    'widget_modifier_class' => 'panel-default',
    'widget_header_class' => 'panel-heading',
    'widget_content_class' => 'panel-block'
  ));
}
add_filter( 'bootstrap_options', 'my_bootstrap_options', 1 );

API

Bootstrap Hooks is highly customizable. That's mostly required because of managing different Bootstrap versions without splitting up the codebase. Normally there should be no need to change a lot.

Methods

edit_post_link( string $text = null, string $before = '', string $after = '', int $id, string $class = 'post-edit-link' )

Displays the edit post link for post. See edit_post_link for further details.

wp_bootstrap_posts_pagination ( array $args = array() )

Displays a paginated navigation to next/previous set of posts, when applicable. See the_posts_pagination for further details.

wp_bootstrap_post_navigation ( array $args = array() )

Displays the navigation to next/previous post, when applicable. See the_post_navigation for further details.

Filters

bootstrap_options ( array $options = array() )

Inject custom options.

Options

Name Description Default
field_class Sets the field class used in forms form-group
text_input_class Sets the text input class used in forms form-control
submit_class Sets submit button css class btn btn-primary
reply_link_class Set reply link css class btn btn-primary btn-xs
comment_label Sets the comment label Comment
img_class Sets the general image class used in content and thumbnails img-responsive
align_left_class Aligns an image to left pull-left
align_right_class Aligns an image to right pull-right
align_center_class Center an image mx-auto
img_caption_tag Sets the tag for the img caption element figure
img_caption_class Sets the css class for the img caption element figure
img_caption_text_tag Sets the tag for the img caption text element figcaption
img_caption_text_class Sets the css class for the img caption text element figure-caption
img_caption_img_class Sets the css class for the image of the img element of the caption figure-img
table_class Sets the table css class table
table_container_tag Sets the table container tag div
table_container_class Sets the table container class table-responsive
blockquote_class Sets the blockquote css class blockquote
blockquote_footer_tag Sets the blockquote footer tag footer
blockquote_footer_class Sets the blockquote footer css class blockquote-footer
edit_post_link_class Sets the edit post link css class btn btn-secondary
edit_post_link_container_class Sets the edit post link container css class form-group btn-group btn-group-sm
search_submit_label Sets the searchfield's submit label 🔎
text_input_class Sets the class of textfields used in search- and password-forms form-control
submit_button_class Sets the class of submit buttons used in search- and password-forms btn btn-primary
gallery_thumbnail_size Sets the default thumbnail size thumbnail
gallery_thumbnail_class Sets the default thumbnail size
gallery_thumbnail_img_class Sets the default thumbnail size
gallery_zoom_size Sets the image size for the carousel view large
close_button_class Sets the modal's close button class btn btn-secondary
close_button_label Sets the modal's close button label __('Close')
carousel_item_class Sets the carousel's item class _item
menu_item_class Sets the menu item class nav-item
menu_item_link_class Sets the menu item link class nav-link
sub_menu_tag Sets the sub menu tag ul
sub_menu_class Sets the sub menu class dropdown-menu
sub_menu_header_class Sets the sub menu class dropdown-header
sub_menu_item_tag Sets the sub menu item tag li
sub_menu_item_class Sets the sub menu item class
sub_menu_item_link_class Sets the sub menu header class dropdown-item
caret Sets the menu item caret class (bs < 5) <span class="caret"></span>
pagination_class Sets the pagination class pagination
page_item_class Sets the page item class page-item
page_item_active_class Sets the page item active class active
page_link_class Sets the page link css class page-link
post_nav_class Sets the post navigation class nav
post_nav_tag Sets the post navigation tag ul
post_nav_item_class Sets the post navigation item class nav-item
post_nav_item_tag Sets the post navigation item tag li
post_nav_link_class Sets the post navigation link class nav-link
paginated_class Sets the paginated class pagination pagination-sm
paginated_tag Sets the paginated tag ul
paginated_item_class Sets the paginated item class page-item
paginated_item_tag Sets the paginated item tag li
paginated_link_class Sets the paginated link tag page-link
next_posts_link_class Specify next posts link class btn btn-sm btn-secondary
previous_posts_link_class Specify previous posts link class btn btn-sm btn-secondary
widget_class Sets the widget class card
widget_modifier_class Sets the widget modifier class card-widget
widget_header_class Sets the widget header class card-header
widget_content_class Sets the widget content class card-body

Development

Download Docker CE for your OS. Download NodeJS for your OS.

WordPress

Run container. Point your browser to http://localhost:8020.

docker-compose up -d

Import test data. Activate wordpress-importer if needed.

docker compose run wp plugin activate wordpress-importer
docker compose run wp import vendor/wptrt/theme-unit-test --authors=skip

Update composer dependencies

docker-compose run composer update

Stop all globally running docker containers

docker stop $(docker ps -a -q)

Frontend

Install front-end dependencies

npm i

Watch front-end dependencies

npm run watch

Create a build for production

npm run build

Releases

Newest 25 of 60 recorded releases.

Tag
Published
v1.1.21 latest
Mar 31, 2025 1 year ago
Feb 7, 2025 1 year ago
Feb 6, 2025 1 year ago
Feb 6, 2025 1 year ago
Dec 19, 2024 1 year ago
Dec 19, 2024 1 year ago
Dec 2, 2024 1 year ago
Nov 30, 2024 1 year ago
Nov 17, 2024 1 year ago
Nov 15, 2024 1 year ago
Nov 10, 2024 1 year ago
Nov 10, 2024 1 year ago
Oct 30, 2024 1 year ago
Oct 19, 2024 2 years ago
Oct 19, 2024 2 years ago
Oct 19, 2024 2 years ago
Oct 18, 2024 2 years ago
Oct 15, 2024 2 years ago
Oct 15, 2024 2 years ago
Oct 14, 2024 2 years ago
Oct 14, 2024 2 years ago
Oct 14, 2024 2 years ago
Oct 12, 2024 2 years ago
Oct 9, 2024 2 years ago
Oct 7, 2024 2 years ago
All releases on GitHub →

These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.

D+ grade

Security

Bootstrap Hooks 1.1.21 · audited by WP Registry

High-severity findings.

2 high 1 medium
Audited release
1.1.21
Findings
3
Worst severity
high
Content hash
8bf7c14bcd0cb95507ea4846…

Findings

  • high Contributor+ local file inclusion via gallery type attribute

    file_access

    bootstrap_gallery() takes $params['type'] from the [gallery] shortcode (post_gallery filter receives raw $attr) or from extra Gutenberg core/gallery block attributes (array_merge keeps type). If file_exists($type) is true, that string is passed to render_gallery_template() which include()s it. No basename/whitelist, no current_theme_supports gate, and no capability check. A Contributor can save [gallery type="/etc/passwd"] (or type="wp-config.php" relative to ABSPATH) and preview the post to have PHP include the file: non-PHP files are dumped into the frontend HTML; existing PHP files are executed in the WordPress process. The same path is reachable from a crafted <!-- wp:gallery {"type":"/etc/passwd"} --> block because render_block_gallery merges block attrs into bootstrap_gallery().

    features/gallery/gallery.php:5-21,109-125

    Recommendation

    Never treat the type attribute as a filesystem path. Map type to an explicit allowlist of plugin templates (carousel, lightbox, table, thumbnails) and resolve only under features/gallery/template with basename() + realpath() confined to that directory. Reject absolute paths, wrappers (phar://, php://), and directory separators in type.

  • high Contributor+ stored XSS in gallery markup from unsanitized shortcode attributes

    xss_stored

    Shortcode attributes id, class, title, and style are merged into $attrs / $id / $title and printed without esc_attr/esc_html. features/gallery/template/gallery.php (and the carousel/table templates) do <?= $name ?>="<?= $value ?>" for every attr; gallery-lightbox.php echoes $title into the modal heading; gallery-thumbnails.php interpolates $id inside a <script> (document.getElementById('<?= $id ?>-thumbnails')). WordPress kses on save does not strip shortcode attributes, so users without unfiltered_html (Contributor/Author, and Editor on multisite) can store [gallery id='" onmouseover="alert(1)'] or [gallery title='<script>alert(1)</script>'] or [gallery style='x:y" onclick="alert(1)']. After publish the payload is server-rendered on the public post; during preview it runs in the administrator's browser. The post_gallery hook is registered even when the theme does not add_theme_support('bootstrap').

    features/gallery/template/gallery.php:13-18

    Recommendation

    Sanitize gallery attributes on input (sanitize_html_class / sanitize_title / wp_kses_post for captions) and escape on output: esc_attr() for attribute values, esc_html() for title/captions, and a strict [A-Za-z0-9_-] pattern before interpolating $id into JavaScript or CSS. Do not iterate arbitrary attr names into HTML.

  • medium Gallery shortcode exposes private/draft posts via attacker-controlled WP_Query

    information_disclosure

    bootstrap_gallery() copies shortcode/block params post_type, post_status, include, exclude, orderby, and nopaging straight into WP_Query with no 'perm' => 'readable' and no current_user_can. Defaults (attachment/inherit) are overridden by array_merge($defaults, $params). A Contributor can preview [gallery post_type="post" post_status="private" nopaging="true"] (or post_status=any / draft) and receive other users' private and draft posts in the loop. Templates print get_the_title() (and attachment images when the type is attachment), so unpublished titles and private media are disclosed. Core's gallery_shortcode does not expose these query vars this way; this plugin replaces that output on every [gallery] via post_gallery at priority 0.

    features/gallery/gallery.php:144-175

    Recommendation

    Do not accept post_type/post_status/nopaging from shortcode or block attributes. Force post_type=attachment and a public-readable status, or run the query with 'perm' => 'readable'. When ids is empty, keep core behavior (attachments of the current post) instead of querying the whole site.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.