Timber - Version 1.8.2

Version Description

Changes for Theme Developers - You can now change the query parameters that are used when getting a posts terms through $post->terms(). #1802 - New attributes for responsive images post.thumbnail.srcset and post.thumbnail.sizes #1819 (thanks @maxxwv)

Fixes and improvements - Using WordPress's wp_check_filetype_and_ext for the mime_type mess #1843 (thanks @gchtr) - Fixed how some previewed data (when looking at an unsaved post from the admin) is handled so that parenting relationships match what happens when published #1752 - Timber\Menu now respects modifications sent through WP's wp_nav_menu_objects filter #1814 (thanks @pascalknecht)

Download this release

Release Info

Developer jarednova
Plugin Icon 128x128 Timber
Version 1.8.2
Comparing to
See all releases

Code changes from version 1.8.1 to 1.8.2

lib/Image.php CHANGED
@@ -452,6 +452,44 @@ class Image extends Post implements CoreInterface {
452
  return $src;
453
  }
454
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  /**
456
  * @internal
457
  * @return bool true if media is an image
@@ -480,6 +518,7 @@ class Image extends Post implements CoreInterface {
480
 
481
  /**
482
  * @deprecated 0.21.9 use TimberImage::src
 
483
  * @internal
484
  * @param string $size
485
  * @return bool|string
@@ -492,6 +531,7 @@ class Image extends Post implements CoreInterface {
492
 
493
  /**
494
  * @deprecated since 0.21.9 use src() instead
 
495
  * @return string
496
  */
497
  public function url( $size = '' ) {
@@ -502,6 +542,7 @@ class Image extends Post implements CoreInterface {
502
 
503
  /**
504
  * @deprecated since 0.21.9 use src() instead
 
505
  * @return string
506
  */
507
  public function get_url( $size = '' ) {
452
  return $src;
453
  }
454
 
455
+ /**
456
+ * @param string $size a size known to WordPress (like "medium")
457
+ * @api
458
+ * @example
459
+ * ```twig
460
+ * <h1>{{ post.title }}</h1>
461
+ * <img src="{{ post.thumbnail.src }}" srcset="{{ post.thumnbail.srcset }}" />
462
+ * ```
463
+ * ```html
464
+ * <img src="http://example.org/wp-content/uploads/2018/10/pic.jpg" srcset="http://example.org/wp-content/uploads/2018/10/pic.jpg 1024w, http://example.org/wp-content/uploads/2018/10/pic-600x338.jpg 600w, http://example.org/wp-content/uploads/2018/10/pic-300x169.jpg 300w" />
465
+ * ```
466
+ * @return bool|string
467
+ */
468
+ public function srcset( $size = "full" ) {
469
+ if( $this->is_image() ){
470
+ return wp_get_attachment_image_srcset($this->ID, $size);
471
+ }
472
+ }
473
+
474
+ /**
475
+ * @param string $size a size known to WordPress (like "medium")
476
+ * @api
477
+ * @example
478
+ * ```twig
479
+ * <h1>{{ post.title }}</h1>
480
+ * <img src="{{ post.thumbnail.src }}" srcset="{{ post.thumnbail.srcset }}" sizes="{{ post.thumbnail.sizes }}" />
481
+ * ```
482
+ * ```html
483
+ * <img src="http://example.org/wp-content/uploads/2018/10/pic.jpg" srcset="http://example.org/wp-content/uploads/2018/10/pic.jpg 1024w, http://example.org/wp-content/uploads/2018/10/pic-600x338.jpg 600w, http://example.org/wp-content/uploads/2018/10/pic-300x169.jpg 300w sizes="(max-width: 1024px) 100vw, 102" />
484
+ * ```
485
+ * @return bool|string
486
+ */
487
+ public function img_sizes( $size = "full" ) {
488
+ if( $this->is_image() ){
489
+ return wp_get_attachment_image_sizes($this->ID, $size);
490
+ }
491
+ }
492
+
493
  /**
494
  * @internal
495
  * @return bool true if media is an image
518
 
519
  /**
520
  * @deprecated 0.21.9 use TimberImage::src
521
+ * @codeCoverageIgnore
522
  * @internal
523
  * @param string $size
524
  * @return bool|string
531
 
532
  /**
533
  * @deprecated since 0.21.9 use src() instead
534
+ * @codeCoverageIgnore
535
  * @return string
536
  */
537
  public function url( $size = '' ) {
542
 
543
  /**
544
  * @deprecated since 0.21.9 use src() instead
545
+ * @codeCoverageIgnore
546
  * @return string
547
  */
548
  public function get_url( $size = '' ) {
lib/ImageHelper.php CHANGED
@@ -141,41 +141,34 @@ class ImageHelper {
141
  fclose($fh);
142
  return $count > 1;
143
  }
144
-
145
- /**
146
- *
147
- * Checks if file is an SVG
148
- * @param string $file_path file path.
149
- * @return boolean true if svg, false if not svg or file doesn't exist.
150
- */
151
- public static function is_svg( $file_path ) {
152
- $ret = false;
153
- if ( isset($file_path) && '' !== $file_path && file_exists($file_path) ) {
154
- $mime = self::_mime_content_type($file_path);
155
- $ret = in_array($mime, ['image/svg+xml', 'text/html', 'text/plain', 'image/svg']);
156
- }
157
- return $ret;
158
- }
159
 
160
  /**
161
- *
162
- * Reads a file's mime type. This is a hack b/c some installs of PHP don't enable this function
163
- * by default. See #1798 for more info
164
- * @since 1.8.1
165
- * @param string $filename to test.
166
- * @return string|boolean mime type if found (eg. `image/svg` or `text/plain`) false if not
167
  */
168
- static function _mime_content_type( $filename ) {
169
- if ( function_exists( 'mime_content_type' ) ) {
170
- return mime_content_type( $filename );
171
  }
172
- $result = new \finfo();
173
-
174
- if ( file_exists( $filename ) === true ) {
175
- return $result->file( $filename, FILEINFO_MIME_TYPE );
176
- }
177
 
178
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  }
180
 
181
  /**
@@ -209,7 +202,7 @@ class ImageHelper {
209
  /**
210
  * Generates a new image by converting the source into WEBP
211
  *
212
- * @param string $src a url or path to the image (http://example.org/wp-content/uploads/2014/image.jpg)
213
  * or (/wp-content/uploads/2014/image.jpg)
214
  * @param int $quality ranges from 0 (worst quality, smaller file) to 100 (best quality, biggest file)
215
  * @param bool $force
141
  fclose($fh);
142
  return $count > 1;
143
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
  /**
146
+ * Checks if file is an SVG.
147
+ *
148
+ * @param string $file_path File path to check.
149
+ * @return bool True if SVG, false if not SVG or file doesn't exist.
 
 
150
  */
151
+ public static function is_svg( $file_path ) {
152
+ if ( ! isset( $file_path ) || '' === $file_path || ! file_exists( $file_path ) ) {
153
+ return false;
154
  }
 
 
 
 
 
155
 
156
+ /**
157
+ * Try reading mime type.
158
+ *
159
+ * SVG images are not allowed by default in WordPress, so we have to pass a default mime
160
+ * type for SVG images.
161
+ */
162
+ $mime = wp_check_filetype_and_ext( $file_path, basename( $file_path ), array(
163
+ 'svg' => 'image/svg+xml',
164
+ ) );
165
+
166
+ return in_array( $mime['type'], array(
167
+ 'image/svg+xml',
168
+ 'text/html',
169
+ 'text/plain',
170
+ 'image/svg',
171
+ ) );
172
  }
173
 
174
  /**
202
  /**
203
  * Generates a new image by converting the source into WEBP
204
  *
205
+ * @param string $src a url or path to the image (http://example.org/wp-content/uploads/2014/image.jpg)
206
  * or (/wp-content/uploads/2014/image.jpg)
207
  * @param int $quality ranges from 0 (worst quality, smaller file) to 100 (best quality, biggest file)
208
  * @param bool $force
lib/Loader.php CHANGED
@@ -151,7 +151,7 @@ class Loader {
151
  $loader = $this->get_loader();
152
  $params = array('debug' => WP_DEBUG, 'autoescape' => false);
153
  if ( isset(Timber::$autoescape) ) {
154
- $params['autoescape'] = Timber::$autoescape;
155
  }
156
  if ( Timber::$cache === true ) {
157
  Timber::$twig_cache = true;
151
  $loader = $this->get_loader();
152
  $params = array('debug' => WP_DEBUG, 'autoescape' => false);
153
  if ( isset(Timber::$autoescape) ) {
154
+ $params['autoescape'] = Timber::$autoescape === true ? 'html' : Timber::$autoescape;
155
  }
156
  if ( Timber::$cache === true ) {
157
  Timber::$twig_cache = true;
lib/Menu.php CHANGED
@@ -100,6 +100,38 @@ class Menu extends Core {
100
  if ( $menu ) {
101
  _wp_menu_item_classes_by_context($menu);
102
  if ( is_array($menu) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  $menu = self::order_children($menu);
104
  $menu = self::strip_to_depth_limit($menu);
105
  }
100
  if ( $menu ) {
101
  _wp_menu_item_classes_by_context($menu);
102
  if ( is_array($menu) ) {
103
+ /**
104
+ * Default arguments from wp_nav_menu() function.
105
+ *
106
+ * @see wp_nav_menu()
107
+ */
108
+ $default_args = array(
109
+ 'menu' => '',
110
+ 'container' => 'div',
111
+ 'container_class' => '',
112
+ 'container_id' => '',
113
+ 'menu_class' => 'menu',
114
+ 'menu_id' => '',
115
+ 'echo' => true,
116
+ 'fallback_cb' => 'wp_page_menu',
117
+ 'before' => '',
118
+ 'after' => '',
119
+ 'link_before' => '',
120
+ 'link_after' => '',
121
+ 'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>',
122
+ 'item_spacing' => 'preserve',
123
+ 'depth' => $this->depth,
124
+ 'walker' => '',
125
+ 'theme_location' => '',
126
+ );
127
+
128
+ /**
129
+ * Improve compatibitility with third-party plugins.
130
+ *
131
+ * @see wp_nav_menu()
132
+ */
133
+ $menu = apply_filters( 'wp_nav_menu_objects', $menu, $default_args );
134
+
135
  $menu = self::order_children($menu);
136
  $menu = self::strip_to_depth_limit($menu);
137
  }
lib/Post.php CHANGED
@@ -191,7 +191,6 @@ class Post extends Core implements CoreInterface {
191
  if ( 'class' === $field ) {
192
  return $this->css_class();
193
  }
194
-
195
  return parent::__get($field);
196
  }
197
 
@@ -213,7 +212,7 @@ class Post extends Core implements CoreInterface {
213
  * Determined whether or not an admin/editor is looking at the post in "preview mode" via the
214
  * WordPress admin
215
  * @internal
216
- * @return bool
217
  */
218
  protected static function is_previewing() {
219
  global $wp_query;
@@ -237,12 +236,7 @@ class Post extends Core implements CoreInterface {
237
  && is_object($wp_query->queried_object)
238
  && get_class($wp_query->queried_object) == 'WP_Post'
239
  ) {
240
-
241
- if ( self::is_previewing() ) {
242
- $pid = $this->get_post_preview_id($wp_query);
243
- } else if ( !$pid ) {
244
- $pid = $wp_query->queried_object_id;
245
- }
246
  } else if ( $pid === null && $wp_query->is_home && isset($wp_query->queried_object_id) && $wp_query->queried_object_id ) {
247
  //hack for static page as home page
248
  $pid = $wp_query->queried_object_id;
@@ -265,15 +259,6 @@ class Post extends Core implements CoreInterface {
265
  if ( $pid === null && ($pid_from_loop = PostGetter::loop_to_id()) ) {
266
  $pid = $pid_from_loop;
267
  }
268
- if (
269
- isset($_GET['preview'])
270
- && isset($_GET['preview_nonce'])
271
- && wp_verify_nonce($_GET['preview_nonce'], 'post_preview_'.$wp_query->queried_object_id)
272
- && isset($wp_query->queried_object_id)
273
- && ($wp_query->queried_object_id === $pid || (is_object($pid) && $wp_query->queried_object_id === $pid->ID))
274
- ) {
275
- $pid = $this->get_post_preview_id($wp_query);
276
- }
277
  return $pid;
278
  }
279
 
@@ -285,6 +270,14 @@ class Post extends Core implements CoreInterface {
285
  return $this->title();
286
  }
287
 
 
 
 
 
 
 
 
 
288
  protected function get_post_preview_id( $query ) {
289
  $can = array(
290
  get_post_type_object($query->queried_object->post_type)->cap->edit_post,
@@ -561,6 +554,11 @@ class Post extends Core implements CoreInterface {
561
  $post->id = $post->ID;
562
  $post->slug = $post->post_name;
563
  $customs = $this->get_post_custom($post->ID);
 
 
 
 
 
564
  $post->custom = $customs;
565
  $post = (object) array_merge((array) $customs, (array) $post);
566
  return $post;
@@ -576,89 +574,167 @@ class Post extends Core implements CoreInterface {
576
  return Helper::get_comment_form($this->ID, $args);
577
  }
578
 
579
-
580
  /**
581
- * Get the terms associated with the post
582
- * This goes across all taxonomies by default
583
  * @api
 
584
  * @example
585
  * ```twig
586
  * <section id="job-feed">
587
  * {% for post in job %}
588
- * <div class="job">
589
- * <h2>{{ post.title }}</h2>
590
- * <p>{{ post.terms('category') | join(', ') }}
591
- * </div>
592
  * {% endfor %}
593
  * </section>
594
  * ```
595
  * ```html
596
  * <section id="job-feed">
597
- * <div class="job">
598
- * <h2>Cheese Maker</h2>
599
- * <p>Food, Cheese, Fromage</p>
600
- * </div>
601
- * <div class="job">
602
- * <h2>Mime</h2>
603
- * <p>Performance, Silence</p>
604
- * </div>
605
  * </section>
606
  * ```
607
- * @param string|array $tax What taxonom(y|ies) to pull from. Defaults to all registered taxonomies for the post type. You can use custom ones, or built-in WordPress taxonomies (category, tag). Timber plays nice and figures out that tag/tags/post_tag are all the same (and categories/category), for custom taxonomies you're on your own.
608
- * @param bool $merge Should the resulting array be one big one (true)? Or should it be an array of sub-arrays for each taxonomy (false)?
609
- * @return array
610
- */
611
- public function terms( $tax = '', $merge = true, $TermClass = '' ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612
  $taxonomies = array();
613
- $TermClass = $TermClass ?: $this->TermClass;
614
 
 
615
  if ( is_string($merge) && class_exists($merge) ) {
616
- $TermClass = $merge;
617
  }
618
- if ( is_array($tax) ) {
 
 
619
  $taxonomies = $tax;
620
- }
621
- if ( is_string($tax) ) {
622
- if ( in_array($tax, array('all', 'any', '')) ) {
623
  $taxonomies = get_object_taxonomies($this->post_type);
624
  } else {
625
  $taxonomies = array($tax);
626
  }
627
  }
628
 
629
- $term_class_objects = array();
630
-
631
- foreach ( $taxonomies as $taxonomy ) {
632
- if ( in_array($taxonomy, array('tag', 'tags')) ) {
633
  $taxonomy = 'post_tag';
634
- }
635
- if ( $taxonomy == 'categories' ) {
636
  $taxonomy = 'category';
637
  }
638
 
639
- $terms = wp_get_post_terms($this->ID, $taxonomy);
 
640
 
641
- if ( is_wp_error($terms) ) {
642
- /* @var $terms WP_Error */
643
- Helper::error_log("Error retrieving terms for taxonomy '$taxonomy' on a post in timber-post.php");
644
- Helper::error_log('tax = '.print_r($tax, true));
645
- Helper::error_log('WP_Error: '.$terms->get_error_message());
646
 
647
- return $term_class_objects;
648
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649
 
650
- // map over array of wordpress terms, and transform them into instances of the TermClass
651
- $terms = array_map(function( $term ) use ($TermClass, $taxonomy) {
652
- return call_user_func(array($TermClass, 'from'), $term->term_id, $taxonomy);
653
- }, $terms);
654
 
655
- if ( $merge && is_array($terms) ) {
656
- $term_class_objects = array_merge($term_class_objects, $terms);
657
- } else if ( count($terms) ) {
658
- $term_class_objects[$taxonomy] = $terms;
659
  }
 
 
660
  }
661
- return $term_class_objects;
 
662
  }
663
 
664
  /**
@@ -744,6 +820,9 @@ class Post extends Core implements CoreInterface {
744
  * @return mixed
745
  */
746
  public function get_field( $field_name ) {
 
 
 
747
  $value = apply_filters('timber_post_get_meta_field_pre', null, $this->ID, $field_name, $this);
748
  if ( $value === null ) {
749
  $value = get_post_meta($this->ID, $field_name);
@@ -789,6 +868,9 @@ class Post extends Core implements CoreInterface {
789
  $old_global_post = $post;
790
  $post = $this;
791
  $class_array = get_post_class($class, $this->ID);
 
 
 
792
  $post = $old_global_post;
793
  if ( is_array($class_array) ) {
794
  return implode(' ', $class_array);
@@ -1004,6 +1086,16 @@ class Post extends Core implements CoreInterface {
1004
  }
1005
  }
1006
 
 
 
 
 
 
 
 
 
 
 
1007
  /**
1008
  * Gets the actual content of a WP Post, as opposed to post_content this will run the hooks/filters attached to the_content. \This guy will return your posts content with WordPress filters run on it (like for shortcodes and wpautop).
1009
  * @api
@@ -1018,6 +1110,9 @@ class Post extends Core implements CoreInterface {
1018
  * @return string
1019
  */
1020
  public function content( $page = 0, $len = -1 ) {
 
 
 
1021
  if ( $form = $this->maybe_show_password_form() ) {
1022
  return $form;
1023
  }
@@ -1387,6 +1482,9 @@ class Post extends Core implements CoreInterface {
1387
  * @return string
1388
  */
1389
  public function title() {
 
 
 
1390
  return apply_filters('the_title', $this->post_title, $this->ID);
1391
  }
1392
 
191
  if ( 'class' === $field ) {
192
  return $this->css_class();
193
  }
 
194
  return parent::__get($field);
195
  }
196
 
212
  * Determined whether or not an admin/editor is looking at the post in "preview mode" via the
213
  * WordPress admin
214
  * @internal
215
+ * @return bool
216
  */
217
  protected static function is_previewing() {
218
  global $wp_query;
236
  && is_object($wp_query->queried_object)
237
  && get_class($wp_query->queried_object) == 'WP_Post'
238
  ) {
239
+ $pid = $wp_query->queried_object_id;
 
 
 
 
 
240
  } else if ( $pid === null && $wp_query->is_home && isset($wp_query->queried_object_id) && $wp_query->queried_object_id ) {
241
  //hack for static page as home page
242
  $pid = $wp_query->queried_object_id;
259
  if ( $pid === null && ($pid_from_loop = PostGetter::loop_to_id()) ) {
260
  $pid = $pid_from_loop;
261
  }
 
 
 
 
 
 
 
 
 
262
  return $pid;
263
  }
264
 
270
  return $this->title();
271
  }
272
 
273
+ protected function get_post_preview_object() {
274
+ global $wp_query;
275
+ if ( $this->is_previewing() ) {
276
+ $revision_id = $this->get_post_preview_id( $wp_query );
277
+ return new $this->PostClass( $revision_id );
278
+ }
279
+ }
280
+
281
  protected function get_post_preview_id( $query ) {
282
  $can = array(
283
  get_post_type_object($query->queried_object->post_type)->cap->edit_post,
554
  $post->id = $post->ID;
555
  $post->slug = $post->post_name;
556
  $customs = $this->get_post_custom($post->ID);
557
+ if ( $this->is_previewing() ) {
558
+ global $wp_query;
559
+ $rev_id = $this->get_post_preview_id($wp_query);
560
+ $customs = $this->get_post_custom($rev_id);
561
+ }
562
  $post->custom = $customs;
563
  $post = (object) array_merge((array) $customs, (array) $post);
564
  return $post;
574
  return Helper::get_comment_form($this->ID, $args);
575
  }
576
 
 
577
  /**
578
+ * Gets the terms associated with the post.
579
+ *
580
  * @api
581
+ * @todo Remove deprecated parameters in 2.x
582
  * @example
583
  * ```twig
584
  * <section id="job-feed">
585
  * {% for post in job %}
586
+ * <div class="job">
587
+ * <h2>{{ post.title }}</h2>
588
+ * <p>{{ post.terms('category')|join(', ') }}</p>
589
+ * </div>
590
  * {% endfor %}
591
  * </section>
592
  * ```
593
  * ```html
594
  * <section id="job-feed">
595
+ * <div class="job">
596
+ * <h2>Cheese Maker</h2>
597
+ * <p>Food, Cheese, Fromage</p>
598
+ * </div>
599
+ * <div class="job">
600
+ * <h2>Mime</h2>
601
+ * <p>Performance, Silence</p>
602
+ * </div>
603
  * </section>
604
  * ```
605
+ * ```php
606
+ * // Get all terms of a taxonomy.
607
+ * $terms = $post->terms( 'category' );
608
+ *
609
+ * // Get terms of multiple taxonomies.
610
+ * $terms = $post->terms( array( 'books', 'movies' ) );
611
+ *
612
+ * // Use custom arguments for taxonomy query and options.
613
+ * $terms = $post->terms( array(
614
+ * 'query' => [
615
+ * 'taxonomy' => 'custom_tax',
616
+ * 'orderby' => 'count',
617
+ * ],
618
+ * 'merge' => false,
619
+ * 'term_class' => 'My_Term_Class'
620
+ * ) );
621
+ * ```
622
+ *
623
+ * @param string|array $args {
624
+ * Optional. Name of the taxonomy or array of arguments.
625
+ *
626
+ * @type array $query Any array of term query parameters for getting the terms. See
627
+ * `WP_Term_Query::__construct()` for supported arguments. Use the
628
+ * `taxonomy` argument to choose which taxonomies to get. Defaults
629
+ * to querying all registered taxonomies for the post type. You can
630
+ * use custom or built-in WordPress taxonomies (category, tag).
631
+ * Timber plays nice and figures out that `tag`, `tags` or
632
+ * `post_tag` are all the same (also for `categories` or
633
+ * `category`). For custom taxonomies you need to define the
634
+ * proper name.
635
+ * @type bool $merge Whether the resulting array should be one big one (`true`) or
636
+ * whether it should be an array of sub-arrays for each taxonomy
637
+ * (`false`). Default `true`.
638
+ * @type string $term_class The Timber term class to use for the term objects.
639
+ * }
640
+ * @param bool $merge Deprecated. Optional. See `$merge` argument in `$args` parameter.
641
+ * @param string $term_class Deprecated. Optional. See `$term_class` argument in `$args`
642
+ * parameter.
643
+ * @return array An array of taxonomies.
644
+ */
645
+ public function terms( $args = array(), $merge = true, $term_class = '' ) {
646
+ // Ensure backwards compatibility.
647
+ if ( ! is_array( $args ) || isset( $args[0] ) ) {
648
+ $args = array(
649
+ 'query' => array(
650
+ 'taxonomy' => $args,
651
+ ),
652
+ 'merge' => $merge,
653
+ 'term_class' => $term_class,
654
+ );
655
+
656
+ if ( empty( $args['term_class']) ) {
657
+ $args['term_class'] = $this->TermClass;
658
+ }
659
+ }
660
+
661
+ // Defaults.
662
+ $args = wp_parse_args( $args, array(
663
+ 'query' => array(
664
+ 'taxonomy' => 'all',
665
+ ),
666
+ 'merge' => true,
667
+ 'term_class' => $this->TermClass,
668
+ ) );
669
+
670
+ $tax = $args['query']['taxonomy'];
671
+ $merge = $args['merge'];
672
+ $term_class = $args['term_class'];
673
+
674
  $taxonomies = array();
 
675
 
676
+ // @todo: Remove in 2.x
677
  if ( is_string($merge) && class_exists($merge) ) {
678
+ $term_class = $merge;
679
  }
680
+
681
+ // Build an array of taxonomies.
682
+ if ( is_array( $tax ) ) {
683
  $taxonomies = $tax;
684
+ } elseif ( is_string( $tax ) ) {
685
+ if ( in_array( $tax, array( 'all', 'any', '' ) ) ) {
 
686
  $taxonomies = get_object_taxonomies($this->post_type);
687
  } else {
688
  $taxonomies = array($tax);
689
  }
690
  }
691
 
692
+ // @todo Remove in 2.x
693
+ $taxonomies = array_map( function( $taxonomy ) {
694
+ if ( in_array( $taxonomy, array( 'tag', 'tags' ), true ) ) {
 
695
  $taxonomy = 'post_tag';
696
+ } elseif ( 'categories' === $taxonomy ) {
 
697
  $taxonomy = 'category';
698
  }
699
 
700
+ return $taxonomy;
701
+ }, $taxonomies );
702
 
703
+ $terms = wp_get_post_terms( $this->ID, $taxonomies, $args['query'] );
 
 
 
 
704
 
705
+ if ( is_wp_error( $terms ) ) {
706
+ /**
707
+ * @var $terms \WP_Error
708
+ */
709
+ Helper::error_log( "Error retrieving terms for taxonomies on a post in timber-post.php" );
710
+ Helper::error_log( 'tax = ' . print_r( $tax, true ) );
711
+ Helper::error_log( 'WP_Error: ' . $terms->get_error_message() );
712
+
713
+ return $terms;
714
+ }
715
+
716
+ // Map over array of WordPress terms and transform them into instances of the chosen term class.
717
+ $terms = array_map( function( $term ) use ( $term_class ) {
718
+ return call_user_func( array( $term_class, 'from' ), $term->term_id, $term->taxonomy );
719
+ }, $terms );
720
+
721
+ if ( ! $merge ) {
722
+ $terms_sorted = array();
723
 
724
+ // Initialize sub-arrays.
725
+ foreach ( $taxonomies as $taxonomy ) {
726
+ $terms_sorted[ $taxonomy ] = array();
727
+ }
728
 
729
+ // Fill terms into arrays.
730
+ foreach ( $terms as $term ) {
731
+ $terms_sorted[ $term->taxonomy ][] = $term;
 
732
  }
733
+
734
+ return $terms_sorted;
735
  }
736
+
737
+ return $terms;
738
  }
739
 
740
  /**
820
  * @return mixed
821
  */
822
  public function get_field( $field_name ) {
823
+ if ( $rd = $this->get_revised_data_from_method('get_field', $field_name) ) {
824
+ return $rd;
825
+ }
826
  $value = apply_filters('timber_post_get_meta_field_pre', null, $this->ID, $field_name, $this);
827
  if ( $value === null ) {
828
  $value = get_post_meta($this->ID, $field_name);
868
  $old_global_post = $post;
869
  $post = $this;
870
  $class_array = get_post_class($class, $this->ID);
871
+ if ( $this->is_previewing() ) {
872
+ $class_array = get_post_class($class, $this->post_parent);
873
+ }
874
  $post = $old_global_post;
875
  if ( is_array($class_array) ) {
876
  return implode(' ', $class_array);
1086
  }
1087
  }
1088
 
1089
+ /**
1090
+ *
1091
+ */
1092
+ protected function get_revised_data_from_method( $method, ...$args ) {
1093
+ $rev = $this->get_post_preview_object();
1094
+ if ( $rev && $this->ID == $rev->post_parent && $this->ID != $rev->ID ) {
1095
+ return call_user_func_array( array($rev, $method), $args );
1096
+ }
1097
+ }
1098
+
1099
  /**
1100
  * Gets the actual content of a WP Post, as opposed to post_content this will run the hooks/filters attached to the_content. \This guy will return your posts content with WordPress filters run on it (like for shortcodes and wpautop).
1101
  * @api
1110
  * @return string
1111
  */
1112
  public function content( $page = 0, $len = -1 ) {
1113
+ if ( $rd = $this->get_revised_data_from_method('content', $page, $len) ) {
1114
+ return $rd;
1115
+ }
1116
  if ( $form = $this->maybe_show_password_form() ) {
1117
  return $form;
1118
  }
1482
  * @return string
1483
  */
1484
  public function title() {
1485
+ if ( $rd = $this->get_revised_data_from_method('title') ) {
1486
+ return $rd;
1487
+ }
1488
  return apply_filters('the_title', $this->post_title, $this->ID);
1489
  }
1490
 
lib/Timber.php CHANGED
@@ -35,7 +35,7 @@ use Timber\Loader;
35
  */
36
  class Timber {
37
 
38
- public static $version = '1.8.1';
39
  public static $locations;
40
  public static $dirname = 'views';
41
  public static $twig_cache = false;
35
  */
36
  class Timber {
37
 
38
+ public static $version = '1.8.2';
39
  public static $locations;
40
  public static $dirname = 'views';
41
  public static $twig_cache = false;
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: jarednova, connorjburton, lggorman
3
  Tags: template engine, templates, twig
4
  Requires at least: 4.7.9
5
  Tested up to: 4.9.6
6
- Stable tag: 1.8.1
7
  PHP version: 5.3.0 or greater
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -34,7 +34,19 @@ _Twig is the template language powering Timber; if you need a little background
34
  - Please add bullet points here with your PR. The heading for this section will get the correct version number once released.
35
 
36
  **Changes for Theme Developers**
37
- - Please add any usage changes here so theme developers are informed of changes.
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  = 1.8.1 =
40
  **Fixes and improvements**
3
  Tags: template engine, templates, twig
4
  Requires at least: 4.7.9
5
  Tested up to: 4.9.6
6
+ Stable tag: 1.8.2
7
  PHP version: 5.3.0 or greater
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
34
  - Please add bullet points here with your PR. The heading for this section will get the correct version number once released.
35
 
36
  **Changes for Theme Developers**
37
+ - Please add bullet points here with your PR. The heading for this section will get the correct version number once released.
38
+
39
+
40
+ = 1.8.2 =
41
+
42
+ **Changes for Theme Developers**
43
+ - You can now change the query parameters that are used when getting a post’s terms through `$post->terms()`. #1802
44
+ - New attributes for responsive images `post.thumbnail.srcset` and `post.thumbnail.sizes` #1819 (thanks @maxxwv)
45
+
46
+ **Fixes and improvements**
47
+ - Using WordPress's `wp_check_filetype_and_ext` for the mime_type mess #1843 (thanks @gchtr)
48
+ - Fixed how some previewed data (when looking at an unsaved post from the admin) is handled so that parenting relationships match what happens when published #1752
49
+ - Timber\Menu now respects modifications sent through WP's `wp_nav_menu_objects` filter #1814 (thanks @pascalknecht)
50
 
51
  = 1.8.1 =
52
  **Fixes and improvements**
timber-starter-theme/index.php CHANGED
@@ -18,6 +18,6 @@ $context['posts'] = new Timber\PostQuery();
18
  $context['foo'] = 'bar';
19
  $templates = array( 'index.twig' );
20
  if ( is_home() ) {
21
- array_unshift( $templates, 'home.twig' );
22
  }
23
  Timber::render( $templates, $context );
18
  $context['foo'] = 'bar';
19
  $templates = array( 'index.twig' );
20
  if ( is_home() ) {
21
+ array_unshift( $templates, 'front-page.twig', 'home.twig' );
22
  }
23
  Timber::render( $templates, $context );
timber.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Timber
4
  Description: The WordPress Timber Library allows you to write themes using the power of Twig templates.
5
  Plugin URI: http://timber.upstatement.com
6
  Author: Jared Novack + Upstatement
7
- Version: 1.8.1
8
  Author URI: http://upstatement.com/
9
  */
10
  // we look for Composer files first in the plugins dir.
4
  Description: The WordPress Timber Library allows you to write themes using the power of Twig templates.
5
  Plugin URI: http://timber.upstatement.com
6
  Author: Jared Novack + Upstatement
7
+ Version: 1.8.2
8
  Author URI: http://upstatement.com/
9
  */
10
  // we look for Composer files first in the plugins dir.
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit6fa8f4c34559c8e21a30db28ddb43132::getLoader();
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInitdfecad9ee9bb796afe4d3895d7c737f7::getLoader();
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit6fa8f4c34559c8e21a30db28ddb43132
6
  {
7
  private static $loader;
8
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit6fa8f4c34559c8e21a30db28ddb43132
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit6fa8f4c34559c8e21a30db28ddb43132', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit6fa8f4c34559c8e21a30db28ddb43132', 'loadClassLoader'));
25
 
26
  $map = require __DIR__ . '/autoload_namespaces.php';
27
  foreach ($map as $namespace => $path) {
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInitdfecad9ee9bb796afe4d3895d7c737f7
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInitdfecad9ee9bb796afe4d3895d7c737f7', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInitdfecad9ee9bb796afe4d3895d7c737f7', 'loadClassLoader'));
25
 
26
  $map = require __DIR__ . '/autoload_namespaces.php';
27
  foreach ($map as $namespace => $path) {