Jaime Martínez | @jmslbam | Level Level | #svsim | 10 mrt 2014
0 sites?
1 site?
2+ sites?
5+ sites?
10+ sites?
Find the right methods and tools
Start here
Intro, just read this
http://codex.wordpress.org/WordPress_Semantics#Terminology_Related_to_Design
and then the handbook :)
http://make.wordpress.org/docs/theme-developer-handbook/
*Read back at home or the office
FireFox and Web Developer + FireBug
Chrome and Web Developer + Chrome tools
Internet Explorer
+ developer toolbar
or
modern.ie || ievms
Online hosting
Otherwise
Local with Homebrew and php-osx #
Recommended ++
wp-config.php
define( 'WP_DEBUG', true );
if ( WP_DEBUG ) {
define( 'SCRIPT_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', true );
@ini_set( 'display_errors', 1 );
}
display_errors = On;
error_reporting = E_ALL | E_STRICT;
Also install Xdebug
without errors or notices
Advanced Custom Fields with the WP-CLI add-on
wp-content/themes/*
Themes consist of a collection of HTML, CSS, PHP and probably JavaScript files
Must have!
/*
Theme Name: Roots
Theme URI: http://roots.io/
Description: Roots is a WordPress starter theme based on HTML5 Boilerplate & Bootstrap.
Version: 6.5.2
Author: Ben Word
Author URI: http://benword.com/
License: MIT License
License URI: http://opensource.org/licenses/MIT
*/
don't put everything in this file... organize it!
The Template file used to render the Archive Index page for a Custom Post Type
archive-{post_type}.php
– If the post type is product
, WordPress will look for archive-product.php
.archive.php
index.php
Only for pages, needs plugin for CPT's
<?php
/*
Template Name: Contact
*/
?>
The Template file used to render a static page (page
post-type)
Custom template file
– The Page Template assigned to the Page. See get_page_templates().page-{slug}.php
– If the page slug is recent-news, WordPress will look to use page-recent-news.php
page-{id}.php
– If the page ID is 6, WordPress will look to use page-6.php
page.php
index.php
Please don't use 2 & 3
Inside a template
Too enqueue scripts and styles (see last slides)
// Most important call in header.php
wp_head();
// Most important call in footer.php
wp_footer();
Sorry no twig or liquid templating
get_template_part
get_template_part
get_template_part
get_template_part
get_template_part
get_template_part
<?php
/*
Template Name: Front page
*/
?>
<div class="main">
<?php get_template_part('templates/post', 'highlighted'); // The same ?>
<?php get_template_part('templates/post', 'latest'); ?>
</div>
<div class="sidebar">
<?php get_template_part('templates/event', 'highlighted'); ?>
<?php
// Remember that on the front_page the global query contains the latest x posts else use get_template_part('templates/post', 'latest');
get_template_part('templates/event', 'latest');
?>
</div>
<?php
/*
Template Name: News
*/
?>
<div class="main">
<?php get_template_part('templates/post', 'highlighted'); // The same ?>
</div>
<div class="sidebar">
<?php get_template_part('templates/post', 'latest'); ?>
<p class="list-view-all-link">
<a class="read-more" href="<?php echo $archive_page; ?>"><?php echo __("More news", "sharedstories"); ?></a>
</p>
<?php endif; ?>
</div>
templates/post-highlighted.php
<article <?php post_class('highlighted'); ?>>
<header>
<?php get_template_part('templates/post', 'meta'); ?>
<h2 class="entry-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
</header>
<?php
if ( has_post_thumbnail() ) :
the_post_thumbnail('medium'); // wrapper link added in custom.php for all post_thumbnail
endif;
?>
<div class="entry-summary">
<?php echo ll_excerpt_by_id( get_the_ID(), POST_EXCERPT_LENGTH , roots_excerpt_more( true ) ); ?>
</div>
</article>
templates/post-meta.php
<?php if( is_front_page() ) { ?>
<?php get_template_part('templates/entry-meta', 'time'); ?>
<?php } else {
$post_taxonomies = get_object_taxonomies( $post ); ?>
<p class="meta">
<?php get_template_part('templates/entry-meta', 'time'); ?>
<?php echo get_the_term_list( get_the_ID(), $post_taxonomies, ' - <span class="lc">', '</span>, <span class="lc">', '</span>' ); ?>
- <span class="byline author vcard"><a href="<?php echo get_author_posts_url(get_the_author_meta('ID')); ?>" rel="author" class="fn"><?php echo get_the_author(); ?></a></span>
</p>
<?php } ?>
<?php $d = "d M";?>
<span class="date">
<time class="updated" datetime="<?php echo get_the_time('c'); ?>"><?php echo get_the_date( $d ); ?></time>
</span>
Like every proper template engine this lets you pass $args to the template file #
/templates/contact-persons.php
$people = '';
foreach ($loc_people as $key => $person) {
$people .= ll_get_template_part('templates/contact-person', array( 'person' => (object)$person['ss_contact_person'], 'return' => true ), false ) ;
}
echo $people;
Simply include the templates from plugins/your-plugin/widget.php
public function form( $instance ) {
wp_enqueue_media();
$defaults = array( 'image-id' => false, 'image-preview' => '', 'url' => false );
$instance = wp_parse_args( $instance, $defaults );
$image_preview = esc_url_raw( $instance['image-preview'] );
$image_id = absint( $instance['image-id'] );
$image_thumbnail = $this->get_image_preview( $image_id );
$url = esc_url_raw( $instance['url'] );
// Display the admin form
include( plugin_dir_path(__FILE__) . 'views/admin.php' );
}
plugins/your-plugin/views/admint.php
<!-- This file is used to markup the administration form of the widget. -->
<p class="abn-sidebar-widget-image"><?php echo $image_thumbnail; ?></p> <p> <a class="ll-sidebar-widget-image-button button button-secondary" data-uploader_title="Select banner" >Select image</a> </p> <p> <label for="<?php echo $this->get_field_name('url'); ?>">Link</label> <input class="ll-sidebar-widget-url widefat" type="text" name="<?php echo $this->get_field_name('url'); ?>" value="<?php echo esc_attr($url); ?>" /> </p> <input class="ll-sidebar-widget-image-preview" type="hidden" name="<?php echo $this->get_field_name('image-preview'); ?>" value="<?php echo esc_attr( $image_preview ); ?>" /> <input class="ll-sidebar-widget-image-id" type="hidden" name="<?php echo $this->get_field_name('image-id'); ?>" value="<?php echo esc_attr( $image_id ); ?>" />
Child themes are themes that piggy-back on another (full) theme.
It only overrides the bare necessary
Updates!
One theme to rule them all...
Payed theme builders don't take care backwards compatibility
woocommerce/templates/cart/cart.php
to
your-theme/woocommerce/cart/cart.php
Or use hooks to add / move content
in your-child-theme/functions.php
// First mimic WooCommerce style loading from theme-actions.php
if ( ! is_admin() ) { add_action( 'wp_enqueue_scripts', 'woo_load_frontend_css', 20 ); }
function woo_load_frontend_css () {
// Mimic WooCommerce style loading - theme-actions.php
wp_register_style( 'woo-theme-stylesheet', get_template_directory_uri() . '/style.css' );
wp_register_style( 'woo-layout', get_template_directory_uri() . '/css/layout.css' );
wp_enqueue_style( 'woo-theme-stylesheet' );
wp_enqueue_style( 'woo-layout' );
// Mimic Woocommerce style loading - theme-woocommerce.php
wp_register_style( 'woocommerce', esc_url( get_template_directory_uri() . '/css/woocommerce.css' ) );
wp_enqueue_style( 'woocommerce' );
// Manualy load Fabulous Women style
wp_enqueue_style('theme-stylesheet', get_stylesheet_directory_uri().'/assets/less/style.less');
//wp_register_style( 'theme-stylesheet', get_stylesheet_uri() );
wp_enqueue_style( 'theme-stylesheet' );
}
//Let's use all code from tri.be and no custom!
$args = array(
'posts_per_page' => 3,
);
$query = TribeEventsQuery::getEvents( $args, true );
if ( $query->tribe_is_event && $query->posts ) : //setup_postdata( $post );
$query->the_post();
echo '<article class="hentry tribe-event">';
tribe_get_template_part( 'list/single', 'event' ); // This way all views will be the same ;)
echo "</article>";
endif;
// Clean up
$event = $query = $highlighted_event = false;
wp_reset_postdata();
Checks where you are on the site
// function.php ;)
function ll_conditional_custom_category_limit( $query ) {
if ( is_admin() || ! $query->is_main_query() )
return;
if ( is_archive('specific-term') ) {
$query->set( 'posts_per_page', 1 );
return;
}
}
add_action( 'pre_get_posts', 'll_conditional_custom_category_limit', 1 );
Why?
Caching plugins
Minify plugins
Unload stylesheets and scripts from plugins
/**
* Enqueue scripts and styles.
*/
function _s_scripts() {
wp_enqueue_style( '_s-style', get_stylesheet_uri() );
wp_enqueue_script( '_s-navigation', get_template_directory_uri() . '/js/navigation.js', array(), '20120206', true );
if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
wp_enqueue_script( 'comment-reply' );
}
}
add_action( 'wp_enqueue_scripts', '_s_scripts' );
Prevent XSS attacks, escape your data!
or stuff like
absint( $int )
sanitize_title( $title )
$wpdb->prepare(...)
wp_safe_redirect($location, $status = 302)
Actions are events in the WordPress page lifecycle when certain things have occurred - certain resources are loaded, certain facilities are available, and, depending on how early the action has occurred, some things have yet to load.
Run this a that moment
http://codex.wordpress.org/Plugin_API/Action_Reference
http://code.tutsplus.com/articles/the-beginners-guide-to-wordpress-actions-and-filters--wp-27373
Filters are functions that WordPress passes data through during certain points of the page lifecycle.
They are primarily responsible for intercepting, managing, and returning data before rendering it to the browser or saving data from the browser to the database.
add_filter( 'the_content', 'll_the_content_filter', 20 ); /** * Add a icon to the beginning of every post page. * * @uses is_single() */ function ll_the_content_filter( $content ) { if ( is_single() ){ // Add image to the beginning of each page $content = $content . 'Facebook | LinkedIn | Twitter | Etc';
}
// Returns the content. } return $content;
Use actions when you want to add something to the existing page such as stylesheets, JavaScript dependencies, or send an email when an event has happened.
Use filters when you want to manipulate data coming out of the database prior to going to the browser, or coming from the browser prior to going into the database.
function ll_exclude_category( $wp_query ) {
// Add the category to an array of excluded categories. In this case, though,
// it's really just one.
$excluded = array( '-1' );
// Note that this is a cleaner way to write: $wp_query->set('category__not_in', $excluded);
set_query_var( 'category__not_in', $excluded );
}
add_action( 'pre_get_posts', 'll_exclude_category' );
To get Custom Post Type data
WP_query is used in The Loop
<?php
$args = array(
'post_type' => 'll-portfolio',
);
$ll_portfolio_items = new WP_Query( $args );
if( $ll_portfolio_items->have_posts() ) : ?>
<div class='items'>
<?php while($ll_portfolio_items->have_posts()) : $ll_portfolio_items->the_post() ?>
<div class='item'>
<?php the_post_thumbnail() ?>
</div>
<?php endwhile ?>
</div>
<?php endif ?>