Timber - Version 0.20.8

Version Description

  • Fixed some Twig deprecation (thanks @alexlrobertson)
  • Support for {{img.src|retina}} filter (@jarednova)
Download this release

Release Info

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

Code changes from version 0.20.7 to 0.20.8

Files changed (44) hide show
  1. functions/functions/cache/KeyGenerator.php +30 -0
  2. functions/functions/cache/TimberKeyGeneratorInterface.php +8 -0
  3. functions/functions/cache/WPObjectCacheAdapter.php +29 -0
  4. functions/functions/cache/loader.php +24 -0
  5. functions/functions/integrations/acf-timber.php +62 -0
  6. functions/functions/integrations/wpcli-timber.php +88 -0
  7. functions/functions/timber-admin.php +31 -0
  8. functions/functions/timber-archives.php +272 -0
  9. functions/functions/timber-comment.php +239 -0
  10. functions/functions/timber-core-interface.php +16 -0
  11. functions/functions/timber-core.php +109 -0
  12. functions/functions/timber-function-wrapper.php +70 -0
  13. functions/functions/timber-helper.php +684 -0
  14. functions/functions/timber-image-helper.php +613 -0
  15. functions/functions/timber-image-retina-helper.php +54 -0
  16. functions/functions/timber-image.php +309 -0
  17. functions/functions/timber-loader.php +410 -0
  18. functions/functions/timber-menu-item.php +234 -0
  19. functions/functions/timber-menu.php +174 -0
  20. functions/functions/timber-page.php +6 -0
  21. functions/functions/timber-post-getter.php +137 -0
  22. functions/functions/timber-post.php +1007 -0
  23. functions/functions/timber-posts-collection.php +97 -0
  24. functions/functions/timber-query-iterator.php +141 -0
  25. functions/functions/timber-routes.php +111 -0
  26. functions/functions/timber-site.php +149 -0
  27. functions/functions/timber-term-getter.php +182 -0
  28. functions/functions/timber-term.php +328 -0
  29. functions/functions/timber-theme.php +58 -0
  30. functions/functions/timber-twig.php +289 -0
  31. functions/functions/timber-url-helper.php +246 -0
  32. functions/functions/timber-user.php +193 -0
  33. functions/timber-helper.php +4 -4
  34. functions/timber-image-helper.php +342 -249
  35. functions/timber-image-retina-helper.php +54 -0
  36. functions/timber-image.php +23 -4
  37. functions/timber-twig.php +177 -160
  38. functions/timber-url-helper.php +13 -0
  39. readme.txt +5 -1
  40. timber.php +1 -1
  41. vendor/autoload.php +1 -1
  42. vendor/composer/autoload_real.php +4 -4
  43. vendor/vendor/autoload.php +1 -1
  44. vendor/vendor/composer/autoload_real.php +4 -4
functions/functions/cache/KeyGenerator.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber\Cache;
4
+
5
+ use Asm89\Twig\CacheExtension\CacheStrategy\KeyGeneratorInterface;
6
+
7
+ class KeyGenerator implements KeyGeneratorInterface {
8
+
9
+ /**
10
+ * @param mixed $value
11
+ * @return string
12
+ */
13
+ public function generateKey($value) {
14
+ if (is_a($value, 'TimberKeyGeneratorInterface')) {
15
+ return $value->_get_cache_key();
16
+ }
17
+
18
+ if (is_array($value) && isset($value['_cache_key'])) {
19
+ return $value['_cache_key'];
20
+ }
21
+
22
+ $key = md5(json_encode($value));
23
+ if (is_object($value)) {
24
+ $key = get_class($value) . '|' . $key;
25
+ }
26
+
27
+ return $key;
28
+ }
29
+
30
+ }
functions/functions/cache/TimberKeyGeneratorInterface.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber\Cache;
4
+
5
+ interface TimberKeyGeneratorInterface
6
+ {
7
+ public function _get_cache_key();
8
+ }
functions/functions/cache/WPObjectCacheAdapter.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php namespace Timber\Cache;
2
+
3
+ use Asm89\Twig\CacheExtension\CacheProviderInterface;
4
+ use TimberLoader;
5
+
6
+ class WPObjectCacheAdapter implements CacheProviderInterface
7
+ {
8
+
9
+ private $cache_group;
10
+
11
+ /**
12
+ * @var TimberLoader
13
+ */
14
+ private $timberloader;
15
+
16
+ public function __construct(TimberLoader $timberloader, $cache_group = 'timber') {
17
+ $this->cache_group = $cache_group;
18
+ $this->timberloader = $timberloader;
19
+ }
20
+
21
+ public function fetch($key) {
22
+ return $this->timberloader->get_cache($key, $this->cache_group, TimberLoader::CACHE_USE_DEFAULT);
23
+ }
24
+
25
+ public function save($key, $value, $expire = 0) {
26
+ return $this->timberloader->set_cache($key, $value, $this->cache_group, $expire, TimberLoader::CACHE_USE_DEFAULT);
27
+ }
28
+
29
+ }
functions/functions/cache/loader.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberCache_Loader
4
+ {
5
+
6
+ public static function register($prepend = false) {
7
+ if (version_compare(phpversion(), '5.3.0', '>=')) {
8
+ spl_autoload_register(array(new self, 'autoload'), true, $prepend);
9
+ } else {
10
+ spl_autoload_register(array(new self, 'autoload'));
11
+ }
12
+ }
13
+
14
+ public static function autoload($class) {
15
+ if (0 === strpos($class, 'Timber\Cache')) {
16
+ $classes = explode('\\', $class);
17
+ array_splice($classes, 0, 2);
18
+ $path = implode($classes, '/');
19
+ if (is_file($file = dirname(__FILE__) . '/' . $path . '.php')) {
20
+ require $file;
21
+ }
22
+ }
23
+ }
24
+ }
functions/functions/integrations/acf-timber.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ACFTimber {
4
+
5
+ function __construct() {
6
+ add_filter( 'timber_post_get_meta', array( $this, 'post_get_meta' ), 10, 2 );
7
+ add_filter( 'timber_post_get_meta_field', array( $this, 'post_get_meta_field' ), 10, 3 );
8
+ add_filter( 'timber_term_get_meta', array( $this, 'term_get_meta' ), 10, 3 );
9
+ add_filter( 'timber_term_get_meta_field', array( $this, 'term_get_meta_field' ), 10, 4 );
10
+ add_filter( 'timber_user_get_meta_field_pre', array( $this, 'user_get_meta_field' ), 10, 3 );
11
+ add_filter( 'timber_term_set_meta', array( $this, 'term_set_meta'), 10, 4 );
12
+ }
13
+
14
+ function post_get_meta( $customs, $post_id ) {
15
+ return $customs;
16
+ }
17
+
18
+ function post_get_meta_field( $value, $post_id, $field_name ) {
19
+ return get_field( $field_name, $post_id );
20
+ }
21
+
22
+ function term_get_meta_field( $value, $term_id, $field_name, $term ) {
23
+ $searcher = $term->taxonomy . "_" . $term->ID;
24
+ return get_field( $field_name, $searcher );
25
+ }
26
+
27
+ function term_set_meta( $value, $field, $term_id, $term ) {
28
+ $searcher = $term->taxonomy . "_" . $term->ID;
29
+ update_field( $field, $value, $searcher );
30
+ return $value;
31
+ }
32
+
33
+ function term_get_meta( $fields, $term_id, $term ) {
34
+ $searcher = $term->taxonomy . "_" . $term->ID; // save to a specific category
35
+ $fds = get_fields( $searcher );
36
+ if ( is_array( $fds ) ) {
37
+ foreach ( $fds as $key => $value ) {
38
+ $key = preg_replace( '/_/', '', $key, 1 );
39
+ $key = str_replace( $searcher, '', $key );
40
+ $key = preg_replace( '/_/', '', $key, 1 );
41
+ $field = get_field( $key, $searcher );
42
+ $fields[$key] = $field;
43
+ }
44
+ $fields = array_merge( $fields, $fds );
45
+ }
46
+ return $fields;
47
+ }
48
+
49
+ function user_get_meta( $fields, $user_id ) {
50
+ return $fields;
51
+ }
52
+
53
+ function user_get_meta_field( $value, $uid, $field ) {
54
+ return get_field( $field, 'user_' . $uid );
55
+ }
56
+ }
57
+
58
+ add_action( 'init', function () {
59
+ if ( class_exists( 'ACF' ) ) {
60
+ new ACFTimber();
61
+ }
62
+ } );
functions/functions/integrations/wpcli-timber.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (class_exists('WP_CLI_Command')) {
3
+ class Timber_WP_CLI_Command extends WP_CLI_Command {
4
+
5
+ /**
6
+ * Clears Timber and Twig's Cache
7
+ *
8
+ * ## EXAMPLES
9
+ *
10
+ * wp timber clear_cache
11
+ *
12
+ */
13
+ public function clear_cache($mode = 'all') {
14
+ TimberCommand::clear_cache($mode);
15
+ }
16
+
17
+ /**
18
+ * Clears Twig's Cache
19
+ *
20
+ * ## EXAMPLES
21
+ *
22
+ * wp timber clear_cache_twig
23
+ *
24
+ */
25
+ function clear_cache_twig(){
26
+ $clear = TimberCommand::clear_cache_twig();
27
+ if ($clear){
28
+ WP_CLI::success('Cleared contents of twig cache');
29
+ } else {
30
+ WP_CLI::warning('Failed to clear twig cache');
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Clears Timber's Cache
36
+ *
37
+ * ## EXAMPLES
38
+ *
39
+ * wp timber clear_cache_timber
40
+ *
41
+ */
42
+ function clear_cache_timber() {
43
+ $clear = TimberCommand::clear_cache_timber();
44
+ $message = 'Failed to clear timber cache';
45
+ if ($clear){
46
+ $message = "Cleared contents of Timber's Cache";
47
+ WP_CLI::success($message);
48
+ } else {
49
+ WP_CLI::warning($message);
50
+ }
51
+ return $message;
52
+ }
53
+
54
+ }
55
+
56
+ WP_CLI::add_command('timber', 'Timber_WP_CLI_Command');
57
+ }
58
+
59
+ class TimberCommand {
60
+
61
+ public static function clear_cache($mode = 'all'){
62
+ if (is_array($mode)){
63
+ $mode = reset($mode);
64
+ }
65
+ if ($mode == 'all') {
66
+ $twig_cache = self::clear_cache_twig();
67
+ $timber_cache = self::clear_cache_timber();
68
+ if ($twig_cache && $timber_cache){
69
+ return true;
70
+ }
71
+ } else if ($mode == 'twig') {
72
+ return self::clear_cache_twig();
73
+ } else if ($mode == 'timber') {
74
+ return self::clear_cache_timber();
75
+ }
76
+ }
77
+
78
+ static function clear_cache_timber(){
79
+ $loader = new TimberLoader();
80
+ return $loader->clear_cache_timber();
81
+ }
82
+
83
+ static function clear_cache_twig(){
84
+ $loader = new TimberLoader();
85
+ return $loader->clear_cache_twig();
86
+ }
87
+
88
+ }
functions/functions/timber-admin.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberAdmin {
4
+
5
+ function __construct() {
6
+ add_filter( 'plugin_row_meta', array( $this, 'meta_links' ), 10, 2 );
7
+ }
8
+
9
+ /**
10
+ *
11
+ *
12
+ * @param array $links
13
+ * @param string $file
14
+ * @return array
15
+ */
16
+ function meta_links( $links, $file ) {
17
+ if ( strstr( $file, '/timber.php' ) ) {
18
+ unset($links[2]);
19
+ $links[] = '<a href="http://localhost/wp-admin/plugin-install.php?tab=plugin-information&amp;plugin=timber-library&amp;TB_iframe=true&amp;width=600&amp;height=550" class="thickbox" aria-label="More information about Timber" data-title="Timber">View details</a>';
20
+ $links[] = '<a href="http://upstatement.com/timber" target="_blank">Homepage</a>';
21
+ $links[] = '<a href="https://github.com/jarednova/timber/wiki" target="_blank">Documentation</a>';
22
+ $links[] = '<a href="https://github.com/jarednova/timber/wiki/getting-started" target="_blank">Starter Guide</a>';
23
+ return $links;
24
+ }
25
+ return $links;
26
+ }
27
+
28
+ }
29
+
30
+ global $timber_admin;
31
+ $timber_admin = new TimberAdmin();
functions/functions/timber-archives.php ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberArchives extends TimberCore
4
+ {
5
+
6
+ public $base = '';
7
+ public $items;
8
+
9
+ function __construct($args = null, $base = '') {
10
+ $this->init($args, $base);
11
+ }
12
+
13
+ /**
14
+ * @param array|string $args
15
+ * @param string $base
16
+ */
17
+ function init($args = null, $base = '') {
18
+ $this->base = $base;
19
+ $this->items = $this->get_items($args);
20
+ }
21
+
22
+ /**
23
+ * @param string $url
24
+ * @param string $text
25
+ * @return mixed
26
+ */
27
+ function get_archives_link($url, $text) {
28
+ $ret = array();
29
+ $ret['text'] = $ret['title'] = $ret['name'] = wptexturize($text);
30
+ $ret['url'] = $ret['link'] = esc_url(TimberURLHelper::prepend_to_url($url, $this->base));
31
+ return $ret;
32
+ }
33
+
34
+ /**
35
+ * @param array|string $args
36
+ * @param string $last_changed
37
+ * @param string $join
38
+ * @param string $where
39
+ * @param string $order
40
+ * @param string $limit
41
+ * @return array
42
+ */
43
+ function get_items_yearly($args, $last_changed, $join, $where, $order, $limit) {
44
+ global $wpdb;
45
+ $output = array();
46
+ $query = "SELECT YEAR(post_date) AS `year`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date) ORDER BY post_date $order $limit";
47
+ $key = md5($query);
48
+ $key = "wp_get_archives:$key:$last_changed";
49
+ if (!$results = wp_cache_get($key, 'posts')) {
50
+ $results = $wpdb->get_results($query);
51
+ wp_cache_set($key, $results, 'posts');
52
+ }
53
+ if ($results) {
54
+ foreach ((array)$results as $result) {
55
+ $url = get_year_link($result->year);
56
+ $text = sprintf('%d', $result->year);
57
+ $output[] = $this->get_archives_link($url, $text);
58
+ }
59
+ }
60
+ return $output;
61
+ }
62
+
63
+ /**
64
+ * @param array|string $args
65
+ * @param string $last_changed
66
+ * @param string $join
67
+ * @param string $where
68
+ * @param string $order
69
+ * @param int $limit
70
+ * @param bool $nested
71
+ * @return array
72
+ */
73
+ function get_items_montly($args, $last_changed, $join, $where, $order, $limit = 1000, $nested = true) {
74
+ global $wpdb, $wp_locale;
75
+ $output = array();
76
+ $defaults = array(
77
+ 'show_year' => true,
78
+ );
79
+ $r = wp_parse_args($args, $defaults);
80
+
81
+ $show_year = null;
82
+ extract($r, EXTR_SKIP);
83
+
84
+ //will need to specify which year we're looking for
85
+ $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts "
86
+ . "FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) "
87
+ . "ORDER BY post_date $order $limit";
88
+ $key = md5($query);
89
+ $key = "wp_get_archives:$key:$last_changed";
90
+ if (!$results = wp_cache_get($key, 'posts')) {
91
+ $results = $wpdb->get_results($query);
92
+ wp_cache_set($key, $results, 'posts');
93
+ }
94
+ if ($results) {
95
+ foreach ((array)$results as $result) {
96
+ $url = get_month_link($result->year, $result->month);
97
+ /* translators: 1: month name, 2: 4-digit year */
98
+
99
+ if ($show_year && !$nested) {
100
+ $text = sprintf(__('%1$s %2$d'), $wp_locale->get_month($result->month), $result->year);
101
+ } else {
102
+ $text = sprintf(__('%1$s'), $wp_locale->get_month($result->month));
103
+ }
104
+ if ($nested) {
105
+ $output[$result->year][] = $this->get_archives_link($url, $text);
106
+ } else {
107
+ $output[] = $this->get_archives_link($url, $text);
108
+ }
109
+ }
110
+ }
111
+ if ($nested) {
112
+ $out2 = array();
113
+ foreach ($output as $year => $months) {
114
+ $out2[] = array('name' => $year, 'children' => $months);
115
+ }
116
+ return $out2;
117
+ }
118
+ return $output;
119
+ }
120
+
121
+ /**
122
+ * @param array|string $args
123
+ * @return array|string
124
+ */
125
+ function get_items($args = null) {
126
+ global $wpdb;
127
+
128
+ $defaults = array(
129
+ 'type' => 'monthly', 'limit' => '',
130
+ 'format' => 'html', 'before' => '',
131
+ 'after' => '', 'show_post_count' => false,
132
+ 'order' => 'DESC',
133
+ 'post_type' => 'post',
134
+ 'nested' => true
135
+ );
136
+
137
+ $r = wp_parse_args($args, $defaults);
138
+ $type = $limit = $order = $post_type = $nested = $format = $before = $after = null;
139
+ extract($r, EXTR_SKIP);
140
+
141
+ if (empty($order)){
142
+ $order = 'DESC';
143
+ }
144
+
145
+ if (empty($post_type)){
146
+ $post_type = 'post';
147
+ }
148
+ if (empty($type)) {
149
+ $type = 'monthly';
150
+ }
151
+
152
+ if (!empty($limit)) {
153
+ $limit = absint($limit);
154
+ $limit = ' LIMIT ' . $limit;
155
+ }
156
+
157
+ $order = strtoupper($order);
158
+ if ($order !== 'ASC') {
159
+ $order = 'DESC';
160
+ }
161
+
162
+ // this is what will separate dates on weekly archive links
163
+ $archive_week_separator = '&#8211;';
164
+
165
+ // over-ride general date format ? 0 = no: use the date format set in Options, 1 = yes: over-ride
166
+ $archive_date_format_over_ride = 0;
167
+
168
+ // options for daily archive (only if you over-ride the general date format)
169
+ $archive_day_date_format = 'Y/m/d';
170
+
171
+ // options for weekly archive (only if you over-ride the general date format)
172
+ $archive_week_start_date_format = 'Y/m/d';
173
+ $archive_week_end_date_format = 'Y/m/d';
174
+
175
+ if (!$archive_date_format_over_ride) {
176
+ $archive_day_date_format = get_option('date_format');
177
+ $archive_week_start_date_format = get_option('date_format');
178
+ $archive_week_end_date_format = get_option('date_format');
179
+ }
180
+
181
+ $where = apply_filters('getarchives_where', 'WHERE post_type = "' . $post_type . '" AND post_status = "publish"', $r);
182
+ $join = apply_filters('getarchives_join', '', $r);
183
+
184
+ $output = array();
185
+
186
+ $last_changed = wp_cache_get('last_changed', 'posts');
187
+ if (!$last_changed) {
188
+ $last_changed = microtime();
189
+ wp_cache_set('last_changed', $last_changed, 'posts');
190
+ }
191
+ if ('monthly' == $type) {
192
+ $output = $this->get_items_montly($args, $last_changed, $join, $where, $order, $limit, $nested);
193
+ } elseif ('yearly' == $type) {
194
+ $output = $this->get_items_yearly($args, $last_changed, $join, $where, $order, $limit);
195
+ } elseif ('yearlymonthly' == $type || 'yearmonth' == $type) {
196
+ $years = $this->get_items_yearly($args, $last_changed, $join, $where, $order, $limit);
197
+ foreach ($years as &$year) {
198
+ $args = array('show_year' => false);
199
+ $year['children'] = $this->get_items_montly($args, $last_changed, $join, $where, $order, $limit);
200
+ }
201
+ $output = $years;
202
+ } elseif ('daily' == $type) {
203
+ $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, DAYOFMONTH(post_date) AS `dayofmonth`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date), DAYOFMONTH(post_date) ORDER BY post_date $order $limit";
204
+ $key = md5($query);
205
+ $key = "wp_get_archives:$key:$last_changed";
206
+ if (!$results = wp_cache_get($key, 'posts')) {
207
+ $results = $wpdb->get_results($query);
208
+ $cache = array();
209
+ $cache[$key] = $results;
210
+ wp_cache_set($key, $results, 'posts');
211
+ }
212
+ if ($results) {
213
+ foreach ((array)$results as $result) {
214
+ $url = get_day_link($result->year, $result->month, $result->dayofmonth);
215
+ $date = sprintf('%1$d-%2$02d-%3$02d 00:00:00', $result->year, $result->month, $result->dayofmonth);
216
+ $text = mysql2date($archive_day_date_format, $date);
217
+ $output[] = $this->get_archives_link($url, $text);
218
+ }
219
+ }
220
+ } elseif ('weekly' == $type) {
221
+ $week = _wp_mysql_week('`post_date`');
222
+ $query = "SELECT DISTINCT $week AS `week`, YEAR( `post_date` ) AS `yr`, DATE_FORMAT( `post_date`, '%Y-%m-%d' ) AS `yyyymmdd`, "
223
+ . "count( `ID` ) AS `posts` FROM `$wpdb->posts` $join $where GROUP BY $week, YEAR( `post_date` ) ORDER BY `post_date` $order $limit";
224
+ $key = md5($query);
225
+ $key = "wp_get_archives:$key:$last_changed";
226
+ if (!$results = wp_cache_get($key, 'posts')) {
227
+ $results = $wpdb->get_results($query);
228
+ wp_cache_set($key, $results, 'posts');
229
+ }
230
+ $arc_w_last = '';
231
+ if ($results) {
232
+ foreach ((array)$results as $result) {
233
+ if ($result->week != $arc_w_last) {
234
+ $arc_year = $result->yr;
235
+ $arc_w_last = $result->week;
236
+ $arc_week = get_weekstartend($result->yyyymmdd, get_option('start_of_week'));
237
+ $arc_week_start = date_i18n($archive_week_start_date_format, $arc_week['start']);
238
+ $arc_week_end = date_i18n($archive_week_end_date_format, $arc_week['end']);
239
+ $url = sprintf('%1$s/%2$s%3$sm%4$s%5$s%6$sw%7$s%8$d', home_url(), '', '?', '=', $arc_year, '&amp;', '=', $result->week);
240
+ $text = $arc_week_start . $archive_week_separator . $arc_week_end;
241
+ $output[] = $this->get_archives_link($url, $text);
242
+ }
243
+ }
244
+ }
245
+ } elseif ('postbypost' == $type || 'alpha' == $type) {
246
+ $orderby = 'alpha' == $type ? 'post_title ASC ' : 'post_date DESC ';
247
+ $query = "SELECT * FROM $wpdb->posts $join $where ORDER BY $orderby $limit";
248
+ $key = md5($query);
249
+ $key = "wp_get_archives:$key:$last_changed";
250
+ if (!$results = wp_cache_get($key, 'posts')) {
251
+ $results = $wpdb->get_results($query);
252
+ wp_cache_set($key, $results, 'posts');
253
+ }
254
+ if ($results) {
255
+ foreach ((array)$results as $result) {
256
+ if ($result->post_date != '0000-00-00 00:00:00') {
257
+ $url = get_permalink($result);
258
+ if ($result->post_title) {
259
+ /** This filter is documented in wp-includes/post-template.php */
260
+ $text = strip_tags(apply_filters('the_title', $result->post_title, $result->ID));
261
+ } else {
262
+ $text = $result->ID;
263
+ }
264
+ $output .= get_archives_link($url, $text, $format, $before, $after);
265
+ }
266
+ }
267
+ }
268
+ }
269
+ return $output;
270
+ }
271
+
272
+ }
functions/functions/timber-comment.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberComment extends TimberCore implements TimberCoreInterface {
4
+
5
+ public $PostClass = 'TimberPost';
6
+ public $object_type = 'comment';
7
+
8
+ public static $representation = 'comment';
9
+
10
+ public $ID;
11
+ public $id;
12
+ public $comment_author_email;
13
+ public $comment_content;
14
+ public $comment_date;
15
+ public $comment_ID;
16
+ public $user_id;
17
+ public $comment_author;
18
+
19
+ /**
20
+ * @param int $cid
21
+ */
22
+ function __construct($cid) {
23
+ $this->init($cid);
24
+ }
25
+
26
+ function __toString(){
27
+ return $this->content();
28
+ }
29
+
30
+ /**
31
+ * @param integer $cid
32
+ */
33
+ function init($cid) {
34
+ $comment_data = $cid;
35
+ if (is_integer($cid)) {
36
+ $comment_data = get_comment($cid);
37
+ }
38
+ $this->import($comment_data);
39
+ $this->ID = $this->comment_ID;
40
+ $this->id = $this->comment_ID;
41
+ $comment_meta_data = $this->get_meta_fields($this->ID);
42
+ $this->import($comment_meta_data);
43
+ }
44
+
45
+ /**
46
+ * @return TimberUser
47
+ */
48
+ public function author() {
49
+ if ($this->user_id) {
50
+ return new TimberUser($this->user_id);
51
+ } else {
52
+ $author = new TimberUser(0);
53
+ if (isset($this->comment_author) && $this->comment_author) {
54
+ $author->name = $this->comment_author;
55
+ } else {
56
+ $author->name = 'Anonymous';
57
+ }
58
+ }
59
+ return $author;
60
+ }
61
+
62
+ /**
63
+ * @param int $size
64
+ * @param string $default
65
+ * @return bool|mixed|string
66
+ */
67
+ public function avatar($size = 92, $default = '') {
68
+ // Fetches the Gravatar
69
+ // use it like this
70
+ // {{comment.avatar(36,template_uri~"/img/dude.jpg")}}
71
+
72
+ if (!get_option('show_avatars')) {
73
+ return false;
74
+ }
75
+ if (!is_numeric($size)) {
76
+ $size = '92';
77
+ }
78
+
79
+ $email = $this->avatar_email();
80
+ $email_hash = '';
81
+ if (!empty($email)) {
82
+ $email_hash = md5(strtolower(trim($email)));
83
+ }
84
+ $host = $this->avatar_host($email_hash);
85
+ $default = $this->avatar_default($default, $email, $size, $host);
86
+ if (!empty($email)) {
87
+ $avatar = $this->avatar_out($email, $default, $host, $email_hash, $size);
88
+ } else {
89
+ $avatar = $default;
90
+ }
91
+ return $avatar;
92
+ }
93
+
94
+ /**
95
+ * @return string
96
+ */
97
+ public function content() {
98
+ return $this->comment_content;
99
+ }
100
+
101
+ /**
102
+ * @return string
103
+ */
104
+ public function date() {
105
+ return $this->comment_date;
106
+ }
107
+
108
+ /**
109
+ * @param string $field_name
110
+ * @return mixed
111
+ */
112
+ public function meta($field_name) {
113
+ return $this->get_meta_field($field_name);
114
+ }
115
+
116
+ /**
117
+ * @param int $comment_id
118
+ * @return mixed
119
+ */
120
+ private function get_meta_fields($comment_id = null) {
121
+ if ($comment_id === null) {
122
+ $comment_id = $this->ID;
123
+ }
124
+ //Could not find a WP function to fetch all comment meta data, so I made one.
125
+ apply_filters('timber_comment_get_meta_pre', array(), $comment_id);
126
+ $comment_metas = get_comment_meta($comment_id);
127
+ foreach ($comment_metas as &$cm) {
128
+ if (is_array($cm) && count($cm) == 1) {
129
+ $cm = $cm[0];
130
+ }
131
+ }
132
+ $comment_metas = apply_filters('timber_comment_get_meta', $comment_metas, $comment_id);
133
+ return $comment_metas;
134
+ }
135
+
136
+ /**
137
+ * @param string $field_name
138
+ * @return mixed
139
+ */
140
+ private function get_meta_field($field_name) {
141
+ $value = apply_filters('timber_comment_get_meta_field_pre', null, $this->ID, $field_name, $this);
142
+ if ($value === null) {
143
+ $value = get_comment_meta($this->ID, $field_name, true);
144
+ }
145
+ $value = apply_filters('timber_comment_get_meta_field', $value, $this->ID, $field_name, $this);
146
+ return $value;
147
+ }
148
+
149
+ /* AVATAR Stuff
150
+ ======================= */
151
+
152
+ /**
153
+ * @return string
154
+ */
155
+ private function avatar_email() {
156
+ $id = (int)$this->user_id;
157
+ $user = get_userdata($id);
158
+ if ($user) {
159
+ $email = $user->user_email;
160
+ } else {
161
+ $email = $this->comment_author_email;
162
+ }
163
+ return $email;
164
+ }
165
+
166
+ /**
167
+ * @param string $email_hash
168
+ * @return string
169
+ */
170
+ private function avatar_host($email_hash) {
171
+ if (is_ssl()) {
172
+ $host = 'https://secure.gravatar.com';
173
+ } else {
174
+ if (!empty($email_hash)) {
175
+ $host = sprintf("http://%d.gravatar.com", (hexdec($email_hash[0]) % 2));
176
+ } else {
177
+ $host = 'http://0.gravatar.com';
178
+ }
179
+ }
180
+ return $host;
181
+ }
182
+
183
+ /**
184
+ * @param string $default
185
+ * @param string $email
186
+ * @param string $size
187
+ * @param string $host
188
+ * @return string
189
+ */
190
+ private function avatar_default($default, $email, $size, $host) {
191
+ # what if its relative.
192
+ if (substr($default, 0, 1) == '/') {
193
+ $default = home_url() . $default;
194
+ }
195
+
196
+ if (empty($default)) {
197
+ $avatar_default = get_option('avatar_default');
198
+ if (empty($avatar_default)) {
199
+ $default = 'mystery';
200
+ } else {
201
+ $default = $avatar_default;
202
+ }
203
+ }
204
+ if ('mystery' == $default) {
205
+ $default = $host . '/avatar/ad516503a11cd5ca435acc9bb6523536?s=' . $size;
206
+ // ad516503a11cd5ca435acc9bb6523536 == md5('unknown@gravatar.com')
207
+ } else if ('blank' == $default) {
208
+ $default = $email ? 'blank' : includes_url('images/blank.gif');
209
+ } else if (!empty($email) && 'gravatar_default' == $default) {
210
+ $default = '';
211
+ } else if ('gravatar_default' == $default) {
212
+ $default = $host . '/avatar/?s=' . $size;
213
+ } else if (empty($email) && !strstr($default, 'http://')) {
214
+ $default = $host . '/avatar/?d=' . $default . '&amp;s=' . $size;
215
+ } else if (strpos($default, 'http://') === 0) {
216
+ //theyre just linking to an image so don't do anything else
217
+ //$default = add_query_arg( 's', $size, $default );
218
+ }
219
+ return $default;
220
+ }
221
+
222
+ /**
223
+ * @param string $email
224
+ * @param string $default
225
+ * @param string $host
226
+ * @param string $email_hash
227
+ * @param string $size
228
+ * @return mixed
229
+ */
230
+ private function avatar_out($email, $default, $host, $email_hash, $size) {
231
+ $out = $host . '/avatar/' . $email_hash . '?s=' . $size . '&amp;d=' . urlencode($default);
232
+ $rating = get_option('avatar_rating');
233
+ if (!empty($rating)) {
234
+ $out .= '&amp;r=' . $rating;
235
+ }
236
+ return str_replace('&#038;', '&amp;', esc_url($out));
237
+ }
238
+
239
+ }
functions/functions/timber-core-interface.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ interface TimberCoreInterface {
4
+
5
+ public function __call( $field, $args );
6
+
7
+ public function __get( $field );
8
+
9
+ /**
10
+ * @return boolean
11
+ */
12
+ public function __isset( $field );
13
+
14
+ public function meta( $key );
15
+
16
+ }
functions/functions/timber-core.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class TimberCore {
4
+
5
+ public $id;
6
+ public $ID;
7
+ public $object_type;
8
+ public $_can_edit;
9
+
10
+ /**
11
+ *
12
+ *
13
+ * @return boolean
14
+ */
15
+ function __isset( $field ) {
16
+ if ( isset( $this->$field ) ) {
17
+ return $this->$field;
18
+ }
19
+ return false;
20
+ }
21
+
22
+ /**
23
+ * This is helpful for twig to return properties and methods see: https://github.com/fabpot/Twig/issues/2
24
+ * @return mixed
25
+ */
26
+ function __call( $field, $args ) {
27
+ return $this->__get( $field );
28
+ }
29
+
30
+ /**
31
+ * This is helpful for twig to return properties and methods see: https://github.com/fabpot/Twig/issues/2
32
+ *
33
+ * @return mixed
34
+ */
35
+ function __get( $field ) {
36
+ if ( isset( $this->$field ) ) {
37
+ return $this->$field;
38
+ }
39
+ if ( $meta_value = $this->meta( $field ) ) {
40
+ return $this->$field = $meta_value;
41
+ }
42
+ if (method_exists($this, $field)) {
43
+ return $this->$field = $this->$field();
44
+ }
45
+ return $this->$field = false;
46
+ }
47
+
48
+ /**
49
+ *
50
+ *
51
+ * @param array|object $info an object or array you want to grab data from to attach to the Timber object
52
+ */
53
+ function import( $info, $force = false ) {
54
+ if ( is_object( $info ) ) {
55
+ $info = get_object_vars( $info );
56
+ }
57
+ if ( is_array( $info ) ) {
58
+ foreach ( $info as $key => $value ) {
59
+ if ( !empty( $key ) && $force ) {
60
+ $this->$key = $value;
61
+ } else if ( !empty( $key ) && !method_exists($this, $key) ){
62
+ $this->$key = $value;
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+
69
+ /**
70
+ *
71
+ *
72
+ * @param string $key
73
+ * @param mixed $value
74
+ */
75
+ function update( $key, $value ) {
76
+ update_metadata( $this->object_type, $this->ID, $key, $value );
77
+ }
78
+
79
+ /**
80
+ *
81
+ *
82
+ * @return bool
83
+ */
84
+ function can_edit() {
85
+ if ( isset( $this->_can_edit ) ) {
86
+ return $this->_can_edit;
87
+ }
88
+ $this->_can_edit = false;
89
+ if ( !function_exists( 'current_user_can' ) ) {
90
+ return false;
91
+ }
92
+ if ( current_user_can( 'edit_post', $this->ID ) ) {
93
+ $this->_can_edit = true;
94
+ }
95
+ return $this->_can_edit;
96
+ }
97
+
98
+ /**
99
+ *
100
+ *
101
+ * @return array
102
+ */
103
+ function get_method_values() {
104
+ $ret = array();
105
+ $ret['can_edit'] = $this->can_edit();
106
+ return $ret;
107
+ }
108
+
109
+ }
functions/functions/timber-function-wrapper.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberFunctionWrapper
4
+ {
5
+
6
+ private $_function;
7
+ private $_args;
8
+ private $_use_ob;
9
+
10
+ public function __toString() {
11
+ return $this->call();
12
+ }
13
+
14
+ /**
15
+ * @param callable $function
16
+ * @param array $args
17
+ * @param bool $return_output_buffer
18
+ */
19
+ public function __construct($function, $args = array(), $return_output_buffer = false) {
20
+ $this->_function = $function;
21
+ $this->_args = $args;
22
+ $this->_use_ob = $return_output_buffer;
23
+
24
+ add_filter('get_twig', array(&$this, 'add_to_twig'));
25
+ }
26
+
27
+ /**
28
+ * @param Twig_Environment $twig
29
+ * @return Twig_Environment
30
+ */
31
+ public function add_to_twig($twig) {
32
+ $wrapper = $this;
33
+
34
+ $twig->addFunction(new Twig_SimpleFunction($this->_function, function () use ($wrapper) {
35
+ return call_user_func_array(array($wrapper, 'call'), func_get_args());
36
+ }));
37
+
38
+ return $twig;
39
+ }
40
+
41
+ /**
42
+ * @return string
43
+ */
44
+ public function call() {
45
+ $args = $this->_parse_args(func_get_args(), $this->_args);
46
+
47
+ if ($this->_use_ob) {
48
+ return TimberHelper::ob_function($this->_function, $args);
49
+ } else {
50
+ return (string)call_user_func_array($this->_function, $args);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * @param array $args
56
+ * @param array $defaults
57
+ * @return array
58
+ */
59
+ private function _parse_args($args, $defaults) {
60
+ $_arg = reset($defaults);
61
+
62
+ foreach ($args as $index => $arg) {
63
+ $defaults[$index] = is_null($arg) ? $_arg : $arg;
64
+ $_arg = next($defaults);
65
+ }
66
+
67
+ return $defaults;
68
+ }
69
+
70
+ }
functions/functions/timber-helper.php ADDED
@@ -0,0 +1,684 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberHelper {
4
+
5
+ /**
6
+ *
7
+ *
8
+ * @param string $slug Unique identifier for transient
9
+ * @param callable $callback Callback that generates the data that's to be cached
10
+ * @param int $transient_time (optional) Expiration of transients in seconds
11
+ * @param int $lock_timeout (optional) How long to lock the transient to prevent race conditions
12
+ * @param bool $force (optional) Force callback to be executed when transient is locked
13
+ * @return mixed
14
+ */
15
+ public static function transient( $slug, $callback, $transient_time = 0, $lock_timeout = 5, $force = false ) {
16
+
17
+ $enable_transients = ( $transient_time === false || ( defined( 'WP_DISABLE_TRANSIENTS' ) && WP_DISABLE_TRANSIENTS ) ) ? false : true;
18
+ $data = $enable_transients ? get_transient( $slug ) : false;
19
+
20
+ if ( false === $data ) {
21
+
22
+ if ( $enable_transients && self::_is_transient_locked( $slug ) ) {
23
+
24
+ $force = apply_filters( 'timber_force_transients', $force );
25
+ $force = apply_filters( 'timber_force_transient_' . $slug, $force );
26
+
27
+ if ( !$force ) {
28
+ //the server is currently executing the process.
29
+ //We're just gonna dump these users. Sorry!
30
+ return false;
31
+ }
32
+
33
+ $enable_transients = false;
34
+ }
35
+
36
+ // lock timeout shouldn't be higher than 5 seconds, unless
37
+ // remote calls with high timeouts are made here
38
+ if ( $enable_transients )
39
+ self::_lock_transient( $slug, $lock_timeout );
40
+
41
+ $data = $callback();
42
+
43
+ if ( $enable_transients ) {
44
+ set_transient( $slug, $data, $transient_time );
45
+ self::_unlock_transient( $slug );
46
+ }
47
+
48
+ }
49
+
50
+ return $data;
51
+
52
+ }
53
+
54
+ /**
55
+ * @param string $slug
56
+ * @param integer $lock_timeout
57
+ */
58
+ public static function _lock_transient( $slug, $lock_timeout ) {
59
+ set_transient( $slug . '_lock', true, $lock_timeout );
60
+ }
61
+
62
+ /**
63
+ * @param string $slug
64
+ */
65
+ public static function _unlock_transient( $slug ) {
66
+ delete_transient( $slug . '_lock', true );
67
+ }
68
+
69
+ /**
70
+ * @param string $slug
71
+ */
72
+ public static function _is_transient_locked( $slug ) {
73
+ return (bool)get_transient( $slug . '_lock' );
74
+ }
75
+
76
+ /* These are for measuring page render time */
77
+
78
+ /**
79
+ *
80
+ *
81
+ * @return float
82
+ */
83
+ public static function start_timer() {
84
+ $time = microtime();
85
+ $time = explode( ' ', $time );
86
+ $time = $time[1] + $time[0];
87
+ return $time;
88
+ }
89
+
90
+ /**
91
+ *
92
+ *
93
+ * @param int $start
94
+ * @return string
95
+ */
96
+ public static function stop_timer( $start ) {
97
+ $time = microtime();
98
+ $time = explode( ' ', $time );
99
+ $time = $time[1] + $time[0];
100
+ $finish = $time;
101
+ $total_time = round( ( $finish - $start ), 4 );
102
+ return $total_time . ' seconds.';
103
+ }
104
+
105
+ /* Function Utilities
106
+ ======================== */
107
+
108
+ /**
109
+ *
110
+ *
111
+ * @param callback $function
112
+ * @param array $args
113
+ * @return string
114
+ */
115
+ public static function ob_function( $function, $args = array( null ) ) {
116
+ ob_start();
117
+ call_user_func_array( $function, $args );
118
+ $data = ob_get_contents();
119
+ ob_end_clean();
120
+ return $data;
121
+ }
122
+
123
+ /**
124
+ *
125
+ *
126
+ * @param string $function_name
127
+ * @param integer[] $defaults
128
+ * @param bool $return_output_buffer
129
+ * @return TimberFunctionWrapper
130
+ */
131
+ public static function function_wrapper( $function_name, $defaults = array(), $return_output_buffer = false ) {
132
+ return new TimberFunctionWrapper( $function_name, $defaults, $return_output_buffer );
133
+ }
134
+
135
+ /**
136
+ *
137
+ *
138
+ * @param unknown $arg
139
+ * @return void
140
+ */
141
+ public static function error_log( $arg ) {
142
+ if ( !WP_DEBUG ) {
143
+ return;
144
+ }
145
+ if ( is_object( $arg ) || is_array( $arg ) ) {
146
+ $arg = print_r( $arg, true );
147
+ }
148
+ error_log( $arg );
149
+ }
150
+
151
+ /**
152
+ *
153
+ *
154
+ * @param string $separator
155
+ * @param string $seplocation
156
+ * @return string
157
+ */
158
+ public static function get_wp_title( $separator = ' ', $seplocation = 'left' ) {
159
+ $separator = apply_filters( 'timber_wp_title_seperator', $separator );
160
+ return trim( wp_title( $separator, false, $seplocation ) );
161
+ }
162
+
163
+ /* Text Utilities
164
+ ======================== */
165
+
166
+ /**
167
+ *
168
+ *
169
+ * @param string $text
170
+ * @param int $num_words
171
+ * @param string $more
172
+ * @param string $allowed_tags
173
+ * @return string
174
+ */
175
+ public static function trim_words( $text, $num_words = 55, $more = null, $allowed_tags = 'p a span b i br' ) {
176
+ if ( null === $more ) {
177
+ $more = __( '&hellip;' );
178
+ }
179
+ $original_text = $text;
180
+ $allowed_tag_string = '';
181
+ foreach ( explode( ' ', $allowed_tags ) as $tag ) {
182
+ $allowed_tag_string .= '<' . $tag . '>';
183
+ }
184
+ $text = strip_tags( $text, $allowed_tag_string );
185
+ /* translators: If your word count is based on single characters (East Asian characters),
186
+ enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
187
+ if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
188
+ $text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
189
+ preg_match_all( '/./u', $text, $words_array );
190
+ $words_array = array_slice( $words_array[0], 0, $num_words + 1 );
191
+ $sep = '';
192
+ } else {
193
+ $words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
194
+ $sep = ' ';
195
+ }
196
+ if ( count( $words_array ) > $num_words ) {
197
+ array_pop( $words_array );
198
+ $text = implode( $sep, $words_array );
199
+ $text = $text . $more;
200
+ } else {
201
+ $text = implode( $sep, $words_array );
202
+ }
203
+ $text = self::close_tags( $text );
204
+ return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text );
205
+ }
206
+
207
+ /**
208
+ *
209
+ *
210
+ * @param string $html
211
+ * @return string
212
+ */
213
+ public static function close_tags( $html ) {
214
+ //put all opened tags into an array
215
+ preg_match_all( '#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result );
216
+ $openedtags = $result[1];
217
+ //put all closed tags into an array
218
+ preg_match_all( '#</([a-z]+)>#iU', $html, $result );
219
+ $closedtags = $result[1];
220
+ $len_opened = count( $openedtags );
221
+ // all tags are closed
222
+ if ( count( $closedtags ) == $len_opened ) {
223
+ return $html;
224
+ }
225
+ $openedtags = array_reverse( $openedtags );
226
+ // close tags
227
+ for ( $i = 0; $i < $len_opened; $i++ ) {
228
+ if ( !in_array( $openedtags[$i], $closedtags ) ) {
229
+ $html .= '</' . $openedtags[$i] . '>';
230
+ } else {
231
+ unset( $closedtags[array_search( $openedtags[$i], $closedtags )] );
232
+ }
233
+ }
234
+ $html = str_replace(array('</br>','</hr>','</wbr>'), '', $html);
235
+ $html = str_replace(array('<br>','<hr>','<wbr>'), array('<br />','<hr />','<wbr />'), $html);
236
+ return $html;
237
+ }
238
+
239
+ /**
240
+ *
241
+ *
242
+ * @param string $ret
243
+ * @return string
244
+ * @deprecated since 0.20.0
245
+ */
246
+ public static function twitterify( $ret ) {
247
+ $ret = preg_replace( "#(^|[\n ])([\w]+?://[\w]+[^ \"\n\r\t< ]*)#", "\\1<a href=\"\\2\" target=\"_blank\">\\2</a>", $ret );
248
+ $ret = preg_replace( "#(^|[\n ])((www|ftp)\.[^ \"\t\n\r< ]*)#", "\\1<a href=\"http://\\2\" target=\"_blank\">\\2</a>", $ret );
249
+ $pattern = '#([0-9a-z]([-_.]?[0-9a-z])*@[0-9a-z]([-.]?[0-9a-z])*\\.';
250
+ $pattern .= '[a-wyz][a-z](fo|g|l|m|mes|o|op|pa|ro|seum|t|u|v|z)?)#i';
251
+ $ret = preg_replace( $pattern, '<a href="mailto:\\1">\\1</a>', $ret );
252
+ $ret = preg_replace( "/\B@(\w+)/", " <a href=\"http://www.twitter.com/\\1\" target=\"_blank\">@\\1</a>", $ret );
253
+ $ret = preg_replace( "/\B#(\w+)/", " <a href=\"http://twitter.com/search?q=\\1\" target=\"_blank\">#\\1</a>", $ret );
254
+ return $ret;
255
+ }
256
+
257
+ /* WordPress Query Utilities
258
+ ======================== */
259
+
260
+ /**
261
+ *
262
+ *
263
+ * @param string $key
264
+ * @param string $value
265
+ * @return array|int
266
+ * @deprecated since 0.20.0
267
+ */
268
+ public static function get_posts_by_meta( $key, $value ) {
269
+ global $wpdb;
270
+ $query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s", $key, $value );
271
+ $results = $wpdb->get_col( $query );
272
+ $pids = array();
273
+ foreach ( $results as $result ) {
274
+ if ( get_post( $result ) ) {
275
+ $pids[] = $result;
276
+ }
277
+ }
278
+ if ( count( $pids ) ) {
279
+ return $pids;
280
+ }
281
+ return 0;
282
+ }
283
+
284
+ /**
285
+ *
286
+ *
287
+ * @param string $key
288
+ * @param string $value
289
+ * @return int
290
+ * @deprecated since 0.20.0
291
+ */
292
+ public static function get_post_by_meta( $key, $value ) {
293
+ global $wpdb;
294
+ $query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s ORDER BY post_id", $key, $value );
295
+ $results = $wpdb->get_col( $query );
296
+ foreach ( $results as $result ) {
297
+ if ( $result && get_post( $result ) ) {
298
+ return $result;
299
+ }
300
+ }
301
+ return 0;
302
+ }
303
+
304
+ /**
305
+ *
306
+ *
307
+ * @param int $ttid
308
+ * @return mixed
309
+ */
310
+ public static function get_term_id_by_term_taxonomy_id( $ttid ) {
311
+ global $wpdb;
312
+ $query = $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %s", $ttid );
313
+ return $wpdb->get_var( $query );
314
+ }
315
+
316
+ /* Object Utilities
317
+ ======================== */
318
+
319
+ /**
320
+ *
321
+ *
322
+ * @param array $array
323
+ * @param string $prop
324
+ * @return void
325
+ */
326
+ public static function osort( &$array, $prop ) {
327
+ usort( $array, function ( $a, $b ) use ( $prop ) {
328
+ return $a->$prop > $b->$prop ? 1 : -1;
329
+ } );
330
+ }
331
+
332
+ /**
333
+ *
334
+ *
335
+ * @param array $arr
336
+ * @return bool
337
+ */
338
+ public static function is_array_assoc( $arr ) {
339
+ if ( !is_array( $arr ) ) {
340
+ return false;
341
+ }
342
+ return (bool)count( array_filter( array_keys( $arr ), 'is_string' ) );
343
+ }
344
+
345
+ /**
346
+ *
347
+ *
348
+ * @param array $array
349
+ * @return stdClass
350
+ */
351
+ public static function array_to_object( $array ) {
352
+ $obj = new stdClass;
353
+ foreach ( $array as $k => $v ) {
354
+ if ( is_array( $v ) ) {
355
+ $obj->{$k} = self::array_to_object( $v ); //RECURSION
356
+ } else {
357
+ $obj->{$k} = $v;
358
+ }
359
+ }
360
+ return $obj;
361
+ }
362
+
363
+ /**
364
+ *
365
+ *
366
+ * @param array $array
367
+ * @param string $key
368
+ * @param mixed $value
369
+ * @return bool|int
370
+ */
371
+ public static function get_object_index_by_property( $array, $key, $value ) {
372
+ if ( is_array( $array ) ) {
373
+ $i = 0;
374
+ foreach ( $array as $arr ) {
375
+ if ( is_array( $arr ) ) {
376
+ if ( $arr[$key] == $value ) {
377
+ return $i;
378
+ }
379
+ } else {
380
+ if ( $arr->$key == $value ) {
381
+ return $i;
382
+ }
383
+ }
384
+ $i++;
385
+ }
386
+ }
387
+ return false;
388
+ }
389
+
390
+ /**
391
+ *
392
+ *
393
+ * @param array $array
394
+ * @param string $key
395
+ * @param mixed $value
396
+ * @return array|null
397
+ * @throws Exception
398
+ */
399
+ public static function get_object_by_property( $array, $key, $value ) {
400
+ if ( is_array( $array ) ) {
401
+ foreach ( $array as $arr ) {
402
+ if ( $arr->$key == $value ) {
403
+ return $arr;
404
+ }
405
+ }
406
+ } else {
407
+ throw new Exception( '$array is not an array, given value: ' . $array );
408
+ }
409
+ return null;
410
+ }
411
+
412
+ /**
413
+ *
414
+ *
415
+ * @param array $array
416
+ * @param int $len
417
+ * @return array
418
+ */
419
+ public static function array_truncate( $array, $len ) {
420
+ if ( sizeof( $array ) > $len ) {
421
+ $array = array_splice( $array, 0, $len );
422
+ }
423
+ return $array;
424
+ }
425
+
426
+ /* Bool Utilities
427
+ ======================== */
428
+
429
+ /**
430
+ *
431
+ *
432
+ * @param mixed $property
433
+ * @return bool
434
+ */
435
+ public static function is_true( $property ) {
436
+ if ( isset( $property ) ) {
437
+ if ( $property == 'true' || $property == 1 || $property == '1' || $property == true ) {
438
+ return true;
439
+ }
440
+ }
441
+ return false;
442
+ }
443
+
444
+ /**
445
+ *
446
+ *
447
+ * @param int $i
448
+ * @return bool
449
+ */
450
+ public static function iseven( $i ) {
451
+ return ( $i % 2 ) == 0;
452
+ }
453
+
454
+ /**
455
+ *
456
+ *
457
+ * @param int $i
458
+ * @return bool
459
+ */
460
+ public static function isodd( $i ) {
461
+ return ( $i % 2 ) != 0;
462
+ }
463
+
464
+ /* Links, Forms, Etc. Utilities
465
+ ======================== */
466
+
467
+ /* this $args thing is a fucking mess, fix at some point:
468
+
469
+ http://codex.wordpress.org/Function_Reference/comment_form */
470
+
471
+ /**
472
+ *
473
+ *
474
+ * @param int $post_id
475
+ * @param array $args
476
+ * @return string
477
+ */
478
+ public static function get_comment_form( $post_id = null, $args = array() ) {
479
+ return self::ob_function( 'comment_form', array( $args, $post_id ) );
480
+ }
481
+
482
+ /**
483
+ *
484
+ *
485
+ * @param string $args
486
+ * @return array
487
+ */
488
+ public static function paginate_links( $args = '' ) {
489
+ $defaults = array(
490
+ 'base' => '%_%', // http://example.com/all_posts.php%_% : %_% is replaced by format (below)
491
+ 'format' => '?page=%#%', // ?page=%#% : %#% is replaced by the page number
492
+ 'total' => 1,
493
+ 'current' => 0,
494
+ 'show_all' => false,
495
+ 'prev_next' => true,
496
+ 'prev_text' => __( '&laquo; Previous' ),
497
+ 'next_text' => __( 'Next &raquo;' ),
498
+ 'end_size' => 1,
499
+ 'mid_size' => 2,
500
+ 'type' => 'array',
501
+ 'add_args' => false, // array of query args to add
502
+ 'add_fragment' => ''
503
+ );
504
+ $args = wp_parse_args( $args, $defaults );
505
+ // Who knows what else people pass in $args
506
+ $args['total'] = intval( (int)$args['total'] );
507
+ if ( $args['total'] < 2 ) {
508
+ return array();
509
+ }
510
+ $args['current'] = (int)$args['current'];
511
+ $args['end_size'] = 0 < (int)$args['end_size'] ? (int)$args['end_size'] : 1; // Out of bounds? Make it the default.
512
+ $args['mid_size'] = 0 <= (int)$args['mid_size'] ? (int)$args['mid_size'] : 2;
513
+ $args['add_args'] = is_array( $args['add_args'] ) ? $args['add_args'] : false;
514
+ $page_links = array();
515
+ $dots = false;
516
+ if ( $args['prev_next'] && $args['current'] && 1 < $args['current'] ) {
517
+ $link = str_replace( '%_%', 2 == $args['current'] ? '' : $args['format'], $args['base'] );
518
+ $link = str_replace( '%#%', $args['current'] - 1, $link );
519
+ if ( $args['add_args'] ) {
520
+ $link = add_query_arg( $args['add_args'], $link );
521
+ }
522
+ $link .= $args['add_fragment'];
523
+ $link = untrailingslashit( $link );
524
+ $page_links[] = array(
525
+ 'class' => 'prev page-numbers',
526
+ 'link' => esc_url( apply_filters( 'paginate_links', $link ) ),
527
+ 'title' => $args['prev_text']
528
+ );
529
+ }
530
+ for ( $n = 1; $n <= $args['total']; $n++ ) {
531
+ $n_display = number_format_i18n( $n );
532
+ if ( $n == $args['current'] ) {
533
+ $page_links[] = array(
534
+ 'class' => 'page-number page-numbers current',
535
+ 'title' => $n_display,
536
+ 'text' => $n_display,
537
+ 'name' => $n_display,
538
+ 'current' => true
539
+ );
540
+ $dots = true;
541
+ } else {
542
+ 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'] ) ) {
543
+ $link = str_replace( '%_%', 1 == $n ? '' : $args['format'], $args['base'] );
544
+ $link = str_replace( '%#%', $n, $link );
545
+ $link = trailingslashit( $link ) . ltrim( $args['add_fragment'], '/' );
546
+ if ( $args['add_args'] ) {
547
+ $link = rtrim( add_query_arg( $args['add_args'], $link ), '/' );
548
+ }
549
+ $link = str_replace(' ', '+', $link);
550
+ $link = untrailingslashit( $link );
551
+ $page_links[] = array(
552
+ 'class' => 'page-number page-numbers',
553
+ 'link' => esc_url( apply_filters( 'paginate_links', $link ) ),
554
+ 'title' => $n_display,
555
+ 'name' => $n_display,
556
+ 'current' => $args['current'] == $n
557
+ );
558
+ $dots = true;
559
+ } elseif ( $dots && !$args['show_all'] ) {
560
+ $page_links[] = array(
561
+ 'class' => 'dots',
562
+ 'title' => __( '&hellip;' )
563
+ );
564
+ $dots = false;
565
+ }
566
+ }
567
+ }
568
+ if ( $args['prev_next'] && $args['current'] && ( $args['current'] < $args['total'] || -1 == $args['total'] ) ) {
569
+ $link = str_replace( '%_%', $args['format'], $args['base'] );
570
+ $link = str_replace( '%#%', $args['current'] + 1, $link );
571
+ if ( $args['add_args'] ) {
572
+ $link = add_query_arg( $args['add_args'], $link );
573
+ }
574
+ $link = untrailingslashit( trailingslashit( $link ) . $args['add_fragment'] );
575
+ $page_links[] = array(
576
+ 'class' => 'next page-numbers',
577
+ 'link' => esc_url( apply_filters( 'paginate_links', $link ) ),
578
+ 'title' => $args['next_text']
579
+ );
580
+ }
581
+ return $page_links;
582
+ }
583
+
584
+ /* LEGACY These have since been re-organized; but keeping linkages for backwards-compatibility */
585
+
586
+ /**
587
+ * @deprecated
588
+ */
589
+ static function get_image_path( $iid ) {
590
+ return TimberImageHelper::get_image_path( $iid );
591
+ }
592
+
593
+ /**
594
+ * @deprecated
595
+ */
596
+ static function get_current_url() {
597
+ return TimberURLHelper::get_current_url();
598
+ }
599
+
600
+ /**
601
+ * @deprecated
602
+ */
603
+ static function is_url( $url ) {
604
+ return TimberURLHelper::is_url( $url );
605
+ }
606
+
607
+ /**
608
+ * @deprecated
609
+ */
610
+ static function get_path_base() {
611
+ return TimberURLHelper::get_path_base();
612
+ }
613
+
614
+ /**
615
+ * @deprecated
616
+ */
617
+ static function get_rel_url( $url, $force = false ) {
618
+ return TimberURLHelper::get_rel_url( $url, $force );
619
+ }
620
+
621
+ /**
622
+ * @deprecated
623
+ */
624
+ static function is_local( $url ) {
625
+ return TimberURLHelper::is_local( $url );
626
+ }
627
+
628
+ /**
629
+ * @deprecated
630
+ */
631
+ static function get_full_path( $src ) {
632
+ return TimberURLHelper::get_full_path( $src );
633
+ }
634
+
635
+ /**
636
+ * @deprecated
637
+ */
638
+ static function get_rel_path( $src ) {
639
+ return TimberURLHelper::get_rel_path( $src );
640
+ }
641
+
642
+ /**
643
+ * @deprecated
644
+ */
645
+ static function remove_double_slashes( $url ) {
646
+ return TimberURLHelper::remove_double_slashes( $url );
647
+ }
648
+
649
+ /**
650
+ * @deprecated
651
+ */
652
+ static function prepend_to_url( $url, $path ) {
653
+ return TimberURLHelper::prepend_to_url( $url, $path );
654
+ }
655
+
656
+ /**
657
+ * @deprecated
658
+ */
659
+ static function preslashit( $path ) {
660
+ return TimberURLHelper::preslashit( $path );
661
+ }
662
+
663
+ /**
664
+ * @deprecated
665
+ */
666
+ static function is_external( $url ) {
667
+ return TimberURLHelper::is_external( $url );
668
+ }
669
+
670
+ /**
671
+ * @deprecated
672
+ */
673
+ static function download_url( $url, $timeout = 300 ) {
674
+ return TimberURLHelper::download_url( $url, $timeout );
675
+ }
676
+
677
+ /**
678
+ * @deprecated
679
+ */
680
+ static function get_params( $i = -1 ) {
681
+ return TimberURLHelper::get_params( $i );
682
+ }
683
+
684
+ }
functions/functions/timber-image-helper.php ADDED
@@ -0,0 +1,613 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once 'timber-image-retina-helper.php';
4
+
5
+ class TimberImageHelper {
6
+
7
+ static function add_actions() {
8
+ add_action( 'delete_post', function ( $post_id ) {
9
+ $post = get_post( $post_id );
10
+ $image_types = array( 'image/jpeg', 'image/png', 'image/gif', 'image/jpg' );
11
+ if ( $post->post_type == 'attachment' && in_array( $post->post_mime_type, $image_types ) ) {
12
+ TimberImageHelper::delete_resized_files_from_url( $post->guid );
13
+ TimberImageHelper::delete_letterboxed_files_from_url( $post->guid );
14
+ }
15
+ } );
16
+ }
17
+
18
+ /**
19
+ * Adds a constant defining the path to the content directory relative to the site
20
+ * for example /wp-content or /content
21
+ */
22
+ static function add_constants() {
23
+ if ( !defined( 'WP_CONTENT_SUBDIR' ) ) {
24
+ $wp_content_path = str_replace( home_url(), '', WP_CONTENT_URL );
25
+ define( 'WP_CONTENT_SUBDIR', $wp_content_path );
26
+ }
27
+ }
28
+
29
+ static function add_filters() {
30
+ add_filter( 'upload_dir', function ( $arr ) {
31
+ $arr['relative'] = str_replace( home_url(), '', $arr['baseurl'] );
32
+ return $arr;
33
+ } );
34
+ }
35
+
36
+ /**
37
+ *
38
+ *
39
+ * @param string $hexstr
40
+ * @return array
41
+ */
42
+ public static function hexrgb( $hexstr ) {
43
+ if ( !strstr( $hexstr, '#' ) ) {
44
+ $hexstr = '#' . $hexstr;
45
+ }
46
+ if ( strlen( $hexstr ) == 4 ) {
47
+ $hexstr = '#' . $hexstr[1] . $hexstr[1] . $hexstr[2] . $hexstr[2] . $hexstr[3] . $hexstr[3];
48
+ }
49
+ $int = hexdec( $hexstr );
50
+ return array( "red" => 0xFF & ( $int >> 0x10 ), "green" => 0xFF & ( $int >> 0x8 ), "blue" => 0xFF & $int );
51
+ }
52
+
53
+ static function delete_resized_files_from_url( $src ) {
54
+ $local = TimberURLHelper::url_to_file_system( $src );
55
+ self::delete_resized_files( $local );
56
+ }
57
+
58
+ static function delete_letterboxed_files_from_url( $src ) {
59
+ $local = TimberURLHelper::url_to_file_system( $src );
60
+ self::delete_letterboxed_files( $local );
61
+ }
62
+
63
+ /**
64
+ *
65
+ *
66
+ * @param string $local_file
67
+ */
68
+ static function delete_resized_files( $local_file ) {
69
+ $info = pathinfo( $local_file );
70
+ $dir = $info['dirname'];
71
+ $ext = $info['extension'];
72
+ $filename = $info['filename'];
73
+ $searcher = '/' . $filename . '-[0-9999999]*';
74
+ foreach ( glob( $dir . $searcher ) as $found_file ) {
75
+ $regexdir = str_replace( '/', '\/', $dir );
76
+ $pattern = '/' . ( $regexdir ) . '\/' . $filename . '-[0-9]*x[0-9]*-c-[a-z]*.' . $ext . '/';
77
+ $match = preg_match( $pattern, $found_file );
78
+ //keeping these here so I know what the hell we're matching
79
+ //$match = preg_match("/\/srv\/www\/wordpress-develop\/src\/wp-content\/uploads\/2014\/05\/$filename-[0-9]*x[0-9]*-c-[a-z]*.jpg/", $found_file);
80
+ //$match = preg_match("/\/srv\/www\/wordpress-develop\/src\/wp-content\/uploads\/2014\/05\/arch-[0-9]*x[0-9]*-c-[a-z]*.jpg/", $filename);
81
+ if ( $match ) {
82
+ unlink( $found_file );
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ *
89
+ *
90
+ * @param string $local_file
91
+ */
92
+ static function delete_letterboxed_files( $local_file ) {
93
+ $info = pathinfo( $local_file );
94
+ $dir = $info['dirname'];
95
+ $ext = $info['extension'];
96
+ $filename = $info['filename'];
97
+ $searcher = '/' . $filename . '-lbox-[0-9999999]*';
98
+ foreach ( glob( $dir . $searcher ) as $found_file ) {
99
+ $regexdir = str_replace( '/', '\/', $dir );
100
+ $pattern = '/' . ( $regexdir ) . '\/' . $filename . '-lbox-[0-9]*x[0-9]*-[a-zA-Z0-9]*.' . $ext . '/';
101
+ $match = preg_match( $pattern, $found_file );
102
+ if ( $match ) {
103
+ unlink( $found_file );
104
+ }
105
+ }
106
+ }
107
+
108
+ /**
109
+ *
110
+ *
111
+ * @param string $src
112
+ * @param int $w
113
+ * @param int $h
114
+ * @param string $color
115
+ * @return string
116
+ */
117
+ public static function get_letterbox_file_rel( $src, $w, $h, $color ) {
118
+ if ( !strlen( $src ) ) {
119
+ return null;
120
+ }
121
+ $new_path = self::get_letterbox_file_name_relative_to_content( $src, $w, $h, $color );
122
+ return WP_CONTENT_SUBDIR . $new_path;
123
+ }
124
+
125
+ /**
126
+ *
127
+ *
128
+ * @param string $src The src of an image can be absolute, relative or server location
129
+ * @return mixed|null
130
+ */
131
+ static function get_directory_relative_to_content( $src ) {
132
+ if ( !strlen( $src ) ) {
133
+ return null;
134
+ }
135
+ if ( !strlen( $src ) ) {
136
+ return null;
137
+ }
138
+ $abs = false;
139
+ if ( strstr( $src, 'http' ) ) {
140
+ $abs = true;
141
+ }
142
+ $path_parts = pathinfo( $src );
143
+ if ( $abs ) {
144
+ $dir_relative_to_content = str_replace( WP_CONTENT_URL, '', $path_parts['dirname'] );
145
+ } else {
146
+ $dir_relative_to_content = str_replace( WP_CONTENT_DIR, '', $path_parts['dirname'] );
147
+ $dir_relative_to_content = str_replace( WP_CONTENT_SUBDIR, '', $dir_relative_to_content );
148
+ }
149
+ return $dir_relative_to_content;
150
+ }
151
+
152
+ /**
153
+ *
154
+ *
155
+ * @param string $src
156
+ * @param int $w
157
+ * @param int $h
158
+ * @param string $color
159
+ * @return string
160
+ */
161
+ static function get_letterbox_file_name_relative_to_content( $src, $w, $h, $color ) {
162
+ $path_parts = pathinfo( $src );
163
+ $dir_relative_to_content = self::get_directory_relative_to_content( $src );
164
+ $color = str_replace( '#', '', $color );
165
+ $newbase = $path_parts['filename'] . '-lbox-' . $w . 'x' . $h . '-' . $color;
166
+ $new_name = $newbase . '.' . $path_parts['extension'];
167
+ return $dir_relative_to_content . '/' . $new_name;
168
+ }
169
+
170
+ /**
171
+ *
172
+ *
173
+ * @param string $src
174
+ * @param int $w
175
+ * @param int $h
176
+ * @param string $color
177
+ * @return string
178
+ */
179
+ public static function get_letterbox_file_path( $src, $w, $h, $color ) {
180
+ $new_name = self::get_letterbox_file_name_relative_to_content( $src, $w, $h, $color );
181
+ $new_server_path = WP_CONTENT_DIR . $new_name;
182
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
183
+ return $new_server_path;
184
+ }
185
+
186
+ /**
187
+ *
188
+ *
189
+ * @param string $src
190
+ * @param int $w
191
+ * @param int $h
192
+ * @param string $crop
193
+ * @return string
194
+ */
195
+ static function get_resize_file_rel( $src, $w, $h, $crop ) {
196
+ if ( !strlen( $src ) ) {
197
+ return null;
198
+ }
199
+ $new_path = self::get_resize_file_name_relative_to_content( $src, $w, $h, $crop );
200
+ return WP_CONTENT_SUBDIR . $new_path;
201
+ }
202
+
203
+ /**
204
+ *
205
+ *
206
+ * @param string $src
207
+ * @param int $w
208
+ * @param int $h
209
+ * @param string $crop
210
+ * @return string
211
+ */
212
+ static function get_resize_file_name_relative_to_content( $src, $w, $h, $crop ) {
213
+ $path_parts = pathinfo( $src );
214
+ $dir_relative_to_content = self::get_directory_relative_to_content( $src );
215
+ $newbase = $path_parts['filename'] . '-' . $w . 'x' . $h . '-c-' . ( $crop ? $crop : 'f' ); // Crop will be either user named or f (false)
216
+ $new_name = $newbase . '.' . $path_parts['extension'];
217
+ return $dir_relative_to_content . '/' . $new_name;
218
+ }
219
+
220
+ /**
221
+ *
222
+ *
223
+ * @param string $src
224
+ */
225
+ public static function in_uploads( $src ) {
226
+ $upload_dir = wp_upload_dir();
227
+ if ( strstr( $src, $upload_dir['relative'] ) ) {
228
+ return true;
229
+ }
230
+ return false;
231
+ }
232
+
233
+ /**
234
+ *
235
+ *
236
+ * @param string $src
237
+ * @param int $w
238
+ * @param int $h
239
+ * @param string $crop
240
+ * @return string
241
+ */
242
+ static function get_resize_file_path( $src, $w, $h, $crop ) {
243
+ $new_name = self::get_resize_file_name_relative_to_content( $src, $w, $h, $crop );
244
+ $new_server_path = WP_CONTENT_DIR . $new_name;
245
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
246
+ return $new_server_path;
247
+ }
248
+
249
+ /**
250
+ *
251
+ *
252
+ * @param int $iid
253
+ * @return string
254
+ */
255
+ public static function get_image_path( $iid ) {
256
+ $size = 'full';
257
+ $src = wp_get_attachment_image_src( $iid, $size );
258
+ $src = $src[0];
259
+ return self::get_rel_path( $src );
260
+ }
261
+
262
+ /**
263
+ *
264
+ *
265
+ * @param string $url
266
+ */
267
+ public static function get_server_location( $url ) {
268
+ if ( strpos( $url, ABSPATH ) === 0 ) {
269
+ return $url;
270
+ }
271
+ $upload_dir = wp_upload_dir();
272
+ $abs = false;
273
+ if ( strstr( $url, 'http' ) ) {
274
+ $abs = true;
275
+ }
276
+ if ( self::in_uploads( $url ) ) {
277
+ if ( $abs ) {
278
+ $relative_to_uploads_dir = str_replace( $upload_dir['baseurl'], '', $url );
279
+ } else {
280
+ $relative_to_uploads_dir = str_replace( $upload_dir['relative'], '', $url );
281
+ }
282
+ return $upload_dir['basedir'] . $relative_to_uploads_dir;
283
+ } else {
284
+ if ( $abs ) {
285
+ $relative_to_wp_content = str_replace( WP_CONTENT_URL, '', $url );
286
+ } else {
287
+ $relative_to_wp_content = str_replace( WP_CONTENT_SUBDIR, '', $url );
288
+ }
289
+ return WP_CONTENT_DIR . $relative_to_wp_content;
290
+ }
291
+ }
292
+
293
+ /**
294
+ *
295
+ *
296
+ * @param string $src
297
+ * @param int $w
298
+ * @param int $h
299
+ * @param string $color
300
+ * @param bool $force
301
+ * @return mixed|null|string
302
+ */
303
+ public static function letterbox( $src, $w, $h, $color = '#000000', $force = false ) {
304
+ if ( strstr( $src, 'http' ) && !strstr( $src, home_url() ) ) {
305
+ $src = self::sideload_image( $src );
306
+ }
307
+ $abs = false;
308
+ if ( strstr( $src, 'http' ) ) {
309
+ $abs = true;
310
+ }
311
+ $new_file_rel = self::get_letterbox_file_rel( $src, $w, $h, $color );
312
+ $new_server_path = self::get_letterbox_file_path( $src, $w, $h, $color );
313
+ $old_server_path = self::get_server_location( $src );
314
+ $old_server_path = TimberURLHelper::remove_double_slashes( $old_server_path );
315
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
316
+ if ( file_exists( $new_server_path ) && !$force ) {
317
+ if ( $abs ) {
318
+ return untrailingslashit( home_url() ) . $new_file_rel;
319
+ } else {
320
+ return TimberURLHelper::preslashit( $new_file_rel );
321
+ }
322
+ }
323
+ $bg = imagecreatetruecolor( $w, $h );
324
+ $c = self::hexrgb( $color );
325
+ $white = imagecolorallocate( $bg, $c['red'], $c['green'], $c['blue'] );
326
+ imagefill( $bg, 0, 0, $white );
327
+ $image = wp_get_image_editor( $old_server_path );
328
+ if ( !is_wp_error( $image ) ) {
329
+ $current_size = $image->get_size();
330
+ $ow = $current_size['width'];
331
+ $oh = $current_size['height'];
332
+ $new_aspect = $w / $h;
333
+ $old_aspect = $ow / $oh;
334
+ if ( $new_aspect > $old_aspect ) {
335
+ //taller than goal
336
+ $h_scale = $h / $oh;
337
+ $owt = $ow * $h_scale;
338
+ $y = 0;
339
+ $x = $w / 2 - $owt / 2;
340
+ $oht = $h;
341
+ $image->crop( 0, 0, $ow, $oh, $owt, $oht );
342
+ } else {
343
+ $w_scale = $w / $ow;
344
+ $oht = $oh * $w_scale;
345
+ $x = 0;
346
+ $y = $h / 2 - $oht / 2;
347
+ $owt = $w;
348
+ $image->crop( 0, 0, $ow, $oh, $owt, $oht );
349
+ }
350
+ $image->save( $new_server_path );
351
+ $func = 'imagecreatefromjpeg';
352
+ $ext = pathinfo( $new_server_path, PATHINFO_EXTENSION );
353
+ if ( $ext == 'gif' ) {
354
+ $func = 'imagecreatefromgif';
355
+ } else if ( $ext == 'png' ) {
356
+ $func = 'imagecreatefrompng';
357
+ }
358
+ $image = $func( $new_server_path );
359
+ imagecopy( $bg, $image, $x, $y, 0, 0, $owt, $oht );
360
+ imagejpeg( $bg, $new_server_path );
361
+ $new_relative_path = TimberURLHelper::get_rel_path( $new_server_path );
362
+ if ( $abs ) {
363
+ return home_url( $new_relative_path );
364
+ }
365
+ return $new_relative_path;
366
+ } else {
367
+ TimberHelper::error_log( $image );
368
+ }
369
+ return null;
370
+ }
371
+
372
+ /**
373
+ *
374
+ *
375
+ * @param string $src a url or path to the image (http://example.org/wp-content/uploads/2014/image.jpg) or (/wp-content/uploads/2014/image.jpg)
376
+ * @param string $bghex
377
+ * @return string
378
+ */
379
+ public static function img_to_jpg( $src, $bghex = '#FFFFFF' ) {
380
+ $path = str_replace( home_url(), '', $src );
381
+ $output = str_replace( '.png', '.jpg', $path );
382
+ $input_file = self::get_server_location( $path );
383
+ $output_file = self::get_server_location( $output );
384
+ if ( file_exists( $output_file ) ) {
385
+ return $output;
386
+ }
387
+ $filename = $output;
388
+ $input = imagecreatefrompng( $input_file );
389
+ list( $width, $height ) = getimagesize( $input_file );
390
+ $output = imagecreatetruecolor( $width, $height );
391
+ $c = self::hexrgb( $bghex );
392
+ $white = imagecolorallocate( $output, $c['red'], $c['green'], $c['blue'] );
393
+ imagefilledrectangle( $output, 0, 0, $width, $height, $white );
394
+ imagecopy( $output, $input, 0, 0, 0, 0, $width, $height );
395
+ imagejpeg( $output, $output_file );
396
+ return $filename;
397
+ }
398
+
399
+ /**
400
+ *
401
+ *
402
+ * @param string $file
403
+ * @return string
404
+ */
405
+ public static function get_sideloaded_file_loc( $file ) {
406
+ $upload = wp_upload_dir();
407
+ $dir = $upload['path'];
408
+ $filename = $file;
409
+ $file = parse_url( $file );
410
+ $path_parts = pathinfo( $file['path'] );
411
+ $basename = md5( $filename );
412
+ $ext = 'jpg';
413
+ if ( isset( $path_parts['extension'] ) ) {
414
+ $ext = $path_parts['extension'];
415
+ }
416
+ return $dir . '/' . $basename . '.' . $ext;
417
+ }
418
+
419
+ /**
420
+ *
421
+ *
422
+ * @param string $file
423
+ * @return string
424
+ */
425
+ public static function sideload_image( $file ) {
426
+ $loc = self::get_sideloaded_file_loc( $file );
427
+ if ( file_exists( $loc ) ) {
428
+ return TimberURLHelper::preslashit( TimberURLHelper::get_rel_path( $loc ) );
429
+ }
430
+ // Download file to temp location
431
+ if ( !function_exists( 'download_url' ) ) {
432
+ require_once ABSPATH . '/wp-admin/includes/file.php';
433
+ }
434
+ $tmp = download_url( $file );
435
+ preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches );
436
+ $file_array = array();
437
+ $file_array['name'] = basename( $matches[0] );
438
+ $file_array['tmp_name'] = $tmp;
439
+ // If error storing temporarily, unlink
440
+ if ( is_wp_error( $tmp ) ) {
441
+ @unlink( $file_array['tmp_name'] );
442
+ $file_array['tmp_name'] = '';
443
+ }
444
+ // do the validation and storage stuff
445
+ $locinfo = pathinfo( $loc );
446
+ $file = wp_upload_bits( $locinfo['basename'], null, file_get_contents( $file_array['tmp_name'] ) );
447
+ return $file['url'];
448
+ }
449
+
450
+ /**
451
+ *
452
+ *
453
+ * @param string $src
454
+ * @param float $multiplier
455
+ */
456
+ public static function retina_resize( $src, $factor = 2 ) {
457
+ if ( empty( $src ) ) {
458
+ return '';
459
+ }
460
+ $abs = false;
461
+ if ( strstr( $src, 'http' ) ) {
462
+ $abs = true;
463
+ }
464
+ if ( strstr( $src, 'http' ) && !strstr( $src, home_url() ) ) {
465
+ $src = self::sideload_image( $src );
466
+ }
467
+ $old_server_path = self::get_server_location( $src );
468
+ $new_path = TimberImageRetinaHelper::get_retina_file_rel( $src, $factor );
469
+ $new_server_path = TimberImageRetinaHelper::get_retina_file_path( $src, $factor );
470
+
471
+ $old_server_path = TimberURLHelper::remove_double_slashes( $old_server_path );
472
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
473
+ if ( file_exists( $new_server_path ) ) {
474
+ if ( !$abs ) {
475
+ return TimberURLHelper::preslashit( $new_path );
476
+ }
477
+ return untrailingslashit( home_url() ) . $new_path;
478
+ }
479
+ $image = wp_get_image_editor( $old_server_path );
480
+ if ( !is_wp_error( $image ) ) {
481
+ $current_size = $image->get_size();
482
+
483
+ $src_w = $current_size['width'];
484
+ $src_h = $current_size['height'];
485
+
486
+ $src_ratio = $src_w / $src_h;
487
+
488
+ // Get ratios
489
+ $w = $src_w * $factor;
490
+ $h = $src_h * $factor;
491
+ $image->crop( 0, 0, $src_w, $src_h, $w, $h );
492
+ $result = $image->save( $new_server_path );
493
+ return $new_path;
494
+ }
495
+ return $src;
496
+
497
+ }
498
+
499
+ /**
500
+ *
501
+ *
502
+ * @param string $src
503
+ * @param int $w
504
+ * @param int $h
505
+ * @param string $crop
506
+ * @param bool $force_resize
507
+ * @return string
508
+ */
509
+ public static function resize( $src, $w, $h = 0, $crop = 'default', $force_resize = false ) {
510
+ if ( empty( $src ) ) {
511
+ return '';
512
+ }
513
+ if ( strstr( $src, 'http' ) && !strstr( $src, home_url() ) ) {
514
+ $src = self::sideload_image( $src );
515
+ }
516
+ $abs = false;
517
+ if ( strstr( $src, 'http' ) ) {
518
+ $abs = true;
519
+ }
520
+ // Sanitize crop position
521
+ $allowed_crop_positions = array( 'default', 'center', 'top', 'bottom', 'left', 'right' );
522
+ if ( $crop !== false && !in_array( $crop, $allowed_crop_positions ) ) {
523
+ $crop = $allowed_crop_positions[0];
524
+ }
525
+ //oh good, it's a relative image in the uploads folder!
526
+ $new_path = self::get_resize_file_rel( $src, $w, $h, $crop );
527
+ $new_server_path = self::get_resize_file_path( $src, $w, $h, $crop );
528
+ $old_server_path = self::get_server_location( $src );
529
+ $old_server_path = TimberURLHelper::remove_double_slashes( $old_server_path );
530
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
531
+ if ( file_exists( $new_server_path ) ) {
532
+ if ( $force_resize ) {
533
+ // Force resize - warning: will regenerate the image on every pageload, use for testing purposes only!
534
+ unlink( $new_server_path );
535
+ } else {
536
+ if ( !$abs ) {
537
+ return TimberURLHelper::preslashit( $new_path );
538
+ }
539
+ return untrailingslashit( home_url() ) . $new_path;
540
+ }
541
+ }
542
+ $image = wp_get_image_editor( $old_server_path );
543
+
544
+ if ( !is_wp_error( $image ) ) {
545
+ $current_size = $image->get_size();
546
+
547
+ $src_w = $current_size['width'];
548
+ $src_h = $current_size['height'];
549
+
550
+ $src_ratio = $src_w / $src_h;
551
+ if ( !$h ) {
552
+ $h = round( $w / $src_ratio );
553
+ }
554
+ if ( !$w ) {
555
+ //the user wants to resize based on constant height
556
+ $w = round( $h * $src_ratio );
557
+ }
558
+ // Get ratios
559
+ $dest_ratio = $w / $h;
560
+ $src_wt = $src_h * $dest_ratio;
561
+ $src_ht = $src_w / $dest_ratio;
562
+
563
+ if ( !$crop ) {
564
+ // Always crop, to allow resizing upwards
565
+ $image->crop( 0, 0, $src_w, $src_h, $w, $h );
566
+ } else {
567
+ //start with defaults:
568
+ $src_x = $src_w / 2 - $src_wt / 2;
569
+ $src_y = ( $src_h - $src_ht ) / 6;
570
+ //now specific overrides based on options:
571
+ if ( $crop == 'center' ) {
572
+ // Get source x and y
573
+ $src_x = round( ( $src_w - $src_wt ) / 2 );
574
+ $src_y = round( ( $src_h - $src_ht ) / 2 );
575
+ } else if ( $crop == 'top' ) {
576
+ $src_y = 0;
577
+ } else if ( $crop == 'bottom' ) {
578
+ $src_y = $src_h - $src_ht;
579
+ } else if ( $crop == 'left' ) {
580
+ $src_x = 0;
581
+ } else if ( $crop == 'right' ) {
582
+ $src_x = $src_w - $src_wt;
583
+ }
584
+
585
+ // Crop the image
586
+ if ( $dest_ratio > $src_ratio ) {
587
+ $image->crop( 0, $src_y, $src_w, $src_ht, $w, $h );
588
+ } else {
589
+ $image->crop( $src_x, 0, $src_wt, $src_h, $w, $h );
590
+ }
591
+
592
+ }
593
+ $result = $image->save( $new_server_path );
594
+ if ( is_wp_error( $result ) ) {
595
+ error_log( 'Error resizing image' );
596
+ error_log( print_r( $result, true ) );
597
+ }
598
+ if ( $abs ) {
599
+ return untrailingslashit( home_url() ) . $new_path;
600
+ }
601
+ return $new_path;
602
+ } else if ( isset( $image->error_data['error_loading_image'] ) ) {
603
+ TimberHelper::error_log( 'Error loading ' . $image->error_data['error_loading_image'] );
604
+ } else {
605
+ TimberHelper::error_log( $image );
606
+ }
607
+ return $src;
608
+ }
609
+ }
610
+
611
+ TimberImageHelper::add_constants();
612
+ TimberImageHelper::add_actions();
613
+ TimberImageHelper::add_filters();
functions/functions/timber-image-retina-helper.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberImageRetinaHelper {
4
+
5
+ /**
6
+ * @param string $src
7
+ * @param int $w
8
+ * @param int $h
9
+ * @param string $crop
10
+ * @return string
11
+ */
12
+ static function get_retina_file_path( $src, $mult = 2 ) {
13
+ $new_name = self::get_retina_file_name_relative_to_content( $src, $mult );
14
+ $new_server_path = WP_CONTENT_DIR . $new_name;
15
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
16
+ return $new_server_path;
17
+ }
18
+
19
+
20
+ /**
21
+ *
22
+ *
23
+ * @param string $src
24
+ * @param int $w
25
+ * @param int $h
26
+ * @param string $crop
27
+ * @return string
28
+ */
29
+ public static function get_retina_file_rel( $src, $factor = 2 ) {
30
+ if ( !strlen( $src ) ) {
31
+ return null;
32
+ }
33
+ $new_path = self::get_retina_file_name_relative_to_content( $src, $factor );
34
+ return WP_CONTENT_SUBDIR . $new_path;
35
+ }
36
+
37
+ /**
38
+ *
39
+ *
40
+ * @param string $src
41
+ * @param int $w
42
+ * @param int $h
43
+ * @param string $crop
44
+ * @return string
45
+ */
46
+ private static function get_retina_file_name_relative_to_content( $src, $mult = 2 ) {
47
+ $path_parts = pathinfo( $src );
48
+ $dir_relative_to_content = TimberImageHelper::get_directory_relative_to_content( $src );
49
+ $newbase = $path_parts['filename'] . '@' . $mult . 'x'; // add @2x, @3x, @1.5x, etc.
50
+ $new_name = $newbase . '.' . $path_parts['extension'];
51
+ return $dir_relative_to_content . '/' . $new_name;
52
+ }
53
+
54
+ }
functions/functions/timber-image.php ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberImage extends TimberPost implements TimberCoreInterface {
4
+
5
+ public $_can_edit;
6
+ public $_dimensions;
7
+ public $abs_url;
8
+ public $PostClass = 'TimberPost';
9
+ public $object_type = 'image';
10
+
11
+ public static $representation = 'image';
12
+
13
+ public $file_loc;
14
+ public $file;
15
+ public $sizes = array();
16
+ public $post_parent;
17
+ public $caption;
18
+ public $_wp_attached_file;
19
+
20
+ /**
21
+ * @param int $iid
22
+ */
23
+ function __construct($iid) {
24
+ $this->init($iid);
25
+ }
26
+
27
+ /**
28
+ * @return string
29
+ */
30
+ function __toString() {
31
+ if ($this->get_src()) {
32
+ return $this->get_src();
33
+ }
34
+ return '';
35
+ }
36
+
37
+ /**
38
+ * @return mixed
39
+ */
40
+ function get_pathinfo() {
41
+ return pathinfo($this->file);
42
+ }
43
+
44
+ /**
45
+ * @param string $dim
46
+ * @return array|int
47
+ */
48
+ function get_dimensions($dim = null) {
49
+ if (isset($this->_dimensions)) {
50
+ return $this->get_dimensions_loaded($dim);
51
+ }
52
+ list($width, $height) = getimagesize($this->file_loc);
53
+ $this->_dimensions = array();
54
+ $this->_dimensions[0] = $width;
55
+ $this->_dimensions[1] = $height;
56
+ return $this->get_dimensions_loaded($dim);
57
+ }
58
+
59
+ /**
60
+ * @param string $dim
61
+ * @return array|int
62
+ */
63
+ function get_dimensions_loaded($dim) {
64
+ if ($dim == null) {
65
+ return $this->_dimensions;
66
+ }
67
+ if ($dim == 'w' || $dim == 'width') {
68
+ return $this->_dimensions[0];
69
+ }
70
+ if ($dim == 'h' || $dim == 'height') {
71
+ return $this->_dimensions[1];
72
+ }
73
+ return null;
74
+ }
75
+
76
+ /**
77
+ * @return int
78
+ */
79
+ function get_width() {
80
+ return $this->get_dimensions('width');
81
+ }
82
+
83
+ /**
84
+ * @return int
85
+ */
86
+ function get_height() {
87
+ return $this->get_dimensions('height');
88
+ }
89
+
90
+ /**
91
+ * @param string $size
92
+ * @return bool|string
93
+ */
94
+ function get_src($size = '') {
95
+ if (isset($this->abs_url)) {
96
+ return $this->_maybe_secure_url($this->abs_url);
97
+ }
98
+
99
+ if ($size && is_string($size) && isset($this->sizes[$size])) {
100
+ $image = image_downsize($this->ID, $size);
101
+ return $this->_maybe_secure_url(reset($image));
102
+ }
103
+
104
+ if (!isset($this->file) && isset($this->_wp_attached_file)) {
105
+ $this->file = $this->_wp_attached_file;
106
+ }
107
+
108
+ if (!isset($this->file)) {
109
+ return false;
110
+ }
111
+
112
+ $dir = self::wp_upload_dir();
113
+ $base = ($dir["baseurl"]);
114
+
115
+ $src = trailingslashit($this->_maybe_secure_url($base)) . $this->file;
116
+ return apply_filters('timber_image_src', $src);
117
+ }
118
+
119
+ private static function _maybe_secure_url($url) {
120
+ if (is_ssl() && strpos($url, 'https') !== 0 && strpos($url, 'http') === 0) {
121
+ $url = 'https' . substr($url, strlen('http'));
122
+ }
123
+
124
+ return $url;
125
+ }
126
+
127
+ public static function wp_upload_dir() {
128
+ static $wp_upload_dir = false;
129
+
130
+ if (!$wp_upload_dir) {
131
+ $wp_upload_dir = wp_upload_dir();
132
+ }
133
+
134
+ return $wp_upload_dir;
135
+ }
136
+
137
+ /**
138
+ * @return string
139
+ */
140
+ function get_path() {
141
+ if (strlen($this->abs_url)) {
142
+ return $this->abs_url;
143
+ }
144
+ return get_permalink($this->ID);
145
+ }
146
+
147
+ /**
148
+ * @return bool|TimberImage
149
+ */
150
+ function get_parent() {
151
+ if (!$this->post_parent) {
152
+ return false;
153
+ }
154
+ return new $this->PostClass($this->post_parent);
155
+ }
156
+
157
+ function get_alt() {
158
+ $alt = trim(strip_tags(get_post_meta($this->ID, '_wp_attachment_image_alt', true)));
159
+ return $alt;
160
+ }
161
+
162
+
163
+ /**
164
+ * @param int $iid
165
+ */
166
+ function init( $iid = false ) {
167
+ if ( !is_numeric( $iid ) && is_string( $iid ) ) {
168
+ if (strstr($iid, '://')) {
169
+ $this->init_with_url($iid);
170
+ return;
171
+ }
172
+ if ( strstr($iid, ABSPATH) ) {
173
+ $this->init_with_file_path($iid);
174
+ return;
175
+ }
176
+ if (strstr(strtolower($iid), '.jpg')) {
177
+ $this->init_with_relative_path($iid);
178
+ return;
179
+ }
180
+ }
181
+
182
+ $image_info = $this->get_image_info($iid);
183
+
184
+ $this->import($image_info);
185
+ $basedir = self::wp_upload_dir();
186
+ $basedir = $basedir['basedir'];
187
+ if (isset($this->file)) {
188
+ $this->file_loc = $basedir . DIRECTORY_SEPARATOR . $this->file;
189
+ } else if (isset($this->_wp_attached_file)) {
190
+ $this->file = reset($this->_wp_attached_file);
191
+ $this->file_loc = $basedir . DIRECTORY_SEPARATOR . $this->file;
192
+ }
193
+ if (isset($image_info['id'])) {
194
+ $this->ID = $image_info['id'];
195
+ } else if (is_numeric($iid)) {
196
+ $this->ID = $iid;
197
+ }
198
+ if (isset($this->ID)) {
199
+ $custom = get_post_custom($this->ID);
200
+ foreach ($custom as $key => $value) {
201
+ $this->$key = $value[0];
202
+ }
203
+ } else {
204
+ if (is_array($iid)) {
205
+ TimberHelper::error_log('Not able to init in TimberImage with iid=');
206
+ TimberHelper::error_log($iid);
207
+ } else {
208
+ TimberHelper::error_log('Not able to init in TimberImage with iid=' . $iid);
209
+ }
210
+ }
211
+ }
212
+
213
+ private function get_image_info( $iid ) {
214
+ $image_info = $iid;
215
+ if (is_numeric($iid)) {
216
+ $image_info = wp_get_attachment_metadata($iid);
217
+ if (!is_array($image_info)) {
218
+ $image_info = array();
219
+ }
220
+ $image_custom = get_post_custom($iid);
221
+ $basic = get_post($iid);
222
+ if ($basic) {
223
+ if (isset($basic->post_excerpt)) {
224
+ $this->caption = $basic->post_excerpt;
225
+ }
226
+ $image_custom = array_merge($image_custom, get_object_vars($basic));
227
+ }
228
+ return array_merge($image_info, $image_custom);
229
+ }
230
+ if (is_array($image_info) && isset($image_info['image'])) {
231
+ return $image_info['image'];
232
+ }
233
+ if (is_object($image_info)) {
234
+ return get_object_vars($image_info);
235
+ }
236
+ return $iid;
237
+ }
238
+
239
+ private function init_with_relative_path( $relative_path ) {
240
+ $this->abs_url = home_url( $relative_path );
241
+ $file_path = TimberURLHelper::get_full_path( $relative_path );
242
+ $this->file_loc = $file_path;
243
+ $this->file = $file_path;
244
+ }
245
+
246
+ private function init_with_file_path( $file_path ) {
247
+ $url = TimberURLHelper::file_system_to_url( $file_path );
248
+ $this->abs_url = $url;
249
+ $this->file_loc = $file_path;
250
+ $this->file = $file_path;
251
+ }
252
+
253
+ /**
254
+ * @param string $url
255
+ */
256
+ private function init_with_url($url) {
257
+ $this->abs_url = $url;
258
+ if (TimberURLHelper::is_local($url)) {
259
+ $this->file = ABSPATH . TimberURLHelper::get_rel_url($url);
260
+ $this->file_loc = ABSPATH . TimberURLHelper::get_rel_url($url);
261
+ }
262
+ }
263
+
264
+ /**
265
+ * @deprecated
266
+ * @return string
267
+ */
268
+ function get_url() {
269
+ return $this->get_src();
270
+ }
271
+
272
+ /* Alias */
273
+
274
+ /**
275
+ * @return float
276
+ */
277
+ public function aspect() {
278
+ $w = intval($this->width());
279
+ $h = intval($this->height());
280
+ return $w / $h;
281
+ }
282
+
283
+ /**
284
+ * @return int
285
+ */
286
+ public function height() {
287
+ return $this->get_height();
288
+ }
289
+
290
+ /**
291
+ * @param string $size
292
+ * @return bool|string
293
+ */
294
+ public function src($size = '') {
295
+ return $this->get_src($size);
296
+ }
297
+
298
+ /**
299
+ * @return int
300
+ */
301
+ public function width() {
302
+ return $this->get_width();
303
+ }
304
+
305
+ public function alt() {
306
+ return $this->get_alt();
307
+ }
308
+
309
+ }
functions/functions/timber-loader.php ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberLoader {
4
+
5
+ const CACHEGROUP = 'timberloader';
6
+
7
+ const TRANS_KEY_LEN = 50;
8
+
9
+ const CACHE_NONE = 'none';
10
+ const CACHE_OBJECT = 'cache';
11
+ const CACHE_TRANSIENT = 'transient';
12
+ const CACHE_SITE_TRANSIENT = 'site-transient';
13
+ const CACHE_USE_DEFAULT = 'default';
14
+
15
+ public static $cache_modes = array(
16
+ self::CACHE_NONE,
17
+ self::CACHE_OBJECT,
18
+ self::CACHE_TRANSIENT,
19
+ self::CACHE_SITE_TRANSIENT
20
+ );
21
+
22
+ protected $cache_mode = self::CACHE_TRANSIENT;
23
+
24
+ public $locations;
25
+
26
+ /**
27
+ * @param bool $caller
28
+ */
29
+ function __construct($caller = false) {
30
+ $this->locations = $this->get_locations($caller);
31
+ $this->cache_mode = apply_filters('timber_cache_mode', $this->cache_mode);
32
+ }
33
+
34
+ /**
35
+ * @param string $file
36
+ * @param array $data
37
+ * @param bool $expires
38
+ * @param string $cache_mode
39
+ * @return bool|string
40
+ */
41
+ function render($file, $data = null, $expires = false, $cache_mode = self::CACHE_USE_DEFAULT) {
42
+ // Different $expires if user is anonymous or logged in
43
+ if (is_array($expires)) {
44
+ if (is_user_logged_in() && isset($expires[1])) {
45
+ $expires = $expires[1];
46
+ } else {
47
+ $expires = $expires[0];
48
+ }
49
+ }
50
+
51
+ $key = null;
52
+ $output = false;
53
+ if (false !== $expires) {
54
+ ksort($data);
55
+ $key = md5($file . json_encode($data));
56
+ $output = $this->get_cache($key, self::CACHEGROUP, $cache_mode);
57
+ }
58
+
59
+ if (false === $output || null === $output) {
60
+ $twig = $this->get_twig();
61
+ if (strlen($file)) {
62
+ $loader = $this->get_loader();
63
+ $result = $loader->getCacheKey($file);
64
+ do_action('timber_loader_render_file', $result);
65
+ }
66
+ $data = apply_filters('timber_loader_render_data', $data);
67
+ $output = $twig->render($file, $data);
68
+ }
69
+
70
+ if (false !== $output && false !== $expires && null !== $key) {
71
+ $this->set_cache($key, $output, self::CACHEGROUP, $expires, $cache_mode);
72
+ }
73
+
74
+ return apply_filters('timber_output', $output);
75
+ }
76
+
77
+ /**
78
+ * @param array $filenames
79
+ * @return bool
80
+ */
81
+ function choose_template($filenames) {
82
+ if (is_array($filenames)) {
83
+ /* its an array so we have to figure out which one the dev wants */
84
+ foreach ($filenames as $filename) {
85
+ if ($this->template_exists($filename)) {
86
+ return $filename;
87
+ }
88
+ }
89
+ return false;
90
+ }
91
+ return $filenames;
92
+ }
93
+
94
+ /**
95
+ * @param string $file
96
+ * @return bool
97
+ */
98
+ function template_exists($file) {
99
+ foreach ($this->locations as $dir) {
100
+ $look_for = trailingslashit($dir) . $file;
101
+ if (file_exists($look_for)) {
102
+ return true;
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+
108
+ /**
109
+ * @return array
110
+ */
111
+ function get_locations_theme() {
112
+ $theme_locs = array();
113
+ $child_loc = get_stylesheet_directory();
114
+ $parent_loc = get_template_directory();
115
+ if (DIRECTORY_SEPARATOR == '\\') {
116
+ $child_loc = str_replace('/', '\\', $child_loc);
117
+ $parent_loc = str_replace('/', '\\', $parent_loc);
118
+ }
119
+ $theme_locs[] = $child_loc;
120
+ foreach ($this->get_locations_theme_dir() as $dirname) {
121
+ $theme_locs[] = trailingslashit($child_loc) . trailingslashit($dirname);
122
+ }
123
+ if ($child_loc != $parent_loc) {
124
+ $theme_locs[] = $parent_loc;
125
+ foreach ($this->get_locations_theme_dir() as $dirname) {
126
+ $theme_locs[] = trailingslashit($parent_loc) . trailingslashit($dirname);
127
+ }
128
+ }
129
+ //now make sure theres a trailing slash on everything
130
+ $theme_locs = array_map('trailingslashit', $theme_locs);
131
+ return $theme_locs;
132
+ }
133
+
134
+ /**
135
+ * returns an array of the directory inside themes that holds twig files
136
+ * @return string[] the names of directores, ie: array('templats', 'views');
137
+ */
138
+ private function get_locations_theme_dir() {
139
+ if (is_string(Timber::$dirname)) {
140
+ return array(Timber::$dirname);
141
+ }
142
+ return Timber::$dirname;
143
+ }
144
+
145
+ /**
146
+ * @return array
147
+ */
148
+ function get_locations_user() {
149
+ $locs = array();
150
+ if (isset(Timber::$locations)) {
151
+ if (is_string(Timber::$locations)) {
152
+ Timber::$locations = array(Timber::$locations);
153
+ }
154
+ foreach (Timber::$locations as $tloc) {
155
+ $tloc = realpath($tloc);
156
+ if (is_dir($tloc)) {
157
+ $locs[] = $tloc;
158
+ }
159
+ }
160
+ }
161
+ return $locs;
162
+ }
163
+
164
+ /**
165
+ * @param bool $caller
166
+ * @return array
167
+ */
168
+ function get_locations_caller($caller = false) {
169
+ $locs = array();
170
+ if ($caller && is_string($caller)) {
171
+ $caller = trailingslashit($caller);
172
+ if (is_dir($caller)) {
173
+ $locs[] = $caller;
174
+ }
175
+ foreach ($this->get_locations_theme_dir() as $dirname) {
176
+ $caller_sub = $caller . trailingslashit($dirname);
177
+ if (is_dir($caller_sub)) {
178
+ $locs[] = $caller_sub;
179
+ }
180
+ }
181
+ }
182
+ return $locs;
183
+ }
184
+
185
+ /**
186
+ * @param bool $caller
187
+ * @return array
188
+ */
189
+ function get_locations($caller = false) {
190
+ //prioirty: user locations, caller (but not theme), child theme, parent theme, caller
191
+ $locs = array();
192
+ $locs = array_merge($locs, $this->get_locations_user());
193
+ $locs = array_merge($locs, $this->get_locations_caller($caller));
194
+ //remove themes from caller
195
+ $locs = array_diff($locs, $this->get_locations_theme());
196
+ $locs = array_merge($locs, $this->get_locations_theme());
197
+ $locs = array_merge($locs, $this->get_locations_caller($caller));
198
+ $locs = array_unique($locs);
199
+ $locs = apply_filters('timber_locations', $locs);
200
+ return $locs;
201
+ }
202
+
203
+ /**
204
+ * @return Twig_Loader_Filesystem
205
+ */
206
+ function get_loader() {
207
+ $paths = array();
208
+ foreach ($this->locations as $loc) {
209
+ $loc = realpath($loc);
210
+ if (is_dir($loc)) {
211
+ $loc = realpath($loc);
212
+ $paths[] = $loc;
213
+ } else {
214
+ //error_log($loc.' is not a directory');
215
+ }
216
+ }
217
+ if (!ini_get('open_basedir')) {
218
+ $paths[] = '/';
219
+ } else {
220
+ $paths[] = ABSPATH;
221
+ }
222
+ $loader = new Twig_Loader_Filesystem($paths);
223
+ return $loader;
224
+ }
225
+
226
+ /**
227
+ * @return Twig_Environment
228
+ */
229
+ function get_twig() {
230
+ $loader = $this->get_loader();
231
+ $params = array('debug' => WP_DEBUG, 'autoescape' => false);
232
+ if (isset(Timber::$autoescape)) {
233
+ $params['autoescape'] = Timber::$autoescape;
234
+ }
235
+ if (Timber::$cache == true) {
236
+ Timber::$twig_cache = true;
237
+ }
238
+ if (Timber::$twig_cache) {
239
+ $twig_cache_loc = TIMBER_LOC . '/cache/twig';
240
+ if (!file_exists($twig_cache_loc)) {
241
+ mkdir($twig_cache_loc, 0777, true);
242
+ }
243
+ $params['cache'] = $twig_cache_loc;
244
+ }
245
+ $twig = new Twig_Environment($loader, $params);
246
+ if (WP_DEBUG) {
247
+ $twig->addExtension(new Twig_Extension_Debug());
248
+ }
249
+ $twig->addExtension($this->_get_cache_extension());
250
+
251
+ $twig = apply_filters('twig_apply_filters', $twig);
252
+ return $twig;
253
+ }
254
+
255
+ public function clear_cache_timber($cache_mode = self::CACHE_USE_DEFAULT){
256
+ //_transient_timberloader
257
+ $object_cache = false;
258
+ if (isset($GLOBALS['wp_object_cache']) && is_object($GLOBALS['wp_object_cache'])) {
259
+ $object_cache = true;
260
+ }
261
+ $cache_mode = $this->_get_cache_mode($cache_mode);
262
+ if (self::CACHE_TRANSIENT === $cache_mode) {
263
+ global $wpdb;
264
+ $query = $wpdb->prepare("DELETE FROM $wpdb->options WHERE option_name LIKE '%s'", '_transient_timberloader_%');
265
+ $wpdb->query( $query );
266
+ return true;
267
+ } else if (self::CACHE_SITE_TRANSIENT === $cache_mode) {
268
+ global $wpdb;
269
+ $query = $wpdb->prepare("DELETE FROM $wpdb->options WHERE option_name LIKE '%s'", '_transient_timberloader_%');
270
+ $wpdb->query( $query );
271
+ return true;
272
+ } else if (self::CACHE_OBJECT === $cache_mode && $object_cache) {
273
+ global $wp_object_cache;
274
+ if (isset($wp_object_cache->cache[self::CACHEGROUP])){
275
+ unset($wp_object_cache->cache[self::CACHEGROUP]);
276
+ return true;
277
+ }
278
+ }
279
+ return false;
280
+ }
281
+
282
+ public function clear_cache_twig() {
283
+ $twig = $this->get_twig();
284
+ $twig->clearCacheFiles();
285
+ $cache = $twig->getCache();
286
+ if ($cache){
287
+ self::rrmdir($twig->getCache());
288
+ return true;
289
+ }
290
+ return false;
291
+ }
292
+
293
+ /**
294
+ * @param string|false $dirPath
295
+ */
296
+ public static function rrmdir($dirPath) {
297
+ if (! is_dir($dirPath)) {
298
+ throw new InvalidArgumentException("$dirPath must be a directory");
299
+ }
300
+ if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
301
+ $dirPath .= '/';
302
+ }
303
+ $files = glob($dirPath . '*', GLOB_MARK);
304
+ foreach ($files as $file) {
305
+ if (is_dir($file)) {
306
+ self::rrmdir($file);
307
+ } else {
308
+ unlink($file);
309
+ }
310
+ }
311
+ rmdir($dirPath);
312
+ }
313
+
314
+ /**
315
+ * @return \Asm89\Twig\CacheExtension\Extension
316
+ */
317
+ private function _get_cache_extension() {
318
+ $loader_loc = trailingslashit(TIMBER_LOC) . 'functions/cache/loader.php';
319
+ require_once($loader_loc);
320
+ TimberCache_Loader::register();
321
+
322
+ $key_generator = new \Timber\Cache\KeyGenerator();
323
+ $cache_provider = new \Timber\Cache\WPObjectCacheAdapter($this);
324
+ $cache_strategy = new \Asm89\Twig\CacheExtension\CacheStrategy\GenerationalCacheStrategy($cache_provider, $key_generator);
325
+ $cache_extension = new \Asm89\Twig\CacheExtension\Extension($cache_strategy);
326
+
327
+ return $cache_extension;
328
+ }
329
+
330
+ /**
331
+ * @param string $key
332
+ * @param string $group
333
+ * @param string $cache_mode
334
+ * @return bool
335
+ */
336
+ public function get_cache($key, $group = self::CACHEGROUP, $cache_mode = self::CACHE_USE_DEFAULT) {
337
+ $object_cache = false;
338
+
339
+ if (isset($GLOBALS['wp_object_cache']) && is_object($GLOBALS['wp_object_cache'])) {
340
+ $object_cache = true;
341
+ }
342
+
343
+ $cache_mode = $this->_get_cache_mode($cache_mode);
344
+
345
+ $value = false;
346
+
347
+ $trans_key = substr($group . '_' . $key, 0, self::TRANS_KEY_LEN);
348
+ if (self::CACHE_TRANSIENT === $cache_mode)
349
+ $value = get_transient($trans_key);
350
+
351
+ elseif (self::CACHE_SITE_TRANSIENT === $cache_mode)
352
+ $value = get_site_transient($trans_key);
353
+
354
+ elseif (self::CACHE_OBJECT === $cache_mode && $object_cache)
355
+ $value = wp_cache_get($key, $group);
356
+
357
+ return $value;
358
+ }
359
+
360
+ /**
361
+ * @param string $key
362
+ * @param string|boolean $value
363
+ * @param string $group
364
+ * @param int $expires
365
+ * @param string $cache_mode
366
+ * @return string|boolean
367
+ */
368
+ public function set_cache($key, $value, $group = self::CACHEGROUP, $expires = 0, $cache_mode = self::CACHE_USE_DEFAULT) {
369
+ $object_cache = false;
370
+
371
+ if (isset($GLOBALS['wp_object_cache']) && is_object($GLOBALS['wp_object_cache'])) {
372
+ $object_cache = true;
373
+ }
374
+
375
+ if ((int)$expires < 1)
376
+ $expires = 0;
377
+
378
+ $cache_mode = self::_get_cache_mode($cache_mode);
379
+ $trans_key = substr($group . '_' . $key, 0, self::TRANS_KEY_LEN);
380
+
381
+ if (self::CACHE_TRANSIENT === $cache_mode)
382
+ set_transient($trans_key, $value, $expires);
383
+
384
+ elseif (self::CACHE_SITE_TRANSIENT === $cache_mode)
385
+ set_site_transient($trans_key, $value, $expires);
386
+
387
+ elseif (self::CACHE_OBJECT === $cache_mode && $object_cache)
388
+ wp_cache_set($key, $value, $group, $expires);
389
+
390
+ return $value;
391
+ }
392
+
393
+ /**
394
+ * @param string $cache_mode
395
+ * @return string
396
+ */
397
+ private function _get_cache_mode($cache_mode) {
398
+ if (empty($cache_mode) || self::CACHE_USE_DEFAULT === $cache_mode) {
399
+ $cache_mode = $this->cache_mode;
400
+ }
401
+
402
+ // Fallback if self::$cache_mode did not get a valid value
403
+ if (!in_array($cache_mode, self::$cache_modes)) {
404
+ $cache_mode = self::CACHE_OBJECT;
405
+ }
406
+
407
+ return $cache_mode;
408
+ }
409
+
410
+ }
functions/functions/timber-menu-item.php ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
4
+
5
+ public $children;
6
+ public $has_child_class = false;
7
+
8
+ public $classes = array();
9
+ public $class = '';
10
+ public $post_name;
11
+ public $type;
12
+
13
+ public $PostClass = 'TimberPost';
14
+
15
+ private $menu_object;
16
+ private $parent_object;
17
+
18
+ /**
19
+ *
20
+ *
21
+ * @param array|object $data
22
+ */
23
+ function __construct( $data ) {
24
+ $this->import( $data );
25
+ $this->import_classes( $data );
26
+ if ( isset( $this->name ) ) {
27
+ $this->_name = $this->name;
28
+ }
29
+ $this->name = $this->name();
30
+ $this->add_class( 'menu-item-' . $this->ID );
31
+ $this->menu_object = $data;
32
+ if ( isset( $this->url ) && $this->url ) {
33
+ $this->url = TimberURLHelper::remove_trailing_slash( $this->url );
34
+ }
35
+ }
36
+
37
+ function __toString() {
38
+ return $this->name();
39
+ }
40
+
41
+ /**
42
+ *
43
+ *
44
+ * @param string $class_name
45
+ */
46
+ function add_class( $class_name ) {
47
+ $this->classes[] = $class_name;
48
+ $this->class .= ' ' . $class_name;
49
+ }
50
+
51
+ /**
52
+ *
53
+ *
54
+ * @return string
55
+ */
56
+ function name() {
57
+ if ( isset( $this->title ) ) {
58
+ return $this->title;
59
+ }
60
+ if ( isset( $this->_name ) ) {
61
+ return $this->_name;
62
+ }
63
+ return '';
64
+ }
65
+
66
+ /**
67
+ *
68
+ *
69
+ * @return string
70
+ */
71
+ function slug() {
72
+ if ( !isset( $this->parent_object ) ) {
73
+ $this->parent_object = $this->get_parent_object();
74
+ }
75
+ if ( isset( $this->parent_object->post_name ) && $this->parent_object->post_name ) {
76
+ return $this->parent_object->post_name;
77
+ }
78
+ return $this->post_name;
79
+ }
80
+
81
+ function get_parent_object() {
82
+ if ( isset( $this->_menu_item_object_id ) ) {
83
+ return new $this->PostClass( $this->_menu_item_object_id );
84
+ }
85
+ }
86
+
87
+ /**
88
+ *
89
+ *
90
+ * @return string
91
+ */
92
+ function get_link() {
93
+ if ( !isset( $this->url ) || !$this->url ) {
94
+ if ( isset( $this->_menu_item_type ) && $this->_menu_item_type == 'custom' ) {
95
+ $this->url = $this->_menu_item_url;
96
+ } else if ( isset( $this->menu_object ) && method_exists( $this->menu_object, 'get_link' ) ) {
97
+ $this->url = $this->menu_object->get_link();
98
+ }
99
+ }
100
+ return TimberURLHelper::remove_trailing_slash( $this->url );
101
+ }
102
+
103
+ /**
104
+ *
105
+ *
106
+ * @return string
107
+ */
108
+ function get_path() {
109
+ return TimberURLHelper::remove_trailing_slash( TimberURLHelper::get_rel_url( $this->get_link() ) );
110
+ }
111
+
112
+ /**
113
+ *
114
+ *
115
+ * @param TimberMenuItem $item
116
+ */
117
+ function add_child( $item ) {
118
+ if ( !$this->has_child_class ) {
119
+ $this->add_class( 'menu-item-has-children' );
120
+ $this->has_child_class = true;
121
+ }
122
+ if ( !isset( $this->children ) ) {
123
+ $this->children = array();
124
+ }
125
+ $this->children[] = $item;
126
+ }
127
+
128
+ /**
129
+ *
130
+ *
131
+ * @param object $data
132
+ */
133
+ function import_classes( $data ) {
134
+ $this->classes = array_merge($this->classes, $data->classes);
135
+ $this->classes = array_unique($this->classes);
136
+ $this->class = trim( implode( ' ', $this->classes ) );
137
+ }
138
+
139
+ /**
140
+ *
141
+ *
142
+ * @return array|bool
143
+ */
144
+ function get_children() {
145
+ if ( isset( $this->children ) ) {
146
+ return $this->children;
147
+ }
148
+ return false;
149
+ }
150
+
151
+ /**
152
+ *
153
+ *
154
+ * @return bool
155
+ */
156
+ function is_external() {
157
+ if ( $this->type != 'custom' ) {
158
+ return false;
159
+ }
160
+ return TimberURLHelper::is_external( $this->url );
161
+ }
162
+
163
+ public function meta( $key ) {
164
+ if ( is_object( $this->menu_object ) && method_exists( $this->menu_object, 'meta' ) ) {
165
+ return $this->menu_object->meta( $key );
166
+ }
167
+ if ( isset( $this->$key ) ) {
168
+ return $this->$key;
169
+ }
170
+ }
171
+
172
+ /* Aliases */
173
+
174
+ /**
175
+ *
176
+ *
177
+ * @return array|bool
178
+ */
179
+ public function children() {
180
+ return $this->get_children();
181
+ }
182
+
183
+ /**
184
+ *
185
+ *
186
+ * @return bool
187
+ */
188
+ public function external() {
189
+ return $this->is_external();
190
+ }
191
+
192
+ /**
193
+ *
194
+ *
195
+ * @return string
196
+ */
197
+ public function link() {
198
+ return $this->get_link();
199
+ }
200
+
201
+ /**
202
+ *
203
+ *
204
+ * @return string
205
+ */
206
+ public function path() {
207
+ return $this->get_path();
208
+ }
209
+
210
+ /**
211
+ *
212
+ *
213
+ * @return string
214
+ */
215
+ public function permalink() {
216
+ return $this->get_link();
217
+ }
218
+
219
+ /**
220
+ *
221
+ *
222
+ * @return string
223
+ */
224
+ public function get_permalink() {
225
+ return $this->get_link();
226
+ }
227
+
228
+ public function title() {
229
+ if (isset($this->__title)){
230
+ return $this->__title;
231
+ }
232
+ }
233
+
234
+ }
functions/functions/timber-menu.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberMenu extends TimberCore {
4
+
5
+ public $MenuItemClass = 'TimberMenuItem';
6
+ public $PostClass = 'TimberPost';
7
+
8
+ public $items = null;
9
+ public $id = null;
10
+ public $ID = null;
11
+ public $name = null;
12
+ public $term_id;
13
+ public $title;
14
+
15
+ /**
16
+ * @param int $slug
17
+ */
18
+ function __construct($slug = 0) {
19
+ $locations = get_nav_menu_locations();
20
+ if ($slug != 0 && is_numeric($slug)) {
21
+ $menu_id = $slug;
22
+ } else if (is_array($locations) && count($locations)) {
23
+ $menu_id = $this->get_menu_id_from_locations($slug, $locations);
24
+ } else if ($slug === false) {
25
+ $menu_id = false;
26
+ } else {
27
+ $menu_id = $this->get_menu_id_from_terms($slug);
28
+ }
29
+ if ($menu_id) {
30
+ $this->init($menu_id);
31
+ } else {
32
+ $this->init_as_page_menu();
33
+ //TimberHelper::error_log("Sorry, the menu you were looking for wasn't found ('" . $slug . "'). Here's what Timber did find:");
34
+ }
35
+ return null;
36
+ }
37
+
38
+ /**
39
+ * @param int $menu_id
40
+ */
41
+ private function init($menu_id) {
42
+ $menu = wp_get_nav_menu_items($menu_id);
43
+ if ($menu) {
44
+ _wp_menu_item_classes_by_context($menu);
45
+ if (is_array($menu)){
46
+ $menu = self::order_children($menu);
47
+ }
48
+ $this->items = $menu;
49
+ $menu_info = wp_get_nav_menu_object($menu_id);
50
+ $this->import($menu_info);
51
+ $this->ID = $this->term_id;
52
+ $this->id = $this->term_id;
53
+ $this->title = $this->name;
54
+ }
55
+ }
56
+
57
+ private function init_as_page_menu() {
58
+ $menu = get_pages();
59
+ if ($menu) {
60
+ foreach($menu as $mi) {
61
+ $mi->__title = $mi->post_title;
62
+ }
63
+ _wp_menu_item_classes_by_context($menu);
64
+ if (is_array($menu)){
65
+ $menu = self::order_children($menu);
66
+ }
67
+ $this->items = $menu;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * @param string $slug
73
+ * @param array $locations
74
+ * @return integer
75
+ */
76
+ private function get_menu_id_from_locations($slug, $locations) {
77
+ if ($slug === 0) {
78
+ $slug = $this->get_menu_id_from_terms($slug);
79
+ }
80
+ if (is_numeric($slug)) {
81
+ $slug = array_search($slug, $locations);
82
+ }
83
+ if (isset($locations[$slug])) {
84
+ return $locations[$slug];
85
+ }
86
+ return null;
87
+ }
88
+
89
+ /**
90
+ * @param int $slug
91
+ * @return int
92
+ */
93
+ private function get_menu_id_from_terms($slug = 0) {
94
+ if (!is_numeric($slug) && is_string($slug)) {
95
+ //we have a string so lets search for that
96
+ $menu_id = get_term_by('slug', $slug, 'nav_menu');
97
+ if ($menu_id) {
98
+ return $menu_id;
99
+ }
100
+ $menu_id = get_term_by('name', $slug, 'nav_menu');
101
+ if ($menu_id) {
102
+ return $menu_id;
103
+ }
104
+ }
105
+ $menus = get_terms('nav_menu', array('hide_empty' => true));
106
+ if (is_array($menus) && count($menus)) {
107
+ if (isset($menus[0]->term_id)) {
108
+ return $menus[0]->term_id;
109
+ }
110
+ }
111
+ return 0;
112
+ }
113
+
114
+ /**
115
+ * @param array $menu_items
116
+ * @param int $parent_id
117
+ * @return TimberMenuItem|null
118
+ */
119
+ function find_parent_item_in_menu($menu_items, $parent_id) {
120
+ foreach ($menu_items as &$item) {
121
+ if ($item->ID == $parent_id) {
122
+ return $item;
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+
128
+ /**
129
+ * @param array $items
130
+ * @return array
131
+ */
132
+ function order_children($items) {
133
+ $index = array();
134
+ $menu = array();
135
+ foreach ($items as $item) {
136
+ if (isset($item->title)) {
137
+ //items from wp can come with a $title property which conflicts with methods
138
+ $item->__title = $item->title;
139
+ unset($item->title);
140
+ }
141
+ if(isset($item->ID)){
142
+ if (is_object($item) && get_class($item) == 'WP_Post'){
143
+ $old_menu_item = $item;
144
+ $item = new $this->PostClass($item);
145
+ }
146
+ $menu_item = new $this->MenuItemClass($item);
147
+ if (isset($old_menu_item)){
148
+ $menu_item->import_classes($old_menu_item);
149
+ }
150
+ $index[$item->ID] = $menu_item;
151
+ }
152
+ }
153
+ foreach ($index as $item) {
154
+ if (isset($item->menu_item_parent) && $item->menu_item_parent && isset($index[$item->menu_item_parent])) {
155
+ $index[$item->menu_item_parent]->add_child($item);
156
+ } else {
157
+ $menu[] = $item;
158
+ }
159
+ }
160
+ return $menu;
161
+ }
162
+
163
+ /**
164
+ * @return array
165
+ */
166
+ function get_items() {
167
+ if (is_array($this->items)) {
168
+ return $this->items;
169
+ }
170
+ return array();
171
+ }
172
+ }
173
+
174
+
functions/functions/timber-page.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberPage extends TimberPost
4
+ {
5
+
6
+ }
functions/functions/timber-post-getter.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberPostGetter {
4
+
5
+ /**
6
+ * @param mixed $query
7
+ * @param string $PostClass
8
+ * @return array|bool|null
9
+ */
10
+ static function get_post($query = false, $PostClass = 'TimberPost') {
11
+ $posts = self::get_posts( $query, $PostClass );
12
+ if ( $post = reset($posts ) ) {
13
+ return $post;
14
+ }
15
+ return false;
16
+ }
17
+
18
+ static function get_posts( $query = false, $PostClass = 'TimberPost', $return_collection = false ) {
19
+ $posts = self::query_posts( $query, $PostClass );
20
+ return apply_filters('timber_post_getter_get_posts', $posts->get_posts( $return_collection ));
21
+ }
22
+
23
+ static function query_post( $query = false, $PostClass = 'TimberPost' ) {
24
+ $posts = self::query_posts( $query, $PostClass );
25
+ if ( $post = $posts->current() ) {
26
+ return $post;
27
+ }
28
+ return false;
29
+ }
30
+
31
+ /**
32
+ * @param mixed $query
33
+ * @param string $PostClass
34
+ * @return array|bool|null
35
+ */
36
+ static function query_posts($query = false, $PostClass = 'TimberPost' ){
37
+ if (self::is_post_class_or_class_map($query)) {
38
+ $PostClass = $query;
39
+ $query = false;
40
+ }
41
+
42
+ if (is_object($query) && !is_a($query, 'WP_Query') ){
43
+ // The only object other than a query is a type of post object
44
+ $query = array( $query );
45
+ }
46
+
47
+ if ( is_array( $query ) && count( $query ) && isset( $query[0] ) && is_object( $query[0] ) ) {
48
+ // We have an array of post objects that already have data
49
+ return new TimberPostsCollection( $query, $PostClass );
50
+ } else {
51
+ // We have a query (of sorts) to work with
52
+ $tqi = new TimberQueryIterator( $query, $PostClass );
53
+ return $tqi;
54
+ }
55
+ }
56
+
57
+ static function get_pids($query){
58
+ $posts = self::get_posts($query);
59
+ $pids = array();
60
+ foreach($posts as $post){
61
+ if (isset($post->ID)){
62
+ $pids[] = $post->ID;
63
+ }
64
+ }
65
+ return $pids;
66
+ }
67
+
68
+ /**
69
+ * @param array $results
70
+ * @param string $PostClass
71
+ * @return TimberPostsCollection
72
+ */
73
+ static function handle_post_results($results, $PostClass = 'TimberPost') {
74
+ $posts = array();
75
+ foreach ($results as $rid) {
76
+ $PostClassUse = $PostClass;
77
+ if (is_array($PostClass)) {
78
+ $post_type = get_post_type($rid);
79
+ $PostClassUse = 'TimberPost';
80
+ if (isset($PostClass[$post_type])) {
81
+ $PostClassUse = $PostClass[$post_type];
82
+ } else {
83
+ if (is_array($PostClass)) {
84
+ TimberHelper::error_log($post_type.' of '.$rid.' not found in ' . print_r($PostClass, true));
85
+ } else {
86
+ TimberHelper::error_log($post_type.' not found in '.$PostClass);
87
+ }
88
+ }
89
+ }
90
+ $post = new $PostClassUse($rid);
91
+ if (isset($post->ID)) {
92
+ $posts[] = $post;
93
+ }
94
+ }
95
+ return new TimberPostsCollection( $posts, $PostClass );
96
+ }
97
+
98
+ static function loop_to_id() {
99
+ if (!self::wp_query_has_posts()) { return false; }
100
+
101
+ global $wp_query;
102
+ $post_num = property_exists($wp_query, 'current_post')
103
+ ? $wp_query->current_post + 1
104
+ : 0
105
+ ;
106
+
107
+ if (!isset($wp_query->posts[$post_num])) { return false; }
108
+
109
+ return $wp_query->posts[$post_num]->ID;
110
+ }
111
+
112
+ /**
113
+ * @return bool
114
+ */
115
+ static function wp_query_has_posts() {
116
+ global $wp_query;
117
+ return ($wp_query && property_exists($wp_query, 'posts') && $wp_query->posts);
118
+ }
119
+
120
+ /**
121
+ * @param string|array $arg
122
+ * @return bool
123
+ */
124
+ static function is_post_class_or_class_map($arg){
125
+ if (is_string($arg) && class_exists($arg)) {
126
+ return true;
127
+ }
128
+ if (is_array($arg)) {
129
+ foreach ($arg as $item) {
130
+ if (is_string($item) && class_exists($item)) {
131
+ return true;
132
+ }
133
+ }
134
+ }
135
+ return false;
136
+ }
137
+ }
functions/functions/timber-post.php ADDED
@@ -0,0 +1,1007 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberPost extends TimberCore implements TimberCoreInterface {
4
+
5
+ public $ImageClass = 'TimberImage';
6
+ public $PostClass = 'TimberPost';
7
+
8
+ public $object_type = 'post';
9
+ public static $representation = 'post';
10
+
11
+ public $_can_edit;
12
+ public $_custom_imported = false;
13
+ public $_content;
14
+ public $_get_terms;
15
+
16
+ private $_next = array();
17
+ private $_prev = array();
18
+
19
+ public $class;
20
+ public $display_date;
21
+ public $id;
22
+ public $ID;
23
+ public $post_content;
24
+ public $post_date;
25
+ public $post_parent;
26
+ public $post_title;
27
+ public $post_type;
28
+ public $slug;
29
+
30
+ /**
31
+ * If you send the constructor nothing it will try to figure out the current post id based on being inside The_Loop
32
+ * @param mixed $pid
33
+ * @return \TimberPost TimberPost object -- woo!
34
+ */
35
+ function __construct($pid = null) {
36
+ global $wp_query;
37
+ if ($pid === null && isset($wp_query->queried_object_id) && $wp_query->queried_object_id) {
38
+ $pid = $wp_query->queried_object_id;
39
+ $this->ID = $pid;
40
+ } else if ($pid === null && get_the_ID()) {
41
+ $pid = get_the_ID();
42
+ $this->ID = $pid;
43
+ } else if ($pid === null && ($pid_from_loop = TimberPostGetter::loop_to_id())) {
44
+ $this->ID = $pid_from_loop;
45
+ }
46
+ if (is_numeric($pid)) {
47
+ $this->ID = $pid;
48
+ }
49
+ $this->init($pid);
50
+ }
51
+
52
+ /**
53
+ * @return string
54
+ */
55
+ function __toString() {
56
+ return $this->title();
57
+ }
58
+
59
+
60
+ /**
61
+ * @param int|bool $pid
62
+ */
63
+ function init($pid = false) {
64
+ if ($pid === false) {
65
+ $pid = get_the_ID();
66
+ }
67
+ $post_info = $this->get_info($pid);
68
+ $this->import($post_info);
69
+ /* deprecated, adding for support for older themes */
70
+ $this->display_date = $this->date();
71
+ //cant have a function, so gots to do it this way
72
+ $post_class = $this->post_class();
73
+ $this->class = $post_class;
74
+ }
75
+
76
+ /**
77
+ * Get the URL that will edit the current post/object
78
+ *
79
+ * @return bool|string
80
+ */
81
+ function get_edit_url() {
82
+ if ($this->can_edit()) {
83
+ return get_edit_post_link($this->ID);
84
+ }
85
+ return false;
86
+ }
87
+
88
+ /**
89
+ * updates the post_meta of the current object with the given value
90
+ *
91
+ * @param string $field
92
+ * @param mixed $value
93
+ */
94
+ public function update($field, $value) {
95
+ if (isset($this->ID)) {
96
+ update_post_meta($this->ID, $field, $value);
97
+ $this->$field = $value;
98
+ }
99
+ }
100
+
101
+
102
+ /**
103
+ * takes a mix of integer (post ID), string (post slug), or object to return a WordPress post object from WP's built-in get_post() function
104
+ *
105
+ * @param mixed $pid
106
+ * @return WP_Post on success
107
+ */
108
+ private function prepare_post_info($pid = 0) {
109
+ if (is_string($pid) || is_numeric($pid) || (is_object($pid) && !isset($pid->post_title)) || $pid === 0) {
110
+ $pid = self::check_post_id($pid);
111
+ $post = get_post($pid);
112
+ if ($post) {
113
+ return $post;
114
+ } else {
115
+ $post = get_page($pid);
116
+ return $post;
117
+ }
118
+ }
119
+ //we can skip if already is WP_Post
120
+ return $pid;
121
+ }
122
+
123
+
124
+ /**
125
+ * helps you find the post id regardless of whetehr you send a string or whatever
126
+ *
127
+ * @param integer $pid ;
128
+ * @return integer ID number of a post
129
+ */
130
+ private function check_post_id($pid) {
131
+ if (is_numeric($pid) && $pid === 0) {
132
+ $pid = get_the_ID();
133
+ return $pid;
134
+ }
135
+ if (!is_numeric($pid) && is_string($pid)) {
136
+ $pid = self::get_post_id_by_name($pid);
137
+ return $pid;
138
+ }
139
+ if (!$pid) {
140
+ return null;
141
+ }
142
+ return $pid;
143
+ }
144
+
145
+
146
+ /**
147
+ * get_post_id_by_name($post_name)
148
+ *
149
+ * @param string $post_name
150
+ * @return int
151
+ */
152
+ public static function get_post_id_by_name($post_name) {
153
+ global $wpdb;
154
+ $query = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_name = %s LIMIT 1", $post_name);
155
+ $result = $wpdb->get_row($query);
156
+ if (!$result) {
157
+ return null;
158
+ }
159
+ return $result->ID;
160
+ }
161
+
162
+
163
+ /**
164
+ * ## get a preview of your post, if you have an excerpt it will use that,
165
+ * ## otherwise it will pull from the post_content.
166
+ * ## If there's a <!-- more --> tag it will use that to mark where to pull through.
167
+ * <p>{{post.get_preview(50)}}</p>
168
+ */
169
+
170
+ /**
171
+ * @param int $len
172
+ * @param bool $force
173
+ * @param string $readmore
174
+ * @param bool $strip
175
+ * @return string
176
+ */
177
+ function get_preview($len = 50, $force = false, $readmore = 'Read More', $strip = true) {
178
+ $text = '';
179
+ $trimmed = false;
180
+ if (isset($this->post_excerpt) && strlen($this->post_excerpt)) {
181
+ if ($force) {
182
+ $text = TimberHelper::trim_words($this->post_excerpt, $len, false);
183
+ $trimmed = true;
184
+ } else {
185
+ $text = $this->post_excerpt;
186
+ }
187
+ }
188
+ if (!strlen($text) && strpos($this->post_content, '<!--more-->') !== false) {
189
+ $pieces = explode('<!--more-->', $this->post_content);
190
+ $text = $pieces[0];
191
+ if ($force) {
192
+ $text = TimberHelper::trim_words($text, $len, false);
193
+ $trimmed = true;
194
+ }
195
+ }
196
+ if (!strlen($text)) {
197
+ $text = TimberHelper::trim_words($this->get_content(), $len, false);
198
+ $trimmed = true;
199
+ }
200
+ if (!strlen(trim($text))) {
201
+ return $text;
202
+ }
203
+ if ($strip) {
204
+ $text = trim(strip_tags($text));
205
+ }
206
+ if (strlen($text)) {
207
+ $text = trim($text);
208
+ $last = $text[strlen($text) - 1];
209
+ if ($last != '.' && $trimmed) {
210
+ $text .= ' &hellip; ';
211
+ }
212
+ if (!$strip) {
213
+ $last_p_tag = strrpos($text, '</p>');
214
+ if ($last_p_tag !== false) {
215
+ $text = substr($text, 0, $last_p_tag);
216
+ }
217
+ if ($last != '.' && $trimmed) {
218
+ $text .= ' &hellip; ';
219
+ }
220
+ }
221
+
222
+ if ($readmore) {
223
+ $text .= ' <a href="' . $this->get_permalink() . '" class="read-more">' . $readmore . '</a>';
224
+ }
225
+ if (!$strip) {
226
+ $text .= '</p>';
227
+ }
228
+ }
229
+ return $text;
230
+ }
231
+
232
+ /**
233
+ * gets the post custom and attaches it to the current object
234
+ * @param bool|int $pid a post ID number
235
+ * @nodoc
236
+ */
237
+ function import_custom($pid = false) {
238
+ if (!$pid) {
239
+ $pid = $this->ID;
240
+ }
241
+ $customs = $this->get_post_custom($pid);
242
+ $this->import($customs);
243
+ }
244
+
245
+ /**
246
+ * @param int $pid
247
+ * @return array
248
+ */
249
+ function get_post_custom($pid) {
250
+ apply_filters('timber_post_get_meta_pre', array(), $pid, $this);
251
+ $customs = get_post_custom($pid);
252
+ if (!is_array($customs) || empty($customs)) {
253
+ return array();
254
+ }
255
+ foreach ($customs as $key => $value) {
256
+ if (is_array($value) && count($value) == 1 && isset($value[0])) {
257
+ $value = $value[0];
258
+ }
259
+ $customs[$key] = maybe_unserialize($value);
260
+ }
261
+ $customs = apply_filters('timber_post_get_meta', $customs, $pid, $this);
262
+ return $customs;
263
+ }
264
+
265
+ /**
266
+ * ## get the featured image as a TimberImage
267
+ * <img src="{{post.get_thumbnail.get_src}}" />
268
+ */
269
+
270
+ /**
271
+ * @return null|TimberImage
272
+ */
273
+ function get_thumbnail() {
274
+ if (function_exists('get_post_thumbnail_id')) {
275
+ $tid = get_post_thumbnail_id($this->ID);
276
+ if ($tid) {
277
+ return new $this->ImageClass($tid);
278
+ }
279
+ }
280
+ return null;
281
+ }
282
+
283
+ /**
284
+ * @return string
285
+ */
286
+ function get_permalink() {
287
+ if (isset($this->permalink)) {
288
+ return $this->permalink;
289
+ }
290
+ $this->permalink = get_permalink($this->ID);
291
+ return $this->permalink;
292
+ }
293
+
294
+ /**
295
+ * @return string
296
+ */
297
+ function get_link() {
298
+ return $this->get_permalink();
299
+ }
300
+
301
+ /**
302
+ * @param bool $taxonomy
303
+ * @return mixed
304
+ */
305
+ function get_next($taxonomy = false) {
306
+ if (!isset($this->_next) || !isset($this->_next[$taxonomy])) {
307
+ global $post;
308
+ $this->_next = array();
309
+ $old_global = $post;
310
+ $post = $this;
311
+ if ($taxonomy) {
312
+ $adjacent = get_adjacent_post(true, '', false, $taxonomy);
313
+ } else {
314
+ $adjacent = get_adjacent_post(false, '', false);
315
+ }
316
+
317
+ if ($adjacent) {
318
+ $this->_next[$taxonomy] = new $this->PostClass($adjacent);
319
+ } else {
320
+ $this->_next[$taxonomy] = false;
321
+ }
322
+ $post = $old_global;
323
+ }
324
+ return $this->_next[$taxonomy];
325
+ }
326
+
327
+ /**
328
+ * @return array
329
+ */
330
+ public function get_pagination() {
331
+ global $post, $page, $numpages, $multipage;
332
+ $post = $this;
333
+ $ret = array();
334
+ if ($multipage) {
335
+ for ($i = 1; $i <= $numpages; $i++) {
336
+ $link = self::get_wp_link_page($i);
337
+ $data = array('name' => $i, 'title' => $i, 'text' => $i, 'link' => $link);
338
+ if ($i == $page) {
339
+ $data['current'] = true;
340
+ }
341
+ $ret['pages'][] = $data;
342
+ }
343
+ $i = $page - 1;
344
+ if ($i) {
345
+ $link = self::get_wp_link_page($i);
346
+ $ret['prev'] = array('link' => $link);
347
+ }
348
+ $i = $page + 1;
349
+ if ($i <= $numpages) {
350
+ $link = self::get_wp_link_page($i);
351
+ $ret['next'] = array('link' => $link);
352
+ }
353
+ }
354
+ return $ret;
355
+ }
356
+
357
+ /**
358
+ * @param int $i
359
+ * @return string
360
+ */
361
+ private static function get_wp_link_page($i) {
362
+ $link = _wp_link_page($i);
363
+ $link = new SimpleXMLElement($link . '</a>');
364
+ if (isset($link['href'])) {
365
+ return $link['href'];
366
+ }
367
+ return '';
368
+ }
369
+
370
+ /**
371
+ * @return string
372
+ */
373
+ function get_path() {
374
+ return TimberURLHelper::get_rel_url($this->get_link());
375
+ }
376
+
377
+ /**
378
+ * @param bool $taxonomy
379
+ * @return mixed
380
+ */
381
+ function get_prev($taxonomy = false) {
382
+ if (isset($this->_prev) && isset($this->_prev[$taxonomy])) {
383
+ return $this->_prev[$taxonomy];
384
+ }
385
+ global $post;
386
+ $old_global = $post;
387
+ $post = $this;
388
+ $within_taxonomy = ($taxonomy) ? $taxonomy : 'category';
389
+ $adjacent = get_adjacent_post(($taxonomy), '', true, $within_taxonomy);
390
+
391
+ $prev_in_taxonomy = false;
392
+ if ($adjacent) {
393
+ $prev_in_taxonomy = new $this->PostClass($adjacent);
394
+ }
395
+ $this->_prev[$taxonomy] = $prev_in_taxonomy;
396
+ $post = $old_global;
397
+ return $this->_prev[$taxonomy];
398
+ }
399
+
400
+ /**
401
+ * @return bool|TimberPost
402
+ */
403
+ function get_parent() {
404
+ if (!$this->post_parent) {
405
+ return false;
406
+ }
407
+ return new $this->PostClass($this->post_parent);
408
+ }
409
+
410
+ /**
411
+ * ## Gets a User object from the author of the post
412
+ * <p class="byline">{{post.get_author.name}}</p>
413
+ */
414
+
415
+ /**
416
+ * @return bool|TimberUser
417
+ */
418
+ function get_author() {
419
+ if (isset($this->post_author)) {
420
+ return new TimberUser($this->post_author);
421
+ }
422
+ return false;
423
+ }
424
+
425
+ /**
426
+ * @return bool|TimberUser
427
+ */
428
+ function get_modified_author() {
429
+ $user_id = get_post_meta($this->ID, '_edit_last', true);
430
+ return ($user_id ? new TimberUser($user_id) : $this->get_author());
431
+ }
432
+
433
+ /**
434
+ * @param int $pid
435
+ * @return null|object|WP_Post
436
+ */
437
+ function get_info($pid) {
438
+ $post = $this->prepare_post_info($pid);
439
+ if (!isset($post->post_status)) {
440
+ return null;
441
+ }
442
+ $post->status = $post->post_status;
443
+ $post->id = $post->ID;
444
+ $post->slug = $post->post_name;
445
+ $customs = $this->get_post_custom($post->ID);
446
+ $post->custom = $customs;
447
+ $post = (object)array_merge((array)$customs, (array)$post);
448
+ return $post;
449
+ }
450
+
451
+ /**
452
+ * This is deprecated!
453
+ * @param string $use
454
+ * @return string
455
+ */
456
+ function get_display_date($use = 'post_date') {
457
+ return date(get_option('date_format'), strtotime($this->$use));
458
+ }
459
+
460
+ /**
461
+ * @param string $date_format
462
+ * @return string
463
+ */
464
+ function get_date($date_format = '') {
465
+ $df = $date_format ? $date_format : get_option('date_format');
466
+ $the_date = (string)mysql2date($df, $this->post_date);
467
+ return apply_filters('get_the_date', $the_date, $date_format);
468
+ }
469
+
470
+ /**
471
+ * @param string $date_format
472
+ * @return string
473
+ */
474
+ function get_modified_date($date_format = '') {
475
+ $df = $date_format ? $date_format : get_option('date_format');
476
+ $the_time = $this->get_modified_time($df, null, $this->ID, true);
477
+ return apply_filters('get_the_modified_date', $the_time, $date_format);
478
+ }
479
+
480
+ /**
481
+ * @param string $time_format
482
+ * @return string
483
+ */
484
+ function get_modified_time($time_format = '') {
485
+ $tf = $time_format ? $time_format : get_option('time_format');
486
+ $the_time = get_post_modified_time($tf, false, $this->ID, true);
487
+ return apply_filters('get_the_modified_time', $the_time, $time_format);
488
+ }
489
+
490
+ /**
491
+ * @param string $post_type
492
+ * @param bool $childPostClass
493
+ * @return array
494
+ */
495
+ function get_children($post_type = 'any', $childPostClass = false) {
496
+ if ($childPostClass == false) {
497
+ $childPostClass = $this->PostClass;
498
+ }
499
+ if ($post_type == 'parent') {
500
+ $post_type = $this->post_type;
501
+ }
502
+ $children = get_children('post_parent=' . $this->ID . '&post_type=' . $post_type . '&numberposts=-1&orderby=menu_order title&order=ASC');
503
+ foreach ($children as &$child) {
504
+ $child = new $childPostClass($child->ID);
505
+ }
506
+ $children = array_values($children);
507
+ return $children;
508
+ }
509
+
510
+ /**
511
+ * {% for comment in post.get_comments %}
512
+ * <p>{{comment.content}}</p>
513
+ * {% endfor %}
514
+ */
515
+
516
+ /**
517
+ * @param int $ct
518
+ * @param string $order
519
+ * @param string $type
520
+ * @param string $status
521
+ * @param string $CommentClass
522
+ * @return mixed
523
+ */
524
+ function get_comments($ct = 0, $order = 'wp', $type = 'comment', $status = 'approve', $CommentClass = 'TimberComment') {
525
+ $args = array('post_id' => $this->ID, 'status' => $status, 'order' => $order);
526
+ if ($ct > 0) {
527
+ $args['number'] = $ct;
528
+ }
529
+ if ($order == 'wp') {
530
+ $args['order'] = get_option('comment_order');
531
+ }
532
+ $comments = get_comments($args);
533
+ foreach ($comments as &$comment) {
534
+ $comment = new $CommentClass($comment);
535
+ }
536
+ return $comments;
537
+ }
538
+
539
+ /**
540
+ * <ul class="categories">
541
+ * {% for category in post.get_categories %}
542
+ * <li>{{category.name}}</li>
543
+ * {% endfor %}
544
+ * </ul>
545
+ */
546
+
547
+ /**
548
+ * @return array
549
+ */
550
+ function get_categories() {
551
+ return $this->get_terms('category');
552
+ }
553
+
554
+ /**
555
+ * @return mixed
556
+ */
557
+ function get_category() {
558
+ $cats = $this->get_categories();
559
+ if (count($cats) && isset($cats[0])) {
560
+ return $cats[0];
561
+ }
562
+ return null;
563
+ }
564
+
565
+ /** # get terms is good
566
+ *
567
+ */
568
+
569
+ /**
570
+ * @param string $tax
571
+ * @param bool $merge
572
+ * @param string $TermClass
573
+ * @return array
574
+ */
575
+ function get_terms($tax = '', $merge = true, $TermClass = 'TimberTerm') {
576
+ if (is_string($merge) && class_exists($merge)){
577
+ $TermClass = $merge;
578
+ }
579
+ if (is_string($tax)) {
580
+ if (isset($this->_get_terms) && isset($this->_get_terms[$tax])) {
581
+ return $this->_get_terms[$tax];
582
+ }
583
+ }
584
+ if (!strlen($tax) || $tax == 'all' || $tax == 'any') {
585
+ $taxs = get_object_taxonomies($this->post_type);
586
+ } else if (is_array($tax)) {
587
+ $taxs = $tax;
588
+ } else {
589
+ $taxs = array($tax);
590
+ }
591
+ $ret = array();
592
+ foreach ($taxs as $tax) {
593
+ if ($tax == 'tags' || $tax == 'tag') {
594
+ $tax = 'post_tag';
595
+ } else if ($tax == 'categories') {
596
+ $tax = 'category';
597
+ }
598
+ $terms = wp_get_post_terms($this->ID, $tax);
599
+ if (!is_array($terms) && is_object($terms) && get_class($terms) == 'WP_Error') {
600
+ //something is very wrong
601
+ TimberHelper::error_log('You have an error retrieving terms on a post in timber-post.php:367');
602
+ TimberHelper::error_log('tax = ' . $tax);
603
+ TimberHelper::error_log($terms);
604
+
605
+ } else {
606
+ foreach ($terms as &$term) {
607
+ $term = new $TermClass($term->term_id, $tax);
608
+ }
609
+ if ($merge && is_array($terms)) {
610
+ $ret = array_merge($ret, $terms);
611
+ } else if (count($terms)) {
612
+ $ret[$tax] = $terms;
613
+ }
614
+ }
615
+ }
616
+ if (!isset($this->_get_terms)) {
617
+ $this->_get_terms = array();
618
+ }
619
+ $this->_get_terms[$tax] = $ret;
620
+ return $ret;
621
+ }
622
+
623
+ /**
624
+ * @param string|int $term_name_or_id
625
+ * @param string $taxonomy
626
+ * @return bool
627
+ */
628
+ function has_term($term_name_or_id, $taxonomy = 'all') {
629
+ if ($taxonomy == 'all' || $taxonomy == 'any') {
630
+ $taxes = get_object_taxonomies($this->post_type, 'names');
631
+ $ret = false;
632
+ foreach ($taxes as $tax) {
633
+ if (has_term($term_name_or_id, $tax, $this->ID)) {
634
+ $ret = true;
635
+ break;
636
+ }
637
+ }
638
+ return $ret;
639
+ }
640
+ return has_term($term_name_or_id, $taxonomy, $this->ID);
641
+ }
642
+
643
+ /**
644
+ * @param string $field
645
+ * @return TimberImage
646
+ */
647
+ function get_image($field) {
648
+ return new $this->ImageClass($this->$field);
649
+ }
650
+
651
+ /**
652
+ * ## Gets an array of tags for you to use
653
+ * <ul class="tags">
654
+ * {% for tag in post.tags %}
655
+ * <li>{{tag.name}}</li>
656
+ * {% endfor %}
657
+ * </ul>
658
+ */
659
+
660
+ /**
661
+ * @return array
662
+ */
663
+ function get_tags() {
664
+ return $this->get_terms('tags');
665
+ }
666
+
667
+ /**
668
+ * ## Outputs the title with filters applied
669
+ * <h1>{{post.get_title}}</h1>
670
+ */
671
+
672
+ /**
673
+ * @return string
674
+ */
675
+ function get_title() {
676
+ return apply_filters('the_title', $this->post_title, $this->ID);
677
+ }
678
+
679
+ /**
680
+ * ## Displays the content of the post with filters, shortcodes and wpautop applied
681
+ * <div class="article-text">{{post.get_content}}</div>
682
+ */
683
+
684
+ /**
685
+ * @param int $len
686
+ * @param int $page
687
+ * @return string
688
+ */
689
+ function get_content($len = 0, $page = 0) {
690
+ if ($len == 0 && $page == 0 && $this->_content) {
691
+ return $this->_content;
692
+ }
693
+ $content = $this->post_content;
694
+ if ($len) {
695
+ $content = wp_trim_words($content, $len);
696
+ }
697
+ if ($page) {
698
+ $contents = explode('<!--nextpage-->', $content);
699
+ $page--;
700
+ if (count($contents) > $page) {
701
+ $content = $contents[$page];
702
+ }
703
+ }
704
+ $content = apply_filters('the_content', ($content));
705
+ if ($len == 0 && $page == 0) {
706
+ $this->_content = $content;
707
+ }
708
+ return $content;
709
+ }
710
+
711
+ /**
712
+ * @return string
713
+ */
714
+ function get_paged_content() {
715
+ global $page;
716
+ return $this->get_content(0, $page);
717
+ }
718
+ /**
719
+ * @return mixed
720
+ */
721
+ public function get_post_type() {
722
+ return get_post_type_object($this->post_type);
723
+ }
724
+
725
+ /**
726
+ * @return int
727
+ */
728
+ public function get_comment_count() {
729
+ if (isset($this->ID)) {
730
+ return get_comments_number($this->ID);
731
+ } else {
732
+ return 0;
733
+ }
734
+ }
735
+
736
+ /**
737
+ * @param string $field_name
738
+ * @return mixed
739
+ */
740
+ public function get_field($field_name) {
741
+ $value = apply_filters('timber_post_get_meta_field_pre', null, $this->ID, $field_name, $this);
742
+ if ($value === null) {
743
+ $value = get_post_meta($this->ID, $field_name);
744
+ if (is_array($value) && count($value) == 1) {
745
+ $value = $value[0];
746
+ }
747
+ if (is_array($value) && count($value) == 0) {
748
+ $value = null;
749
+ }
750
+ }
751
+ $value = apply_filters('timber_post_get_meta_field', $value, $this->ID, $field_name, $this);
752
+ return $value;
753
+ }
754
+
755
+ /**
756
+ * @param string $field_name
757
+ */
758
+ function import_field($field_name) {
759
+ $this->$field_name = $this->get_field($field_name);
760
+ }
761
+
762
+ /**
763
+ * @return mixed
764
+ */
765
+ function get_format() {
766
+ return get_post_format($this->ID);
767
+ }
768
+
769
+ /**
770
+ * @param string $class
771
+ * @return string
772
+ */
773
+ public function post_class($class='') {
774
+ global $post;
775
+ $old_global_post = $post;
776
+ $post = $this;
777
+ $class_array = get_post_class($class, $this->ID);
778
+ $post = $old_global_post;
779
+ if (is_array($class_array)){
780
+ return implode(' ', $class_array);
781
+ }
782
+ return $class_array;
783
+ }
784
+
785
+ // Docs
786
+
787
+ /**
788
+ * @return array
789
+ */
790
+ public function get_method_values() {
791
+ $ret = parent::get_method_values();
792
+ $ret['author'] = $this->author();
793
+ $ret['categories'] = $this->categories();
794
+ $ret['category'] = $this->category();
795
+ $ret['children'] = $this->children();
796
+ $ret['comments'] = $this->comments();
797
+ $ret['content'] = $this->content();
798
+ $ret['edit_link'] = $this->edit_link();
799
+ $ret['format'] = $this->format();
800
+ $ret['link'] = $this->link();
801
+ $ret['next'] = $this->next();
802
+ $ret['pagination'] = $this->pagination();
803
+ $ret['parent'] = $this->parent();
804
+ $ret['path'] = $this->path();
805
+ $ret['prev'] = $this->prev();
806
+ $ret['terms'] = $this->terms();
807
+ $ret['tags'] = $this->tags();
808
+ $ret['thumbnail'] = $this->thumbnail();
809
+ $ret['title'] = $this->title();
810
+ return $ret;
811
+ }
812
+
813
+ // Aliases
814
+ /**
815
+ * @return bool|TimberUser
816
+ */
817
+ public function author() {
818
+ return $this->get_author();
819
+ }
820
+
821
+ /**
822
+ * @return bool|TimberUser
823
+ */
824
+ public function modified_author() {
825
+ return $this->get_modified_author();
826
+ }
827
+
828
+ /**
829
+ * @return array
830
+ */
831
+ public function categories() {
832
+ return $this->get_terms('category');
833
+ }
834
+
835
+ /**
836
+ * @return mixed
837
+ */
838
+ public function category() {
839
+ return $this->get_category();
840
+ }
841
+
842
+ /**
843
+ * @return array
844
+ */
845
+ public function children( $post_type = 'any', $childPostClass = false ) {
846
+ return $this->get_children( $post_type, $childPostClass );
847
+ }
848
+
849
+ /**
850
+ * @return mixed
851
+ */
852
+ public function comments() {
853
+ return $this->get_comments();
854
+ }
855
+
856
+ /**
857
+ * @param int $page
858
+ * @return string
859
+ */
860
+ public function content($page = 0) {
861
+ return $this->get_content(0, $page);
862
+ }
863
+
864
+ /**
865
+ * @return string
866
+ */
867
+ public function paged_content() {
868
+ return $this->get_paged_content();
869
+ }
870
+
871
+ /**
872
+ * @param string $date_format
873
+ * @return string
874
+ */
875
+ public function date($date_format = '') {
876
+ return $this->get_date($date_format);
877
+ }
878
+
879
+ /**
880
+ * @return bool|string
881
+ */
882
+ public function edit_link() {
883
+ return $this->get_edit_url();
884
+ }
885
+
886
+ /**
887
+ * @return mixed
888
+ */
889
+ public function format() {
890
+ return $this->get_format();
891
+ }
892
+
893
+ /**
894
+ * @return string
895
+ */
896
+ public function link() {
897
+ return $this->get_permalink();
898
+ }
899
+
900
+ /**
901
+ * @param string $field_name
902
+ * @return mixed
903
+ */
904
+ public function meta($field_name = null) {
905
+ if ($field_name == null) {
906
+ $field_name = 'meta';
907
+ }
908
+ return $this->get_field($field_name);
909
+ }
910
+
911
+ /**
912
+ * @return string
913
+ */
914
+ public function name(){
915
+ return $this->title();
916
+ }
917
+
918
+ /**
919
+ * @param string $date_format
920
+ * @return string
921
+ */
922
+ public function modified_date($date_format = '') {
923
+ return $this->get_modified_date($date_format);
924
+ }
925
+
926
+ /**
927
+ * @param string $time_format
928
+ * @return string
929
+ */
930
+ public function modified_time($time_format = '') {
931
+ return $this->get_modified_time($time_format);
932
+ }
933
+
934
+ /**
935
+ * @param bool $in_same_cat
936
+ * @return mixed
937
+ */
938
+ public function next($in_same_cat = false) {
939
+ return $this->get_next($in_same_cat);
940
+ }
941
+
942
+ /**
943
+ * @return array
944
+ */
945
+ public function pagination() {
946
+ return $this->get_pagination();
947
+ }
948
+
949
+ /**
950
+ * @return bool|TimberPost
951
+ */
952
+ public function parent() {
953
+ return $this->get_parent();
954
+ }
955
+
956
+ /**
957
+ * @return string
958
+ */
959
+ public function path() {
960
+ return $this->get_path();
961
+ }
962
+
963
+ /**
964
+ * @return string
965
+ */
966
+ public function permalink() {
967
+ return $this->get_permalink();
968
+ }
969
+
970
+ /**
971
+ * @param bool $in_same_cat
972
+ * @return mixed
973
+ */
974
+ public function prev($in_same_cat = false) {
975
+ return $this->get_prev($in_same_cat);
976
+ }
977
+
978
+ /**
979
+ * @param string $tax
980
+ * @return array
981
+ */
982
+ public function terms($tax = '') {
983
+ return $this->get_terms($tax);
984
+ }
985
+
986
+ /**
987
+ * @return array
988
+ */
989
+ public function tags() {
990
+ return $this->get_tags();
991
+ }
992
+
993
+ /**
994
+ * @return null|TimberImage
995
+ */
996
+ public function thumbnail() {
997
+ return $this->get_thumbnail();
998
+ }
999
+
1000
+ /**
1001
+ * @return string
1002
+ */
1003
+ public function title() {
1004
+ return $this->get_title();
1005
+ }
1006
+
1007
+ }
functions/functions/timber-posts-collection.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( !defined( 'ABSPATH' ) )
5
+ exit;
6
+
7
+ class TimberPostsCollection extends ArrayObject {
8
+
9
+ public function __construct( $array = array(), $post_class = 'TimberPost' ) {
10
+ $posts = array();
11
+ if ( is_null( $array ) ){
12
+ $array = array();
13
+ }
14
+ foreach ( $array as $rid ) {
15
+ $post_class_use = $post_class;
16
+
17
+ if ( is_array( $post_class ) ) {
18
+ $post_type = get_post_type( $rid );
19
+ $post_class_use = 'TimberPost';
20
+
21
+ if ( isset( $post_class[$post_type] ) ) {
22
+ $post_class_use = $post_class[$post_type];
23
+
24
+ } else {
25
+ if ( is_array( $post_class ) ) {
26
+ TimberHelper::error_log( $post_type . ' of ' . $rid . ' not found in ' . print_r( $post_class, true ) );
27
+ } else {
28
+ TimberHelper::error_log( $post_type . ' not found in ' . $post_class );
29
+ }
30
+ }
31
+ }
32
+
33
+ // Don't create yet another object if $rid is already of the right type
34
+ if ( is_a( $rid, $post_class_use ) ) {
35
+ $post = $rid;
36
+ } else {
37
+ $post = new $post_class_use( $rid );
38
+ }
39
+
40
+ if ( isset( $post->ID ) ) {
41
+ $posts[] = $post;
42
+ }
43
+ }
44
+
45
+ $posts = self::maybe_set_preview($posts);
46
+
47
+ parent::__construct( $posts, $flags = 0, 'TimberPostsIterator' );
48
+ }
49
+
50
+ public function get_posts() {
51
+ return $this->getArrayCopy();
52
+ }
53
+
54
+ /**
55
+ * @param array $posts
56
+ * @return array
57
+ */
58
+ static function maybe_set_preview( $posts ) {
59
+ if ( is_array( $posts ) && isset( $_GET['preview'] ) && $_GET['preview']
60
+ && isset( $_GET['preview_id'] ) && $_GET['preview_id']
61
+ && current_user_can( 'edit_post', $_GET['preview_id'] ) ) {
62
+ // No need to check the nonce, that already happened in _show_post_preview on init
63
+
64
+ $preview_id = $_GET['preview_id'];
65
+ foreach( $posts as &$post ) {
66
+ if ( is_object( $post ) && $post->ID == $preview_id ) {
67
+ // Based on _set_preview( $post ), but adds import_custom
68
+ $preview = wp_get_post_autosave( $preview_id );
69
+ if ( is_object($preview) ) {
70
+
71
+ $preview = sanitize_post($preview);
72
+
73
+ $post->post_content = $preview->post_content;
74
+ $post->post_title = $preview->post_title;
75
+ $post->post_excerpt = $preview->post_excerpt;
76
+ $post->import_custom( $preview_id );
77
+
78
+ add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
79
+ }
80
+ }
81
+ }
82
+
83
+ }
84
+
85
+ return $posts;
86
+ }
87
+
88
+ }
89
+
90
+ class TimberPostsIterator extends ArrayIterator {
91
+
92
+ public function current() {
93
+ global $post;
94
+ $post = parent::current();
95
+ return $post;
96
+ }
97
+ }
functions/functions/timber-query-iterator.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( !defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ class TimberQueryIterator implements Iterator {
7
+
8
+ /**
9
+ *
10
+ *
11
+ * @var WP_Query
12
+ */
13
+ private $_query = null;
14
+ private $_posts_class = 'TimberPost';
15
+
16
+ public function __construct( $query = false, $posts_class = 'TimberPost' ) {
17
+ add_action( 'pre_get_posts', array($this, 'fix_number_posts_wp_quirk' ));
18
+ if ( $posts_class )
19
+ $this->_posts_class = $posts_class;
20
+
21
+ if ( is_a( $query, 'WP_Query' ) ) {
22
+ // We got a full-fledged WP Query, look no further!
23
+ $the_query = $query;
24
+
25
+ } elseif ( false === $query ) {
26
+ // If query is explicitly set to false, use the main loop
27
+ global $wp_query;
28
+ $the_query =& $wp_query;
29
+
30
+ } elseif ( TimberHelper::is_array_assoc( $query ) || ( is_string( $query ) && strstr( $query, '=' ) ) ) {
31
+ // We have a regularly formed WP query string or array to use
32
+ $the_query = new WP_Query( $query );
33
+
34
+ } elseif ( is_numeric( $query ) || is_string( $query ) ) {
35
+ // We have what could be a post name or post ID to pull out
36
+ $the_query = self::get_query_from_string( $query );
37
+
38
+ } elseif ( is_array( $query ) && count( $query ) && ( is_integer( $query[0] ) || is_string( $query[0] ) ) ) {
39
+ // We have a list of pids (post IDs) to extract from
40
+ $the_query = self::get_query_from_array_of_ids( $query );
41
+ } elseif ( is_array($query) && empty($query)) {
42
+ // it's an empty array
43
+ $the_query = array();
44
+ } else {
45
+ TimberHelper::error_log( 'I have failed you! in ' . basename( __FILE__ ) . '::' . __LINE__ );
46
+ TimberHelper::error_log( $query );
47
+
48
+ // We have failed hard, at least let get something.
49
+ $the_query = new WP_Query();
50
+ }
51
+
52
+ $this->_query = $the_query;
53
+
54
+ }
55
+
56
+ public function get_posts( $return_collection = false ) {
57
+ if (isset($this->_query->posts)){
58
+ $posts = new TimberPostsCollection( $this->_query->posts, $this->_posts_class );
59
+ return ( $return_collection ) ? $posts : $posts->get_posts();
60
+ }
61
+ }
62
+
63
+ //
64
+ // GET POSTS
65
+ //
66
+ public static function get_query_from_array_of_ids( $query = array() ) {
67
+ if ( !is_array( $query ) || !count( $query ) )
68
+ return null;
69
+
70
+ return new WP_Query( array(
71
+ 'post_type'=> 'any',
72
+ 'ignore_sticky_posts' => true,
73
+ 'post__in' => $query,
74
+ 'orderby' => 'post__in',
75
+ 'nopaging' => true
76
+ ) );
77
+ }
78
+
79
+ public static function get_query_from_string( $string = '' ) {
80
+ $post_type = false;
81
+
82
+ if ( is_string( $string ) && strstr( $string, '#' ) ) {
83
+ //we have a post_type directive here
84
+ list( $post_type, $string ) = explode( '#', $string );
85
+ }
86
+
87
+ $query = array(
88
+ 'post_type' => ( $post_type ) ? $post_type : 'any'
89
+ );
90
+
91
+ if ( is_numeric( $string ) ) {
92
+ $query['p'] = $string;
93
+
94
+ } else {
95
+ $query['name'] = $string;
96
+ }
97
+
98
+ return new WP_Query( $query );
99
+ }
100
+
101
+ //
102
+ // Iterator Interface
103
+ //
104
+
105
+ public function valid() {
106
+ return $this->_query->have_posts();
107
+ }
108
+
109
+ public function current() {
110
+ global $post;
111
+
112
+ $this->_query->the_post();
113
+
114
+ // Sets up the global post, but also return the post, for use in Twig template
115
+ $posts_class = $this->_posts_class;
116
+ return new $posts_class( $post );
117
+ }
118
+
119
+ /**
120
+ * Don't implement next, because current already advances the loop
121
+ */
122
+ final public function next() {}
123
+
124
+ public function rewind() {
125
+ $this->_query->rewind_posts();
126
+ }
127
+
128
+ public function key() {
129
+ $this->_query->current_post;
130
+ }
131
+
132
+ //get_posts users numberposts
133
+ static function fix_number_posts_wp_quirk( $query ) {
134
+ if (isset($query->query) && isset($query->query['numberposts'])
135
+ && !isset($query->query['posts_per_page'])) {
136
+ $query->set( 'posts_per_page', $query->query['numberposts'] );
137
+ }
138
+ return $query;
139
+ }
140
+
141
+ }
functions/functions/timber-routes.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberRoutes {
4
+
5
+ protected $router;
6
+
7
+ function __construct(){
8
+ add_action('init', array($this, 'init'));
9
+ }
10
+
11
+ function init() {
12
+ global $timber;
13
+ if (isset($timber->router)) {
14
+ $route = $timber->router->matchCurrentRequest();
15
+ if ($route) {
16
+ $callback = $route->getTarget();
17
+ $params = $route->getParameters();
18
+ $callback($params);
19
+ }
20
+ }
21
+ }
22
+
23
+ /**
24
+ * @param string $route
25
+ * @param callable $callback
26
+ */
27
+ public static function add_route($route, $callback, $args = array()) {
28
+ global $timber;
29
+ if (!isset($timber->router)) {
30
+ if (class_exists('PHPRouter\Router')){
31
+ $timber->router = new PHPRouter\Router();
32
+ $site_url = get_bloginfo('url');
33
+ $site_url_parts = explode('/', $site_url);
34
+ $site_url_parts = array_slice($site_url_parts, 3);
35
+ $base_path = implode('/', $site_url_parts);
36
+ if (!$base_path || strpos($route, $base_path) === 0) {
37
+ $base_path = '/';
38
+ } else {
39
+ $base_path = '/' . $base_path . '/';
40
+ }
41
+ $timber->router->setBasePath($base_path);
42
+ }
43
+ }
44
+ if (class_exists('PHPRouter\Router')){
45
+ $timber->router->map($route, $callback, $args);
46
+ }
47
+ }
48
+
49
+ /**
50
+ * @param array $template
51
+ * @param mixed $query
52
+ * @param int $status_code
53
+ * @param bool $tparams
54
+ * @return bool
55
+ */
56
+ public static function load_view($template, $query = false, $status_code = 200, $tparams = false) {
57
+ $fullPath = is_readable($template);
58
+ if (!$fullPath) {
59
+ $template = locate_template($template);
60
+ }
61
+ if ($tparams){
62
+ global $params;
63
+ $params = $tparams;
64
+ }
65
+ if ($status_code) {
66
+ add_filter('status_header', function($status_header, $header, $text, $protocol) use ($status_code) {
67
+ $text = get_status_header_desc($status_code);
68
+ $header_string = "$protocol $status_code $text";
69
+ return $header_string;
70
+ }, 10, 4 );
71
+ if (404 != $status_code) {
72
+ add_action('parse_query', function($query) {
73
+ if ($query->is_main_query()){
74
+ $query->is_404 = false;
75
+ }
76
+ },1);
77
+ add_action('template_redirect', function(){
78
+ global $wp_query;
79
+ $wp_query->is_404 = false;
80
+ },1);
81
+ }
82
+ }
83
+
84
+ if ($query) {
85
+ add_action('do_parse_request', function() use ($query) {
86
+ global $wp;
87
+ if ( is_callable($query) )
88
+ $query = call_user_func($query);
89
+
90
+ if ( is_array($query) )
91
+ $wp->query_vars = $query;
92
+ elseif ( !empty($query) )
93
+ parse_str($query, $wp->query_vars);
94
+ else
95
+ return true; // Could not interpret query. Let WP try.
96
+
97
+ return false;
98
+ });
99
+ }
100
+ if ($template) {
101
+ add_filter('template_include', function($t) use ($template) {
102
+ return $template;
103
+ });
104
+ return true;
105
+ }
106
+ return false;
107
+ }
108
+ }
109
+
110
+ global $timberRoutes;
111
+ $timberRoutes = new TimberRoutes();
functions/functions/timber-site.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberSite extends TimberCore implements TimberCoreInterface {
4
+
5
+ public $admin_email;
6
+ public $blogname;
7
+ public $charset;
8
+ public $description;
9
+ public $id;
10
+ public $language;
11
+ public $language_attributes;
12
+ public $multisite;
13
+ public $name;
14
+ public $pingback_url;
15
+ public $siteurl;
16
+ public $theme;
17
+ public $title;
18
+ public $url;
19
+
20
+ /**
21
+ *
22
+ *
23
+ * @param string|int $site_name_or_id
24
+ */
25
+ function __construct( $site_name_or_id = null ) {
26
+ if ( is_multisite() ) {
27
+ $this->init_with_multisite( $site_name_or_id );
28
+ } else {
29
+ $this->init();
30
+ }
31
+ }
32
+
33
+ /**
34
+ *
35
+ *
36
+ * @param string|int $site_name_or_id
37
+ */
38
+ function init_with_multisite( $site_name_or_id ) {
39
+ if ( $site_name_or_id === null ) {
40
+ //this is necessary for some reason, otherwise returns 1 all the time
41
+ if ( is_multisite() ) {
42
+ restore_current_blog();
43
+ $site_name_or_id = get_current_blog_id();
44
+ }
45
+ }
46
+ $info = get_blog_details( $site_name_or_id );
47
+ $this->import( $info );
48
+ $this->ID = $info->blog_id;
49
+ $this->name = $this->blogname;
50
+ $this->title = $this->blogname;
51
+ $this->url = $this->siteurl;
52
+ $this->id = $this->ID;
53
+ $theme_slug = get_blog_option( $info->blog_id, 'stylesheet' );
54
+ $this->theme = new TimberTheme( $theme_slug );
55
+ $this->description = get_blog_option( $info->blog_id, 'blogdescription' );
56
+ $this->multisite = true;
57
+ }
58
+
59
+ function init() {
60
+ $this->admin_email = get_bloginfo( 'admin_email' );
61
+ $this->name = get_bloginfo( 'name' );
62
+ $this->title = $this->name;
63
+ $this->description = get_bloginfo( 'description' );
64
+ $this->url = get_bloginfo( 'url' );
65
+ $this->language = get_bloginfo( 'language' );
66
+ $this->charset = get_bloginfo( 'charset' );
67
+ $this->pingback_url = get_bloginfo( 'pingback_url' );
68
+ $this->theme = new TimberTheme();
69
+ $this->language_attributes = TimberHelper::function_wrapper( 'language_attributes' );
70
+ $this->multisite = false;
71
+ }
72
+
73
+ /**
74
+ *
75
+ *
76
+ * @param string $field
77
+ * @return mixed
78
+ */
79
+ function __get( $field ) {
80
+ if ( !isset( $this->$field ) ) {
81
+ if ( is_multisite() ) {
82
+ $this->$field = get_blog_option( $this->ID, $field );
83
+ } else {
84
+ $this->$field = get_option( $field );
85
+ }
86
+ }
87
+ return $this->$field;
88
+ }
89
+
90
+ /**
91
+ *
92
+ *
93
+ * @return string
94
+ */
95
+ function get_link() {
96
+ return $this->url;
97
+ }
98
+
99
+ /**
100
+ *
101
+ *
102
+ * @return string
103
+ */
104
+ function get_url() {
105
+ return $this->get_link();
106
+ }
107
+
108
+ /**
109
+ *
110
+ *
111
+ * @return string
112
+ */
113
+ function link() {
114
+ return $this->get_link();
115
+ }
116
+
117
+ /**
118
+ *
119
+ */
120
+ function meta( $field ) {
121
+ return $this->__get( $field );
122
+ }
123
+
124
+ /**
125
+ *
126
+ *
127
+ * @param string $key
128
+ * @param mixed $value
129
+ */
130
+ function update( $key, $value ) {
131
+ $value = apply_filters( 'timber_site_set_meta', $value, $key, $this->ID, $this );
132
+ if ( is_multisite() ) {
133
+ update_blog_option( $this->ID, $key, $value );
134
+ } else {
135
+ update_option( $key, $value );
136
+ }
137
+ $this->$key = $value;
138
+ }
139
+
140
+ /**
141
+ *
142
+ *
143
+ * @return string
144
+ */
145
+ function url() {
146
+ return $this->get_link();
147
+ }
148
+
149
+ }
functions/functions/timber-term-getter.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberTermGetter
4
+ {
5
+
6
+ /**
7
+ * @param string|array $args
8
+ * @param array $maybe_args
9
+ * @param string $TermClass
10
+ * @return mixed
11
+ */
12
+ public static function get_terms($args = null, $maybe_args = array(), $TermClass = 'TimberTerm'){
13
+ if (is_string($maybe_args) && !strstr($maybe_args, '=')){
14
+ //the user is sending the $TermClass in the second argument
15
+ $TermClass = $maybe_args;
16
+ }
17
+ if (is_string($maybe_args) && strstr($maybe_args, '=')){
18
+ parse_str($maybe_args, $maybe_args);
19
+ }
20
+ if (is_string($args) && strstr($args, '=')){
21
+ //a string and a query string!
22
+ $parsed = TimberTermGetter::get_term_query_from_query_string($args);
23
+ if (is_array($maybe_args)){
24
+ $parsed->args = array_merge($parsed->args, $maybe_args);
25
+ }
26
+ return self::handle_term_query($parsed->taxonomies, $parsed->args, $TermClass);
27
+ } else if (is_string($args)){
28
+ //its just a string with a single taxonomy
29
+ $parsed = TimberTermGetter::get_term_query_from_string($args);
30
+ if (is_array($maybe_args)){
31
+ $parsed->args = array_merge($parsed->args, $maybe_args);
32
+ }
33
+ return self::handle_term_query($parsed->taxonomies, $parsed->args, $TermClass);
34
+ } else if (is_array($args) && TimberHelper::is_array_assoc($args)){
35
+ //its an associative array, like a good ole query
36
+ $parsed = TimberTermGetter::get_term_query_from_assoc_array($args);
37
+ return self::handle_term_query($parsed->taxonomies, $parsed->args, $TermClass);
38
+ } else if (is_array($args)){
39
+ //its just an array of strings or IDs (hopefully)
40
+ $parsed = TimberTermGetter::get_term_query_from_array($args);
41
+ if (is_array($maybe_args)){
42
+ $parsed->args = array_merge($parsed->args, $maybe_args);
43
+ }
44
+ return self::handle_term_query($parsed->taxonomies, $parsed->args, $TermClass);
45
+ } else if (is_null($args)) {
46
+ return self::handle_term_query(get_taxonomies(), array(), $TermClass);
47
+ }
48
+ return null;
49
+ }
50
+
51
+ /**
52
+ * @param string|array $taxonomies
53
+ * @param string|array $args
54
+ * @param string $TermClass
55
+ * @return mixed
56
+ */
57
+ public static function handle_term_query($taxonomies, $args, $TermClass){
58
+ if (!isset($args['hide_empty'])){
59
+ $args['hide_empty'] = false;
60
+ }
61
+ if (isset($args['term_id']) && is_int($args['term_id'])){
62
+ $args['term_id'] = array($args['term_id']);
63
+ }
64
+ if (isset($args['term_id'])){
65
+ $args['include'] = $args['term_id'];
66
+ }
67
+ $terms = get_terms($taxonomies, $args);
68
+ foreach($terms as &$term){
69
+ $term = new $TermClass($term->term_id, $term->taxonomy);
70
+ }
71
+ return $terms;
72
+ }
73
+
74
+ /**
75
+ * @param string $query_string
76
+ * @return stdClass
77
+ */
78
+ public static function get_term_query_from_query_string($query_string) {
79
+ $args = array();
80
+ parse_str($query_string, $args);
81
+ $ret = self::get_term_query_from_assoc_array($args);
82
+ return $ret;
83
+ }
84
+
85
+ /**
86
+ * @param string $taxs
87
+ * @return stdClass
88
+ */
89
+ public static function get_term_query_from_string($taxs) {
90
+ $ret = new stdClass();
91
+ $ret->args = array();
92
+ if (is_string($taxs)) {
93
+ $taxs = array($taxs);
94
+ }
95
+ $ret->taxonomies = self::correct_taxonomy_names($taxs);
96
+ return $ret;
97
+ }
98
+
99
+ /**
100
+ * @param array $args
101
+ * @return stdClass
102
+ */
103
+ public static function get_term_query_from_assoc_array($args) {
104
+ $ret = new stdClass();
105
+ $ret->args = $args;
106
+ if (isset($ret->args['tax'])) {
107
+ $ret->taxonomies = $ret->args['tax'];
108
+ } else if (isset($ret->args['taxonomies'])) {
109
+ $ret->taxonomies = $ret->args['taxonomies'];
110
+ } else if (isset($ret->args['taxs'])) {
111
+ $ret->taxonomies = $ret->args['taxs'];
112
+ } else if (isset($ret->args['taxonomy'])) {
113
+ $ret->taxonomies = $ret->args['taxonomy'];
114
+ }
115
+ if (isset($ret->taxonomies)) {
116
+ if (is_string($ret->taxonomies)) {
117
+ $ret->taxonomies = array($ret->taxonomies);
118
+ }
119
+ $ret->taxonomies = self::correct_taxonomy_names($ret->taxonomies);
120
+ } else {
121
+ $ret->taxonomies = get_taxonomies();
122
+ }
123
+ return $ret;
124
+ }
125
+
126
+ /**
127
+ * @param array $args
128
+ * @return stdClass
129
+ */
130
+ public static function get_term_query_from_array($args) {
131
+ if (is_array($args) && !empty($args)) {
132
+ //okay its an array with content
133
+ if (is_int($args[0])) {
134
+ return self::get_term_query_from_array_of_ids($args);
135
+ } else if (is_string($args[0])) {
136
+ return self::get_term_query_from_array_of_strings($args);
137
+ }
138
+ }
139
+ return null;
140
+ }
141
+
142
+ /**
143
+ * @param integer[] $args
144
+ * @return stdClass
145
+ */
146
+ public static function get_term_query_from_array_of_ids($args) {
147
+ $ret = new stdClass();
148
+ $ret->taxonomies = get_taxonomies();
149
+ $ret->args['include'] = $args;
150
+ return $ret;
151
+ }
152
+
153
+ /**
154
+ * @param string[] $args
155
+ * @return stdClass
156
+ */
157
+ public static function get_term_query_from_array_of_strings($args) {
158
+ $ret = new stdClass();
159
+ $ret->taxonomies = self::correct_taxonomy_names($args);
160
+ $ret->args = array();
161
+ return $ret;
162
+ }
163
+
164
+ /**
165
+ * @param string|array $taxs
166
+ * @return array
167
+ */
168
+ private static function correct_taxonomy_names($taxs) {
169
+ if (is_string($taxs)) {
170
+ $taxs = array($taxs);
171
+ }
172
+ foreach ($taxs as &$tax) {
173
+ if ($tax == 'tags' || $tax == 'tag') {
174
+ $tax = 'post_tag';
175
+ } else if ($tax == 'categories') {
176
+ $tax = 'category';
177
+ }
178
+ }
179
+ return $taxs;
180
+ }
181
+
182
+ }
functions/functions/timber-term.php ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberTerm extends TimberCore implements TimberCoreInterface {
4
+
5
+ public $PostClass = 'TimberPost';
6
+ public $TermClass = 'TimberTerm';
7
+
8
+ public $object_type = 'term';
9
+ public static $representation = 'term';
10
+
11
+ public $_children;
12
+ public $name;
13
+ public $taxonomy;
14
+
15
+ /**
16
+ * @param int $tid
17
+ * @param string $tax
18
+ */
19
+ function __construct($tid = null, $tax = '') {
20
+ if ($tid === null) {
21
+ $tid = $this->get_term_from_query();
22
+ }
23
+ if (strlen($tax)) {
24
+ $this->taxonomy = $tax;
25
+ }
26
+ $this->init($tid);
27
+ }
28
+
29
+ /**
30
+ * @return string
31
+ */
32
+ function __toString() {
33
+ return $this->name;
34
+ }
35
+
36
+
37
+
38
+ /* Setup
39
+ ===================== */
40
+
41
+ /**
42
+ * @return integer
43
+ */
44
+ private function get_term_from_query() {
45
+ global $wp_query;
46
+ $qo = $wp_query->queried_object;
47
+ return $qo->term_id;
48
+ }
49
+
50
+ /**
51
+ * @param int $tid
52
+ */
53
+ private function init($tid) {
54
+ $term = $this->get_term($tid);
55
+ if (isset($term->id)) {
56
+ $term->ID = $term->id;
57
+ } else if (isset($term->term_id)) {
58
+ $term->ID = $term->term_id;
59
+ } else if (is_string($tid)) {
60
+ //echo 'bad call using '.$tid;
61
+ //TimberHelper::error_log(debug_backtrace());
62
+ }
63
+ if (isset($term->ID)){
64
+ $term->id = $term->ID;
65
+ $this->import($term);
66
+ if (isset($term->term_id)) {
67
+ $custom = $this->get_term_meta($term->term_id);
68
+ $this->import($custom);
69
+ }
70
+ }
71
+ }
72
+
73
+ /**
74
+ * @param int $tid
75
+ * @return array
76
+ */
77
+ private function get_term_meta($tid) {
78
+ $customs = array();
79
+ $customs = apply_filters('timber_term_get_meta', $customs, $tid, $this);
80
+ return $customs;
81
+ }
82
+
83
+ /**
84
+ * @param int $tid
85
+ * @return mixed
86
+ */
87
+ private function get_term($tid) {
88
+ if (is_object($tid) || is_array($tid)) {
89
+ return $tid;
90
+ }
91
+ $tid = self::get_tid($tid);
92
+
93
+ if (isset($this->taxonomy) && strlen($this->taxonomy)) {
94
+ return get_term($tid, $this->taxonomy);
95
+ } else {
96
+ global $wpdb;
97
+ $query = $wpdb->prepare("SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d LIMIT 1", $tid);
98
+ $tax = $wpdb->get_var($query);
99
+ if (isset($tax) && strlen($tax)) {
100
+ return get_term($tid, $tax);
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+
106
+ /**
107
+ * @param int $tid
108
+ * @return int
109
+ */
110
+ private function get_tid($tid) {
111
+ global $wpdb;
112
+ if (is_numeric($tid)) {
113
+ return $tid;
114
+ }
115
+ if (gettype($tid) == 'object') {
116
+ $tid = $tid->term_id;
117
+ }
118
+ if (is_numeric($tid)) {
119
+ $query = $wpdb->prepare("SELECT * FROM $wpdb->terms WHERE term_id = %d", $tid);
120
+ } else {
121
+ $query = $wpdb->prepare("SELECT * FROM $wpdb->terms WHERE slug = %s", $tid);
122
+ }
123
+ $result = $wpdb->get_row($query);
124
+ if (isset($result->term_id)) {
125
+ $result->ID = $result->term_id;
126
+ $result->id = $result->term_id;
127
+ return $result->ID;
128
+ }
129
+ return 0;
130
+ }
131
+
132
+ /* Public methods
133
+ ===================== */
134
+
135
+ /**
136
+ * @return string
137
+ */
138
+ public function get_edit_url() {
139
+ return get_edit_term_link($this->ID, $this->taxonomy);
140
+ }
141
+
142
+ /**
143
+ * @param string $field_name
144
+ * @return string
145
+ */
146
+ public function get_meta_field($field_name) {
147
+ if (!isset($this->$field_name)) {
148
+ $field_value = '';
149
+ $field_value = apply_filters('timber_term_get_meta_field', $field_value, $this->ID, $field_name, $this);
150
+ $this->$field_name = $field_value;
151
+ }
152
+ return $this->$field_name;
153
+ }
154
+
155
+ /**
156
+ * @return string
157
+ */
158
+ public function get_path() {
159
+ $link = $this->get_link();
160
+ $rel = TimberURLHelper::get_rel_url($link, true);
161
+ return apply_filters('timber_term_path', $rel, $this);
162
+ }
163
+
164
+ /**
165
+ * @return string
166
+ */
167
+ public function get_link() {
168
+ $link = get_term_link($this);
169
+ return apply_filters('timber_term_link', $link, $this);
170
+ }
171
+
172
+ /**
173
+ * @param int $numberposts
174
+ * @param string $post_type
175
+ * @param string $PostClass
176
+ * @return array|bool|null
177
+ */
178
+ public function get_posts($numberposts = 10, $post_type = 'any', $PostClass = '') {
179
+ if (!strlen($PostClass)) {
180
+ $PostClass = $this->PostClass;
181
+ }
182
+ $default_tax_query = array(array(
183
+ 'field' => 'id',
184
+ 'terms' => $this->ID,
185
+ 'taxonomy' => $this->taxonomy,
186
+ ));
187
+ if (is_string($numberposts) && strstr($numberposts, '=')) {
188
+ $args = $numberposts;
189
+ $new_args = array();
190
+ parse_str($args, $new_args);
191
+ $args = $new_args;
192
+ $args['tax_query'] = $default_tax_query;
193
+ if (!isset($args['post_type'])) {
194
+ $args['post_type'] = 'any';
195
+ }
196
+ if (class_exists($post_type)) {
197
+ $PostClass = $post_type;
198
+ }
199
+ } else if (is_array($numberposts)) {
200
+ //they sent us an array already baked
201
+ $args = $numberposts;
202
+ if (!isset($args['tax_query'])) {
203
+ $args['tax_query'] = $default_tax_query;
204
+ }
205
+ if (class_exists($post_type)) {
206
+ $PostClass = $post_type;
207
+ }
208
+ if (!isset($args['post_type'])) {
209
+ $args['post_type'] = 'any';
210
+ }
211
+ } else {
212
+ $args = array(
213
+ 'numberposts' => $numberposts,
214
+ 'tax_query' => $default_tax_query,
215
+ 'post_type' => $post_type
216
+ );
217
+ }
218
+ return Timber::get_posts($args, $PostClass);
219
+ }
220
+
221
+ /**
222
+ * @return array
223
+ */
224
+ public function get_children() {
225
+ if (!isset($this->_children)) {
226
+ $children = get_term_children($this->ID, $this->taxonomy);
227
+ foreach ($children as &$child) {
228
+ $child = new TimberTerm($child);
229
+ }
230
+ $this->_children = $children;
231
+ }
232
+ return $this->_children;
233
+ }
234
+
235
+ /**
236
+ *
237
+ *
238
+ * @param string $key
239
+ * @param mixed $value
240
+ */
241
+ function update( $key, $value ) {
242
+ $value = apply_filters( 'timber_term_set_meta', $value, $key, $this->ID, $this );
243
+ $this->$key = $value;
244
+ }
245
+
246
+ /* Alias
247
+ ====================== */
248
+
249
+ /**
250
+ * @return array
251
+ */
252
+ public function children() {
253
+ return $this->get_children();
254
+ }
255
+
256
+ /**
257
+ * @return string
258
+ */
259
+ public function edit_link() {
260
+ return $this->get_edit_url();
261
+ }
262
+
263
+ /**
264
+ * @return string
265
+ */
266
+ public function get_url() {
267
+ return $this->get_link();
268
+ }
269
+
270
+ /**
271
+ * @return string
272
+ */
273
+ public function link() {
274
+ return $this->get_link();
275
+ }
276
+
277
+ /**
278
+ * @param string $field_name
279
+ * @return string
280
+ */
281
+ public function meta($field_name) {
282
+ return $this->get_meta_field($field_name);
283
+ }
284
+
285
+ /**
286
+ * @return string
287
+ */
288
+ public function path() {
289
+ return $this->get_path();
290
+ }
291
+
292
+ /**
293
+ * @param int $numberposts_or_args
294
+ * @param string $post_type_or_class
295
+ * @param string $post_class
296
+ * @return array|bool|null
297
+ */
298
+ public function posts($numberposts_or_args = 10, $post_type_or_class = 'any', $post_class = '') {
299
+ return $this->get_posts($numberposts_or_args, $post_type_or_class, $post_class);
300
+ }
301
+
302
+ /**
303
+ * @return string
304
+ */
305
+ public function title() {
306
+ return $this->name;
307
+ }
308
+
309
+ /**
310
+ * @return string
311
+ */
312
+ public function url() {
313
+ return $this->get_url();
314
+ }
315
+
316
+ /* Deprecated
317
+ ===================== */
318
+
319
+ /**
320
+ * @deprecated
321
+ * @param int $i
322
+ * @return string
323
+ */
324
+ function get_page($i) {
325
+ return $this->get_path() . '/page/' . $i;
326
+ }
327
+
328
+ }
functions/functions/timber-theme.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberTheme extends TimberCore
4
+ {
5
+
6
+ public $link;
7
+ public $name;
8
+ public $path;
9
+ public $parent;
10
+ public $parent_slug;
11
+ public $slug;
12
+ public $uri;
13
+
14
+ /**
15
+ * @param string $slug
16
+ */
17
+ function __construct($slug = null) {
18
+ $this->init($slug);
19
+ }
20
+
21
+ /**
22
+ * @param string $slug
23
+ */
24
+ function init($slug = null) {
25
+ $data = wp_get_theme($slug);
26
+ $this->name = $data->get('Name');
27
+ $ss = $data->get_stylesheet();
28
+ $this->slug = $ss;
29
+ $this->path = WP_CONTENT_SUBDIR . str_replace(WP_CONTENT_DIR, '', get_stylesheet_directory());
30
+ $this->uri = get_stylesheet_directory_uri();
31
+ $this->link = $this->uri;
32
+ $this->parent_slug = $data->get('Template');
33
+ if (!$this->parent_slug) {
34
+ $this->path = WP_CONTENT_SUBDIR . str_replace(WP_CONTENT_DIR, '', get_template_directory());
35
+ $this->uri = get_template_directory_uri();
36
+ }
37
+ if ($this->parent_slug && $this->parent_slug != $this->slug) {
38
+ $this->parent = new TimberTheme($this->parent_slug);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * @param string $name
44
+ * @param bool $default
45
+ * @return string
46
+ */
47
+ public function theme_mod($name, $default = false) {
48
+ return get_theme_mod($name, $default);
49
+ }
50
+
51
+ /**
52
+ * @return string
53
+ */
54
+ public function theme_mods() {
55
+ return get_theme_mods();
56
+ }
57
+
58
+ }
functions/functions/timber-twig.php ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberTwig {
4
+
5
+ public static $dir_name;
6
+
7
+ function __construct() {
8
+ add_action( 'twig_apply_filters', array( $this, 'add_timber_filters_deprecated' ) );
9
+ add_action( 'twig_apply_filters', array( $this, 'add_timber_filters' ) );
10
+ }
11
+
12
+ /**
13
+ * These are all deprecated and will be removed in 0.21.0
14
+ *
15
+ * @param Twig_Environment $twig
16
+ * @deprecated since 0.20.7
17
+ * @return Twig_Environment
18
+ */
19
+ function add_timber_filters_deprecated( $twig ) {
20
+ $twig->addFilter( new Twig_SimpleFilter( 'get_src_from_attachment_id', 'twig_get_src_from_attachment_id' ) );
21
+ $twig->addFilter( new Twig_SimpleFilter( 'wp_body_class', array( $this, 'body_class' ) ) );
22
+ $twig->addFilter( new Twig_SimpleFilter( 'twitterify', array( 'TimberHelper', 'twitterify' ) ) );
23
+ $twig->addFilter( new Twig_SimpleFilter( 'twitterfy', array( 'TimberHelper', 'twitterify' ) ) );
24
+ return $twig;
25
+ }
26
+
27
+ /**
28
+ *
29
+ *
30
+ * @param Twig_Environment $twig
31
+ * @return Twig_Environment
32
+ */
33
+ function add_timber_filters( $twig ) {
34
+ /* image filters */
35
+ $twig->addFilter( new Twig_SimpleFilter( 'resize', array( 'TimberImageHelper', 'resize' ) ) );
36
+ $twig->addFilter( new Twig_SimpleFilter( 'retina', array( 'TimberImageHelper', 'retina_resize' ) ) );
37
+ $twig->addFilter( new Twig_SimpleFilter( 'letterbox', array( 'TimberImageHelper', 'letterbox' ) ) );
38
+ $twig->addFilter( new Twig_SimpleFilter( 'tojpg', array( 'TimberImageHelper', 'img_to_jpg' ) ) );
39
+
40
+ /* debugging filters */
41
+ $twig->addFilter( new Twig_SimpleFilter( 'docs', 'twig_object_docs' ) );
42
+ $twig->addFilter( new Twig_SimpleFilter( 'get_class', 'get_class' ) );
43
+ $twig->addFilter( new Twig_SimpleFilter( 'get_type', 'get_type' ) );
44
+ $twig->addFilter( new Twig_SimpleFilter( 'print_r', function( $arr ) {
45
+ return print_r( $arr, true );
46
+ } ) );
47
+ $twig->addFilter( new Twig_SimpleFilter( 'print_a', function( $arr ) {
48
+ return '<pre>' . self::object_docs( $arr, true ) . '</pre>';
49
+ } ) );
50
+
51
+ /* other filters */
52
+ $twig->addFilter( new Twig_SimpleFilter( 'stripshortcodes', 'strip_shortcodes' ) );
53
+ $twig->addFilter( new Twig_SimpleFilter( 'array', array( $this, 'to_array' ) ) );
54
+ $twig->addFilter( new Twig_SimpleFilter( 'string', array( $this, 'to_string' ) ) );
55
+ $twig->addFilter( new Twig_SimpleFilter( 'excerpt', 'wp_trim_words' ) );
56
+ $twig->addFilter( new Twig_SimpleFilter( 'function', array( $this, 'exec_function' ) ) );
57
+ $twig->addFilter( new Twig_SimpleFilter( 'pretags', array( $this, 'twig_pretags' ) ) );
58
+ $twig->addFilter( new Twig_SimpleFilter( 'sanitize', 'sanitize_title' ) );
59
+ $twig->addFilter( new Twig_SimpleFilter( 'shortcodes', 'do_shortcode' ) );
60
+ $twig->addFilter( new Twig_SimpleFilter( 'time_ago', array( $this, 'time_ago' ) ) );
61
+ $twig->addFilter( new Twig_SimpleFilter( 'wpautop', 'wpautop' ) );
62
+
63
+ $twig->addFilter( new Twig_SimpleFilter( 'relative', function ( $link ) {
64
+ return TimberURLHelper::get_rel_url( $link, true );
65
+ } ) );
66
+
67
+ $twig->addFilter( new Twig_SimpleFilter( 'date', array( $this, 'intl_date' ) ) );
68
+
69
+ $twig->addFilter( new Twig_SimpleFilter( 'truncate', function ( $text, $len ) {
70
+ return TimberHelper::trim_words( $text, $len );
71
+ } ) );
72
+
73
+ /* actions and filters */
74
+ $twig->addFunction( new Twig_SimpleFunction( 'action', function ( $context ) {
75
+ $args = func_get_args();
76
+ array_shift( $args );
77
+ $args[] = $context;
78
+ call_user_func_array( 'do_action', $args );
79
+ }, array( 'needs_context' => true ) ) );
80
+
81
+ $twig->addFilter( new Twig_SimpleFilter( 'apply_filters', function () {
82
+ $args = func_get_args();
83
+ $tag = current( array_splice( $args, 1, 1 ) );
84
+
85
+ return apply_filters_ref_array( $tag, $args );
86
+ } ) );
87
+ $twig->addFunction( new Twig_SimpleFunction( 'function', array( &$this, 'exec_function' ) ) );
88
+ $twig->addFunction( new Twig_SimpleFunction( 'fn', array( &$this, 'exec_function' ) ) );
89
+
90
+ /* TimberObjects */
91
+ $twig->addFunction( new Twig_SimpleFunction( 'TimberPost', function ( $pid, $PostClass = 'TimberPost' ) {
92
+ if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
93
+ foreach ( $pid as &$p ) {
94
+ $p = new $PostClass( $p );
95
+ }
96
+ return $pid;
97
+ }
98
+ return new $PostClass( $pid );
99
+ } ) );
100
+ $twig->addFunction( new Twig_SimpleFunction( 'TimberImage', function ( $pid, $ImageClass = 'TimberImage' ) {
101
+ if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
102
+ foreach ( $pid as &$p ) {
103
+ $p = new $ImageClass( $p );
104
+ }
105
+ return $pid;
106
+ }
107
+ return new $ImageClass( $pid );
108
+ } ) );
109
+ $twig->addFunction( new Twig_SimpleFunction( 'TimberTerm', function ( $pid, $TermClass = 'TimberTerm' ) {
110
+ if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
111
+ foreach ( $pid as &$p ) {
112
+ $p = new $TermClass( $p );
113
+ }
114
+ return $pid;
115
+ }
116
+ return new $TermClass( $pid );
117
+ } ) );
118
+ $twig->addFunction( new Twig_SimpleFunction( 'TimberUser', function ( $pid, $UserClass = 'TimberUser' ) {
119
+ if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
120
+ foreach ( $pid as &$p ) {
121
+ $p = new $UserClass( $p );
122
+ }
123
+ return $pid;
124
+ }
125
+ return new $UserClass( $pid );
126
+ } ) );
127
+
128
+ /* bloginfo and translate */
129
+ $twig->addFunction( 'bloginfo', new Twig_SimpleFunction( 'bloginfo', function ( $show = '', $filter = 'raw' ) {
130
+ return get_bloginfo( $show, $filter );
131
+ } ) );
132
+ $twig->addFunction( '__', new Twig_SimpleFunction( '__', function ( $text, $domain = 'default' ) {
133
+ return __( $text, $domain );
134
+ } ) );
135
+
136
+ $twig = apply_filters( 'get_twig', $twig );
137
+
138
+ return $twig;
139
+ }
140
+
141
+ /**
142
+ *
143
+ *
144
+ * @param mixed $arr
145
+ * @return array
146
+ */
147
+ function to_array( $arr ) {
148
+ if ( is_array( $arr ) ) {
149
+ return $arr;
150
+ }
151
+ $arr = array( $arr );
152
+ return $arr;
153
+ }
154
+
155
+ /**
156
+ *
157
+ *
158
+ * @param mixed $arr
159
+ * @param string $glue
160
+ * @return string
161
+ */
162
+ function to_string( $arr, $glue = ' ' ) {
163
+ if ( is_string( $arr ) ) {
164
+ return $arr;
165
+ }
166
+ if ( is_array( $arr ) && count( $arr ) == 1 ) {
167
+ return $arr[0];
168
+ }
169
+ if ( is_array( $arr ) ) {
170
+ return implode( $glue, $arr );
171
+ }
172
+ return null;
173
+ }
174
+
175
+ /**
176
+ *
177
+ *
178
+ * @param string $function_name
179
+ * @return mixed
180
+ */
181
+ function exec_function( $function_name ) {
182
+ $args = func_get_args();
183
+ array_shift( $args );
184
+ return call_user_func_array( trim( $function_name ), ( $args ) );
185
+ }
186
+
187
+ /**
188
+ *
189
+ *
190
+ * @param string $content
191
+ * @return string
192
+ */
193
+ function twig_pretags( $content ) {
194
+ return preg_replace_callback( '|<pre.*>(.*)</pre|isU', array( &$this, 'convert_pre_entities' ), $content );
195
+ }
196
+
197
+ /**
198
+ *
199
+ *
200
+ * @param array $matches
201
+ * @return string
202
+ */
203
+ function convert_pre_entities( $matches ) {
204
+ return str_replace( $matches[1], htmlentities( $matches[1] ), $matches[0] );
205
+ }
206
+
207
+ /**
208
+ *
209
+ *
210
+ * @param mixed $body_classes
211
+ * @return string
212
+ */
213
+ function body_class( $body_classes ) {
214
+ ob_start();
215
+ if ( is_array( $body_classes ) ) {
216
+ $body_classes = explode( ' ', $body_classes );
217
+ }
218
+ body_class( $body_classes );
219
+ $return = ob_get_contents();
220
+ ob_end_clean();
221
+ return $return;
222
+ }
223
+
224
+ /**
225
+ *
226
+ *
227
+ * @param string $date
228
+ * @param string $format (optional)
229
+ * @return string
230
+ */
231
+ function intl_date( $date, $format = null ) {
232
+ if ( $format === null ) {
233
+ $format = get_option( 'date_format' );
234
+ }
235
+
236
+ if ( $date instanceof DateTime ) {
237
+ $timestamp = $date->getTimestamp();
238
+ } else {
239
+ $timestamp = strtotime( $date );
240
+ }
241
+
242
+ return date_i18n( $format, $timestamp );
243
+ }
244
+
245
+ //debug
246
+
247
+ /**
248
+ *
249
+ *
250
+ * @param mixed $obj
251
+ * @param bool $methods
252
+ * @return string
253
+ */
254
+ function object_docs( $obj, $methods = true ) {
255
+ $class = get_class( $obj );
256
+ $properties = (array)$obj;
257
+ if ( $methods ) {
258
+ /** @var array $methods */
259
+ $methods = $obj->get_method_values();
260
+ }
261
+ $rets = array_merge( $properties, $methods );
262
+ ksort( $rets );
263
+ $str = print_r( $rets, true );
264
+ $str = str_replace( 'Array', $class . ' Object', $str );
265
+ return $str;
266
+ }
267
+
268
+ /**
269
+ * @param int|string $from
270
+ * @param int|string $to
271
+ * @param string $format_past
272
+ * @param string $format_future
273
+ * @return string
274
+ */
275
+ function time_ago( $from, $to = null, $format_past = '%s ago', $format_future = '%s from now' ) {
276
+ $to = $to === null ? time() : $to;
277
+ $to = is_int( $to ) ? $to : strtotime( $to );
278
+ $from = is_int( $from ) ? $from : strtotime( $from );
279
+
280
+ if ( $from < $to ) {
281
+ return sprintf( $format_past, human_time_diff( $from, $to ) );
282
+ } else {
283
+ return sprintf( $format_future, human_time_diff( $to, $from ) );
284
+ }
285
+ }
286
+
287
+ }
288
+
289
+ new TimberTwig();
functions/functions/timber-url-helper.php ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberURLHelper {
4
+
5
+ /**
6
+ * @return string
7
+ */
8
+ public static function get_current_url() {
9
+ $pageURL = "http://";
10
+ if (isset($_SERVER['HTTPS']) && $_SERVER["HTTPS"] == "on") {
11
+ $pageURL = "https://";;
12
+ }
13
+ if ($_SERVER["SERVER_PORT"] != "80") {
14
+ $pageURL .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
15
+ } else {
16
+ $pageURL .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
17
+ }
18
+ return $pageURL;
19
+ }
20
+
21
+ /**
22
+ * @param string $url
23
+ * @return bool
24
+ */
25
+ public static function is_url($url) {
26
+ if (!is_string($url)) {
27
+ return false;
28
+ }
29
+ $url = strtolower($url);
30
+ if (strstr($url, '://')) {
31
+ return true;
32
+ }
33
+ return false;
34
+ }
35
+
36
+ /**
37
+ * @return string
38
+ */
39
+ public static function get_path_base() {
40
+ $struc = get_option('permalink_structure');
41
+ $struc = explode('/', $struc);
42
+ $p = '/';
43
+ foreach ($struc as $s) {
44
+ if (!strstr($s, '%') && strlen($s)) {
45
+ $p .= $s . '/';
46
+ }
47
+ }
48
+ return $p;
49
+ }
50
+
51
+ /**
52
+ * @param string $url
53
+ * @param bool $force
54
+ * @return string
55
+ */
56
+ public static function get_rel_url($url, $force = false) {
57
+ $url_info = parse_url($url);
58
+ if (isset($url_info['host']) && $url_info['host'] != $_SERVER['HTTP_HOST'] && !$force) {
59
+ return $url;
60
+ }
61
+ $link = '';
62
+ if (isset($url_info['path'])){
63
+ $link = $url_info['path'];
64
+ }
65
+ if (isset($url_info['query']) && strlen($url_info['query'])) {
66
+ $link .= '?' . $url_info['query'];
67
+ }
68
+ if (isset($url_info['fragment']) && strlen($url_info['fragment'])) {
69
+ $link .= '#' . $url_info['fragment'];
70
+ }
71
+ $link = TimberURLHelper::remove_double_slashes($link);
72
+ return $link;
73
+ }
74
+
75
+ /**
76
+ * @param string $url
77
+ * @return bool
78
+ */
79
+ public static function is_local($url) {
80
+ if (strstr($url, $_SERVER['HTTP_HOST'])) {
81
+ return true;
82
+ }
83
+ return false;
84
+ }
85
+
86
+ /**
87
+ * @param string $src
88
+ * @return string
89
+ */
90
+ public static function get_full_path($src) {
91
+ $root = ABSPATH;
92
+ $old_root_path = $root . $src;
93
+ $old_root_path = str_replace('//', '/', $old_root_path);
94
+ return $old_root_path;
95
+ }
96
+
97
+ /**
98
+ * Takes a url and figures out its place based in the file system based on path
99
+ * NOTE: Not fool-proof, makes a lot of assumptions about the file path
100
+ * matching the URL path
101
+ * @param string $url
102
+ * @return string
103
+ */
104
+ public static function url_to_file_system($url) {
105
+ $url_parts = parse_url($url);
106
+ $path = ABSPATH . $url_parts['path'];
107
+ $path = str_replace('//', '/', $path);
108
+ return $path;
109
+ }
110
+
111
+ public static function file_system_to_url( $fs ) {
112
+ $relative_path = self::get_rel_path($fs);
113
+ $home = home_url('/'.$relative_path);
114
+ return $home;
115
+ }
116
+
117
+ /**
118
+ * @param string $src
119
+ * @return string
120
+ */
121
+ public static function get_rel_path($src) {
122
+ if (strstr($src, ABSPATH)) {
123
+ return str_replace(ABSPATH, '', $src);
124
+ }
125
+ //its outside the wordpress directory, alternate setups:
126
+ $src = str_replace(WP_CONTENT_DIR, '', $src);
127
+ return WP_CONTENT_SUBDIR . $src;
128
+ }
129
+
130
+ /**
131
+ * @param string $url
132
+ * @return string
133
+ */
134
+ public static function remove_double_slashes($url) {
135
+ $url = str_replace('//', '/', $url);
136
+ if (strstr($url, 'http:') && !strstr($url, 'http://')) {
137
+ $url = str_replace('http:/', 'http://', $url);
138
+ }
139
+ return $url;
140
+ }
141
+
142
+ /**
143
+ * @param string $url
144
+ * @param string $path
145
+ * @return string
146
+ */
147
+ public static function prepend_to_url($url, $path) {
148
+ if (strstr(strtolower($url), 'http')) {
149
+ $url_parts = parse_url($url);
150
+ $url = $url_parts['scheme'] . '://' . $url_parts['host'] . $path . $url_parts['path'];
151
+ } else {
152
+ $url = $url . $path;
153
+ }
154
+ return self::remove_double_slashes($url);
155
+ }
156
+
157
+ /**
158
+ * @param string $path
159
+ * @return string
160
+ */
161
+ public static function preslashit($path) {
162
+ if (strpos($path, '/') != 0) {
163
+ $path = '/' . $path;
164
+ }
165
+ return $path;
166
+ }
167
+
168
+ /**
169
+ * @param string $url
170
+ * @return bool
171
+ */
172
+ public static function is_external($url) {
173
+ $has_http = strstr(strtolower($url), 'http');
174
+ $on_domain = strstr($url, $_SERVER['HTTP_HOST']);
175
+ if ($has_http && !$on_domain) {
176
+ return true;
177
+ }
178
+ return false;
179
+ }
180
+
181
+ /**
182
+ * Pass links through untrailingslashit unless they are a single /
183
+ *
184
+ * @param string $link
185
+ * @return string
186
+ */
187
+ public static function remove_trailing_slash($link) {
188
+ if ( $link != "/")
189
+ $link = untrailingslashit( $link );
190
+ return $link;
191
+ }
192
+
193
+ /**
194
+ * @param string $url
195
+ * @param int $timeout
196
+ * @return string|WP_Error
197
+ * @deprecated since 0.20.0
198
+ */
199
+ static function download_url($url, $timeout = 300) {
200
+ if (!$url) {
201
+ return new WP_Error('http_no_url', __('Invalid URL Provided.'));
202
+ }
203
+
204
+ $tmpfname = wp_tempnam($url);
205
+ if (!$tmpfname) {
206
+ return new WP_Error('http_no_file', __('Could not create Temporary file.'));
207
+ }
208
+
209
+ $response = wp_remote_get($url, array('timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname));
210
+
211
+ if (is_wp_error($response)) {
212
+ unlink($tmpfname);
213
+ return $response;
214
+ }
215
+ if (200 != wp_remote_retrieve_response_code($response)) {
216
+ unlink($tmpfname);
217
+ return new WP_Error('http_404', trim(wp_remote_retrieve_response_message($response)));
218
+ }
219
+ return $tmpfname;
220
+ }
221
+
222
+ /**
223
+ * @param int $i
224
+ * @return array
225
+ */
226
+ public static function get_params($i = false) {
227
+ $args = explode('/', trim(strtolower($_SERVER['REQUEST_URI'])));
228
+ $newargs = array();
229
+ foreach ($args as $arg) {
230
+ if (strlen($arg)) {
231
+ $newargs[] = $arg;
232
+ }
233
+ }
234
+ if ($i === false){
235
+ return $newargs;
236
+ }
237
+ if ($i < 0){
238
+ //count from end
239
+ $i = count($newargs) + $i;
240
+ }
241
+ if (isset($newargs[$i])) {
242
+ return $newargs[$i];
243
+ }
244
+ }
245
+
246
+ }
functions/functions/timber-user.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberUser extends TimberCore implements TimberCoreInterface {
4
+
5
+ public $object_type = 'user';
6
+ public static $representation = 'user';
7
+
8
+ public $_link;
9
+
10
+ public $display_name;
11
+ public $id;
12
+ public $name;
13
+ public $user_nicename;
14
+
15
+ /**
16
+ * @param int|bool $uid
17
+ */
18
+ function __construct($uid = false) {
19
+ $this->init($uid);
20
+ }
21
+
22
+ /**
23
+ * @return string
24
+ */
25
+ function __toString() {
26
+ $name = $this->name();
27
+ if (strlen($name)) {
28
+ return $name;
29
+ }
30
+ if (strlen($this->name)) {
31
+ return $this->name;
32
+ }
33
+ return '';
34
+ }
35
+
36
+ /**
37
+ * @param string $field_name
38
+ * @return null
39
+ */
40
+ function get_meta($field_name) {
41
+ return $this->get_meta_field( $field_name );
42
+ }
43
+
44
+ /**
45
+ * @param string $field
46
+ * @param mixed $value
47
+ */
48
+ function __set($field, $value) {
49
+ if ($field == 'name') {
50
+ $this->display_name = $value;
51
+ }
52
+ $this->$field = $value;
53
+ }
54
+
55
+ /**
56
+ * @return string
57
+ */
58
+ public function get_link() {
59
+ if (!$this->_link) {
60
+ $this->_link = get_author_posts_url($this->ID);
61
+ }
62
+ return $this->_link;
63
+ }
64
+
65
+ /**
66
+ * @param int|bool $uid
67
+ */
68
+ function init($uid = false) {
69
+ if ($uid === false) {
70
+ $uid = get_current_user_id();
71
+ }
72
+ if (is_object($uid) || is_array($uid)){
73
+ $data = $uid;
74
+ if (is_array($uid)){
75
+ $data = (object) $uid;
76
+ }
77
+ $uid = $data->ID;
78
+ }
79
+ if (is_numeric($uid)) {
80
+ $data = get_userdata($uid);
81
+ }
82
+ if (isset($data) && is_object($data)) {
83
+ if (isset($data->data)){
84
+ $this->import($data->data);
85
+ } else {
86
+ $this->import($data);
87
+ }
88
+ }
89
+ $this->id = $this->ID;
90
+ $this->name = $this->name();
91
+ $this->import_custom();
92
+ }
93
+
94
+ /**
95
+ * @param string $field_name
96
+ * @return mixed
97
+ */
98
+ function get_meta_field($field_name) {
99
+ $value = null;
100
+ $value = apply_filters('timber_user_get_meta_field_pre', $value, $this->ID, $field_name, $this);
101
+ if ($value === null) {
102
+ $value = get_user_meta($this->ID, $field_name, true);
103
+ }
104
+ $value = apply_filters('timber_user_get_meta_field', $value, $this->ID, $field_name, $this);
105
+ return $value;
106
+ }
107
+
108
+ /**
109
+ * @return array|null
110
+ */
111
+ function get_custom() {
112
+ if ($this->ID) {
113
+ $um = array();
114
+ $um = apply_filters('timber_user_get_meta_pre', $um, $this->ID, $this);
115
+ if (empty($um)) {
116
+ $um = get_user_meta($this->ID);
117
+ }
118
+ $custom = array();
119
+ foreach ($um as $key => $value) {
120
+ if (is_array($value) && count($value) == 1) {
121
+ $value = $value[0];
122
+ }
123
+ $custom[$key] = maybe_unserialize($value);
124
+ }
125
+ $custom = apply_filters('timber_user_get_meta', $custom, $this->ID, $this);
126
+ return $custom;
127
+ }
128
+ return null;
129
+ }
130
+
131
+ function import_custom() {
132
+ $custom = $this->get_custom();
133
+ $this->import($custom);
134
+ }
135
+
136
+ /**
137
+ * @return string
138
+ */
139
+ function name() {
140
+ return $this->display_name;
141
+ }
142
+
143
+ /**
144
+ * @return string
145
+ */
146
+ function get_permalink() {
147
+ return $this->get_link();
148
+ }
149
+
150
+ /**
151
+ * @return string
152
+ */
153
+ function permalink() {
154
+ return $this->get_link();
155
+ }
156
+
157
+ /**
158
+ * @return string
159
+ */
160
+ function get_path() {
161
+ return $this->get_link();
162
+ }
163
+
164
+ /**
165
+ * @param string $field_name
166
+ * @return mixed
167
+ */
168
+ function meta($field_name) {
169
+ return $this->get_meta_field($field_name);
170
+ }
171
+
172
+ /**
173
+ * @return string
174
+ */
175
+ function path() {
176
+ return $this->get_path();
177
+ }
178
+
179
+ /**
180
+ * @return string
181
+ */
182
+ function slug() {
183
+ return $this->user_nicename;
184
+ }
185
+
186
+ /**
187
+ * @return string
188
+ */
189
+ function link() {
190
+ return $this->get_link();
191
+ }
192
+
193
+ }
functions/timber-helper.php CHANGED
@@ -13,11 +13,8 @@ class TimberHelper {
13
  * @return mixed
14
  */
15
  public static function transient( $slug, $callback, $transient_time = 0, $lock_timeout = 5, $force = false ) {
16
- $enable_transients = true;
17
- if ( $transient_time === false || ( defined( 'WP_DISABLE_TRANSIENTS' ) && WP_DISABLE_TRANSIENTS ) ) {
18
- $enable_transients = false;
19
- }
20
 
 
21
  $data = $enable_transients ? get_transient( $slug ) : false;
22
 
23
  if ( false === $data ) {
@@ -47,8 +44,11 @@ class TimberHelper {
47
  set_transient( $slug, $data, $transient_time );
48
  self::_unlock_transient( $slug );
49
  }
 
50
  }
 
51
  return $data;
 
52
  }
53
 
54
  /**
13
  * @return mixed
14
  */
15
  public static function transient( $slug, $callback, $transient_time = 0, $lock_timeout = 5, $force = false ) {
 
 
 
 
16
 
17
+ $enable_transients = ( $transient_time === false || ( defined( 'WP_DISABLE_TRANSIENTS' ) && WP_DISABLE_TRANSIENTS ) ) ? false : true;
18
  $data = $enable_transients ? get_transient( $slug ) : false;
19
 
20
  if ( false === $data ) {
44
  set_transient( $slug, $data, $transient_time );
45
  self::_unlock_transient( $slug );
46
  }
47
+
48
  }
49
+
50
  return $data;
51
+
52
  }
53
 
54
  /**
functions/timber-image-helper.php CHANGED
@@ -1,17 +1,18 @@
1
  <?php
2
 
3
- class TimberImageHelper
4
- {
 
5
 
6
  static function add_actions() {
7
- add_action('delete_post', function ($post_id) {
8
- $post = get_post($post_id);
9
- $image_types = array('image/jpeg', 'image/png', 'image/gif', 'image/jpg');
10
- if ($post->post_type == 'attachment' && in_array($post->post_mime_type, $image_types)) {
11
- TimberImageHelper::delete_resized_files_from_url($post->guid);
12
- TimberImageHelper::delete_letterboxed_files_from_url($post->guid);
13
- }
14
- });
15
  }
16
 
17
  /**
@@ -19,497 +20,589 @@ class TimberImageHelper
19
  * for example /wp-content or /content
20
  */
21
  static function add_constants() {
22
- if (!defined('WP_CONTENT_SUBDIR')) {
23
- $wp_content_path = str_replace(home_url(), '', WP_CONTENT_URL);
24
- define('WP_CONTENT_SUBDIR', $wp_content_path);
25
  }
26
  }
27
 
28
  static function add_filters() {
29
- add_filter('upload_dir', function ($arr) {
30
- $arr['relative'] = str_replace(home_url(), '', $arr['baseurl']);
31
- return $arr;
32
- });
33
  }
34
 
35
  /**
36
- * @param string $hexstr
 
 
37
  * @return array
38
  */
39
- public static function hexrgb($hexstr) {
40
- if (!strstr($hexstr, '#')) {
41
  $hexstr = '#' . $hexstr;
42
  }
43
- if (strlen($hexstr) == 4) {
44
  $hexstr = '#' . $hexstr[1] . $hexstr[1] . $hexstr[2] . $hexstr[2] . $hexstr[3] . $hexstr[3];
45
  }
46
- $int = hexdec($hexstr);
47
- return array("red" => 0xFF & ($int >> 0x10), "green" => 0xFF & ($int >> 0x8), "blue" => 0xFF & $int);
48
  }
49
 
50
- static function delete_resized_files_from_url($src) {
51
- $local = TimberURLHelper::url_to_file_system($src);
52
- self::delete_resized_files($local);
53
  }
54
 
55
- static function delete_letterboxed_files_from_url($src) {
56
- $local = TimberURLHelper::url_to_file_system($src);
57
- self::delete_letterboxed_files($local);
58
  }
59
 
60
  /**
61
- * @param string $local_file
 
 
62
  */
63
- static function delete_resized_files($local_file) {
64
- $info = pathinfo($local_file);
65
  $dir = $info['dirname'];
66
  $ext = $info['extension'];
67
  $filename = $info['filename'];
68
  $searcher = '/' . $filename . '-[0-9999999]*';
69
- foreach (glob($dir . $searcher) as $found_file) {
70
- $regexdir = str_replace('/', '\/', $dir);
71
- $pattern = '/' . ($regexdir) . '\/' . $filename . '-[0-9]*x[0-9]*-c-[a-z]*.' . $ext . '/';
72
- $match = preg_match($pattern, $found_file);
73
  //keeping these here so I know what the hell we're matching
74
  //$match = preg_match("/\/srv\/www\/wordpress-develop\/src\/wp-content\/uploads\/2014\/05\/$filename-[0-9]*x[0-9]*-c-[a-z]*.jpg/", $found_file);
75
  //$match = preg_match("/\/srv\/www\/wordpress-develop\/src\/wp-content\/uploads\/2014\/05\/arch-[0-9]*x[0-9]*-c-[a-z]*.jpg/", $filename);
76
- if ($match) {
77
- unlink($found_file);
78
  }
79
  }
80
  }
81
 
82
  /**
83
- * @param string $local_file
 
 
84
  */
85
- static function delete_letterboxed_files($local_file) {
86
- $info = pathinfo($local_file);
87
  $dir = $info['dirname'];
88
  $ext = $info['extension'];
89
  $filename = $info['filename'];
90
  $searcher = '/' . $filename . '-lbox-[0-9999999]*';
91
- foreach (glob($dir . $searcher) as $found_file) {
92
- $regexdir = str_replace('/', '\/', $dir);
93
- $pattern = '/' . ($regexdir) . '\/' . $filename . '-lbox-[0-9]*x[0-9]*-[a-zA-Z0-9]*.' . $ext . '/';
94
- $match = preg_match($pattern, $found_file);
95
- if ($match) {
96
- unlink($found_file);
97
  }
98
  }
99
  }
100
 
101
  /**
102
- * @param string $src
103
- * @param int $w
104
- * @param int $h
105
- * @param string $color
 
 
106
  * @return string
107
  */
108
- public static function get_letterbox_file_rel($src, $w, $h, $color) {
109
- if (!strlen($src)) {
110
  return null;
111
  }
112
- $new_path = self::get_letterbox_file_name_relative_to_content($src, $w, $h, $color);
113
  return WP_CONTENT_SUBDIR . $new_path;
114
  }
115
 
116
  /**
117
- * @param string $src The src of an image can be absolute, relative or server location
 
 
118
  * @return mixed|null
119
  */
120
- static function get_directory_relative_to_content($src) {
121
- if (!strlen($src)) {
122
  return null;
123
  }
124
- if (!strlen($src)) {
125
  return null;
126
  }
127
  $abs = false;
128
- if (strstr($src, 'http')) {
129
  $abs = true;
130
  }
131
- $path_parts = pathinfo($src);
132
- if ($abs) {
133
- $dir_relative_to_content = str_replace(WP_CONTENT_URL, '', $path_parts['dirname']);
134
  } else {
135
- $dir_relative_to_content = str_replace(WP_CONTENT_DIR, '', $path_parts['dirname']);
136
- $dir_relative_to_content = str_replace(WP_CONTENT_SUBDIR, '', $dir_relative_to_content);
137
  }
138
  return $dir_relative_to_content;
139
  }
140
 
141
  /**
142
- * @param string $src
143
- * @param int $w
144
- * @param int $h
145
- * @param string $color
 
 
146
  * @return string
147
  */
148
- static function get_letterbox_file_name_relative_to_content($src, $w, $h, $color) {
149
- $path_parts = pathinfo($src);
150
- $dir_relative_to_content = self::get_directory_relative_to_content($src);
151
- $color = str_replace('#', '', $color);
152
  $newbase = $path_parts['filename'] . '-lbox-' . $w . 'x' . $h . '-' . $color;
153
  $new_name = $newbase . '.' . $path_parts['extension'];
154
  return $dir_relative_to_content . '/' . $new_name;
155
  }
156
 
157
  /**
158
- * @param string $src
159
- * @param int $w
160
- * @param int $h
161
- * @param string $color
 
 
162
  * @return string
163
  */
164
- public static function get_letterbox_file_path($src, $w, $h, $color) {
165
- $new_name = self::get_letterbox_file_name_relative_to_content($src, $w, $h, $color);
166
  $new_server_path = WP_CONTENT_DIR . $new_name;
167
- $new_server_path = TimberURLHelper::remove_double_slashes($new_server_path);
168
  return $new_server_path;
169
  }
170
 
171
  /**
172
- * @param string $src
173
- * @param int $w
174
- * @param int $h
175
- * @param string $crop
 
 
176
  * @return string
177
  */
178
- static function get_resize_file_rel($src, $w, $h, $crop) {
179
- if (!strlen($src)) {
180
  return null;
181
  }
182
- $new_path = self::get_resize_file_name_relative_to_content($src, $w, $h, $crop);
183
  return WP_CONTENT_SUBDIR . $new_path;
184
  }
185
 
186
  /**
187
- * @param string $src
188
- * @param int $w
189
- * @param int $h
190
- * @param string $crop
 
 
191
  * @return string
192
  */
193
- static function get_resize_file_name_relative_to_content($src, $w, $h, $crop) {
194
- $path_parts = pathinfo($src);
195
- $dir_relative_to_content = self::get_directory_relative_to_content($src);
196
- $newbase = $path_parts['filename'] . '-' . $w . 'x' . $h . '-c-' . ($crop ? $crop : 'f'); // Crop will be either user named or f (false)
197
  $new_name = $newbase . '.' . $path_parts['extension'];
198
  return $dir_relative_to_content . '/' . $new_name;
199
  }
200
 
201
  /**
202
- * @param string $src
 
 
203
  */
204
- public static function in_uploads($src) {
205
  $upload_dir = wp_upload_dir();
206
- if (strstr($src, $upload_dir['relative'])) {
207
  return true;
208
  }
209
  return false;
210
  }
211
 
212
  /**
213
- * @param string $src
214
- * @param int $w
215
- * @param int $h
216
- * @param string $crop
 
 
217
  * @return string
218
  */
219
- static function get_resize_file_path($src, $w, $h, $crop) {
220
- $new_name = self::get_resize_file_name_relative_to_content($src, $w, $h, $crop);
221
  $new_server_path = WP_CONTENT_DIR . $new_name;
222
- $new_server_path = TimberURLHelper::remove_double_slashes($new_server_path);
223
  return $new_server_path;
224
  }
225
 
226
  /**
227
- * @param int $iid
 
 
228
  * @return string
229
  */
230
- public static function get_image_path($iid) {
231
  $size = 'full';
232
- $src = wp_get_attachment_image_src($iid, $size);
233
  $src = $src[0];
234
- return self::get_rel_path($src);
235
  }
236
 
237
  /**
238
- * @param string $url
 
 
239
  */
240
- public static function get_server_location($url) {
 
 
 
241
  $upload_dir = wp_upload_dir();
242
  $abs = false;
243
- if (strstr($url, 'http')) {
244
  $abs = true;
245
  }
246
- if (self::in_uploads($url)) {
247
- if ($abs) {
248
- $relative_to_uploads_dir = str_replace($upload_dir['baseurl'], '', $url);
249
  } else {
250
- $relative_to_uploads_dir = str_replace($upload_dir['relative'], '', $url);
251
  }
252
  return $upload_dir['basedir'] . $relative_to_uploads_dir;
253
  } else {
254
- if ($abs) {
255
- $relative_to_wp_content = str_replace(WP_CONTENT_URL, '', $url);
256
  } else {
257
- $relative_to_wp_content = str_replace(WP_CONTENT_SUBDIR, '', $url);
258
  }
259
  return WP_CONTENT_DIR . $relative_to_wp_content;
260
  }
261
  }
262
 
263
  /**
264
- * @param string $src
265
- * @param int $w
266
- * @param int $h
267
- * @param string $color
268
- * @param bool $force
 
 
269
  * @return mixed|null|string
270
  */
271
- public static function letterbox($src, $w, $h, $color = '#000000', $force = false) {
272
- if (strstr($src, 'http') && !strstr($src, home_url())) {
273
- $src = self::sideload_image($src);
274
  }
275
  $abs = false;
276
- if (strstr($src, 'http')) {
277
  $abs = true;
278
  }
279
- $new_file_rel = self::get_letterbox_file_rel($src, $w, $h, $color);
280
- $new_server_path = self::get_letterbox_file_path($src, $w, $h, $color);
281
- $old_server_path = self::get_server_location($src);
282
- $old_server_path = TimberURLHelper::remove_double_slashes($old_server_path);
283
- $new_server_path = TimberURLHelper::remove_double_slashes($new_server_path);
284
- if (file_exists($new_server_path) && !$force) {
285
- if ($abs) {
286
- return untrailingslashit(home_url()) . $new_file_rel;
287
  } else {
288
- return TimberURLHelper::preslashit($new_file_rel);
289
  }
290
  }
291
- $bg = imagecreatetruecolor($w, $h);
292
- $c = self::hexrgb($color);
293
- $white = imagecolorallocate($bg, $c['red'], $c['green'], $c['blue']);
294
- imagefill($bg, 0, 0, $white);
295
- $image = wp_get_image_editor($old_server_path);
296
- if (!is_wp_error($image)) {
297
  $current_size = $image->get_size();
298
  $ow = $current_size['width'];
299
  $oh = $current_size['height'];
300
  $new_aspect = $w / $h;
301
  $old_aspect = $ow / $oh;
302
- if ($new_aspect > $old_aspect) {
303
  //taller than goal
304
  $h_scale = $h / $oh;
305
  $owt = $ow * $h_scale;
306
  $y = 0;
307
  $x = $w / 2 - $owt / 2;
308
  $oht = $h;
309
- $image->crop(0, 0, $ow, $oh, $owt, $oht);
310
  } else {
311
  $w_scale = $w / $ow;
312
  $oht = $oh * $w_scale;
313
  $x = 0;
314
  $y = $h / 2 - $oht / 2;
315
  $owt = $w;
316
- $image->crop(0, 0, $ow, $oh, $owt, $oht);
317
  }
318
- $image->save($new_server_path);
319
  $func = 'imagecreatefromjpeg';
320
- $ext = pathinfo($new_server_path, PATHINFO_EXTENSION);
321
- if ($ext == 'gif') {
322
  $func = 'imagecreatefromgif';
323
- } else if ($ext == 'png') {
324
- $func = 'imagecreatefrompng';
325
- }
326
- $image = $func($new_server_path);
327
- imagecopy($bg, $image, $x, $y, 0, 0, $owt, $oht);
328
- imagejpeg($bg, $new_server_path);
329
- $new_relative_path = TimberURLHelper::get_rel_path($new_server_path);
330
- if ($abs) {
331
- return home_url($new_relative_path);
332
  }
333
  return $new_relative_path;
334
  } else {
335
- TimberHelper::error_log($image);
336
  }
337
  return null;
338
  }
339
 
340
  /**
341
- * @param string $src a url or path to the image (http://example.org/wp-content/uploads/2014/image.jpg) or (/wp-content/uploads/2014/image.jpg)
342
- * @param string $bghex
 
 
343
  * @return string
344
  */
345
- public static function img_to_jpg($src, $bghex = '#FFFFFF') {
346
- $path = str_replace(home_url(), '', $src);
347
- $output = str_replace('.png', '.jpg', $path);
348
- $input_file = self::get_server_location($path);
349
- $output_file = self::get_server_location($output);
350
- if (file_exists($output_file)) {
351
  return $output;
352
  }
353
  $filename = $output;
354
- $input = imagecreatefrompng($input_file);
355
- list($width, $height) = getimagesize($input_file);
356
- $output = imagecreatetruecolor($width, $height);
357
- $c = self::hexrgb($bghex);
358
- $white = imagecolorallocate($output, $c['red'], $c['green'], $c['blue']);
359
- imagefilledrectangle($output, 0, 0, $width, $height, $white);
360
- imagecopy($output, $input, 0, 0, 0, 0, $width, $height);
361
- imagejpeg($output, $output_file);
362
  return $filename;
363
  }
364
 
365
  /**
366
- * @param string $file
 
 
367
  * @return string
368
  */
369
- public static function get_sideloaded_file_loc($file) {
370
  $upload = wp_upload_dir();
371
  $dir = $upload['path'];
372
  $filename = $file;
373
- $file = parse_url($file);
374
- $path_parts = pathinfo($file['path']);
375
- $basename = md5($filename);
376
  $ext = 'jpg';
377
- if (isset($path_parts['extension'])) {
378
  $ext = $path_parts['extension'];
379
  }
380
  return $dir . '/' . $basename . '.' . $ext;
381
  }
382
 
383
  /**
384
- * @param string $file
 
 
385
  * @return string
386
  */
387
- public static function sideload_image($file) {
388
- $loc = self::get_sideloaded_file_loc($file);
389
- if (file_exists($loc)) {
390
- return TimberURLHelper::preslashit(TimberURLHelper::get_rel_path($loc));
391
  }
392
  // Download file to temp location
393
- if (!function_exists('download_url')) {
394
- require_once(ABSPATH . '/wp-admin/includes/file.php');
395
  }
396
- $tmp = download_url($file);
397
- preg_match('/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches);
398
  $file_array = array();
399
- $file_array['name'] = basename($matches[0]);
400
  $file_array['tmp_name'] = $tmp;
401
  // If error storing temporarily, unlink
402
- if (is_wp_error($tmp)) {
403
- @unlink($file_array['tmp_name']);
404
  $file_array['tmp_name'] = '';
405
  }
406
  // do the validation and storage stuff
407
- $locinfo = pathinfo($loc);
408
- $file = wp_upload_bits($locinfo['basename'], null, file_get_contents($file_array['tmp_name']));
409
  return $file['url'];
410
  }
411
 
412
  /**
413
- * @param string $src
414
- * @param int $w
415
- * @param int $h
416
- * @param string $crop
417
- * @param bool $force_resize
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  * @return string
419
  */
420
- public static function resize($src, $w, $h = 0, $crop = 'default', $force_resize = false) {
421
- if (empty($src)) {
422
  return '';
423
  }
424
- if (strstr($src, 'http') && !strstr($src, home_url())) {
425
- $src = self::sideload_image($src);
426
  }
427
  $abs = false;
428
- if (strstr($src, 'http')) {
429
  $abs = true;
430
  }
431
  // Sanitize crop position
432
- $allowed_crop_positions = array('default', 'center', 'top', 'bottom', 'left', 'right');
433
- if ($crop !== false && !in_array($crop, $allowed_crop_positions)) {
434
  $crop = $allowed_crop_positions[0];
435
  }
436
  //oh good, it's a relative image in the uploads folder!
437
- $new_path = self::get_resize_file_rel($src, $w, $h, $crop);
438
- $new_server_path = self::get_resize_file_path($src, $w, $h, $crop);
439
- $old_server_path = self::get_server_location($src);
440
- $old_server_path = TimberURLHelper::remove_double_slashes($old_server_path);
441
- $new_server_path = TimberURLHelper::remove_double_slashes($new_server_path);
442
- if (file_exists($new_server_path)) {
443
- if ($force_resize) {
444
  // Force resize - warning: will regenerate the image on every pageload, use for testing purposes only!
445
- unlink($new_server_path);
446
  } else {
447
- if (!$abs) {
448
- return TimberURLHelper::preslashit($new_path);
449
  }
450
- return untrailingslashit(home_url()) . $new_path;
451
  }
452
  }
453
- $image = wp_get_image_editor($old_server_path);
454
 
455
- if (!is_wp_error($image)) {
456
  $current_size = $image->get_size();
457
 
458
  $src_w = $current_size['width'];
459
  $src_h = $current_size['height'];
460
 
461
  $src_ratio = $src_w / $src_h;
462
- if (!$h) {
463
- $h = round($w / $src_ratio);
 
 
 
 
464
  }
465
  // Get ratios
466
  $dest_ratio = $w / $h;
467
  $src_wt = $src_h * $dest_ratio;
468
  $src_ht = $src_w / $dest_ratio;
469
 
470
- if (!$crop) {
471
  // Always crop, to allow resizing upwards
472
- $image->crop(0, 0, $src_w, $src_h, $w, $h);
473
  } else {
474
  //start with defaults:
475
  $src_x = $src_w / 2 - $src_wt / 2;
476
- $src_y = ($src_h - $src_ht) / 6;
477
  //now specific overrides based on options:
478
- if ($crop == 'center') {
479
  // Get source x and y
480
- $src_x = round(($src_w - $src_wt) / 2);
481
- $src_y = round(($src_h - $src_ht) / 2);
482
- } else if ($crop == 'top') {
483
- $src_y = 0;
484
- } else if ($crop == 'bottom') {
485
- $src_y = $src_h - $src_ht;
486
- } else if ($crop == 'left') {
487
- $src_x = 0;
488
- } else if ($crop == 'right') {
489
- $src_x = $src_w - $src_wt;
490
- }
491
 
492
  // Crop the image
493
- if ($dest_ratio > $src_ratio) {
494
- $image->crop(0, $src_y, $src_w, $src_ht, $w, $h);
495
  } else {
496
- $image->crop($src_x, 0, $src_wt, $src_h, $w, $h);
497
  }
498
 
499
  }
500
- $result = $image->save($new_server_path);
501
- if (is_wp_error($result)) {
502
- error_log('Error resizing image');
503
- error_log(print_r($result, true));
504
  }
505
- if ($abs) {
506
- return untrailingslashit(home_url()) . $new_path;
507
  }
508
  return $new_path;
509
- } else if (isset($image->error_data['error_loading_image'])) {
510
- TimberHelper::error_log('Error loading ' . $image->error_data['error_loading_image']);
511
- } else {
512
- TimberHelper::error_log($image);
513
  }
514
  return $src;
515
  }
1
  <?php
2
 
3
+ require_once 'timber-image-retina-helper.php';
4
+
5
+ class TimberImageHelper {
6
 
7
  static function add_actions() {
8
+ add_action( 'delete_post', function ( $post_id ) {
9
+ $post = get_post( $post_id );
10
+ $image_types = array( 'image/jpeg', 'image/png', 'image/gif', 'image/jpg' );
11
+ if ( $post->post_type == 'attachment' && in_array( $post->post_mime_type, $image_types ) ) {
12
+ TimberImageHelper::delete_resized_files_from_url( $post->guid );
13
+ TimberImageHelper::delete_letterboxed_files_from_url( $post->guid );
14
+ }
15
+ } );
16
  }
17
 
18
  /**
20
  * for example /wp-content or /content
21
  */
22
  static function add_constants() {
23
+ if ( !defined( 'WP_CONTENT_SUBDIR' ) ) {
24
+ $wp_content_path = str_replace( home_url(), '', WP_CONTENT_URL );
25
+ define( 'WP_CONTENT_SUBDIR', $wp_content_path );
26
  }
27
  }
28
 
29
  static function add_filters() {
30
+ add_filter( 'upload_dir', function ( $arr ) {
31
+ $arr['relative'] = str_replace( home_url(), '', $arr['baseurl'] );
32
+ return $arr;
33
+ } );
34
  }
35
 
36
  /**
37
+ *
38
+ *
39
+ * @param string $hexstr
40
  * @return array
41
  */
42
+ public static function hexrgb( $hexstr ) {
43
+ if ( !strstr( $hexstr, '#' ) ) {
44
  $hexstr = '#' . $hexstr;
45
  }
46
+ if ( strlen( $hexstr ) == 4 ) {
47
  $hexstr = '#' . $hexstr[1] . $hexstr[1] . $hexstr[2] . $hexstr[2] . $hexstr[3] . $hexstr[3];
48
  }
49
+ $int = hexdec( $hexstr );
50
+ return array( "red" => 0xFF & ( $int >> 0x10 ), "green" => 0xFF & ( $int >> 0x8 ), "blue" => 0xFF & $int );
51
  }
52
 
53
+ static function delete_resized_files_from_url( $src ) {
54
+ $local = TimberURLHelper::url_to_file_system( $src );
55
+ self::delete_resized_files( $local );
56
  }
57
 
58
+ static function delete_letterboxed_files_from_url( $src ) {
59
+ $local = TimberURLHelper::url_to_file_system( $src );
60
+ self::delete_letterboxed_files( $local );
61
  }
62
 
63
  /**
64
+ *
65
+ *
66
+ * @param string $local_file
67
  */
68
+ static function delete_resized_files( $local_file ) {
69
+ $info = pathinfo( $local_file );
70
  $dir = $info['dirname'];
71
  $ext = $info['extension'];
72
  $filename = $info['filename'];
73
  $searcher = '/' . $filename . '-[0-9999999]*';
74
+ foreach ( glob( $dir . $searcher ) as $found_file ) {
75
+ $regexdir = str_replace( '/', '\/', $dir );
76
+ $pattern = '/' . ( $regexdir ) . '\/' . $filename . '-[0-9]*x[0-9]*-c-[a-z]*.' . $ext . '/';
77
+ $match = preg_match( $pattern, $found_file );
78
  //keeping these here so I know what the hell we're matching
79
  //$match = preg_match("/\/srv\/www\/wordpress-develop\/src\/wp-content\/uploads\/2014\/05\/$filename-[0-9]*x[0-9]*-c-[a-z]*.jpg/", $found_file);
80
  //$match = preg_match("/\/srv\/www\/wordpress-develop\/src\/wp-content\/uploads\/2014\/05\/arch-[0-9]*x[0-9]*-c-[a-z]*.jpg/", $filename);
81
+ if ( $match ) {
82
+ unlink( $found_file );
83
  }
84
  }
85
  }
86
 
87
  /**
88
+ *
89
+ *
90
+ * @param string $local_file
91
  */
92
+ static function delete_letterboxed_files( $local_file ) {
93
+ $info = pathinfo( $local_file );
94
  $dir = $info['dirname'];
95
  $ext = $info['extension'];
96
  $filename = $info['filename'];
97
  $searcher = '/' . $filename . '-lbox-[0-9999999]*';
98
+ foreach ( glob( $dir . $searcher ) as $found_file ) {
99
+ $regexdir = str_replace( '/', '\/', $dir );
100
+ $pattern = '/' . ( $regexdir ) . '\/' . $filename . '-lbox-[0-9]*x[0-9]*-[a-zA-Z0-9]*.' . $ext . '/';
101
+ $match = preg_match( $pattern, $found_file );
102
+ if ( $match ) {
103
+ unlink( $found_file );
104
  }
105
  }
106
  }
107
 
108
  /**
109
+ *
110
+ *
111
+ * @param string $src
112
+ * @param int $w
113
+ * @param int $h
114
+ * @param string $color
115
  * @return string
116
  */
117
+ public static function get_letterbox_file_rel( $src, $w, $h, $color ) {
118
+ if ( !strlen( $src ) ) {
119
  return null;
120
  }
121
+ $new_path = self::get_letterbox_file_name_relative_to_content( $src, $w, $h, $color );
122
  return WP_CONTENT_SUBDIR . $new_path;
123
  }
124
 
125
  /**
126
+ *
127
+ *
128
+ * @param string $src The src of an image can be absolute, relative or server location
129
  * @return mixed|null
130
  */
131
+ static function get_directory_relative_to_content( $src ) {
132
+ if ( !strlen( $src ) ) {
133
  return null;
134
  }
135
+ if ( !strlen( $src ) ) {
136
  return null;
137
  }
138
  $abs = false;
139
+ if ( strstr( $src, 'http' ) ) {
140
  $abs = true;
141
  }
142
+ $path_parts = pathinfo( $src );
143
+ if ( $abs ) {
144
+ $dir_relative_to_content = str_replace( WP_CONTENT_URL, '', $path_parts['dirname'] );
145
  } else {
146
+ $dir_relative_to_content = str_replace( WP_CONTENT_DIR, '', $path_parts['dirname'] );
147
+ $dir_relative_to_content = str_replace( WP_CONTENT_SUBDIR, '', $dir_relative_to_content );
148
  }
149
  return $dir_relative_to_content;
150
  }
151
 
152
  /**
153
+ *
154
+ *
155
+ * @param string $src
156
+ * @param int $w
157
+ * @param int $h
158
+ * @param string $color
159
  * @return string
160
  */
161
+ static function get_letterbox_file_name_relative_to_content( $src, $w, $h, $color ) {
162
+ $path_parts = pathinfo( $src );
163
+ $dir_relative_to_content = self::get_directory_relative_to_content( $src );
164
+ $color = str_replace( '#', '', $color );
165
  $newbase = $path_parts['filename'] . '-lbox-' . $w . 'x' . $h . '-' . $color;
166
  $new_name = $newbase . '.' . $path_parts['extension'];
167
  return $dir_relative_to_content . '/' . $new_name;
168
  }
169
 
170
  /**
171
+ *
172
+ *
173
+ * @param string $src
174
+ * @param int $w
175
+ * @param int $h
176
+ * @param string $color
177
  * @return string
178
  */
179
+ public static function get_letterbox_file_path( $src, $w, $h, $color ) {
180
+ $new_name = self::get_letterbox_file_name_relative_to_content( $src, $w, $h, $color );
181
  $new_server_path = WP_CONTENT_DIR . $new_name;
182
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
183
  return $new_server_path;
184
  }
185
 
186
  /**
187
+ *
188
+ *
189
+ * @param string $src
190
+ * @param int $w
191
+ * @param int $h
192
+ * @param string $crop
193
  * @return string
194
  */
195
+ static function get_resize_file_rel( $src, $w, $h, $crop ) {
196
+ if ( !strlen( $src ) ) {
197
  return null;
198
  }
199
+ $new_path = self::get_resize_file_name_relative_to_content( $src, $w, $h, $crop );
200
  return WP_CONTENT_SUBDIR . $new_path;
201
  }
202
 
203
  /**
204
+ *
205
+ *
206
+ * @param string $src
207
+ * @param int $w
208
+ * @param int $h
209
+ * @param string $crop
210
  * @return string
211
  */
212
+ static function get_resize_file_name_relative_to_content( $src, $w, $h, $crop ) {
213
+ $path_parts = pathinfo( $src );
214
+ $dir_relative_to_content = self::get_directory_relative_to_content( $src );
215
+ $newbase = $path_parts['filename'] . '-' . $w . 'x' . $h . '-c-' . ( $crop ? $crop : 'f' ); // Crop will be either user named or f (false)
216
  $new_name = $newbase . '.' . $path_parts['extension'];
217
  return $dir_relative_to_content . '/' . $new_name;
218
  }
219
 
220
  /**
221
+ *
222
+ *
223
+ * @param string $src
224
  */
225
+ public static function in_uploads( $src ) {
226
  $upload_dir = wp_upload_dir();
227
+ if ( strstr( $src, $upload_dir['relative'] ) ) {
228
  return true;
229
  }
230
  return false;
231
  }
232
 
233
  /**
234
+ *
235
+ *
236
+ * @param string $src
237
+ * @param int $w
238
+ * @param int $h
239
+ * @param string $crop
240
  * @return string
241
  */
242
+ static function get_resize_file_path( $src, $w, $h, $crop ) {
243
+ $new_name = self::get_resize_file_name_relative_to_content( $src, $w, $h, $crop );
244
  $new_server_path = WP_CONTENT_DIR . $new_name;
245
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
246
  return $new_server_path;
247
  }
248
 
249
  /**
250
+ *
251
+ *
252
+ * @param int $iid
253
  * @return string
254
  */
255
+ public static function get_image_path( $iid ) {
256
  $size = 'full';
257
+ $src = wp_get_attachment_image_src( $iid, $size );
258
  $src = $src[0];
259
+ return self::get_rel_path( $src );
260
  }
261
 
262
  /**
263
+ *
264
+ *
265
+ * @param string $url
266
  */
267
+ public static function get_server_location( $url ) {
268
+ if ( strpos( $url, ABSPATH ) === 0 ) {
269
+ return $url;
270
+ }
271
  $upload_dir = wp_upload_dir();
272
  $abs = false;
273
+ if ( strstr( $url, 'http' ) ) {
274
  $abs = true;
275
  }
276
+ if ( self::in_uploads( $url ) ) {
277
+ if ( $abs ) {
278
+ $relative_to_uploads_dir = str_replace( $upload_dir['baseurl'], '', $url );
279
  } else {
280
+ $relative_to_uploads_dir = str_replace( $upload_dir['relative'], '', $url );
281
  }
282
  return $upload_dir['basedir'] . $relative_to_uploads_dir;
283
  } else {
284
+ if ( $abs ) {
285
+ $relative_to_wp_content = str_replace( WP_CONTENT_URL, '', $url );
286
  } else {
287
+ $relative_to_wp_content = str_replace( WP_CONTENT_SUBDIR, '', $url );
288
  }
289
  return WP_CONTENT_DIR . $relative_to_wp_content;
290
  }
291
  }
292
 
293
  /**
294
+ *
295
+ *
296
+ * @param string $src
297
+ * @param int $w
298
+ * @param int $h
299
+ * @param string $color
300
+ * @param bool $force
301
  * @return mixed|null|string
302
  */
303
+ public static function letterbox( $src, $w, $h, $color = '#000000', $force = false ) {
304
+ if ( strstr( $src, 'http' ) && !strstr( $src, home_url() ) ) {
305
+ $src = self::sideload_image( $src );
306
  }
307
  $abs = false;
308
+ if ( strstr( $src, 'http' ) ) {
309
  $abs = true;
310
  }
311
+ $new_file_rel = self::get_letterbox_file_rel( $src, $w, $h, $color );
312
+ $new_server_path = self::get_letterbox_file_path( $src, $w, $h, $color );
313
+ $old_server_path = self::get_server_location( $src );
314
+ $old_server_path = TimberURLHelper::remove_double_slashes( $old_server_path );
315
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
316
+ if ( file_exists( $new_server_path ) && !$force ) {
317
+ if ( $abs ) {
318
+ return untrailingslashit( home_url() ) . $new_file_rel;
319
  } else {
320
+ return TimberURLHelper::preslashit( $new_file_rel );
321
  }
322
  }
323
+ $bg = imagecreatetruecolor( $w, $h );
324
+ $c = self::hexrgb( $color );
325
+ $white = imagecolorallocate( $bg, $c['red'], $c['green'], $c['blue'] );
326
+ imagefill( $bg, 0, 0, $white );
327
+ $image = wp_get_image_editor( $old_server_path );
328
+ if ( !is_wp_error( $image ) ) {
329
  $current_size = $image->get_size();
330
  $ow = $current_size['width'];
331
  $oh = $current_size['height'];
332
  $new_aspect = $w / $h;
333
  $old_aspect = $ow / $oh;
334
+ if ( $new_aspect > $old_aspect ) {
335
  //taller than goal
336
  $h_scale = $h / $oh;
337
  $owt = $ow * $h_scale;
338
  $y = 0;
339
  $x = $w / 2 - $owt / 2;
340
  $oht = $h;
341
+ $image->crop( 0, 0, $ow, $oh, $owt, $oht );
342
  } else {
343
  $w_scale = $w / $ow;
344
  $oht = $oh * $w_scale;
345
  $x = 0;
346
  $y = $h / 2 - $oht / 2;
347
  $owt = $w;
348
+ $image->crop( 0, 0, $ow, $oh, $owt, $oht );
349
  }
350
+ $image->save( $new_server_path );
351
  $func = 'imagecreatefromjpeg';
352
+ $ext = pathinfo( $new_server_path, PATHINFO_EXTENSION );
353
+ if ( $ext == 'gif' ) {
354
  $func = 'imagecreatefromgif';
355
+ } else if ( $ext == 'png' ) {
356
+ $func = 'imagecreatefrompng';
357
+ }
358
+ $image = $func( $new_server_path );
359
+ imagecopy( $bg, $image, $x, $y, 0, 0, $owt, $oht );
360
+ imagejpeg( $bg, $new_server_path );
361
+ $new_relative_path = TimberURLHelper::get_rel_path( $new_server_path );
362
+ if ( $abs ) {
363
+ return home_url( $new_relative_path );
364
  }
365
  return $new_relative_path;
366
  } else {
367
+ TimberHelper::error_log( $image );
368
  }
369
  return null;
370
  }
371
 
372
  /**
373
+ *
374
+ *
375
+ * @param string $src a url or path to the image (http://example.org/wp-content/uploads/2014/image.jpg) or (/wp-content/uploads/2014/image.jpg)
376
+ * @param string $bghex
377
  * @return string
378
  */
379
+ public static function img_to_jpg( $src, $bghex = '#FFFFFF' ) {
380
+ $path = str_replace( home_url(), '', $src );
381
+ $output = str_replace( '.png', '.jpg', $path );
382
+ $input_file = self::get_server_location( $path );
383
+ $output_file = self::get_server_location( $output );
384
+ if ( file_exists( $output_file ) ) {
385
  return $output;
386
  }
387
  $filename = $output;
388
+ $input = imagecreatefrompng( $input_file );
389
+ list( $width, $height ) = getimagesize( $input_file );
390
+ $output = imagecreatetruecolor( $width, $height );
391
+ $c = self::hexrgb( $bghex );
392
+ $white = imagecolorallocate( $output, $c['red'], $c['green'], $c['blue'] );
393
+ imagefilledrectangle( $output, 0, 0, $width, $height, $white );
394
+ imagecopy( $output, $input, 0, 0, 0, 0, $width, $height );
395
+ imagejpeg( $output, $output_file );
396
  return $filename;
397
  }
398
 
399
  /**
400
+ *
401
+ *
402
+ * @param string $file
403
  * @return string
404
  */
405
+ public static function get_sideloaded_file_loc( $file ) {
406
  $upload = wp_upload_dir();
407
  $dir = $upload['path'];
408
  $filename = $file;
409
+ $file = parse_url( $file );
410
+ $path_parts = pathinfo( $file['path'] );
411
+ $basename = md5( $filename );
412
  $ext = 'jpg';
413
+ if ( isset( $path_parts['extension'] ) ) {
414
  $ext = $path_parts['extension'];
415
  }
416
  return $dir . '/' . $basename . '.' . $ext;
417
  }
418
 
419
  /**
420
+ *
421
+ *
422
+ * @param string $file
423
  * @return string
424
  */
425
+ public static function sideload_image( $file ) {
426
+ $loc = self::get_sideloaded_file_loc( $file );
427
+ if ( file_exists( $loc ) ) {
428
+ return TimberURLHelper::preslashit( TimberURLHelper::get_rel_path( $loc ) );
429
  }
430
  // Download file to temp location
431
+ if ( !function_exists( 'download_url' ) ) {
432
+ require_once ABSPATH . '/wp-admin/includes/file.php';
433
  }
434
+ $tmp = download_url( $file );
435
+ preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches );
436
  $file_array = array();
437
+ $file_array['name'] = basename( $matches[0] );
438
  $file_array['tmp_name'] = $tmp;
439
  // If error storing temporarily, unlink
440
+ if ( is_wp_error( $tmp ) ) {
441
+ @unlink( $file_array['tmp_name'] );
442
  $file_array['tmp_name'] = '';
443
  }
444
  // do the validation and storage stuff
445
+ $locinfo = pathinfo( $loc );
446
+ $file = wp_upload_bits( $locinfo['basename'], null, file_get_contents( $file_array['tmp_name'] ) );
447
  return $file['url'];
448
  }
449
 
450
  /**
451
+ *
452
+ *
453
+ * @param string $src
454
+ * @param float $multiplier
455
+ */
456
+ public static function retina_resize( $src, $factor = 2 ) {
457
+ if ( empty( $src ) ) {
458
+ return '';
459
+ }
460
+ $abs = false;
461
+ if ( strstr( $src, 'http' ) ) {
462
+ $abs = true;
463
+ }
464
+ if ( strstr( $src, 'http' ) && !strstr( $src, home_url() ) ) {
465
+ $src = self::sideload_image( $src );
466
+ }
467
+ $old_server_path = self::get_server_location( $src );
468
+ $new_path = TimberImageRetinaHelper::get_retina_file_rel( $src, $factor );
469
+ $new_server_path = TimberImageRetinaHelper::get_retina_file_path( $src, $factor );
470
+
471
+ $old_server_path = TimberURLHelper::remove_double_slashes( $old_server_path );
472
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
473
+ if ( file_exists( $new_server_path ) ) {
474
+ if ( !$abs ) {
475
+ return TimberURLHelper::preslashit( $new_path );
476
+ }
477
+ return untrailingslashit( home_url() ) . $new_path;
478
+ }
479
+ $image = wp_get_image_editor( $old_server_path );
480
+ if ( !is_wp_error( $image ) ) {
481
+ $current_size = $image->get_size();
482
+
483
+ $src_w = $current_size['width'];
484
+ $src_h = $current_size['height'];
485
+
486
+ $src_ratio = $src_w / $src_h;
487
+
488
+ // Get ratios
489
+ $w = $src_w * $factor;
490
+ $h = $src_h * $factor;
491
+ $image->crop( 0, 0, $src_w, $src_h, $w, $h );
492
+ $result = $image->save( $new_server_path );
493
+ return $new_path;
494
+ }
495
+ return $src;
496
+
497
+ }
498
+
499
+ /**
500
+ *
501
+ *
502
+ * @param string $src
503
+ * @param int $w
504
+ * @param int $h
505
+ * @param string $crop
506
+ * @param bool $force_resize
507
  * @return string
508
  */
509
+ public static function resize( $src, $w, $h = 0, $crop = 'default', $force_resize = false ) {
510
+ if ( empty( $src ) ) {
511
  return '';
512
  }
513
+ if ( strstr( $src, 'http' ) && !strstr( $src, home_url() ) ) {
514
+ $src = self::sideload_image( $src );
515
  }
516
  $abs = false;
517
+ if ( strstr( $src, 'http' ) ) {
518
  $abs = true;
519
  }
520
  // Sanitize crop position
521
+ $allowed_crop_positions = array( 'default', 'center', 'top', 'bottom', 'left', 'right' );
522
+ if ( $crop !== false && !in_array( $crop, $allowed_crop_positions ) ) {
523
  $crop = $allowed_crop_positions[0];
524
  }
525
  //oh good, it's a relative image in the uploads folder!
526
+ $new_path = self::get_resize_file_rel( $src, $w, $h, $crop );
527
+ $new_server_path = self::get_resize_file_path( $src, $w, $h, $crop );
528
+ $old_server_path = self::get_server_location( $src );
529
+ $old_server_path = TimberURLHelper::remove_double_slashes( $old_server_path );
530
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
531
+ if ( file_exists( $new_server_path ) ) {
532
+ if ( $force_resize ) {
533
  // Force resize - warning: will regenerate the image on every pageload, use for testing purposes only!
534
+ unlink( $new_server_path );
535
  } else {
536
+ if ( !$abs ) {
537
+ return TimberURLHelper::preslashit( $new_path );
538
  }
539
+ return untrailingslashit( home_url() ) . $new_path;
540
  }
541
  }
542
+ $image = wp_get_image_editor( $old_server_path );
543
 
544
+ if ( !is_wp_error( $image ) ) {
545
  $current_size = $image->get_size();
546
 
547
  $src_w = $current_size['width'];
548
  $src_h = $current_size['height'];
549
 
550
  $src_ratio = $src_w / $src_h;
551
+ if ( !$h ) {
552
+ $h = round( $w / $src_ratio );
553
+ }
554
+ if ( !$w ) {
555
+ //the user wants to resize based on constant height
556
+ $w = round( $h * $src_ratio );
557
  }
558
  // Get ratios
559
  $dest_ratio = $w / $h;
560
  $src_wt = $src_h * $dest_ratio;
561
  $src_ht = $src_w / $dest_ratio;
562
 
563
+ if ( !$crop ) {
564
  // Always crop, to allow resizing upwards
565
+ $image->crop( 0, 0, $src_w, $src_h, $w, $h );
566
  } else {
567
  //start with defaults:
568
  $src_x = $src_w / 2 - $src_wt / 2;
569
+ $src_y = ( $src_h - $src_ht ) / 6;
570
  //now specific overrides based on options:
571
+ if ( $crop == 'center' ) {
572
  // Get source x and y
573
+ $src_x = round( ( $src_w - $src_wt ) / 2 );
574
+ $src_y = round( ( $src_h - $src_ht ) / 2 );
575
+ } else if ( $crop == 'top' ) {
576
+ $src_y = 0;
577
+ } else if ( $crop == 'bottom' ) {
578
+ $src_y = $src_h - $src_ht;
579
+ } else if ( $crop == 'left' ) {
580
+ $src_x = 0;
581
+ } else if ( $crop == 'right' ) {
582
+ $src_x = $src_w - $src_wt;
583
+ }
584
 
585
  // Crop the image
586
+ if ( $dest_ratio > $src_ratio ) {
587
+ $image->crop( 0, $src_y, $src_w, $src_ht, $w, $h );
588
  } else {
589
+ $image->crop( $src_x, 0, $src_wt, $src_h, $w, $h );
590
  }
591
 
592
  }
593
+ $result = $image->save( $new_server_path );
594
+ if ( is_wp_error( $result ) ) {
595
+ error_log( 'Error resizing image' );
596
+ error_log( print_r( $result, true ) );
597
  }
598
+ if ( $abs ) {
599
+ return untrailingslashit( home_url() ) . $new_path;
600
  }
601
  return $new_path;
602
+ } else if ( isset( $image->error_data['error_loading_image'] ) ) {
603
+ TimberHelper::error_log( 'Error loading ' . $image->error_data['error_loading_image'] );
604
+ } else {
605
+ TimberHelper::error_log( $image );
606
  }
607
  return $src;
608
  }
functions/timber-image-retina-helper.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TimberImageRetinaHelper {
4
+
5
+ /**
6
+ * @param string $src
7
+ * @param int $w
8
+ * @param int $h
9
+ * @param string $crop
10
+ * @return string
11
+ */
12
+ static function get_retina_file_path( $src, $mult = 2 ) {
13
+ $new_name = self::get_retina_file_name_relative_to_content( $src, $mult );
14
+ $new_server_path = WP_CONTENT_DIR . $new_name;
15
+ $new_server_path = TimberURLHelper::remove_double_slashes( $new_server_path );
16
+ return $new_server_path;
17
+ }
18
+
19
+
20
+ /**
21
+ *
22
+ *
23
+ * @param string $src
24
+ * @param int $w
25
+ * @param int $h
26
+ * @param string $crop
27
+ * @return string
28
+ */
29
+ public static function get_retina_file_rel( $src, $factor = 2 ) {
30
+ if ( !strlen( $src ) ) {
31
+ return null;
32
+ }
33
+ $new_path = self::get_retina_file_name_relative_to_content( $src, $factor );
34
+ return WP_CONTENT_SUBDIR . $new_path;
35
+ }
36
+
37
+ /**
38
+ *
39
+ *
40
+ * @param string $src
41
+ * @param int $w
42
+ * @param int $h
43
+ * @param string $crop
44
+ * @return string
45
+ */
46
+ private static function get_retina_file_name_relative_to_content( $src, $mult = 2 ) {
47
+ $path_parts = pathinfo( $src );
48
+ $dir_relative_to_content = TimberImageHelper::get_directory_relative_to_content( $src );
49
+ $newbase = $path_parts['filename'] . '@' . $mult . 'x'; // add @2x, @3x, @1.5x, etc.
50
+ $new_name = $newbase . '.' . $path_parts['extension'];
51
+ return $dir_relative_to_content . '/' . $new_name;
52
+ }
53
+
54
+ }
functions/timber-image.php CHANGED
@@ -164,13 +164,18 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
164
  * @param int $iid
165
  */
166
  function init( $iid = false ) {
167
- if (!is_numeric($iid) && is_string($iid)) {
168
  if (strstr($iid, '://')) {
169
  $this->init_with_url($iid);
170
  return;
171
  }
 
 
 
 
172
  if (strstr(strtolower($iid), '.jpg')) {
173
- $this->init_with_url($iid);
 
174
  }
175
  }
176
 
@@ -205,7 +210,7 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
205
  }
206
  }
207
 
208
- private function get_image_info($iid) {
209
  $image_info = $iid;
210
  if (is_numeric($iid)) {
211
  $image_info = wp_get_attachment_metadata($iid);
@@ -231,14 +236,28 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
231
  return $iid;
232
  }
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  /**
235
  * @param string $url
236
  */
237
  private function init_with_url($url) {
238
  $this->abs_url = $url;
239
- $this->file_loc = $url;
240
  if (TimberURLHelper::is_local($url)) {
241
  $this->file = ABSPATH . TimberURLHelper::get_rel_url($url);
 
242
  }
243
  }
244
 
164
  * @param int $iid
165
  */
166
  function init( $iid = false ) {
167
+ if ( !is_numeric( $iid ) && is_string( $iid ) ) {
168
  if (strstr($iid, '://')) {
169
  $this->init_with_url($iid);
170
  return;
171
  }
172
+ if ( strstr($iid, ABSPATH) ) {
173
+ $this->init_with_file_path($iid);
174
+ return;
175
+ }
176
  if (strstr(strtolower($iid), '.jpg')) {
177
+ $this->init_with_relative_path($iid);
178
+ return;
179
  }
180
  }
181
 
210
  }
211
  }
212
 
213
+ private function get_image_info( $iid ) {
214
  $image_info = $iid;
215
  if (is_numeric($iid)) {
216
  $image_info = wp_get_attachment_metadata($iid);
236
  return $iid;
237
  }
238
 
239
+ private function init_with_relative_path( $relative_path ) {
240
+ $this->abs_url = home_url( $relative_path );
241
+ $file_path = TimberURLHelper::get_full_path( $relative_path );
242
+ $this->file_loc = $file_path;
243
+ $this->file = $file_path;
244
+ }
245
+
246
+ private function init_with_file_path( $file_path ) {
247
+ $url = TimberURLHelper::file_system_to_url( $file_path );
248
+ $this->abs_url = $url;
249
+ $this->file_loc = $file_path;
250
+ $this->file = $file_path;
251
+ }
252
+
253
  /**
254
  * @param string $url
255
  */
256
  private function init_with_url($url) {
257
  $this->abs_url = $url;
 
258
  if (TimberURLHelper::is_local($url)) {
259
  $this->file = ABSPATH . TimberURLHelper::get_rel_url($url);
260
+ $this->file_loc = ABSPATH . TimberURLHelper::get_rel_url($url);
261
  }
262
  }
263
 
functions/timber-twig.php CHANGED
@@ -1,236 +1,267 @@
1
  <?php
2
 
3
- class TimberTwig
4
- {
5
 
6
  public static $dir_name;
7
 
8
  function __construct() {
9
- add_action('twig_apply_filters', array($this, 'add_twig_filters'));
 
10
  }
11
 
12
  /**
 
 
13
  * @param Twig_Environment $twig
 
14
  * @return Twig_Environment
15
  */
16
- function add_twig_filters($twig) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  /* image filters */
18
- $twig->addFilter('resize', new Twig_Filter_Function(array('TimberImageHelper', 'resize')));
19
- $twig->addFilter('letterbox', new Twig_Filter_Function(array('TimberImageHelper', 'letterbox')));
20
- $twig->addFilter('tojpg', new Twig_Filter_Function(array('TimberImageHelper', 'img_to_jpg')));
21
- $twig->addFilter('get_src_from_attachment_id', new Twig_Filter_Function('twig_get_src_from_attachment_id'));
22
 
23
  /* debugging filters */
24
- $twig->addFilter('docs', new Twig_Filter_function('twig_object_docs'));
25
- $twig->addFilter('get_class', new Twig_Filter_Function('get_class'));
26
- $twig->addFilter('get_type', new Twig_Filter_Function('get_type'));
27
- $twig->addFilter('print_r', new Twig_Filter_Function(function($arr){
28
- return print_r($arr, true);
29
- }));
30
- $twig->addFilter('print_a', new Twig_Filter_Function(function($arr){
31
- return '<pre>' . self::object_docs($arr, true) . '</pre>';
32
- }));
33
 
34
  /* other filters */
35
- $twig->addFilter('stripshortcodes', new Twig_Filter_Function('strip_shortcodes'));
36
- $twig->addFilter('array', new Twig_Filter_Function(array($this, 'to_array')));
37
- $twig->addFilter('string', new Twig_Filter_Function(array($this, 'to_string')));
38
- $twig->addFilter('excerpt', new Twig_Filter_Function('wp_trim_words'));
39
- $twig->addFilter('function', new Twig_Filter_Function(array($this, 'exec_function')));
40
- $twig->addFilter('path', new Twig_Filter_Function('twig_get_path'));
41
- $twig->addFilter('pretags', new Twig_Filter_Function(array($this, 'twig_pretags')));
42
- $twig->addFilter('sanitize', new Twig_Filter_Function('sanitize_title'));
43
- $twig->addFilter('shortcodes', new Twig_Filter_Function('do_shortcode'));
44
- $twig->addFilter('time_ago', new Twig_Filter_Function(array($this, 'time_ago')));
45
- $twig->addFilter('twitterify', new Twig_Filter_Function(array('TimberHelper', 'twitterify')));
46
- $twig->addFilter('twitterfy', new Twig_Filter_Function(array('TimberHelper', 'twitterify')));
47
- $twig->addFilter('wp_body_class', new Twig_Filter_Function(array($this, 'body_class')));
48
- $twig->addFilter('wpautop', new Twig_Filter_Function('wpautop'));
49
- $twig->addFilter('relative', new Twig_Filter_Function(function ($link) {
50
- return TimberURLHelper::get_rel_url($link, true);
51
- }));
52
- $twig->addFilter('date', new Twig_Filter_Function(array($this, 'intl_date')));
53
-
54
- $twig->addFilter('truncate', new Twig_Filter_Function(function ($text, $len) {
55
- return TimberHelper::trim_words($text, $len);
56
- }));
57
 
58
  /* actions and filters */
59
- $twig->addFunction(new Twig_SimpleFunction('action', function ($context) {
60
- $args = func_get_args();
61
- array_shift($args);
62
- $args[] = $context;
63
- call_user_func_array('do_action', $args);
64
- }, array('needs_context' => true)));
65
-
66
- $twig->addFilter(new Twig_SimpleFilter('apply_filters', function () {
67
- $args = func_get_args();
68
- $tag = current(array_splice($args, 1, 1));
69
-
70
- return apply_filters_ref_array($tag, $args);
71
- }));
72
- $twig->addFunction(new Twig_SimpleFunction('function', array(&$this, 'exec_function')));
73
- $twig->addFunction(new Twig_SimpleFunction('fn', array(&$this, 'exec_function')));
74
 
75
  /* TimberObjects */
76
- $twig->addFunction(new Twig_SimpleFunction('TimberPost', function ($pid, $PostClass = 'TimberPost') {
77
- if (is_array($pid) && !TimberHelper::is_array_assoc($pid)) {
78
- foreach ($pid as &$p) {
79
- $p = new $PostClass($p);
80
- }
81
- return $pid;
82
- }
83
- return new $PostClass($pid);
84
- }));
85
- $twig->addFunction(new Twig_SimpleFunction('TimberImage', function ($pid, $ImageClass = 'TimberImage') {
86
- if (is_array($pid) && !TimberHelper::is_array_assoc($pid)) {
87
- foreach ($pid as &$p) {
88
- $p = new $ImageClass($p);
89
- }
90
- return $pid;
91
- }
92
- return new $ImageClass($pid);
93
- }));
94
- $twig->addFunction(new Twig_SimpleFunction('TimberTerm', function ($pid, $TermClass = 'TimberTerm') {
95
- if (is_array($pid) && !TimberHelper::is_array_assoc($pid)) {
96
- foreach ($pid as &$p) {
97
- $p = new $TermClass($p);
98
- }
99
- return $pid;
100
- }
101
- return new $TermClass($pid);
102
- }));
103
- $twig->addFunction(new Twig_SimpleFunction('TimberUser', function ($pid, $UserClass = 'TimberUser') {
104
- if (is_array($pid) && !TimberHelper::is_array_assoc($pid)) {
105
- foreach ($pid as &$p) {
106
- $p = new $UserClass($p);
107
- }
108
- return $pid;
109
- }
110
- return new $UserClass($pid);
111
- }));
112
 
113
  /* bloginfo and translate */
114
- $twig->addFunction('bloginfo', new Twig_SimpleFunction('bloginfo', function ($show = '', $filter = 'raw') {
115
- return get_bloginfo($show, $filter);
116
- }));
117
- $twig->addFunction('__', new Twig_SimpleFunction('__', function ($text, $domain = 'default') {
118
- return __($text, $domain);
119
- }));
120
 
121
- $twig = apply_filters('get_twig', $twig);
122
 
123
  return $twig;
124
  }
125
 
126
  /**
127
- * @param mixed $arr
 
 
128
  * @return array
129
  */
130
- function to_array($arr) {
131
- if (is_array($arr)) {
132
  return $arr;
133
  }
134
- $arr = array($arr);
135
  return $arr;
136
  }
137
 
138
  /**
139
- * @param mixed $arr
140
- * @param string $glue
 
 
141
  * @return string
142
  */
143
- function to_string($arr, $glue = ' ') {
144
- if (is_string($arr)) {
145
  return $arr;
146
  }
147
- if (is_array($arr) && count($arr) == 1) {
148
  return $arr[0];
149
  }
150
- if (is_array($arr)) {
151
- return implode($glue, $arr);
152
  }
153
  return null;
154
  }
155
 
156
  /**
157
- * @param string $function_name
 
 
158
  * @return mixed
159
  */
160
- function exec_function($function_name) {
161
  $args = func_get_args();
162
- array_shift($args);
163
- return call_user_func_array(trim($function_name), ($args));
164
  }
165
 
166
  /**
167
- * @param string $content
 
 
168
  * @return string
169
  */
170
- function twig_pretags($content) {
171
- return preg_replace_callback('|<pre.*>(.*)</pre|isU', array(&$this, 'convert_pre_entities'), $content);
172
  }
173
 
174
  /**
175
- * @param array $matches
 
 
176
  * @return string
177
  */
178
- function convert_pre_entities($matches) {
179
- return str_replace($matches[1], htmlentities($matches[1]), $matches[0]);
180
  }
181
 
182
  /**
183
- * @param mixed $body_classes
 
 
184
  * @return string
185
  */
186
- function body_class($body_classes) {
187
  ob_start();
188
- if (is_array($body_classes)) {
189
- $body_classes = explode(' ', $body_classes);
190
  }
191
- body_class($body_classes);
192
  $return = ob_get_contents();
193
  ob_end_clean();
194
  return $return;
195
  }
196
 
197
  /**
198
- * @param string $date
199
- * @param string $format (optional)
 
 
200
  * @return string
201
  */
202
- function intl_date($date, $format = null) {
203
- if ($format === null) {
204
- $format = get_option('date_format');
205
  }
206
 
207
- if ($date instanceof DateTime) {
208
  $timestamp = $date->getTimestamp();
209
  } else {
210
- $timestamp = strtotime($date);
211
  }
212
 
213
- return date_i18n($format, $timestamp);
214
  }
215
 
216
  //debug
217
 
218
  /**
219
- * @param mixed $obj
220
- * @param bool $methods
 
 
221
  * @return string
222
  */
223
- function object_docs($obj, $methods = true) {
224
- $class = get_class($obj);
225
  $properties = (array)$obj;
226
- if ($methods) {
227
  /** @var array $methods */
228
  $methods = $obj->get_method_values();
229
  }
230
- $rets = array_merge($properties, $methods);
231
- ksort($rets);
232
- $str = print_r($rets, true);
233
- $str = str_replace('Array', $class . ' Object', $str);
234
  return $str;
235
  }
236
 
@@ -241,32 +272,18 @@ class TimberTwig
241
  * @param string $format_future
242
  * @return string
243
  */
244
- function time_ago($from, $to = null, $format_past = '%s ago', $format_future = '%s from now') {
245
  $to = $to === null ? time() : $to;
246
- $to = is_int($to) ? $to : strtotime($to);
247
- $from = is_int($from) ? $from : strtotime($from);
248
 
249
- if ($from < $to) {
250
- return sprintf($format_past, human_time_diff($from, $to));
251
  } else {
252
- return sprintf($format_future, human_time_diff($to, $from));
253
  }
254
  }
255
 
256
  }
257
 
258
  new TimberTwig();
259
-
260
-
261
- /* deprecated */
262
-
263
- /**
264
- * @param string $string
265
- * @param array $data
266
- * @return string
267
- */
268
- function render_twig_string($string, $data = array()) {
269
- return Timber::render_string($string, $data);
270
- }
271
-
272
-
1
  <?php
2
 
3
+ class TimberTwig {
 
4
 
5
  public static $dir_name;
6
 
7
  function __construct() {
8
+ add_action( 'twig_apply_filters', array( $this, 'add_timber_filters_deprecated' ) );
9
+ add_action( 'twig_apply_filters', array( $this, 'add_timber_filters' ) );
10
  }
11
 
12
  /**
13
+ * These are all deprecated and will be removed in 0.21.0
14
+ *
15
  * @param Twig_Environment $twig
16
+ * @deprecated since 0.20.7
17
  * @return Twig_Environment
18
  */
19
+ function add_timber_filters_deprecated( $twig ) {
20
+ $twig->addFilter( new Twig_SimpleFilter( 'get_src_from_attachment_id', 'twig_get_src_from_attachment_id' ) );
21
+ $twig->addFilter( new Twig_SimpleFilter( 'wp_body_class', array( $this, 'body_class' ) ) );
22
+ $twig->addFilter( new Twig_SimpleFilter( 'twitterify', array( 'TimberHelper', 'twitterify' ) ) );
23
+ $twig->addFilter( new Twig_SimpleFilter( 'twitterfy', array( 'TimberHelper', 'twitterify' ) ) );
24
+ return $twig;
25
+ }
26
+
27
+ /**
28
+ *
29
+ *
30
+ * @param Twig_Environment $twig
31
+ * @return Twig_Environment
32
+ */
33
+ function add_timber_filters( $twig ) {
34
  /* image filters */
35
+ $twig->addFilter( new Twig_SimpleFilter( 'resize', array( 'TimberImageHelper', 'resize' ) ) );
36
+ $twig->addFilter( new Twig_SimpleFilter( 'retina', array( 'TimberImageHelper', 'retina_resize' ) ) );
37
+ $twig->addFilter( new Twig_SimpleFilter( 'letterbox', array( 'TimberImageHelper', 'letterbox' ) ) );
38
+ $twig->addFilter( new Twig_SimpleFilter( 'tojpg', array( 'TimberImageHelper', 'img_to_jpg' ) ) );
39
 
40
  /* debugging filters */
41
+ $twig->addFilter( new Twig_SimpleFilter( 'docs', 'twig_object_docs' ) );
42
+ $twig->addFilter( new Twig_SimpleFilter( 'get_class', 'get_class' ) );
43
+ $twig->addFilter( new Twig_SimpleFilter( 'get_type', 'get_type' ) );
44
+ $twig->addFilter( new Twig_SimpleFilter( 'print_r', function( $arr ) {
45
+ return print_r( $arr, true );
46
+ } ) );
47
+ $twig->addFilter( new Twig_SimpleFilter( 'print_a', function( $arr ) {
48
+ return '<pre>' . self::object_docs( $arr, true ) . '</pre>';
49
+ } ) );
50
 
51
  /* other filters */
52
+ $twig->addFilter( new Twig_SimpleFilter( 'stripshortcodes', 'strip_shortcodes' ) );
53
+ $twig->addFilter( new Twig_SimpleFilter( 'array', array( $this, 'to_array' ) ) );
54
+ $twig->addFilter( new Twig_SimpleFilter( 'string', array( $this, 'to_string' ) ) );
55
+ $twig->addFilter( new Twig_SimpleFilter( 'excerpt', 'wp_trim_words' ) );
56
+ $twig->addFilter( new Twig_SimpleFilter( 'function', array( $this, 'exec_function' ) ) );
57
+ $twig->addFilter( new Twig_SimpleFilter( 'pretags', array( $this, 'twig_pretags' ) ) );
58
+ $twig->addFilter( new Twig_SimpleFilter( 'sanitize', 'sanitize_title' ) );
59
+ $twig->addFilter( new Twig_SimpleFilter( 'shortcodes', 'do_shortcode' ) );
60
+ $twig->addFilter( new Twig_SimpleFilter( 'time_ago', array( $this, 'time_ago' ) ) );
61
+ $twig->addFilter( new Twig_SimpleFilter( 'wpautop', 'wpautop' ) );
62
+
63
+ $twig->addFilter( new Twig_SimpleFilter( 'relative', function ( $link ) {
64
+ return TimberURLHelper::get_rel_url( $link, true );
65
+ } ) );
66
+
67
+ $twig->addFilter( new Twig_SimpleFilter( 'date', array( $this, 'intl_date' ) ) );
68
+
69
+ $twig->addFilter( new Twig_SimpleFilter( 'truncate', function ( $text, $len ) {
70
+ return TimberHelper::trim_words( $text, $len );
71
+ } ) );
 
 
72
 
73
  /* actions and filters */
74
+ $twig->addFunction( new Twig_SimpleFunction( 'action', function ( $context ) {
75
+ $args = func_get_args();
76
+ array_shift( $args );
77
+ $args[] = $context;
78
+ call_user_func_array( 'do_action', $args );
79
+ }, array( 'needs_context' => true ) ) );
80
+
81
+ $twig->addFilter( new Twig_SimpleFilter( 'apply_filters', function () {
82
+ $args = func_get_args();
83
+ $tag = current( array_splice( $args, 1, 1 ) );
84
+
85
+ return apply_filters_ref_array( $tag, $args );
86
+ } ) );
87
+ $twig->addFunction( new Twig_SimpleFunction( 'function', array( &$this, 'exec_function' ) ) );
88
+ $twig->addFunction( new Twig_SimpleFunction( 'fn', array( &$this, 'exec_function' ) ) );
89
 
90
  /* TimberObjects */
91
+ $twig->addFunction( new Twig_SimpleFunction( 'TimberPost', function ( $pid, $PostClass = 'TimberPost' ) {
92
+ if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
93
+ foreach ( $pid as &$p ) {
94
+ $p = new $PostClass( $p );
95
+ }
96
+ return $pid;
97
+ }
98
+ return new $PostClass( $pid );
99
+ } ) );
100
+ $twig->addFunction( new Twig_SimpleFunction( 'TimberImage', function ( $pid, $ImageClass = 'TimberImage' ) {
101
+ if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
102
+ foreach ( $pid as &$p ) {
103
+ $p = new $ImageClass( $p );
104
+ }
105
+ return $pid;
106
+ }
107
+ return new $ImageClass( $pid );
108
+ } ) );
109
+ $twig->addFunction( new Twig_SimpleFunction( 'TimberTerm', function ( $pid, $TermClass = 'TimberTerm' ) {
110
+ if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
111
+ foreach ( $pid as &$p ) {
112
+ $p = new $TermClass( $p );
113
+ }
114
+ return $pid;
115
+ }
116
+ return new $TermClass( $pid );
117
+ } ) );
118
+ $twig->addFunction( new Twig_SimpleFunction( 'TimberUser', function ( $pid, $UserClass = 'TimberUser' ) {
119
+ if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
120
+ foreach ( $pid as &$p ) {
121
+ $p = new $UserClass( $p );
122
+ }
123
+ return $pid;
124
+ }
125
+ return new $UserClass( $pid );
126
+ } ) );
127
 
128
  /* bloginfo and translate */
129
+ $twig->addFunction( 'bloginfo', new Twig_SimpleFunction( 'bloginfo', function ( $show = '', $filter = 'raw' ) {
130
+ return get_bloginfo( $show, $filter );
131
+ } ) );
132
+ $twig->addFunction( '__', new Twig_SimpleFunction( '__', function ( $text, $domain = 'default' ) {
133
+ return __( $text, $domain );
134
+ } ) );
135
 
136
+ $twig = apply_filters( 'get_twig', $twig );
137
 
138
  return $twig;
139
  }
140
 
141
  /**
142
+ *
143
+ *
144
+ * @param mixed $arr
145
  * @return array
146
  */
147
+ function to_array( $arr ) {
148
+ if ( is_array( $arr ) ) {
149
  return $arr;
150
  }
151
+ $arr = array( $arr );
152
  return $arr;
153
  }
154
 
155
  /**
156
+ *
157
+ *
158
+ * @param mixed $arr
159
+ * @param string $glue
160
  * @return string
161
  */
162
+ function to_string( $arr, $glue = ' ' ) {
163
+ if ( is_string( $arr ) ) {
164
  return $arr;
165
  }
166
+ if ( is_array( $arr ) && count( $arr ) == 1 ) {
167
  return $arr[0];
168
  }
169
+ if ( is_array( $arr ) ) {
170
+ return implode( $glue, $arr );
171
  }
172
  return null;
173
  }
174
 
175
  /**
176
+ *
177
+ *
178
+ * @param string $function_name
179
  * @return mixed
180
  */
181
+ function exec_function( $function_name ) {
182
  $args = func_get_args();
183
+ array_shift( $args );
184
+ return call_user_func_array( trim( $function_name ), ( $args ) );
185
  }
186
 
187
  /**
188
+ *
189
+ *
190
+ * @param string $content
191
  * @return string
192
  */
193
+ function twig_pretags( $content ) {
194
+ return preg_replace_callback( '|<pre.*>(.*)</pre|isU', array( &$this, 'convert_pre_entities' ), $content );
195
  }
196
 
197
  /**
198
+ *
199
+ *
200
+ * @param array $matches
201
  * @return string
202
  */
203
+ function convert_pre_entities( $matches ) {
204
+ return str_replace( $matches[1], htmlentities( $matches[1] ), $matches[0] );
205
  }
206
 
207
  /**
208
+ *
209
+ *
210
+ * @param mixed $body_classes
211
  * @return string
212
  */
213
+ function body_class( $body_classes ) {
214
  ob_start();
215
+ if ( is_array( $body_classes ) ) {
216
+ $body_classes = explode( ' ', $body_classes );
217
  }
218
+ body_class( $body_classes );
219
  $return = ob_get_contents();
220
  ob_end_clean();
221
  return $return;
222
  }
223
 
224
  /**
225
+ *
226
+ *
227
+ * @param string $date
228
+ * @param string $format (optional)
229
  * @return string
230
  */
231
+ function intl_date( $date, $format = null ) {
232
+ if ( $format === null ) {
233
+ $format = get_option( 'date_format' );
234
  }
235
 
236
+ if ( $date instanceof DateTime ) {
237
  $timestamp = $date->getTimestamp();
238
  } else {
239
+ $timestamp = strtotime( $date );
240
  }
241
 
242
+ return date_i18n( $format, $timestamp );
243
  }
244
 
245
  //debug
246
 
247
  /**
248
+ *
249
+ *
250
+ * @param mixed $obj
251
+ * @param bool $methods
252
  * @return string
253
  */
254
+ function object_docs( $obj, $methods = true ) {
255
+ $class = get_class( $obj );
256
  $properties = (array)$obj;
257
+ if ( $methods ) {
258
  /** @var array $methods */
259
  $methods = $obj->get_method_values();
260
  }
261
+ $rets = array_merge( $properties, $methods );
262
+ ksort( $rets );
263
+ $str = print_r( $rets, true );
264
+ $str = str_replace( 'Array', $class . ' Object', $str );
265
  return $str;
266
  }
267
 
272
  * @param string $format_future
273
  * @return string
274
  */
275
+ function time_ago( $from, $to = null, $format_past = '%s ago', $format_future = '%s from now' ) {
276
  $to = $to === null ? time() : $to;
277
+ $to = is_int( $to ) ? $to : strtotime( $to );
278
+ $from = is_int( $from ) ? $from : strtotime( $from );
279
 
280
+ if ( $from < $to ) {
281
+ return sprintf( $format_past, human_time_diff( $from, $to ) );
282
  } else {
283
+ return sprintf( $format_future, human_time_diff( $to, $from ) );
284
  }
285
  }
286
 
287
  }
288
 
289
  new TimberTwig();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
functions/timber-url-helper.php CHANGED
@@ -94,6 +94,13 @@ class TimberURLHelper {
94
  return $old_root_path;
95
  }
96
 
 
 
 
 
 
 
 
97
  public static function url_to_file_system($url) {
98
  $url_parts = parse_url($url);
99
  $path = ABSPATH . $url_parts['path'];
@@ -101,6 +108,12 @@ class TimberURLHelper {
101
  return $path;
102
  }
103
 
 
 
 
 
 
 
104
  /**
105
  * @param string $src
106
  * @return string
94
  return $old_root_path;
95
  }
96
 
97
+ /**
98
+ * Takes a url and figures out its place based in the file system based on path
99
+ * NOTE: Not fool-proof, makes a lot of assumptions about the file path
100
+ * matching the URL path
101
+ * @param string $url
102
+ * @return string
103
+ */
104
  public static function url_to_file_system($url) {
105
  $url_parts = parse_url($url);
106
  $path = ABSPATH . $url_parts['path'];
108
  return $path;
109
  }
110
 
111
+ public static function file_system_to_url( $fs ) {
112
+ $relative_path = self::get_rel_path($fs);
113
+ $home = home_url('/'.$relative_path);
114
+ return $home;
115
+ }
116
+
117
  /**
118
  * @param string $src
119
  * @return string
readme.txt CHANGED
@@ -2,7 +2,7 @@
2
  Contributors: jarednova
3
  Tags: template engine, templates, twig
4
  Requires at least: 3.7
5
- Stable tag: 0.20.7
6
  Tested up to: 4.0
7
  PHP version: 5.3.0 or greater
8
  License: GPLv2 or later
@@ -41,6 +41,10 @@ Timber is great for any WordPress developer who cares about writing good, mainta
41
 
42
  == Changelog ==
43
 
 
 
 
 
44
  = 0.20.7 =
45
  * Cleaned-up logic for {{post.next}} and {{post.prev}} (thanks @alexlrobertson)
46
  * Simplifiying internals of TimberCore, TimberPost (thanks @alexlrobertson)
2
  Contributors: jarednova
3
  Tags: template engine, templates, twig
4
  Requires at least: 3.7
5
+ Stable tag: 0.20.8
6
  Tested up to: 4.0
7
  PHP version: 5.3.0 or greater
8
  License: GPLv2 or later
41
 
42
  == Changelog ==
43
 
44
+ = 0.20.8 =
45
+ * Fixed some Twig deprecation (thanks @alexlrobertson)
46
+ * Support for {{img.src|retina}} filter (@jarednova)
47
+
48
  = 0.20.7 =
49
  * Cleaned-up logic for {{post.next}} and {{post.prev}} (thanks @alexlrobertson)
50
  * Simplifiying internals of TimberCore, TimberPost (thanks @alexlrobertson)
timber.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Timber
4
  Plugin URI: http://timber.upstatement.com
5
  Description: The WordPress Timber Library allows you to write themes using the power Twig templates
6
  Author: Jared Novack + Upstatement
7
- Version: 0.20.7
8
  Author URI: http://upstatement.com/
9
  */
10
 
4
  Plugin URI: http://timber.upstatement.com
5
  Description: The WordPress Timber Library allows you to write themes using the power Twig templates
6
  Author: Jared Novack + Upstatement
7
+ Version: 0.20.8
8
  Author URI: http://upstatement.com/
9
  */
10
 
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit36822b6ae1dbe3e7b97c1dd14c0cb078::getLoader();
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInitba2f6a6f731944fa517757b2bd6a18c4::getLoader();
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit36822b6ae1dbe3e7b97c1dd14c0cb078
6
  {
7
  private static $loader;
8
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit36822b6ae1dbe3e7b97c1dd14c0cb078
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit36822b6ae1dbe3e7b97c1dd14c0cb078', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit36822b6ae1dbe3e7b97c1dd14c0cb078', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(__DIR__);
27
  $baseDir = dirname($vendorDir);
@@ -47,7 +47,7 @@ class ComposerAutoloaderInit36822b6ae1dbe3e7b97c1dd14c0cb078
47
  }
48
  }
49
 
50
- function composerRequire36822b6ae1dbe3e7b97c1dd14c0cb078($file)
51
  {
52
  require $file;
53
  }
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInitba2f6a6f731944fa517757b2bd6a18c4
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInitba2f6a6f731944fa517757b2bd6a18c4', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInitba2f6a6f731944fa517757b2bd6a18c4', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(__DIR__);
27
  $baseDir = dirname($vendorDir);
47
  }
48
  }
49
 
50
+ function composerRequireba2f6a6f731944fa517757b2bd6a18c4($file)
51
  {
52
  require $file;
53
  }
vendor/vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit289078358ffed06c21324f80319e78c1::getLoader();
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInita70107d44215103d417eb8d0d8675954::getLoader();
vendor/vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit289078358ffed06c21324f80319e78c1
6
  {
7
  private static $loader;
8
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit289078358ffed06c21324f80319e78c1
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit289078358ffed06c21324f80319e78c1', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit289078358ffed06c21324f80319e78c1', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(__DIR__);
27
  $baseDir = dirname($vendorDir);
@@ -47,7 +47,7 @@ class ComposerAutoloaderInit289078358ffed06c21324f80319e78c1
47
  }
48
  }
49
 
50
- function composerRequire289078358ffed06c21324f80319e78c1($file)
51
  {
52
  require $file;
53
  }
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInita70107d44215103d417eb8d0d8675954
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInita70107d44215103d417eb8d0d8675954', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInita70107d44215103d417eb8d0d8675954', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(__DIR__);
27
  $baseDir = dirname($vendorDir);
47
  }
48
  }
49
 
50
+ function composerRequirea70107d44215103d417eb8d0d8675954($file)
51
  {
52
  require $file;
53
  }