Timber - Version 1.1.4

Version Description

  • Native support for Co-Authors Plus! just use {{ post.authors }} 939331e282fd54bf3e210645964504304f2b071b
  • New filter to enable PW propmpt for PW protected posts (timber/post/content/show_password_form_for_protected) 0f9b20ec90b34059634c25bc27671875c18f8fcb
  • New filter for custom loaders (timber/loader/custom) (thanks @tnottu!) 9097984a7c3df23068056d7835465e0690338567
  • Fixed some updating bugs with 4.6 (thanks @daronspence) 16b8bd71571be71b298e6306abe2cd4b95d8c9e8
  • You can now count Query results (thanks Evan Mattson) 141624a0ac18d9dcce62a2a681134009a2b79814
Download this release

Release Info

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

Code changes from version 1.1.1 to 1.1.4

README.md CHANGED
@@ -1,11 +1,11 @@
1
  <div style="text-align:center">
2
- <a href="http://timber.github.io/timber"><img src="http://i.imgur.com/oM1AHrz.jpg" style="display:block; margin:auto; width:100%; max-width:100%"/></a>
3
  <div>
4
  By Jared Novack (<a href="https://twitter.com/jarednova">@JaredNova</a>) and <a href="http://upstatement.com">Upstatement</a> (<a href="https://twitter.com/upstatement">@Upstatement</a>)</div>
5
  </div>
6
 
7
  [![Build Status](https://img.shields.io/travis/timber/timber/master.svg?style=flat-square)](https://travis-ci.org/timber/timber)
8
- [![Coverage Status](https://img.shields.io/codecov/c/github/timber/timber.svg?style=flat-square)](hhttps://codecov.io/gh/timber/timber)
9
  [![Dependency Status](https://www.versioneye.com/user/projects/574e40e6e298f30048059b9f/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/574e40e6e298f30048059b9f)
10
  [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/timber/timber.svg?style=flat-square)](https://scrutinizer-ci.com/g/timber/timber/?branch=master)
11
  [![Latest Stable Version](https://img.shields.io/packagist/v/timber/timber.svg?style=flat-square)](https://packagist.org/packages/timber/timber)
@@ -111,11 +111,12 @@ Documentation for Timber classes and functions is [auto generated](https://githu
111
  To publish docs:
112
  1. `composer install` if not already run
113
  2. Clone the [timber/slate](https://github.com/timber/slate) repo at the same directory level as Timber
114
- 3. From the root of the slate directory
115
- - `gem install bundler`
116
- - `bundle install` (you'll need at least Ruby 2.0 or newer)
117
- - `sh publish-docs.sh`
118
-
 
119
 
120
 
121
 
1
  <div style="text-align:center">
2
+ <a href="http://timber.github.io/timber"><img src="http://i.imgur.com/PbEwvZ9.png" style="display:block; margin:auto; width:100%; max-width:100%"/></a>
3
  <div>
4
  By Jared Novack (<a href="https://twitter.com/jarednova">@JaredNova</a>) and <a href="http://upstatement.com">Upstatement</a> (<a href="https://twitter.com/upstatement">@Upstatement</a>)</div>
5
  </div>
6
 
7
  [![Build Status](https://img.shields.io/travis/timber/timber/master.svg?style=flat-square)](https://travis-ci.org/timber/timber)
8
+ [![Coverage Status](https://img.shields.io/codecov/c/github/timber/timber.svg?style=flat-square)](https://codecov.io/gh/timber/timber)
9
  [![Dependency Status](https://www.versioneye.com/user/projects/574e40e6e298f30048059b9f/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/574e40e6e298f30048059b9f)
10
  [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/timber/timber.svg?style=flat-square)](https://scrutinizer-ci.com/g/timber/timber/?branch=master)
11
  [![Latest Stable Version](https://img.shields.io/packagist/v/timber/timber.svg?style=flat-square)](https://packagist.org/packages/timber/timber)
111
  To publish docs:
112
  1. `composer install` if not already run
113
  2. Clone the [timber/slate](https://github.com/timber/slate) repo at the same directory level as Timber
114
+ 3. From the root of the slate directory, run these commands:
115
+ ```bash
116
+ gem install bundler
117
+ bundle install
118
+ sh publish-docs.sh
119
+ ```
120
 
121
 
122
 
lib/Admin.php CHANGED
@@ -48,18 +48,18 @@ class Admin {
48
 
49
  if ( version_compare("1.0.0", $plugin_data['new_version']) <= 0 ) {
50
  //a version of 1.0.0 or greater is availalbe
51
- $m .= '<p><b>Warning:</b> Timber 1.0 removed a number of features and methods. Before upgrading please test your theme on a local or staging site to ensure that your theme will work with the newest version.</p>
52
 
53
- <p><strong>Is your theme in active development?</strong> That is, is someone actively in PHP files writing new code? If you answered "no", then <i>do not upgrade</i>. You will not benefit from Timber 1.0</p>';
54
 
55
- $m .= '<p>Read the <strong><a href="https://github.com/timber/timber/wiki/1.0-Upgrade-Guide">Upgrade Guide</a></strong> for more information</p>';
56
 
57
- $m .= "<p>You can also <b><a href='https://downloads.wordpress.org/plugin/timber-library.0.22.6.zip'>upgrade to version 0.22.6</a></b> if you want to upgrade, but are unsure if you're ready for 1.0";
58
 
59
  }
60
 
61
  if ( version_compare("1.0.0", $plugin_data['Version']) <= 0 ) {
62
- $m .= "<p>Are you seeing errors since upgrading to 1.0? Download <b><a href='https://downloads.wordpress.org/plugin/timber-library.0.22.6.zip'>Version 0.22.6</a></b> to bring things back to stability.";
63
  }
64
 
65
  // show message
48
 
49
  if ( version_compare("1.0.0", $plugin_data['new_version']) <= 0 ) {
50
  //a version of 1.0.0 or greater is availalbe
51
+ $m .= '<br><b>Warning:</b> Timber 1.0 removed a number of features and methods. Before upgrading please test your theme on a local or staging site to ensure that your theme will work with the newest version.<br>
52
 
53
+ <br><strong>Is your theme in active development?</strong> That is, is someone actively in PHP files writing new code? If you answered "no", then <i>do not upgrade</i>. You will not benefit from Timber 1.0<br>';
54
 
55
+ $m .= '<br>Read the <strong><a href="https://github.com/timber/timber/wiki/1.0-Upgrade-Guide">Upgrade Guide</a></strong> for more information<br>';
56
 
57
+ $m .= "<br>You can also <b><a href='https://downloads.wordpress.org/plugin/timber-library.0.22.6.zip'>upgrade to version 0.22.6</a></b> if you want to upgrade, but are unsure if you're ready for 1.0<br>";
58
 
59
  }
60
 
61
  if ( version_compare("1.0.0", $plugin_data['Version']) <= 0 ) {
62
+ $m .= "<br>Are you seeing errors since upgrading to 1.0? Download <b><a href='https://downloads.wordpress.org/plugin/timber-library.0.22.6.zip'>Version 0.22.6</a></b> to bring things back to stability.";
63
  }
64
 
65
  // show message
lib/Core.php CHANGED
@@ -117,4 +117,12 @@ abstract class Core {
117
  $ret['can_edit'] = $this->can_edit();
118
  return $ret;
119
  }
 
 
 
 
 
 
 
 
120
  }
117
  $ret['can_edit'] = $this->can_edit();
118
  return $ret;
119
  }
120
+
121
+ /**
122
+ * @param string $field_name
123
+ * @return mixed
124
+ */
125
+ public function get_field( $field_name ) {
126
+ return $this->get_meta_field($field_name);
127
+ }
128
  }
lib/Helper.php CHANGED
@@ -444,6 +444,25 @@ class Helper {
444
  return ($i % 2) != 0;
445
  }
446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  /* Links, Forms, Etc. Utilities
448
  ======================== */
449
 
@@ -461,81 +480,13 @@ class Helper {
461
 
462
  /**
463
  *
464
- *
465
- * @param string $args
466
  * @return array
467
  */
468
- public static function paginate_links( $args = '' ) {
469
- $defaults = array(
470
- 'base' => '%_%', // http://example.com/all_posts.php%_% : %_% is replaced by format (below)
471
- 'format' => '?page=%#%', // ?page=%#% : %#% is replaced by the page number
472
- 'total' => 1,
473
- 'current' => 0,
474
- 'show_all' => false,
475
- 'prev_next' => false,
476
- 'prev_text' => __('&laquo; Previous'),
477
- 'next_text' => __('Next &raquo;'),
478
- 'end_size' => 1,
479
- 'mid_size' => 2,
480
- 'type' => 'array',
481
- 'add_args' => false, // array of query args to add
482
- 'add_fragment' => ''
483
- );
484
- $args = wp_parse_args($args, $defaults);
485
- // Who knows what else people pass in $args
486
- $args['total'] = intval((int) $args['total']);
487
- if ( $args['total'] < 2 ) {
488
- return array();
489
- }
490
- $args['current'] = (int) $args['current'];
491
- $args['end_size'] = 0 < (int) $args['end_size'] ? (int) $args['end_size'] : 1; // Out of bounds? Make it the default.
492
- $args['mid_size'] = 0 <= (int) $args['mid_size'] ? (int) $args['mid_size'] : 2;
493
- $args['add_args'] = is_array($args['add_args']) ? $args['add_args'] : false;
494
- $page_links = array();
495
- $dots = false;
496
- for ( $n = 1; $n <= $args['total']; $n++ ) {
497
- $n_display = number_format_i18n($n);
498
- if ( $n == $args['current'] ) {
499
- $page_links[] = array(
500
- 'class' => 'page-number page-numbers current',
501
- 'title' => $n_display,
502
- 'text' => $n_display,
503
- 'name' => $n_display,
504
- 'current' => true
505
- );
506
- $dots = true;
507
- } else {
508
- if ( $args['show_all'] || ($n <= $args['end_size'] || ($args['current'] && $n >= $args['current'] - $args['mid_size'] && $n <= $args['current'] + $args['mid_size']) || $n > $args['total'] - $args['end_size']) ) {
509
- $link = str_replace('%_%', 1 == $n ? '' : $args['format'], $args['base']);
510
- $link = str_replace('%#%', $n, $link);
511
- $link = trailingslashit($link).ltrim($args['add_fragment'], '/');
512
- if ( $args['add_args'] ) {
513
- $link = rtrim(add_query_arg($args['add_args'], $link), '/');
514
- }
515
- $link = str_replace(' ', '+', $link);
516
- $link = untrailingslashit($link);
517
- $link = esc_url(apply_filters('paginate_links', $link));
518
- $link = user_trailingslashit($link);
519
-
520
- $page_links[] = array(
521
- 'class' => 'page-number page-numbers',
522
- 'link' => $link,
523
- 'title' => $n_display,
524
- 'name' => $n_display,
525
- 'current' => $args['current'] == $n
526
- );
527
- $dots = true;
528
- } elseif ( $dots && !$args['show_all'] ) {
529
- $page_links[] = array(
530
- 'class' => 'dots',
531
- 'title' => __('&hellip;')
532
- );
533
- $dots = false;
534
- }
535
- }
536
- }
537
-
538
- return $page_links;
539
  }
540
 
541
  /**
444
  return ($i % 2) != 0;
445
  }
446
 
447
+ /**
448
+ * Plucks the values of a certain key from an array of objects
449
+ * @param array $array
450
+ * @param string $key
451
+ */
452
+ public static function pluck( $array, $key ) {
453
+ $return = array();
454
+ foreach ( $array as $obj ) {
455
+ if ( is_object($obj) && method_exists($obj, $key) ) {
456
+ $return[] = $obj->$key();
457
+ } elseif ( is_object($obj) && property_exists($obj, $key) ) {
458
+ $return[] = $obj->$key;
459
+ } elseif ( isset($obj[$key]) ) {
460
+ $return[] = $obj[$key];
461
+ }
462
+ }
463
+ return $return;
464
+ }
465
+
466
  /* Links, Forms, Etc. Utilities
467
  ======================== */
468
 
480
 
481
  /**
482
  *
483
+ * @deprecated since 1.1.2
484
+ * @param array $args
485
  * @return array
486
  */
487
+ public static function paginate_links( $args = array() ) {
488
+ Helper::warn('Helper/paginate_links has been moved to Pagination/paginate_links');
489
+ return Pagination::paginate_links($args);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  }
491
 
492
  /**
lib/Image/Operation/Resize.php CHANGED
@@ -170,20 +170,20 @@ class Resize extends ImageOperation {
170
  * @return boolean|null true if everything went fine, false otherwise
171
  */
172
  public function run( $load_filename, $save_filename ) {
173
- $image = wp_get_image_editor( $load_filename );
174
- if ( !is_wp_error( $image ) ) {
175
  //should be resized by gif resizer
176
- if ( ImageHelper::is_animated_gif( $load_filename ) ) {
177
  //attempt to resize
178
  //return if successful
179
  //proceed if not
180
- $gif = self::run_animated_gif( $load_filename, $save_filename, $image );
181
  if ( $gif ) {
182
  return true;
183
  }
184
  }
185
 
186
- $crop = self::get_target_sizes( $image );
187
  $image->crop( $crop['x'],
188
  $crop['y'],
189
  $crop['src_w'],
@@ -191,24 +191,24 @@ class Resize extends ImageOperation {
191
  $crop['target_w'],
192
  $crop['target_h']
193
  );
194
- $result = $image->save( $save_filename );
195
- if ( is_wp_error( $result ) ) {
196
  // @codeCoverageIgnoreStart
197
- Helper::error_log( 'Error resizing image' );
198
- Helper::error_log( $result );
199
  return false;
200
  // @codeCoverageIgnoreEnd
201
  } else {
202
  return true;
203
  }
204
- } else if ( isset( $image->error_data['error_loading_image'] ) ) {
205
  // @codeCoverageIgnoreStart
206
- Helper::error_log( 'Error loading ' . $image->error_data['error_loading_image'] );
207
  } else {
208
- if(!extension_loaded('gd')) {
209
- Helper::error_log( 'Can not resize image, please installed php-gd' );
210
  } else {
211
- Helper::error_log( $image );
212
  }
213
  // @codeCoverageIgnoreEnd
214
  }
170
  * @return boolean|null true if everything went fine, false otherwise
171
  */
172
  public function run( $load_filename, $save_filename ) {
173
+ $image = wp_get_image_editor($load_filename);
174
+ if ( !is_wp_error($image) ) {
175
  //should be resized by gif resizer
176
+ if ( ImageHelper::is_animated_gif($load_filename) ) {
177
  //attempt to resize
178
  //return if successful
179
  //proceed if not
180
+ $gif = self::run_animated_gif($load_filename, $save_filename, $image);
181
  if ( $gif ) {
182
  return true;
183
  }
184
  }
185
 
186
+ $crop = self::get_target_sizes($image);
187
  $image->crop( $crop['x'],
188
  $crop['y'],
189
  $crop['src_w'],
191
  $crop['target_w'],
192
  $crop['target_h']
193
  );
194
+ $result = $image->save($save_filename);
195
+ if ( is_wp_error($result) ) {
196
  // @codeCoverageIgnoreStart
197
+ Helper::error_log('Error resizing image');
198
+ Helper::error_log($result);
199
  return false;
200
  // @codeCoverageIgnoreEnd
201
  } else {
202
  return true;
203
  }
204
+ } else if ( isset($image->error_data['error_loading_image']) ) {
205
  // @codeCoverageIgnoreStart
206
+ Helper::error_log('Error loading '.$image->error_data['error_loading_image']);
207
  } else {
208
+ if ( !extension_loaded('gd') ) {
209
+ Helper::error_log('Can not resize image, please installed php-gd');
210
  } else {
211
+ Helper::error_log($image);
212
  }
213
  // @codeCoverageIgnoreEnd
214
  }
lib/Integrations.php CHANGED
@@ -2,25 +2,33 @@
2
 
3
  namespace Timber;
4
 
5
- use Timber\Integrations\ACF;
6
-
7
  /**
8
  * This is for integrating external plugins into timber
9
  * @package timber
10
  */
11
  class Integrations {
 
 
 
 
 
 
 
12
 
13
- public static function init() {
14
- add_action( 'init', array( __CLASS__, 'maybe_init_acftimber' ) );
15
 
16
- if ( class_exists( 'WP_CLI_Command' ) ) {
17
- \WP_CLI::add_command( 'timber', 'Timber\Integrations\Timber_WP_CLI_Command' );
18
  }
19
  }
20
 
21
- public static function maybe_init_acftimber() {
22
- if ( class_exists( 'ACF' ) ) {
23
- new ACF();
 
 
 
24
  }
25
  }
26
  }
2
 
3
  namespace Timber;
4
 
 
 
5
  /**
6
  * This is for integrating external plugins into timber
7
  * @package timber
8
  */
9
  class Integrations {
10
+
11
+ var $acf;
12
+ var $coauthors_plus;
13
+
14
+ public function __construct() {
15
+ $this->init();
16
+ }
17
 
18
+ public function init() {
19
+ add_action('init', array($this, 'maybe_init_integrations'));
20
 
21
+ if ( class_exists('WP_CLI_Command') ) {
22
+ \WP_CLI::add_command('timber', 'Timber\Integrations\Timber_WP_CLI_Command');
23
  }
24
  }
25
 
26
+ public function maybe_init_integrations() {
27
+ if ( class_exists('ACF') ) {
28
+ $this->acf = new Integrations\ACF();
29
+ }
30
+ if ( class_exists('CoAuthors_Plus') ) {
31
+ $this->coauthors_plus = new Integrations\CoAuthorsPlus();
32
  }
33
  }
34
  }
lib/Integrations/CoAuthorsPlus.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber\Integrations;
4
+
5
+ class CoAuthorsPlus {
6
+
7
+ /**
8
+ * @codeCoverageIgnore
9
+ */
10
+ public function __construct() {
11
+ add_filter('timber/post/authors', array($this, 'authors'), 10, 2);
12
+ }
13
+
14
+ /**
15
+ * Filters {{ post.authors }} to return authors stored from Co-Authors Plus
16
+ * @since 1.1.4
17
+ * @param array $author
18
+ * @param Post $post
19
+ * @return array of User objects
20
+ */
21
+ public function authors( $author, $post ) {
22
+ $authors = array();
23
+ $cauthors = get_coauthors($post->ID);
24
+ foreach ( $cauthors as $author ) {
25
+ $authors[] = new \Timber\User($author);
26
+ }
27
+ return $authors;
28
+ }
29
+
30
+ }
lib/Loader.php CHANGED
@@ -29,16 +29,16 @@ class Loader {
29
  * @param bool|string $caller the calling directory or false
30
  */
31
  public function __construct( $caller = false ) {
32
- $this->locations = $this->get_locations($caller);
33
  $this->cache_mode = apply_filters('timber_cache_mode', $this->cache_mode);
34
  $this->cache_mode = apply_filters('timber/cache/mode', $this->cache_mode);
35
  }
36
 
37
  /**
38
- * @param string $file
39
- * @param array $data
40
- * @param array|bool $expires
41
- * @param string $cache_mode
42
  * @return bool|string
43
  */
44
  public function render( $file, $data = null, $expires = false, $cache_mode = self::CACHE_USE_DEFAULT ) {
@@ -68,7 +68,7 @@ class Loader {
68
  do_action('timber_loader_render_file', $result);
69
  }
70
  $data = apply_filters('timber_loader_render_data', $data);
71
- $data = apply_filters( 'timber/loader/render_data', $data, $file );
72
  $output = $twig->render($file, $data);
73
  }
74
 
@@ -76,7 +76,7 @@ class Loader {
76
  $this->set_cache($key, $output, self::CACHEGROUP, $expires, $cache_mode);
77
  }
78
  $output = apply_filters('timber_output', $output);
79
- return apply_filters( 'timber/output', $output, $data, $file );
80
  }
81
 
82
  /**
@@ -110,115 +110,20 @@ class Loader {
110
  return false;
111
  }
112
 
113
- /**
114
- * @return array
115
- */
116
- protected function get_locations_theme() {
117
- $theme_locs = array();
118
- $theme_dirs = $this->get_locations_theme_dir();
119
- $roots = array(get_stylesheet_directory(), get_template_directory());
120
- $roots = array_map('realpath', $roots);
121
- $roots = array_unique($roots);
122
- foreach ( $roots as $root ) {
123
- if ( !is_dir($root) ) {
124
- continue;
125
- }
126
- $theme_locs[] = $root;
127
- $root = trailingslashit($root);
128
- foreach ( $theme_dirs as $dirname ) {
129
- $tloc = realpath($root.$dirname);
130
- if ( is_dir($tloc) ) {
131
- $theme_locs[] = $tloc;
132
- }
133
- }
134
- }
135
-
136
- return $theme_locs;
137
- }
138
-
139
- /**
140
- * returns an array of the directory inside themes that holds twig files
141
- * @return string[] the names of directores, ie: array('templats', 'views');
142
- */
143
- protected function get_locations_theme_dir() {
144
- if ( is_string(Timber::$dirname) ) {
145
- return array(Timber::$dirname);
146
- }
147
- return Timber::$dirname;
148
- }
149
-
150
- /**
151
- *
152
- * @return array
153
- */
154
- protected function get_locations_user() {
155
- $locs = array();
156
- if ( isset(Timber::$locations) ) {
157
- if ( is_string(Timber::$locations) ) {
158
- Timber::$locations = array(Timber::$locations);
159
- }
160
- foreach ( Timber::$locations as $tloc ) {
161
- $tloc = realpath($tloc);
162
- if ( is_dir($tloc) ) {
163
- $locs[] = $tloc;
164
- }
165
- }
166
- }
167
- return $locs;
168
- }
169
 
170
  /**
171
- * @param bool|string $caller the calling directory
172
- * @return array
173
- */
174
- protected function get_locations_caller( $caller = false ) {
175
- $locs = array();
176
- if ( $caller && is_string($caller) ) {
177
- $caller = realpath($caller);
178
- if ( is_dir($caller) ) {
179
- $locs[] = $caller;
180
- }
181
- $caller = trailingslashit($caller);
182
- foreach ( $this->get_locations_theme_dir() as $dirname ) {
183
- $caller_sub = realpath($caller.$dirname);
184
- if ( is_dir($caller_sub) ) {
185
- $locs[] = $caller_sub;
186
- }
187
- }
188
- }
189
- return $locs;
190
- }
191
-
192
- /**
193
- * @param bool|string $caller the calling directory (or false)
194
- * @return array
195
- */
196
- public function get_locations( $caller = false ) {
197
- //prioirty: user locations, caller (but not theme), child theme, parent theme, caller
198
- $locs = array();
199
- $locs = array_merge($locs, $this->get_locations_user());
200
- $locs = array_merge($locs, $this->get_locations_caller($caller));
201
- //remove themes from caller
202
- $locs = array_diff($locs, $this->get_locations_theme());
203
- $locs = array_merge($locs, $this->get_locations_theme());
204
- $locs = array_merge($locs, $this->get_locations_caller($caller));
205
- $locs = array_unique($locs);
206
- //now make sure theres a trailing slash on everything
207
- $locs = array_map('trailingslashit', $locs);
208
- $locs = apply_filters('timber_locations', $locs);
209
- $locs = apply_filters('timber/locations', $locs);
210
- return $locs;
211
- }
212
-
213
- /**
214
- * @return \Twig_Loader_Filesystem
215
  */
216
  public function get_loader() {
217
- $paths = array_merge($this->locations, array(ini_get('open_basedir') ? ABSPATH : '/'));
218
- $paths = apply_filters('timber/loader/paths', $paths);
219
- return new \Twig_Loader_Filesystem($paths);
 
 
 
220
  }
221
 
 
222
  /**
223
  * @return \Twig_Environment
224
  */
@@ -356,7 +261,7 @@ class Loader {
356
  * @param string $key
357
  * @param string|boolean $value
358
  * @param string $group
359
- * @param int $expires
360
  * @param string $cache_mode
361
  * @return string|boolean
362
  */
29
  * @param bool|string $caller the calling directory or false
30
  */
31
  public function __construct( $caller = false ) {
32
+ $this->locations = LocationManager::get_locations($caller);
33
  $this->cache_mode = apply_filters('timber_cache_mode', $this->cache_mode);
34
  $this->cache_mode = apply_filters('timber/cache/mode', $this->cache_mode);
35
  }
36
 
37
  /**
38
+ * @param string $file
39
+ * @param array $data
40
+ * @param array|boolean $expires (array for options, false for none, integer for # of seconds)
41
+ * @param string $cache_mode
42
  * @return bool|string
43
  */
44
  public function render( $file, $data = null, $expires = false, $cache_mode = self::CACHE_USE_DEFAULT ) {
68
  do_action('timber_loader_render_file', $result);
69
  }
70
  $data = apply_filters('timber_loader_render_data', $data);
71
+ $data = apply_filters('timber/loader/render_data', $data, $file);
72
  $output = $twig->render($file, $data);
73
  }
74
 
76
  $this->set_cache($key, $output, self::CACHEGROUP, $expires, $cache_mode);
77
  }
78
  $output = apply_filters('timber_output', $output);
79
+ return apply_filters('timber/output', $output, $data, $file);
80
  }
81
 
82
  /**
110
  return false;
111
  }
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  /**
115
+ * @return \Twig_Loader_Chain
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  */
117
  public function get_loader() {
118
+ $filesystem_paths = array_merge($this->locations, array(ini_get('open_basedir') ? ABSPATH : '/'));
119
+ $filesystem_paths = apply_filters('timber/loader/paths', $filesystem_paths);
120
+ $filesystem_loader = array(new \Twig_Loader_Filesystem($filesystem_paths));
121
+ $custom_loaders = apply_filters('timber/loader/custom', array());
122
+ $all_loaders = array_merge($custom_loaders, $filesystem_loader);
123
+ return new \Twig_Loader_Chain($all_loaders);
124
  }
125
 
126
+
127
  /**
128
  * @return \Twig_Environment
129
  */
261
  * @param string $key
262
  * @param string|boolean $value
263
  * @param string $group
264
+ * @param integer $expires
265
  * @param string $cache_mode
266
  * @return string|boolean
267
  */
lib/LocationManager.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ class LocationManager {
6
+
7
+
8
+ /**
9
+ * @param bool|string $caller the calling directory (or false)
10
+ * @return array
11
+ */
12
+ public static function get_locations( $caller = false ) {
13
+ //prioirty: user locations, caller (but not theme), child theme, parent theme, caller
14
+ $locs = array();
15
+ $locs = array_merge($locs, self::get_locations_user());
16
+ $locs = array_merge($locs, self::get_locations_caller($caller));
17
+ //remove themes from caller
18
+ $locs = array_diff($locs, self::get_locations_theme());
19
+ $locs = array_merge($locs, self::get_locations_theme());
20
+ $locs = array_merge($locs, self::get_locations_caller($caller));
21
+ $locs = array_unique($locs);
22
+ //now make sure theres a trailing slash on everything
23
+ $locs = array_map('trailingslashit', $locs);
24
+ $locs = apply_filters('timber_locations', $locs);
25
+ $locs = apply_filters('timber/locations', $locs);
26
+ return $locs;
27
+ }
28
+
29
+
30
+ /**
31
+ * @return array
32
+ */
33
+ protected static function get_locations_theme() {
34
+ $theme_locs = array();
35
+ $theme_dirs = LocationManager::get_locations_theme_dir();
36
+ $roots = array(get_stylesheet_directory(), get_template_directory());
37
+ $roots = array_map('realpath', $roots);
38
+ $roots = array_unique($roots);
39
+ foreach ( $roots as $root ) {
40
+ if ( !is_dir($root) ) {
41
+ continue;
42
+ }
43
+ $theme_locs[] = $root;
44
+ $root = trailingslashit($root);
45
+ foreach ( $theme_dirs as $dirname ) {
46
+ $tloc = realpath($root.$dirname);
47
+ if ( is_dir($tloc) ) {
48
+ $theme_locs[] = $tloc;
49
+ }
50
+ }
51
+ }
52
+
53
+ return $theme_locs;
54
+ }
55
+
56
+
57
+ /**
58
+ * Get calling script file.
59
+ * @api
60
+ * @param int $offset
61
+ * @return string|null
62
+ */
63
+ public static function get_calling_script_file( $offset = 0 ) {
64
+ $callers = array();
65
+ $backtrace = debug_backtrace();
66
+ foreach ( $backtrace as $trace ) {
67
+ if ( array_key_exists('file', $trace) && $trace['file'] != __FILE__ ) {
68
+ $callers[] = $trace['file'];
69
+ }
70
+ }
71
+ $callers = array_unique($callers);
72
+ $callers = array_values($callers);
73
+ return $callers[$offset];
74
+ }
75
+
76
+ /**
77
+ * Get calling script dir.
78
+ * @api
79
+ * @return string
80
+ */
81
+ public static function get_calling_script_dir( $offset = 0 ) {
82
+ $caller = self::get_calling_script_file($offset);
83
+ if ( !is_null($caller) ) {
84
+ $pathinfo = pathinfo($caller);
85
+ $dir = $pathinfo['dirname'];
86
+ return $dir;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * returns an array of the directory inside themes that holds twig files
92
+ * @return string[] the names of directores, ie: array('templats', 'views');
93
+ */
94
+ public static function get_locations_theme_dir() {
95
+ if ( is_string(Timber::$dirname) ) {
96
+ return array(Timber::$dirname);
97
+ }
98
+ return Timber::$dirname;
99
+ }
100
+
101
+
102
+ /**
103
+ *
104
+ * @return array
105
+ */
106
+ protected static function get_locations_user() {
107
+ $locs = array();
108
+ if ( isset(Timber::$locations) ) {
109
+ if ( is_string(Timber::$locations) ) {
110
+ Timber::$locations = array(Timber::$locations);
111
+ }
112
+ foreach ( Timber::$locations as $tloc ) {
113
+ $tloc = realpath($tloc);
114
+ if ( is_dir($tloc) ) {
115
+ $locs[] = $tloc;
116
+ }
117
+ }
118
+ }
119
+ return $locs;
120
+ }
121
+
122
+ /**
123
+ * @param bool|string $caller the calling directory
124
+ * @return array
125
+ */
126
+ protected static function get_locations_caller( $caller = false ) {
127
+ $locs = array();
128
+ if ( $caller && is_string($caller) ) {
129
+ $caller = realpath($caller);
130
+ if ( is_dir($caller) ) {
131
+ $locs[] = $caller;
132
+ }
133
+ $caller = trailingslashit($caller);
134
+ foreach ( LocationManager::get_locations_theme_dir() as $dirname ) {
135
+ $caller_sub = realpath($caller.$dirname);
136
+ if ( is_dir($caller_sub) ) {
137
+ $locs[] = $caller_sub;
138
+ }
139
+ }
140
+ }
141
+ return $locs;
142
+ }
143
+
144
+
145
+ }
lib/Pagination.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ class Pagination {
6
+
7
+ /**
8
+ * Get pagination.
9
+ * @api
10
+ * @param array $prefs
11
+ * @return array mixed
12
+ */
13
+ public static function get_pagination( $prefs ) {
14
+ global $wp_query;
15
+ global $paged;
16
+ global $wp_rewrite;
17
+ $args = array();
18
+ $args['total'] = ceil($wp_query->found_posts / $wp_query->query_vars['posts_per_page']);
19
+ if ( $wp_rewrite->using_permalinks() ) {
20
+ $url = explode('?', get_pagenum_link(0));
21
+ if ( isset($url[1]) ) {
22
+ parse_str($url[1], $query);
23
+ $args['add_args'] = $query;
24
+ }
25
+ $args['format'] = $wp_rewrite->pagination_base.'/%#%';
26
+ $args['base'] = trailingslashit($url[0]).'%_%';
27
+ } else {
28
+ $big = 999999999;
29
+ $args['base'] = str_replace($big, '%#%', esc_url(get_pagenum_link($big)));
30
+ }
31
+ $args['type'] = 'array';
32
+ $args['current'] = max(1, get_query_var('paged'));
33
+ $args['mid_size'] = max(9 - $args['current'], 3);
34
+ if ( is_int($prefs) ) {
35
+ $args['mid_size'] = $prefs - 2;
36
+ } else {
37
+ $args = array_merge($args, $prefs);
38
+ }
39
+ $data = array();
40
+ $data['current'] = $args['current'];
41
+ $data['total'] = $args['total'];
42
+ $data['pages'] = Pagination::paginate_links($args);
43
+
44
+ if ( $data['total'] <= count($data['pages']) ) {
45
+ // decrement current so that it matches up with the 0 based index used by the pages array
46
+ $current = $data['current'] - 1;
47
+ } else {
48
+ // $data['current'] can't be used b/c there are more than 10 pages and we are condensing with dots
49
+ foreach ( $data['pages'] as $key => $page ) {
50
+ if ( !empty($page['current']) ) {
51
+ $current = $key;
52
+ break;
53
+ }
54
+ }
55
+ }
56
+
57
+ // set next and prev using pages array generated by paginate links
58
+ if ( isset($current) && isset($data['pages'][$current + 1]) ) {
59
+ $data['next'] = array('link' => user_trailingslashit($data['pages'][$current + 1]['link']), 'class' => 'page-numbers next');
60
+ if ( Pagination::is_search_query($data['next']['link']) ) {
61
+ $data['next']['link'] = untrailingslashit($data['next']['link']);
62
+ }
63
+ }
64
+ if ( isset($current) && isset($data['pages'][$current - 1]) ) {
65
+ $data['prev'] = array('link' => user_trailingslashit($data['pages'][$current - 1]['link']), 'class' => 'page-numbers prev');
66
+ if ( Pagination::is_search_query($data['prev']['link']) ) {
67
+ $data['prev']['link'] = untrailingslashit($data['prev']['link']);
68
+ }
69
+ }
70
+ if ( $paged < 2 ) {
71
+ $data['prev'] = '';
72
+ }
73
+ if ( $data['total'] === (double) 0 ) {
74
+ $data['next'] = '';
75
+ }
76
+ return $data;
77
+ }
78
+
79
+ /**
80
+ * Checks to see whether the given URL has a search query in it (s=*)
81
+ * @param string $url
82
+ * @return boolean
83
+ */
84
+ public static function is_search_query( $url ) {
85
+ if ( strpos($url, 's=') !== false ) {
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+
91
+ /**
92
+ *
93
+ *
94
+ * @param array $args
95
+ * @return array
96
+ */
97
+ public static function paginate_links( $args = array() ) {
98
+ $defaults = array(
99
+ 'base' => '%_%', // http://example.com/all_posts.php%_% : %_% is replaced by format (below)
100
+ 'format' => '?page=%#%', // ?page=%#% : %#% is replaced by the page number
101
+ 'total' => 1,
102
+ 'current' => 0,
103
+ 'show_all' => false,
104
+ 'prev_next' => false,
105
+ 'prev_text' => __('&laquo; Previous'),
106
+ 'next_text' => __('Next &raquo;'),
107
+ 'end_size' => 1,
108
+ 'mid_size' => 2,
109
+ 'type' => 'array',
110
+ 'add_args' => false, // array of query args to add
111
+ 'add_fragment' => ''
112
+ );
113
+ $args = wp_parse_args($args, $defaults);
114
+ // Who knows what else people pass in $args
115
+ $args['total'] = intval((int) $args['total']);
116
+ if ( $args['total'] < 2 ) {
117
+ return array();
118
+ }
119
+ $args['current'] = (int) $args['current'];
120
+ $args['end_size'] = 0 < (int) $args['end_size'] ? (int) $args['end_size'] : 1; // Out of bounds? Make it the default.
121
+ $args['mid_size'] = 0 <= (int) $args['mid_size'] ? (int) $args['mid_size'] : 2;
122
+ $args['add_args'] = is_array($args['add_args']) ? $args['add_args'] : false;
123
+ $page_links = array();
124
+ $dots = false;
125
+ for ( $n = 1; $n <= $args['total']; $n++ ) {
126
+ $n_display = number_format_i18n($n);
127
+ if ( $n == $args['current'] ) {
128
+ $page_links[] = array(
129
+ 'class' => 'page-number page-numbers current',
130
+ 'title' => $n_display,
131
+ 'text' => $n_display,
132
+ 'name' => $n_display,
133
+ 'current' => true
134
+ );
135
+ $dots = true;
136
+ } else {
137
+ if ( $args['show_all'] || ($n <= $args['end_size'] || ($args['current'] && $n >= $args['current'] - $args['mid_size'] && $n <= $args['current'] + $args['mid_size']) || $n > $args['total'] - $args['end_size']) ) {
138
+ $link = str_replace('%_%', 1 == $n ? '' : $args['format'], $args['base']);
139
+ $link = str_replace('%#%', $n, $link);
140
+ $link = trailingslashit($link).ltrim($args['add_fragment'], '/');
141
+ if ( $args['add_args'] ) {
142
+ $link = rtrim(add_query_arg($args['add_args'], $link), '/');
143
+ }
144
+ $link = str_replace(' ', '+', $link);
145
+ $link = untrailingslashit($link);
146
+ $link = esc_url(apply_filters('paginate_links', $link));
147
+ $link = user_trailingslashit($link);
148
+ if ( self::is_search_query($link) ) {
149
+ $link = untrailingslashit($link);
150
+ }
151
+ $page_links[] = array(
152
+ 'class' => 'page-number page-numbers',
153
+ 'link' => $link,
154
+ 'title' => $n_display,
155
+ 'name' => $n_display,
156
+ 'current' => $args['current'] == $n
157
+ );
158
+ $dots = true;
159
+ } elseif ( $dots && !$args['show_all'] ) {
160
+ $page_links[] = array(
161
+ 'class' => 'dots',
162
+ 'title' => __('&hellip;')
163
+ );
164
+ $dots = false;
165
+ }
166
+ }
167
+ }
168
+
169
+ return $page_links;
170
+ }
171
+
172
+ }
lib/Post.php CHANGED
@@ -389,9 +389,10 @@ class Post extends Core implements CoreInterface {
389
  */
390
  public function get_preview( $len = 50, $force = false, $readmore = 'Read More', $strip = true, $end = '&hellip;' ) {
391
  $text = '';
 
392
  $trimmed = false;
393
  $last_p_tag = null;
394
- if ( isset($this->post_excerpt) && strlen($this->post_excerpt) ) {
395
  if ( $force ) {
396
  $text = Helper::trim_words($this->post_excerpt, $len, false);
397
  $trimmed = true;
@@ -436,10 +437,11 @@ class Post extends Core implements CoreInterface {
436
  }
437
  $read_more_class = apply_filters('timber/post/get_preview/read_more_class', "read-more");
438
  if ( $readmore && isset($readmore_matches) && !empty($readmore_matches[1]) ) {
439
- $text .= ' <a href="'.$this->link().'" class="'.$read_more_class.'">'.trim($readmore_matches[1]).'</a>';
440
  } elseif ( $readmore ) {
441
- $text .= ' <a href="'.$this->link().'" class="'.$read_more_class.'">'.trim($readmore).'</a>';
442
  }
 
443
  if ( !$strip && $last_p_tag && (strpos($text, '<p>') || strpos($text, '<p ')) ) {
444
  $text .= '</p>';
445
  }
@@ -649,7 +651,7 @@ class Post extends Core implements CoreInterface {
649
  * @return boolean
650
  */
651
  public function has_field( $field_name ) {
652
- return (!$this->get_field( $field_name )) ? false : true;
653
  }
654
 
655
 
@@ -753,7 +755,13 @@ class Post extends Core implements CoreInterface {
753
  * @return User|null A User object if found, false if not
754
  */
755
  public function author() {
756
- return $this->get_author();
 
 
 
 
 
 
757
  }
758
 
759
  /**
@@ -769,7 +777,7 @@ class Post extends Core implements CoreInterface {
769
  */
770
  public function modified_author() {
771
  $user_id = get_post_meta($this->ID, '_edit_last', true);
772
- return ($user_id ? new User($user_id) : $this->get_author());
773
  }
774
 
775
  /**
@@ -906,6 +914,20 @@ class Post extends Core implements CoreInterface {
906
  return $timber_comments;
907
  }
908
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
909
  /**
910
  * 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).
911
  * @api
@@ -920,6 +942,9 @@ class Post extends Core implements CoreInterface {
920
  * @return string
921
  */
922
  public function content( $page = 0, $len = -1 ) {
 
 
 
923
  if ( $len == -1 && $page == 0 && $this->_content ) {
924
  return $this->_content;
925
  }
@@ -1040,6 +1065,15 @@ class Post extends Core implements CoreInterface {
1040
  public function format() {
1041
  return get_post_format($this->ID);
1042
  }
 
 
 
 
 
 
 
 
 
1043
 
1044
  /**
1045
  * get the permalink for a post object
@@ -1504,9 +1538,7 @@ class Post extends Core implements CoreInterface {
1504
  * @return User|null
1505
  */
1506
  public function get_author() {
1507
- if ( isset($this->post_author) ) {
1508
- return new User($this->post_author);
1509
- }
1510
  }
1511
 
1512
  /**
389
  */
390
  public function get_preview( $len = 50, $force = false, $readmore = 'Read More', $strip = true, $end = '&hellip;' ) {
391
  $text = '';
392
+ $link = '';
393
  $trimmed = false;
394
  $last_p_tag = null;
395
+ if ( isset($this->post_excerpt) && strlen(trim($this->post_excerpt)) ) {
396
  if ( $force ) {
397
  $text = Helper::trim_words($this->post_excerpt, $len, false);
398
  $trimmed = true;
437
  }
438
  $read_more_class = apply_filters('timber/post/get_preview/read_more_class', "read-more");
439
  if ( $readmore && isset($readmore_matches) && !empty($readmore_matches[1]) ) {
440
+ $link = ' <a href="'.$this->link().'" class="'.$read_more_class.'">'.trim($readmore_matches[1]).'</a>';
441
  } elseif ( $readmore ) {
442
+ $link = ' <a href="'.$this->link().'" class="'.$read_more_class.'">'.trim($readmore).'</a>';
443
  }
444
+ $text .= apply_filters('timber/post/get_preview/read_more_link', $link);
445
  if ( !$strip && $last_p_tag && (strpos($text, '<p>') || strpos($text, '<p ')) ) {
446
  $text .= '</p>';
447
  }
651
  * @return boolean
652
  */
653
  public function has_field( $field_name ) {
654
+ return (!$this->get_field($field_name)) ? false : true;
655
  }
656
 
657
 
755
  * @return User|null A User object if found, false if not
756
  */
757
  public function author() {
758
+ if ( isset($this->post_author) ) {
759
+ return new User($this->post_author);
760
+ }
761
+ }
762
+
763
+ public function authors() {
764
+ return apply_filters('timber/post/authors', array($this->author()), $this);
765
  }
766
 
767
  /**
777
  */
778
  public function modified_author() {
779
  $user_id = get_post_meta($this->ID, '_edit_last', true);
780
+ return ($user_id ? new User($user_id) : $this->author());
781
  }
782
 
783
  /**
914
  return $timber_comments;
915
  }
916
 
917
+ /**
918
+ * If the Password form is to be shown, show it!
919
+ * @return string|void
920
+ */
921
+ protected function maybe_show_password_form(){
922
+ if ( $this->password_required() ) {
923
+ $show_pw = false;
924
+ $show_pw = apply_filters('timber/post/content/show_password_form_for_protected', $show_pw);
925
+ if ($show_pw) {
926
+ return apply_filters('timber/post/content/password_form', get_the_password_form($this->ID), $this);
927
+ }
928
+ }
929
+ }
930
+
931
  /**
932
  * 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).
933
  * @api
942
  * @return string
943
  */
944
  public function content( $page = 0, $len = -1 ) {
945
+ if ( $form = $this->maybe_show_password_form() ) {
946
+ return $form;
947
+ }
948
  if ( $len == -1 && $page == 0 && $this->_content ) {
949
  return $this->_content;
950
  }
1065
  public function format() {
1066
  return get_post_format($this->ID);
1067
  }
1068
+
1069
+ /**
1070
+ * whether post requires password and correct password has been provided
1071
+ * @api
1072
+ * @return boolean
1073
+ */
1074
+ public function password_required() {
1075
+ return post_password_required($this->ID);
1076
+ }
1077
 
1078
  /**
1079
  * get the permalink for a post object
1538
  * @return User|null
1539
  */
1540
  public function get_author() {
1541
+ return $this->author();
 
 
1542
  }
1543
 
1544
  /**
lib/PostGetter.php CHANGED
@@ -9,10 +9,22 @@ class PostGetter {
9
 
10
  /**
11
  * @param mixed $query
12
- * @param string $PostClass
13
  * @return array|bool|null
14
  */
15
  public static function get_post( $query = false, $PostClass = '\Timber\Post' ) {
 
 
 
 
 
 
 
 
 
 
 
 
16
  $posts = self::get_posts($query, $PostClass);
17
  if ( $post = reset($posts) ) {
18
  return $post;
@@ -33,7 +45,7 @@ class PostGetter {
33
 
34
  /**
35
  * @param mixed $query
36
- * @param string $PostClass
37
  * @return array|bool|null
38
  */
39
  public static function query_posts( $query = false, $PostClass = '\Timber\Post' ) {
@@ -84,6 +96,35 @@ class PostGetter {
84
  return ($wp_query && property_exists($wp_query, 'posts') && $wp_query->posts);
85
  }
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  /**
88
  * @param string|array $arg
89
  * @return boolean
9
 
10
  /**
11
  * @param mixed $query
12
+ * @param string|array $PostClass
13
  * @return array|bool|null
14
  */
15
  public static function get_post( $query = false, $PostClass = '\Timber\Post' ) {
16
+ // if a post id is passed, grab the post directly
17
+ if ( is_numeric($query) ) {
18
+ $post_type = get_post_type($query);
19
+ $PostClass = PostGetter::get_post_class($post_type, $PostClass);
20
+ $post = new $PostClass($query);
21
+ // get the latest revision if we're dealing with a preview
22
+ $posts = PostsCollection::maybe_set_preview(array($post));
23
+ if ( $post = reset($posts) ) {
24
+ return $post;
25
+ }
26
+ }
27
+
28
  $posts = self::get_posts($query, $PostClass);
29
  if ( $post = reset($posts) ) {
30
  return $post;
45
 
46
  /**
47
  * @param mixed $query
48
+ * @param string|array $PostClass
49
  * @return array|bool|null
50
  */
51
  public static function query_posts( $query = false, $PostClass = '\Timber\Post' ) {
96
  return ($wp_query && property_exists($wp_query, 'posts') && $wp_query->posts);
97
  }
98
 
99
+ /**
100
+ * @param string $post_type
101
+ * @param string|array $post_class
102
+ *
103
+ * @return string
104
+ */
105
+ public static function get_post_class( $post_type, $post_class = '\Timber\Post' ) {
106
+ $post_class = apply_filters( 'Timber\PostClassMap', $post_class );
107
+ $post_class_use = '\Timber\Post';
108
+
109
+ if ( is_array($post_class) ) {
110
+ if ( isset( $post_class[$post_type]) ) {
111
+ $post_class_use = $post_class[$post_type];
112
+ } else {
113
+ Helper::error_log($post_type . ' not found in ' . print_r($post_class, true));
114
+ }
115
+ } elseif ( is_string($post_class) ) {
116
+ $post_class_use = $post_class;
117
+ } else {
118
+ Helper::error_log('Unexpeted value for PostClass: ' . print_r( $post_class, true));
119
+ }
120
+
121
+ if ( !class_exists( $post_class_use ) || !( is_subclass_of($post_class_use, '\Timber\Post') || is_a($post_class_use, '\Timber\Post', true) ) ) {
122
+ Helper::error_log('Class ' . $post_class_use . ' either does not exist or implement \Timber\Post');
123
+ }
124
+
125
+ return $post_class_use;
126
+ }
127
+
128
  /**
129
  * @param string|array $arg
130
  * @return boolean
lib/PostType.php CHANGED
@@ -8,6 +8,9 @@ namespace Timber;
8
  */
9
  class PostType {
10
 
 
 
 
11
  public function __construct( $post_type ) {
12
  $this->slug = $post_type;
13
  $this->init($post_type);
8
  */
9
  class PostType {
10
 
11
+ /**
12
+ * @param string $post_type
13
+ */
14
  public function __construct( $post_type ) {
15
  $this->slug = $post_type;
16
  $this->init($post_type);
lib/PostsCollection.php CHANGED
@@ -18,22 +18,9 @@ class PostsCollection extends \ArrayObject {
18
  $posts = array();
19
  }
20
  foreach ( $posts as $post_object ) {
21
- $post_class_use = $post_class;
22
- if ( is_array($post_class) ) {
23
- $post_type = get_post_type($post_object);
24
- $post_class_use = '\Timber\Post';
25
-
26
- if ( isset($post_class[$post_type]) ) {
27
- $post_class_use = $post_class[$post_type];
28
-
29
- } else {
30
- if ( is_array($post_class) ) {
31
- Helper::error_log($post_type.' of '.$post_object->ID.' not found in '.print_r($post_class, true));
32
- } else {
33
- Helper::error_log($post_type.' not found in '.$post_class);
34
- }
35
- }
36
- }
37
  // Don't create yet another object if $post_object is already of the right type
38
  if ( is_a($post_object, $post_class_use) ) {
39
  $post = $post_object;
18
  $posts = array();
19
  }
20
  foreach ( $posts as $post_object ) {
21
+ $post_type = get_post_type($post_object);
22
+ $post_class_use = PostGetter::get_post_class($post_type, $post_class);
23
+
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  // Don't create yet another object if $post_object is already of the right type
25
  if ( is_a($post_object, $post_class_use) ) {
26
  $post = $post_object;
lib/QueryIterator.php CHANGED
@@ -10,7 +10,7 @@ if ( !defined('ABSPATH') ) {
10
  exit;
11
  }
12
 
13
- class QueryIterator implements \Iterator {
14
 
15
  /**
16
  *
@@ -22,13 +22,14 @@ class QueryIterator implements \Iterator {
22
 
23
  public function __construct( $query = false, $posts_class = 'Timber\Post' ) {
24
  add_action('pre_get_posts', array($this, 'fix_number_posts_wp_quirk'));
25
- if ( $posts_class )
 
26
  $this->_posts_class = $posts_class;
 
27
 
28
  if ( is_a($query, 'WP_Query') ) {
29
  // We got a full-fledged WP Query, look no further!
30
  $the_query = $query;
31
-
32
  } elseif ( false === $query ) {
33
  // If query is explicitly set to false, use the main loop
34
  global $wp_query;
@@ -38,11 +39,9 @@ class QueryIterator implements \Iterator {
38
  } elseif ( Helper::is_array_assoc($query) || (is_string($query) && strstr($query, '=')) ) {
39
  // We have a regularly formed WP query string or array to use
40
  $the_query = new \WP_Query($query);
41
-
42
  } elseif ( is_numeric($query) || is_string($query) ) {
43
  // We have what could be a post name or post ID to pull out
44
  $the_query = self::get_query_from_string($query);
45
-
46
  } elseif ( is_array($query) && count($query) && (is_integer($query[0]) || is_string($query[0])) ) {
47
  // We have a list of pids (post IDs) to extract from
48
  $the_query = self::get_query_from_array_of_ids($query);
@@ -52,7 +51,6 @@ class QueryIterator implements \Iterator {
52
  } else {
53
  Helper::error_log('I have failed you! in '.basename(__FILE__).'::'.__LINE__);
54
  Helper::error_log($query);
55
-
56
  // We have failed hard, at least let get something.
57
  $the_query = new \WP_Query();
58
  }
@@ -62,7 +60,7 @@ class QueryIterator implements \Iterator {
62
  }
63
 
64
  public function post_count() {
65
- return $this->_query->post_count;
66
  }
67
 
68
  public function get_posts( $return_collection = false ) {
@@ -76,8 +74,9 @@ class QueryIterator implements \Iterator {
76
  // GET POSTS
77
  //
78
  public static function get_query_from_array_of_ids( $query = array() ) {
79
- if ( !is_array($query) || !count($query) )
80
  return null;
 
81
 
82
  return new \WP_Query(array(
83
  'post_type'=> 'any',
@@ -150,6 +149,16 @@ class QueryIterator implements \Iterator {
150
  return $query;
151
  }
152
 
 
 
 
 
 
 
 
 
 
 
153
  /**
154
  * this will test for whether a custom page to display posts is active, and if so, set the query to the default
155
  * @param WP_Query $query the original query recived from WordPress
@@ -164,4 +173,21 @@ class QueryIterator implements \Iterator {
164
  return $query;
165
  }
166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  }
10
  exit;
11
  }
12
 
13
+ class QueryIterator implements \Iterator, \Countable {
14
 
15
  /**
16
  *
22
 
23
  public function __construct( $query = false, $posts_class = 'Timber\Post' ) {
24
  add_action('pre_get_posts', array($this, 'fix_number_posts_wp_quirk'));
25
+ add_action('pre_get_posts', array($this, 'fix_cat_wp_quirk'));
26
+ if ( $posts_class ) {
27
  $this->_posts_class = $posts_class;
28
+ }
29
 
30
  if ( is_a($query, 'WP_Query') ) {
31
  // We got a full-fledged WP Query, look no further!
32
  $the_query = $query;
 
33
  } elseif ( false === $query ) {
34
  // If query is explicitly set to false, use the main loop
35
  global $wp_query;
39
  } elseif ( Helper::is_array_assoc($query) || (is_string($query) && strstr($query, '=')) ) {
40
  // We have a regularly formed WP query string or array to use
41
  $the_query = new \WP_Query($query);
 
42
  } elseif ( is_numeric($query) || is_string($query) ) {
43
  // We have what could be a post name or post ID to pull out
44
  $the_query = self::get_query_from_string($query);
 
45
  } elseif ( is_array($query) && count($query) && (is_integer($query[0]) || is_string($query[0])) ) {
46
  // We have a list of pids (post IDs) to extract from
47
  $the_query = self::get_query_from_array_of_ids($query);
51
  } else {
52
  Helper::error_log('I have failed you! in '.basename(__FILE__).'::'.__LINE__);
53
  Helper::error_log($query);
 
54
  // We have failed hard, at least let get something.
55
  $the_query = new \WP_Query();
56
  }
60
  }
61
 
62
  public function post_count() {
63
+ return $this->_query->post_count;
64
  }
65
 
66
  public function get_posts( $return_collection = false ) {
74
  // GET POSTS
75
  //
76
  public static function get_query_from_array_of_ids( $query = array() ) {
77
+ if ( !is_array($query) || !count($query) ) {
78
  return null;
79
+ }
80
 
81
  return new \WP_Query(array(
82
  'post_type'=> 'any',
149
  return $query;
150
  }
151
 
152
+ //get_posts uses category, WP_Query uses cat. Why? who knows...
153
+ public static function fix_cat_wp_quirk( $query ) {
154
+ if ( isset($query->query) && isset($query->query['category'])
155
+ && !isset($query->query['cat']) ) {
156
+ $query->set('cat', $query->query['category']);
157
+ unset($query->query['category']);
158
+ }
159
+ return $query;
160
+ }
161
+
162
  /**
163
  * this will test for whether a custom page to display posts is active, and if so, set the query to the default
164
  * @param WP_Query $query the original query recived from WordPress
173
  return $query;
174
  }
175
 
176
+ /**
177
+ * Count elements of an object.
178
+ *
179
+ * Necessary for some Twig `loop` variable properties.
180
+ * @see http://twig.sensiolabs.org/doc/tags/for.html#the-loop-variable
181
+ *
182
+ * @link http://php.net/manual/en/countable.count.php
183
+ * @return int The custom count as an integer.
184
+ * </p>
185
+ * <p>
186
+ * The return value is cast to an integer.
187
+ * @since 5.1.0
188
+ */
189
+ public function count()
190
+ {
191
+ return $this->post_count();
192
+ }
193
  }
lib/Term.php CHANGED
@@ -339,7 +339,7 @@ class Term extends Core implements CoreInterface {
339
  $prefix = '<p>';
340
  $desc = term_description($this->ID, $this->taxonomy);
341
  if ( substr($desc, 0, strlen($prefix)) == $prefix ) {
342
- $desc = substr($desc, strlen($prefix));
343
  }
344
  $desc = preg_replace('/'.preg_quote('</p>', '/').'$/', '', $desc);
345
  return trim($desc);
339
  $prefix = '<p>';
340
  $desc = term_description($this->ID, $this->taxonomy);
341
  if ( substr($desc, 0, strlen($prefix)) == $prefix ) {
342
+ $desc = substr($desc, strlen($prefix));
343
  }
344
  $desc = preg_replace('/'.preg_quote('</p>', '/').'$/', '', $desc);
345
  return trim($desc);
lib/TermGetter.php CHANGED
@@ -11,7 +11,7 @@ class TermGetter {
11
  * @param string $taxonomy
12
  * @return Timber\Term|WP_Error|null
13
  */
14
- public static function get_term( $term, $taxonomy, $TermClass = 'Term' ) {
15
  $term = get_term($term, $taxonomy);
16
  return new $TermClass($term->term_id, $term->taxonomy);
17
  }
@@ -22,7 +22,7 @@ class TermGetter {
22
  * @param string $TermClass
23
  * @return mixed
24
  */
25
- public static function get_terms( $args = null, $maybe_args = array(), $TermClass = 'Term' ) {
26
  if ( is_string($maybe_args) && !strstr($maybe_args, '=') ) {
27
  //the user is sending the $TermClass in the second argument
28
  $TermClass = $maybe_args;
11
  * @param string $taxonomy
12
  * @return Timber\Term|WP_Error|null
13
  */
14
+ public static function get_term( $term, $taxonomy, $TermClass = '\Timber\Term' ) {
15
  $term = get_term($term, $taxonomy);
16
  return new $TermClass($term->term_id, $term->taxonomy);
17
  }
22
  * @param string $TermClass
23
  * @return mixed
24
  */
25
+ public static function get_terms( $args = null, $maybe_args = array(), $TermClass = '\Timber\Term' ) {
26
  if ( is_string($maybe_args) && !strstr($maybe_args, '=') ) {
27
  //the user is sending the $TermClass in the second argument
28
  $TermClass = $maybe_args;
lib/Timber.php CHANGED
@@ -11,6 +11,7 @@ use Timber\TermGetter;
11
  use Timber\Site;
12
  use Timber\URLHelper;
13
  use Timber\Helper;
 
14
  use Timber\Request;
15
  use Timber\User;
16
  use Timber\Loader;
@@ -34,7 +35,7 @@ use Timber\Loader;
34
  */
35
  class Timber {
36
 
37
- public static $version = '1.0.3';
38
  public static $locations;
39
  public static $dirname = 'views';
40
  public static $twig_cache = false;
@@ -55,7 +56,7 @@ class Timber {
55
  $this->test_compatibility();
56
  $this->backwards_compatibility();
57
  $this->init_constants();
58
- $this::init();
59
  }
60
  }
61
 
@@ -102,12 +103,12 @@ class Timber {
102
  /**
103
  * @codeCoverageIgnore
104
  */
105
- protected static function init() {
106
  if ( class_exists('\WP') && !defined('TIMBER_LOADED') ) {
107
  Twig::init();
108
  ImageHelper::init();
109
  Admin::init();
110
- Integrations::init();
111
  define('TIMBER_LOADED', true);
112
  }
113
  }
@@ -119,7 +120,7 @@ class Timber {
119
  * Get post.
120
  * @api
121
  * @param mixed $query
122
- * @param string $PostClass
123
  * @return array|bool|null
124
  */
125
  public static function get_post( $query = false, $PostClass = 'Timber\Post' ) {
@@ -224,7 +225,7 @@ class Timber {
224
  */
225
  public static function get_context() {
226
  if ( empty(self::$context_cache) ) {
227
- self::$context_cache['http_host'] = 'http://'.URLHelper::get_host();
228
  self::$context_cache['wp_title'] = Helper::get_wp_title();
229
  self::$context_cache['wp_head'] = Helper::function_wrapper('wp_head');
230
  self::$context_cache['wp_footer'] = Helper::function_wrapper('wp_footer');
@@ -251,7 +252,7 @@ class Timber {
251
  * @api
252
  * @param array $filenames
253
  * @param array $data
254
- * @param bool $expires
255
  * @param string $cache_mode
256
  * @param bool $via_render
257
  * @return bool|string
@@ -260,9 +261,11 @@ class Timber {
260
  if ( !defined('TIMBER_LOADED') ) {
261
  self::init();
262
  }
263
- $caller = self::get_calling_script_dir();
264
  $loader = new Loader($caller);
265
  $file = $loader->choose_template($filenames);
 
 
266
  $output = '';
267
  if ( is_null($data) ) {
268
  $data = array();
@@ -321,9 +324,9 @@ class Timber {
321
  * @api
322
  * @param array $filenames
323
  * @param array $data
324
- * @param bool $expires
325
  * @param string $cache_mode
326
- * @return bool|string
327
  */
328
  public static function render( $filenames, $data = array(), $expires = false, $cache_mode = Loader::CACHE_USE_DEFAULT ) {
329
  $output = self::fetch($filenames, $data, $expires, $cache_mode);
@@ -370,9 +373,8 @@ class Timber {
370
  * @return string
371
  */
372
  public static function get_sidebar_from_php( $sidebar = '', $data ) {
373
- $caller = self::get_calling_script_dir();
374
- $loader = new Loader();
375
- $uris = $loader->get_locations($caller);
376
  ob_start();
377
  $found = false;
378
  foreach ( $uris as $uri ) {
@@ -413,83 +415,12 @@ class Timber {
413
  * @return array mixed
414
  */
415
  public static function get_pagination( $prefs = array() ) {
416
- global $wp_query;
417
- global $paged;
418
- global $wp_rewrite;
419
- $args = array();
420
- $args['total'] = ceil($wp_query->found_posts / $wp_query->query_vars['posts_per_page']);
421
- if ( $wp_rewrite->using_permalinks() ) {
422
- $url = explode('?', get_pagenum_link(0));
423
- if ( isset($url[1]) ) {
424
- parse_str($url[1], $query);
425
- $args['add_args'] = $query;
426
- }
427
- $args['format'] = $wp_rewrite->pagination_base.'/%#%';
428
- $args['base'] = trailingslashit($url[0]).'%_%';
429
- } else {
430
- $big = 999999999;
431
- $args['base'] = str_replace($big, '%#%', esc_url(get_pagenum_link($big)));
432
- }
433
- $args['type'] = 'array';
434
- $args['current'] = max(1, get_query_var('paged'));
435
- $args['mid_size'] = max(9 - $args['current'], 3);
436
- if ( is_int($prefs) ) {
437
- $args['mid_size'] = $prefs - 2;
438
- } else {
439
- $args = array_merge($args, $prefs);
440
- }
441
- $data = array();
442
- $data['current'] = $args['current'];
443
- $data['total'] = $args['total'];
444
- $data['pages'] = Helper::paginate_links($args);
445
-
446
- if ( $data['total'] <= count($data['pages']) ) {
447
- // decrement current so that it matches up with the 0 based index used by the pages array
448
- $current = $data['current'] - 1;
449
- }
450
- // $data['current'] can't be used b/c there are more than 10 pages and we are condensing with dots
451
- else {
452
- foreach ( $data['pages'] as $key => $page ) {
453
- if ( !empty($page['current']) ) {
454
- $current = $key;
455
- break;
456
- }
457
- }
458
- }
459
-
460
- // set next and prev using pages array generated by paginate links
461
- if ( isset($current) && isset($data['pages'][$current + 1]) ) {
462
- $data['next'] = array('link' => user_trailingslashit($data['pages'][$current + 1]['link']), 'class' => 'page-numbers next');
463
- }
464
- if ( isset($current) && isset($data['pages'][$current - 1]) ) {
465
- $data['prev'] = array('link' => user_trailingslashit($data['pages'][$current - 1]['link']), 'class' => 'page-numbers prev');
466
- }
467
- if ( $paged < 2 ) {
468
- $data['prev'] = '';
469
- }
470
- if ( $data['total'] === (double) 0 ) {
471
- $data['next'] = '';
472
- }
473
- return $data;
474
  }
475
 
476
  /* Utility
477
  ================================ */
478
 
479
- /**
480
- * Get calling script dir.
481
- * @api
482
- * @return string
483
- */
484
- public static function get_calling_script_dir( $offset = 0 ) {
485
- $caller = self::get_calling_script_file($offset);
486
- if ( !is_null($caller) ) {
487
- $pathinfo = pathinfo($caller);
488
- $dir = $pathinfo['dirname'];
489
- return $dir;
490
- }
491
- }
492
-
493
 
494
  /**
495
  * Add route.
@@ -504,27 +435,5 @@ class Timber {
504
  \Routes::map($route, $callback, $args);
505
  }
506
 
507
- /**
508
- * Get calling script file.
509
- * @api
510
- * @param int $offset
511
- * @return string|null
512
- * @deprecated since 0.20.0
513
- */
514
- public static function get_calling_script_file( $offset = 0 ) {
515
- $caller = null;
516
- $backtrace = debug_backtrace();
517
- $i = 0;
518
- foreach ( $backtrace as $trace ) {
519
- if ( array_key_exists('file', $trace) && $trace['file'] != __FILE__ ) {
520
- $caller = $trace['file'];
521
- break;
522
- }
523
- $i++;
524
- }
525
- if ( $offset ) {
526
- $caller = $backtrace[$i + $offset]['file'];
527
- }
528
- return $caller;
529
- }
530
  }
11
  use Timber\Site;
12
  use Timber\URLHelper;
13
  use Timber\Helper;
14
+ use Timber\Pagination;
15
  use Timber\Request;
16
  use Timber\User;
17
  use Timber\Loader;
35
  */
36
  class Timber {
37
 
38
+ public static $version = '1.1.4';
39
  public static $locations;
40
  public static $dirname = 'views';
41
  public static $twig_cache = false;
56
  $this->test_compatibility();
57
  $this->backwards_compatibility();
58
  $this->init_constants();
59
+ $this->init();
60
  }
61
  }
62
 
103
  /**
104
  * @codeCoverageIgnore
105
  */
106
+ protected function init() {
107
  if ( class_exists('\WP') && !defined('TIMBER_LOADED') ) {
108
  Twig::init();
109
  ImageHelper::init();
110
  Admin::init();
111
+ new Integrations();
112
  define('TIMBER_LOADED', true);
113
  }
114
  }
120
  * Get post.
121
  * @api
122
  * @param mixed $query
123
+ * @param string|array $PostClass
124
  * @return array|bool|null
125
  */
126
  public static function get_post( $query = false, $PostClass = 'Timber\Post' ) {
225
  */
226
  public static function get_context() {
227
  if ( empty(self::$context_cache) ) {
228
+ self::$context_cache['http_host'] = URLHelper::get_scheme().'://'.URLHelper::get_host();
229
  self::$context_cache['wp_title'] = Helper::get_wp_title();
230
  self::$context_cache['wp_head'] = Helper::function_wrapper('wp_head');
231
  self::$context_cache['wp_footer'] = Helper::function_wrapper('wp_footer');
252
  * @api
253
  * @param array $filenames
254
  * @param array $data
255
+ * @param boolean|integer $expires
256
  * @param string $cache_mode
257
  * @param bool $via_render
258
  * @return bool|string
261
  if ( !defined('TIMBER_LOADED') ) {
262
  self::init();
263
  }
264
+ $caller = LocationManager::get_calling_script_dir(1);
265
  $loader = new Loader($caller);
266
  $file = $loader->choose_template($filenames);
267
+ $caller_file = LocationManager::get_calling_script_file(1);
268
+ apply_filters('timber/calling_php_file', $caller_file);
269
  $output = '';
270
  if ( is_null($data) ) {
271
  $data = array();
324
  * @api
325
  * @param array $filenames
326
  * @param array $data
327
+ * @param boolean|integer $expires
328
  * @param string $cache_mode
329
+ * @return boolean|string
330
  */
331
  public static function render( $filenames, $data = array(), $expires = false, $cache_mode = Loader::CACHE_USE_DEFAULT ) {
332
  $output = self::fetch($filenames, $data, $expires, $cache_mode);
373
  * @return string
374
  */
375
  public static function get_sidebar_from_php( $sidebar = '', $data ) {
376
+ $caller = LocationManager::get_calling_script_dir(1);
377
+ $uris = LocationManager::get_locations($caller);
 
378
  ob_start();
379
  $found = false;
380
  foreach ( $uris as $uri ) {
415
  * @return array mixed
416
  */
417
  public static function get_pagination( $prefs = array() ) {
418
+ return Pagination::get_pagination($prefs);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  }
420
 
421
  /* Utility
422
  ================================ */
423
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
 
425
  /**
426
  * Add route.
435
  \Routes::map($route, $callback, $args);
436
  }
437
 
438
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  }
lib/Twig.php CHANGED
@@ -61,6 +61,8 @@ class Twig {
61
  $twig->addFilter(new \Twig_SimpleFilter('wpautop', 'wpautop'));
62
  $twig->addFilter(new \Twig_SimpleFilter('list', array($this, 'add_list_separators')));
63
 
 
 
64
  $twig->addFilter(new \Twig_SimpleFilter('relative', function( $link ) {
65
  return URLHelper::get_rel_url($link, true);
66
  } ));
@@ -217,12 +219,20 @@ class Twig {
217
  */
218
  public function add_timber_escapers( $twig ) {
219
 
220
- $twig->getExtension( 'core' )->setEscaper( 'esc_url', function( \Twig_Environment $env, $string ) {
221
  return esc_url( $string );
222
- } );
223
- $twig->getExtension( 'core' )->setEscaper( 'wp_kses_post', function( \Twig_Environment $env, $string ) {
224
  return wp_kses_post( $string );
225
- } );
 
 
 
 
 
 
 
 
226
 
227
  return $twig;
228
 
61
  $twig->addFilter(new \Twig_SimpleFilter('wpautop', 'wpautop'));
62
  $twig->addFilter(new \Twig_SimpleFilter('list', array($this, 'add_list_separators')));
63
 
64
+ $twig->addFilter(new \Twig_SimpleFilter('pluck', array('Timber\Helper', 'pluck')));
65
+
66
  $twig->addFilter(new \Twig_SimpleFilter('relative', function( $link ) {
67
  return URLHelper::get_rel_url($link, true);
68
  } ));
219
  */
220
  public function add_timber_escapers( $twig ) {
221
 
222
+ $twig->getExtension('core')->setEscaper('esc_url', function( \Twig_Environment $env, $string ) {
223
  return esc_url( $string );
224
+ });
225
+ $twig->getExtension('core')->setEscaper('wp_kses_post', function( \Twig_Environment $env, $string ) {
226
  return wp_kses_post( $string );
227
+ });
228
+
229
+ $twig->getExtension('core')->setEscaper('esc_html', function( \Twig_Environment $env, $string ) {
230
+ return esc_html( $string );
231
+ });
232
+
233
+ $twig->getExtension('core')->setEscaper('esc_js', function( \Twig_Environment $env, $string ) {
234
+ return esc_js( $string );
235
+ });
236
 
237
  return $twig;
238
 
lib/URLHelper.php CHANGED
@@ -10,10 +10,7 @@ class URLHelper {
10
  * @return string
11
  */
12
  public static function get_current_url() {
13
- $pageURL = "http://";
14
- if ( isset($_SERVER['HTTPS']) && $_SERVER["HTTPS"] == "on" ) {
15
- $pageURL = "https://"; ;
16
- }
17
  if ( isset($_SERVER["SERVER_PORT"]) && $_SERVER["SERVER_PORT"] && $_SERVER["SERVER_PORT"] != "80" ) {
18
  $pageURL .= self::get_host().":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
19
  } else {
@@ -22,6 +19,17 @@ class URLHelper {
22
  return $pageURL;
23
  }
24
 
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  *
27
  *
10
  * @return string
11
  */
12
  public static function get_current_url() {
13
+ $pageURL = self::get_scheme()."://";
 
 
 
14
  if ( isset($_SERVER["SERVER_PORT"]) && $_SERVER["SERVER_PORT"] && $_SERVER["SERVER_PORT"] != "80" ) {
15
  $pageURL .= self::get_host().":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
16
  } else {
19
  return $pageURL;
20
  }
21
 
22
+ /**
23
+ *
24
+ * Get url scheme
25
+ * @return string
26
+ */
27
+ public static function get_scheme()
28
+ {
29
+ return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
30
+ }
31
+
32
+
33
  /**
34
  *
35
  *
lib/User.php CHANGED
@@ -10,11 +10,11 @@ use Timber\URLHelper;
10
  use Timber\Image;
11
 
12
  /**
13
- * This is used in Timber to represent users retrived from WordPress. You can call `$my_user = new TimberUser(123);` directly, or access it through the `{{ post.author }}` method.
14
  * @example
15
  * ```php
16
- * $context['current_user'] = new TimberUser();
17
- * $context['post'] = new TimberPost();
18
  * Timber::render('single.twig', $context);
19
  * ```
20
  * ```twig
@@ -203,7 +203,7 @@ class User extends Core implements CoreInterface {
203
  * @return string the human-friendly name of the user (ex: "Buster Bluth")
204
  */
205
  public function name() {
206
- return $this->display_name;
207
  }
208
 
209
  /**
10
  use Timber\Image;
11
 
12
  /**
13
+ * This is used in Timber to represent users retrived from WordPress. You can call `$my_user = new Timber\User(123);` directly, or access it through the `{{ post.author }}` method.
14
  * @example
15
  * ```php
16
+ * $context['current_user'] = new Timber\User();
17
+ * $context['post'] = new Timber\Post();
18
  * Timber::render('single.twig', $context);
19
  * ```
20
  * ```twig
203
  * @return string the human-friendly name of the user (ex: "Buster Bluth")
204
  */
205
  public function name() {
206
+ return apply_filters('timber/user/name', $this->display_name, $this);
207
  }
208
 
209
  /**
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: jarednova, connorjburton, lggorman
3
  Tags: template engine, templates, twig
4
  Requires at least: 3.7
5
- Stable tag: 1.1.1
6
- Tested up to: 4.5.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
@@ -41,6 +41,25 @@ Timber is great for any WordPress developer who cares about writing good, mainta
41
 
42
  == Changelog ==
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  = 1.1.1 =
45
  * Fixed 301 redirects for pagination (thanks @xavivars)
46
  * Added new escaping filter options for `|e('wp_kses_post')` and `|e('esc_url')`(thanks @matgargano)
2
  Contributors: jarednova, connorjburton, lggorman
3
  Tags: template engine, templates, twig
4
  Requires at least: 3.7
5
+ Stable tag: 1.1.4
6
+ Tested up to: 4.6
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
41
 
42
  == Changelog ==
43
 
44
+ = 1.1.4 =
45
+ * Native support for Co-Authors Plus! just use `{{ post.authors }}` 939331e282fd54bf3e210645964504304f2b071b
46
+ * New filter to enable PW propmpt for PW protected posts (`timber/post/content/show_password_form_for_protected`) 0f9b20ec90b34059634c25bc27671875c18f8fcb
47
+ * New filter for custom loaders (`timber/loader/custom`) (thanks @tnottu!) 9097984a7c3df23068056d7835465e0690338567
48
+ * Fixed some updating bugs with 4.6 (thanks @daronspence) 16b8bd71571be71b298e6306abe2cd4b95d8c9e8
49
+ * You can now count Query results (thanks Evan Mattson) 141624a0ac18d9dcce62a2a681134009a2b79814
50
+
51
+ = 1.1.3 =
52
+ * New escapers! (thanks @matgargano) c7e8ed34da6fcd13bdc9005c04045f3a6b33595b
53
+ * Fix to how categories work in Timber::get_posts 49f6007db3f829097f82ed41d389dd39053fb84a
54
+ * Fix to usage of class maps in Timber::get_posts (thanks @vilpersson) b1387e443850aa021a0a70203bc20d238d4b21cb
55
+ * Added Post::password_required method (thanks @marclarr) 2e685ce3d05c50e879817e51256202e032e77122
56
+ * You can filter the link markup for Post::get_preview (thanks @LiljebergXYZ) b8100d7f2601b4da40bcc0a873c071b6ecf267f1
57
+
58
+ = 1.1.2 =
59
+ * Fix to how post IDs are retrieved (thanks @lggorman) 798acd90ee603de2d009828127bdeaab503beb10
60
+ * Fixes to pagination in search (@jarednova) 1d1ab67f124b02d8c60646f7b133abdf68cedc38
61
+ * Fixes to hooks for Timber Debug Bar (@jarednova) 82a914ec0be5be1011a15c1584c2c8e2999f1c1c
62
+
63
  = 1.1.1 =
64
  * Fixed 301 redirects for pagination (thanks @xavivars)
65
  * Added new escaping filter options for `|e('wp_kses_post')` and `|e('esc_url')`(thanks @matgargano)
timber.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Timber
4
  Description: The WordPress Timber Library allows you to write themes using the power Twig templates.
5
  Plugin URI: http://timber.upstatement.com
6
  Author: Jared Novack + Upstatement
7
- Version: 1.1.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 Twig templates.
5
  Plugin URI: http://timber.upstatement.com
6
  Author: Jared Novack + Upstatement
7
+ Version: 1.1.4
8
  Author URI: http://upstatement.com/
9
  */
10
  // we look for Composer files first in the plugins dir.
vendor/asm89/twig-cache-extension/README.md CHANGED
@@ -40,6 +40,33 @@ $cacheExtension = new CacheExtension($cacheStrategy);
40
  $twig->addExtension($cacheExtension);
41
  ```
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  ### Usage
44
 
45
  To cache a part of a template in Twig surround the code with a `cache` block.
40
  $twig->addExtension($cacheExtension);
41
  ```
42
 
43
+ ### Want to use a PSR-6 cache pool?
44
+
45
+ Instead of using the default `DoctrineCacheAdapter` the extension also has
46
+ a `PSR-6` compatible adapter. You need to instantiate one of the cache pool
47
+ implementations as can be found on: http://php-cache.readthedocs.io/en/latest/
48
+
49
+ Example: Making use of the `ApcuCachePool` via the `PsrCacheAdapter`:
50
+
51
+ ```bash
52
+ composer require cache/apcu-adapter
53
+ ```
54
+
55
+ ```php
56
+ <?php
57
+
58
+ use Asm89\Twig\CacheExtension\CacheProvider\PsrCacheAdapter;
59
+ use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy;
60
+ use Asm89\Twig\CacheExtension\Extension as CacheExtension;
61
+ use Cache\Adapter\Apcu\ApcuCachePool();
62
+
63
+ $cacheProvider = new PsrCacheAdapter(new ApcuCachePool());
64
+ $cacheStrategy = new LifetimeCacheStrategy($cacheProvider);
65
+ $cacheExtension = new CacheExtension($cacheStrategy);
66
+
67
+ $twig->addExtension($cacheExtension);
68
+ ```
69
+
70
  ### Usage
71
 
72
  To cache a part of a template in Twig surround the code with a `cache` block.
vendor/asm89/twig-cache-extension/composer.json CHANGED
@@ -18,6 +18,9 @@
18
  "require-dev": {
19
  "doctrine/cache": "~1.0"
20
  },
 
 
 
21
  "autoload": {
22
  "psr-4": {
23
  "": "lib/"
@@ -30,7 +33,7 @@
30
  },
31
  "extra": {
32
  "branch-alias": {
33
- "dev-master": "1.1-dev"
34
  }
35
  }
36
  }
18
  "require-dev": {
19
  "doctrine/cache": "~1.0"
20
  },
21
+ "suggest": {
22
+ "psr/cache-implementation": "To make use of PSR-6 cache implementation via PsrCacheAdapter."
23
+ },
24
  "autoload": {
25
  "psr-4": {
26
  "": "lib/"
33
  },
34
  "extra": {
35
  "branch-alias": {
36
+ "dev-master": "1.2-dev"
37
  }
38
  }
39
  }
vendor/asm89/twig-cache-extension/lib/Asm89/Twig/CacheExtension/CacheProvider/PsrCacheAdapter.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of twig-cache-extension.
5
+ *
6
+ * (c) Alexander <iam.asm89@gmail.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Asm89\Twig\CacheExtension\CacheProvider;
13
+
14
+ use Asm89\Twig\CacheExtension\CacheProviderInterface;
15
+ use Psr\Cache\CacheItemPoolInterface;
16
+
17
+ /**
18
+ * Adapter class to make extension interoperable with every PSR-6 adapter.
19
+ *
20
+ * @see http://php-cache.readthedocs.io/
21
+ *
22
+ * @author Rvanlaak <rvanlaak@gmail.com>
23
+ */
24
+ class PsrCacheAdapter implements CacheProviderInterface
25
+ {
26
+ /**
27
+ * @var CacheItemPoolInterface
28
+ */
29
+ private $cache;
30
+
31
+ /**
32
+ * @param CacheItemPoolInterface $cache
33
+ */
34
+ public function __construct(CacheItemPoolInterface $cache)
35
+ {
36
+ $this->cache = $cache;
37
+ }
38
+
39
+ /**
40
+ * @param string $key
41
+ * @return mixed|false
42
+ */
43
+ public function fetch($key)
44
+ {
45
+ // PSR-6 implementation returns null, CacheProviderInterface expects false
46
+ $item = $this->cache->getItem($key);
47
+ if ($item->isHit()) {
48
+ return $item->get();
49
+ }
50
+ return false;
51
+ }
52
+
53
+ /**
54
+ * @param string $key
55
+ * @param string $value
56
+ * @param int|\DateInterval $lifetime
57
+ * @return bool
58
+ */
59
+ public function save($key, $value, $lifetime = 0)
60
+ {
61
+ $item = $this->cache->getItem($key);
62
+ $item->set($value);
63
+ $item->expiresAfter($lifetime);
64
+
65
+ return $this->cache->save($item);
66
+ }
67
+
68
+ }
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit6db8cd96031eb5111a748582779e5b56::getLoader();
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit6fd1e70a55055dbbade2a373bb243cbf::getLoader();
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit6db8cd96031eb5111a748582779e5b56
6
  {
7
  private static $loader;
8
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit6db8cd96031eb5111a748582779e5b56
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit6db8cd96031eb5111a748582779e5b56', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit6db8cd96031eb5111a748582779e5b56', '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 ComposerAutoloaderInit6fd1e70a55055dbbade2a373bb243cbf
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInit6fd1e70a55055dbbade2a373bb243cbf', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit6fd1e70a55055dbbade2a373bb243cbf', 'loadClassLoader'));
25
 
26
  $map = require __DIR__ . '/autoload_namespaces.php';
27
  foreach ($map as $namespace => $path) {
vendor/composer/installed.json CHANGED
@@ -168,17 +168,17 @@
168
  },
169
  {
170
  "name": "asm89/twig-cache-extension",
171
- "version": "1.2.0",
172
- "version_normalized": "1.2.0.0",
173
  "source": {
174
  "type": "git",
175
  "url": "https://github.com/asm89/twig-cache-extension.git",
176
- "reference": "76a801d6da8e1a1015af974d93df4ade0dda7267"
177
  },
178
  "dist": {
179
  "type": "zip",
180
- "url": "https://api.github.com/repos/asm89/twig-cache-extension/zipball/76a801d6da8e1a1015af974d93df4ade0dda7267",
181
- "reference": "76a801d6da8e1a1015af974d93df4ade0dda7267",
182
  "shasum": ""
183
  },
184
  "require": {
@@ -188,11 +188,14 @@
188
  "require-dev": {
189
  "doctrine/cache": "~1.0"
190
  },
191
- "time": "2016-02-14 13:52:38",
 
 
 
192
  "type": "library",
193
  "extra": {
194
  "branch-alias": {
195
- "dev-master": "1.1-dev"
196
  }
197
  },
198
  "installation-source": "dist",
168
  },
169
  {
170
  "name": "asm89/twig-cache-extension",
171
+ "version": "1.3.0",
172
+ "version_normalized": "1.3.0.0",
173
  "source": {
174
  "type": "git",
175
  "url": "https://github.com/asm89/twig-cache-extension.git",
176
+ "reference": "37cfee1f06fe8372cbe714d2d9224db276790936"
177
  },
178
  "dist": {
179
  "type": "zip",
180
+ "url": "https://api.github.com/repos/asm89/twig-cache-extension/zipball/37cfee1f06fe8372cbe714d2d9224db276790936",
181
+ "reference": "37cfee1f06fe8372cbe714d2d9224db276790936",
182
  "shasum": ""
183
  },
184
  "require": {
188
  "require-dev": {
189
  "doctrine/cache": "~1.0"
190
  },
191
+ "suggest": {
192
+ "psr/cache-implementation": "To make use of PSR-6 cache implementation via PsrCacheAdapter."
193
+ },
194
+ "time": "2016-06-14 09:16:57",
195
  "type": "library",
196
  "extra": {
197
  "branch-alias": {
198
+ "dev-master": "1.2-dev"
199
  }
200
  },
201
  "installation-source": "dist",