Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Beyond Posts & Pages: Structured Content in WordPress

John Eckman
October 26, 2013

Beyond Posts & Pages: Structured Content in WordPress

The introduction of Custom Content Types was one of the core tipping points where WordPress went from a solid blogging platform on which you could do some simple CMS-style sites to a full fledged CMS which was capable of driving more complex content-centered web applications.

In this talk I’ll briefly cover the “blobs vs chunks” distinction popularized by Karen McGrane and why content modeling matters in the CMS world.

Then we’ll cover custom post types, custom meta fields, custom taxonomies, and other ways of doing structured content in WordPress.

John Eckman

October 26, 2013
Tweet

More Decks by John Eckman

Other Decks in Technology

Transcript

  1. #wcbos #chunky @jeckman “We don't need more content – we

    need content that does more” - Sara Wachter Boettcher h"p://www.cmsmyth.com/2013/04/content-­‐ that-­‐does-­‐more/  
  2. #wcbos #chunky @jeckman Adaptive Content 1.  Reusable content 2.  Structured

    content 3.  Presentation-independent content 4.  Meaningful metadata 5.  Usable CMS interfaces h"p://www.abookapart.com/products/ content-­‐strategy-­‐for-­‐mobile  
  3. #wcbos #chunky @jeckman “WPT’s capture content with the primary purpose

    of publishing web pages. . . . CMS’s, on the other hand, store the content cleanly, enabling the presentation layers to worry about how to display the content.” - Daniel Jacobson h"p://www.markboulton.co.uk/journal/ adapBve_content_management  
  4. #wcbos #chunky @jeckman register_post_type() Arguments passed control: •  What to

    call it (Labels) •  Where to show it –  Public, Show UI, Searchable, has_archive –  Menu position •  Who can use it (capabilities) •  What it includes (supports)
  5. #wcbos #chunky @jeckman   add_action(  'init',  'create_post_type'  );   function

     create_post_type()  {    register_post_type(  'alerts',      array('labels'  =>  array(        'name'  =>  __(  'Alerts'  ),        'singular_name'  =>  __(  'Alert'  )),      'public'  =>  true,      'has_archive'  =>  true,      ));   }  
  6. #wcbos #chunky @jeckman function  meteorslides_register_slides()  {    $meteor_labels  =  array(

       'name'                    =>  __(  'Slides',  'meteor-­‐slides'  ),    'singular_name’  =>  __(  'Slide',  'meteor-­‐slides'  ),    'add_new'              =>  __(  'Add  New',  'meteor-­‐slides'  ),    'add_new_item’    =>  __(  'Add  New  Slide',  'meteor-­‐slides'  ),    'edit_item'            =>  __(  'Edit  Slide',  'meteor-­‐slides'  ),    'new_item'              =>  __(  'New  Slide',  'meteor-­‐slides'  ),    'view_item'            =>  __(  'View  Slide',  'meteor-­‐slides'  ),    'search_items'  =>  __(  'Search  Slides',  'meteor-­‐slides'  ),    'not_found'      =>  __(  'No  slides  found',  'meteor-­‐slides'  ),    'not_found_in_trash'  =>  __(  'No  slides  found  in  Trash',          'meteor-­‐slides'  ),      'parent_item_colon’  =>  '',    'menu_name’  =>  __(  'Slides',  'meteor-­‐slides'  )    );  
  7. #wcbos #chunky @jeckman $meteor_args  =  array(      'labels'  

                             =>  $meteor_labels,      'public'                            =>  true,      'publicly_queryable'    =>  false,      'exclude_from_search'  =>  true,      'show_ui'                          =>  true,      'show_in_menu'                =>  true,      'menu_icon'                      =>  ''.  plugins_url(            '/images/slides-­‐icon-­‐20x20.png',  __FILE__  ),      'capability_type'          =>  $meteor_capabilitytype,      'capabilities'                =>  $meteor_capabilities,      'map_meta_cap'                =>  $meteor_mapmetacap,      'hierarchical'                =>  false,      'supports'                        =>  array(  'title',  'thumbnail'  ),      'taxonomies'                    =>  array(  'slideshow'  ),      'has_archive'                  =>  false,      'rewrite'                          =>  false,      'query_var'                      =>  true,      'can_export'                    =>  true,      'show_in_nav_menus'      =>  false);  
  8. #wcbos #chunky @jeckman Here  we  have  a  custom  post  type

     for   “Stories”  with  two  custom  taxonomies:   LocaBons  and  Topics   Example: Stories
  9. #wcbos #chunky @jeckman These  Meta  Boxes  enable  selecBon  of  

    LocaBon  /  Topic  from  a  pre-­‐defined  set  
  10. #wcbos #chunky @jeckman function  gc_taxonomies_story_topic()  {      $labels  =

     array(        'name  =>'  _x(  'Topics',  'taxonomy  general  name'  ),        'singular_name’  =>  _x(  'Topic’,'taxonomy  singular  name'  ),        'search_items'            =>  __(  'Search  Topics'  ),        'all_items'                  =>  __(  'All  Topics'  ),        'edit_item'                  =>  __(  'Edit  Topic'  ),          'update_item'              =>  __(  'Update  Topics'  ),        'add_new_item'            =>  __(  'Add  New  Topic'  ),        'new_item_name'          =>  __(  'New  Topic'  ),        'menu_name'                  =>  __(  'Topics'  ),        'popular_items'    =>  NULL,  );      $args  =  array(            'labels'  =>  $labels,            'hierarchical'  =>  false,      'show_tagcloud'  =>  false,      'show_admin_column'  =>  true,      );               register_taxonomy(  'topic',  'story',  $args  );   }  
  11. #wcbos #chunky @jeckman add_action('admin_menu','remove_my_meta_boxen');     function  remove_my_meta_boxen()  {  

     remove_meta_box('tagsdiv-­‐locations','story','core');    remove_meta_box('tagsdiv-­‐topic','story','core');   }     function  add_locations_box()  {      add_meta_box('location_box_ID',  __('Location'),    'gc_style_locations','story',  'side',  'core');   }               function  add_topics_box()  {        add_meta_box('topic_box_ID',__('Topic'),   'gc_style_topics',  'story',  'side',  'core');   }  
  12. #wcbos #chunky @jeckman function  gc_style_locations($post)  {      echo  '<input

     type="hidden"  name="taxonomy_noncename"                    id="taxonomy_noncename"  value="'  .                  wp_create_nonce(  'taxonomy_location'  )  .  '"  />’;                $locations  =  get_terms('locations',  'hide_empty=0');     ?><select  name='story_locations'  id='story_location’><?php                                $names  =  wp_get_object_terms($post-­‐>ID,  'locations');                    print_r($post);   ?><option  class='location-­‐option'  value=’’     <?php  if  (!count($names))  echo  "selected";?>>None</option>   <?php      foreach  ($locations  as  $location)  {          if  (!is_wp_error($names)  &&  !empty($names)        &&  !strcmp($location-­‐>slug,  $names[0]-­‐>slug))        echo  "<option  class='location-­‐option'  value='"  .  $location-­‐ >term_id  .  "'  selected>"  .  $location-­‐>name  .  "</option>\n";            else                  echo  "<option  class='location-­‐option'  value='"  .  $location-­‐ >term_id  .  "'>"  .  $location-­‐>name  .  "</option>\n";                  }              ?>        </select>                <?php         }  
  13. #wcbos #chunky @jeckman We’ve  also  got  custom  meta  data  here

     for:   •  Pull  Quote   •  School   •  Teacher   •  Democracy  Coaches  
  14. #wcbos #chunky @jeckman Custom Post Meta Boxes •  add_meta_box() passed

    a styling function •  style function outputs the html needed for admin screen •  save function added to save_post action •  update_post_meta to store
  15. #wcbos #chunky @jeckman function  add_meta_boxen()  {        add_meta_box('pullquote_box_ID',__('Quote'),

         'gc_style_pullquote','story','side','core');      add_meta_box('school_box_ID',__('School'),      'gc_style_school','story','side','core');              add_meta_box('teacher_box_ID',__('Teacher'),      'gc_style_teacher','story','side','core');                add_meta_box('coaches_box_ID',__(’Coaches'),      'gc_style_coaches’,'story','side','core');   }  
  16. #wcbos #chunky @jeckman  function  save_taxonomy_data($post_id)  {        //

     <snip>      $post  =  get_post($post_id);        if  ($post-­‐>post_type  ==  'story')  {          $location  =  $_POST['story_locations'];        wp_set_object_terms(  $post_id,(int)  $location,                'locations'  );        }            if  ($post-­‐>post_type  ==  'story')  {          $topic  =  $_POST['story_topics'];            wp_set_object_terms(  $post_id,  (int)  $topic,  'topic'  );        }                   update_post_meta($post_id,  'pullquote',$_POST['pullquote']);   update_post_meta($post_id,  'school',  $_POST['school']  );   update_post_meta($post_id,  'teacher',  $_POST['teacher']  );   update_post_meta($post_id,  'coaches',  $_POST['coaches']  );   }  
  17. #wcbos #chunky @jeckman Chunky Via Plugins •  Custom Post Type

    UI –  http://wordpress.org/plugins/custom-post-type-ui/ •  Custom Post Type List Shortcode –  http://wordpress.org/plugins/custom-post-type-list- shortcode/ •  Secondary HTML Content –  http://wordpress.org/extend/plugins/secondary- html-content/ •  Attachments –  http://wordpress.org/extend/plugins/attachments/
  18. #wcbos #chunky @jeckman For Further Exploration •  Relationships between Posts

    •  Display of Custom Post Types –  archive-{post_type}.php –  single-{post_type}.php •  Expanding the WordPress presentation tier –  Timber ( http://jarednova.github.io/timber/ ) –  JavaScript ( see http://www.kadamwhite.com/ archives/2013/video-evolving-your-javascript-with- backbone-js ) –  API (http://developer.wordpress.com/docs/api/ )