Timber - Version 1.0.0

Version Description

Download this release

Release Info

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

Code changes from version 0.22.5 to 1.0.0

Files changed (128) hide show
  1. README.md +35 -20
  2. init.php +6 -0
  3. lib/Admin.php +70 -0
  4. lib/{timber-archives.php → Archives.php} +35 -31
  5. lib/{cache → Cache}/KeyGenerator.php +0 -0
  6. lib/{cache → Cache}/TimberKeyGeneratorInterface.php +0 -0
  7. lib/{cache → Cache}/WPObjectCacheAdapter.php +6 -6
  8. lib/{timber-comment.php → Comment.php} +56 -49
  9. lib/Core.php +118 -0
  10. lib/{timber-core-interface.php → CoreInterface.php} +3 -1
  11. lib/FunctionWrapper.php +98 -0
  12. lib/{timber-helper.php → Helper.php} +110 -250
  13. lib/{timber-image.php → Image.php} +83 -88
  14. lib/{image/timber-image-operation.php → Image/Operation.php} +13 -9
  15. lib/{image/timber-image-operation-letterbox.php → Image/Operation/Letterbox.php} +45 -30
  16. lib/{image/timber-image-operation-resize.php → Image/Operation/Resize.php} +77 -51
  17. lib/Image/Operation/Retina.php +80 -0
  18. lib/{image/timber-image-operation-tojpg.php → Image/Operation/ToJpg.php} +19 -15
  19. lib/{timber-image-helper.php → ImageHelper.php} +131 -111
  20. lib/Integrations.php +29 -0
  21. lib/Integrations/ACF.php +60 -0
  22. lib/Integrations/Command.php +40 -0
  23. lib/Integrations/Timber_WP_CLI_Command.php +62 -0
  24. lib/{timber-loader.php → Loader.php} +97 -95
  25. lib/{timber-menu.php → Menu.php} +43 -38
  26. lib/{timber-menu-item.php → MenuItem.php} +59 -60
  27. lib/{timber-post.php → Post.php} +800 -693
  28. lib/PostGetter.php +131 -0
  29. lib/PostsCollection.php +100 -0
  30. lib/QueryIterator.php +162 -0
  31. lib/Request.php +41 -0
  32. lib/{timber-site.php → Site.php} +61 -52
  33. lib/{timber-term.php → Term.php} +43 -56
  34. lib/{timber-term-getter.php → TermGetter.php} +22 -17
  35. lib/{timber-theme.php → Theme.php} +14 -8
  36. lib/Timber.php +486 -0
  37. lib/Twig.php +324 -0
  38. lib/{timber-url-helper.php → URLHelper.php} +60 -89
  39. lib/{timber-user.php → User.php} +17 -43
  40. lib/image/timber-image-operation-retina.php +0 -74
  41. lib/integrations/acf-timber.php +0 -58
  42. lib/integrations/timber-command.php +0 -36
  43. lib/integrations/wpcli-timber.php +0 -57
  44. lib/timber-admin.php +0 -26
  45. lib/timber-core.php +0 -117
  46. lib/timber-function-wrapper.php +0 -77
  47. lib/timber-integrations.php +0 -24
  48. lib/timber-page.php +0 -9
  49. lib/timber-post-getter.php +0 -104
  50. lib/timber-posts-collection.php +0 -97
  51. lib/timber-query-iterator.php +0 -156
  52. lib/timber-routes.php +0 -33
  53. lib/timber-twig.php +0 -347
  54. readme.txt +13 -13
  55. timber-starter-theme/README.md +33 -3
  56. timber-starter-theme/archive.php +10 -10
  57. timber-starter-theme/author.php +5 -5
  58. timber-starter-theme/composer.json +8 -8
  59. timber-starter-theme/composer.lock +21 -15
  60. timber-starter-theme/footer.php +5 -1
  61. timber-starter-theme/functions.php +6 -6
  62. timber-starter-theme/header.php +5 -1
  63. timber-starter-theme/templates/base.twig +2 -17
  64. timber-starter-theme/templates/single-password.twig +1 -1
  65. timber-starter-theme/tests/test-timber-starter-theme.php +3 -3
  66. timber-starter-theme/views/menu.twig +8 -0
  67. timber.php +3 -614
  68. vendor/asm89/twig-cache-extension/.travis.yml +1 -1
  69. vendor/asm89/twig-cache-extension/composer.json +1 -1
  70. vendor/asm89/twig-cache-extension/lib/Asm89/Twig/CacheExtension/Node/CacheNode.php +1 -1
  71. vendor/autoload.php +1 -1
  72. vendor/composer/ClassLoader.php +4 -4
  73. vendor/composer/LICENSE +1 -1
  74. vendor/composer/autoload_classmap.php +0 -38
  75. vendor/composer/autoload_files.php +10 -0
  76. vendor/composer/autoload_namespaces.php +0 -1
  77. vendor/composer/autoload_psr4.php +2 -0
  78. vendor/composer/autoload_real.php +14 -5
  79. vendor/composer/installed.json +26 -24
  80. vendor/composer/installers/.travis.yml +1 -1
  81. vendor/composer/installers/README.md +21 -4
  82. vendor/composer/installers/composer.json +8 -3
  83. vendor/composer/installers/src/Composer/Installers/BaseInstaller.php +4 -3
  84. vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php +59 -25
  85. vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php +2 -0
  86. vendor/composer/installers/src/Composer/Installers/ImageCMSInstaller.php +11 -0
  87. vendor/composer/installers/src/Composer/Installers/Installer.php +2 -0
  88. vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php +2 -0
  89. vendor/composer/installers/src/Composer/Installers/MauticInstaller.php +25 -0
  90. vendor/composer/installers/src/Composer/Installers/OxidInstaller.php +48 -0
  91. vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php +2 -0
  92. vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php +27 -0
  93. vendor/twig/twig/CHANGELOG +18 -1
  94. vendor/twig/twig/LICENSE +1 -1
  95. vendor/twig/twig/composer.json +1 -1
  96. vendor/twig/twig/doc/advanced.rst +1 -1
  97. vendor/twig/twig/doc/deprecated.rst +5 -3
  98. vendor/twig/twig/doc/templates.rst +8 -1
  99. vendor/twig/twig/ext/twig/php_twig.h +1 -1
  100. vendor/twig/twig/lib/Twig/Autoloader.php +3 -3
  101. vendor/twig/twig/lib/Twig/Cache/Filesystem.php +2 -2
  102. vendor/twig/twig/lib/Twig/Environment.php +23 -23
  103. vendor/twig/twig/lib/Twig/ExpressionParser.php +7 -1
  104. vendor/twig/twig/lib/Twig/Extension/Core.php +20 -16
  105. vendor/twig/twig/lib/Twig/Extension/Escaper.php +1 -1
  106. vendor/twig/twig/lib/Twig/ExtensionInterface.php +1 -1
  107. vendor/twig/twig/lib/Twig/Lexer.php +1 -1
  108. vendor/twig/twig/lib/Twig/Node.php +1 -1
  109. vendor/twig/twig/lib/Twig/Node/Expression/Call.php +51 -45
  110. vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php +4 -0
  111. vendor/twig/twig/lib/Twig/Node/Expression/Name.php +0 -8
  112. vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php +23 -0
  113. vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php +4 -2
  114. vendor/twig/twig/lib/Twig/Parser.php +6 -1
  115. vendor/twig/twig/lib/Twig/SimpleFilter.php +5 -0
  116. vendor/twig/twig/lib/Twig/SimpleFunction.php +5 -0
  117. vendor/twig/twig/lib/Twig/SimpleTest.php +5 -0
  118. vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php +1 -1
  119. vendor/twig/twig/test/Twig/Tests/Cache/FilesystemTest.php +42 -18
  120. vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php +6 -5
  121. vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php +1 -1
  122. vendor/twig/twig/test/Twig/Tests/FileCachingTest.php +7 -29
  123. vendor/twig/twig/test/Twig/Tests/FilesystemHelper.php +30 -0
  124. vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test +4 -1
  125. vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test +21 -0
  126. vendor/twig/twig/test/Twig/Tests/Fixtures/tests/null_coalesce.test +30 -0
  127. vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php +1 -1
  128. vendor/twig/twig/test/Twig/Tests/escapingTest.php +1 -1
README.md CHANGED
@@ -1,16 +1,18 @@
1
  <div style="text-align:center">
2
- <a href="http://jarednova.github.com/timber"><img src="http://i.imgur.com/oM1AHrz.jpg" style="display:block; margin:auto; width:100%; max-width:100%"/></a>
3
  <div>
4
- By Jared Novack (<a href="http://twitter.com/jarednova">@JaredNova</a>) and <a href="http://upstatement.com">Upstatement</a> (<a href="http://twitter.com/upstatement">@Upstatement</a>)</div>
5
  </div>
6
 
7
- [![Build Status](https://img.shields.io/travis/jarednova/timber/master.svg?style=flat-square)](https://travis-ci.org/jarednova/timber)
8
- [![Coverage Status](https://img.shields.io/coveralls/jarednova/timber.svg?style=flat-square)](https://coveralls.io/r/jarednova/timber?branch=master)
9
  [![Dependency Status](https://img.shields.io/versioneye/d/ruby/rails.svg?style=flat-square)](https://www.versioneye.com/user/projects/54e3c717d1ec5734f4000099)
10
- [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/jarednova/timber.svg?style=flat-square)](https://scrutinizer-ci.com/g/jarednova/timber/?branch=master)
11
- [![Latest Stable Version](https://img.shields.io/packagist/v/jarednova/timber.svg?style=flat-square)](https://packagist.org/packages/jarednova/timber)
12
  [![WordPress Download Count](https://img.shields.io/wordpress/plugin/dt/timber-library.svg?style=flat-square)](https://wordpress.org/plugins/timber-library/)
13
- [![HHVM Status](https://img.shields.io/hhvm/jarednova/timber.svg?style=flat-square)](http://hhvm.h4cc.de/package/jarednova/timber)
 
 
14
 
15
  ### Because WordPress is awesome, but the_loop isn't
16
  Timber helps you create fully-customized WordPress themes faster with more sustainable code. With Timber, you write your HTML using the [Twig Template Engine](http://twig.sensiolabs.org/) separate from your PHP files.
@@ -33,23 +35,28 @@ This is what Timber's `.twig` files look like:
33
  Once Timber is installed and activated in your plugin directory, it gives any WordPress theme the ability to take advantage of the power of Twig and other Timber features.
34
 
35
  ### Looking for docs?
36
- * [Timber Documentation](https://github.com/jarednova/timber/wiki/)
37
  * [Twig Reference](http://twig.sensiolabs.org/doc/templates.html)
38
- * [Video Tutorials](https://github.com/jarednova/timber/wiki/Video-Tutorials)
39
- * [Overview / Getting Started Guide](https://github.com/jarednova/timber/wiki/getting-started)
40
 
41
  * * *
42
 
43
  ### Installation
44
 
45
- The GitHub version of Timber requires [Composer](https://getcomposer.org/download/). If you'd prefer one-click installation, you should use the [WordPress.org](http://wordpress.org/plugins/timber-library/) version.
46
 
47
  ```shell
48
- composer create-project --no-dev jarednova/timber ~/MYSITE/wp-content/plugins/timber
49
  ```
50
 
51
- Once this is complete, activate Timber your WordPress admin. If you're looking for a 'blank' theme to start developing with, download the [timber-starter-theme](https://github.com/upstatement/timber-starter-theme) into your themes directory.
 
 
 
 
52
 
 
53
  * * *
54
 
55
  ### Mission Statement
@@ -65,24 +72,32 @@ Nothing. Timber is meant for you to build a theme on. Like the [Starkers](https:
65
  Timber is great for any WordPress developer who cares about writing good, maintainable code. It helps teams of designers and developers working together. At [Upstatement](http://upstatement.com) we made Timber because while our entire team needs to participate in building WordPress sites, not everyone knows the ins-and-outs of the_loop(), codex and PHP (nor should they). With Timber your best WordPress dev can focus on building the .php files with requests from WordPress and pass the data into .twig files. Once there, designers can easily mark-up data and build out a site's look-and-feel.
66
 
67
  #### Related Projects
68
- * [**Timber Starter Theme**](https://github.com/upstatement/timber-starter-theme) The "_s" of Timber to give you an easy start to the most basic theme you can build upon and customize.
69
- * [**Timber Debug Bar**](https://github.com/upstatement/debug-bar-timber) Adds a debug bar panel that will show you which template is in-use and the data sent to your twig file.
70
  * [**TimberPhoton**](https://github.com/slimndap/TimberPhoton) Plug-in to use JetPack's free Photon image manipulation and CDN with Timber.
71
- * [**Timber Sugar**](https://github.com/Upstatement/timber-sugar) A catch-all for goodies to use w Timber.
 
 
72
  * [**Twig**](https://github.com/fabpot/Twig) The template language used by Timber.
73
 
74
  #### Projects that use Timber
75
  * [**Gantry5**](https://wordpress.org/plugins/gantry5/) a framework for theme development
76
 
77
  #### Helpful Links
78
- * [**CSS Tricks**](https://css-tricks.com/timber-and-twig-reignited-my-love-for-wordpress/) introduction to Timber
79
- * [**Twig for Timber Cheatsheet**](http://notlaura.com/the-twig-for-timber-cheatsheet/) by @laras126
 
 
 
 
80
 
81
  #### Should I use it?
82
- It's MIT-licensed, so please use in personal or commercial work. Just don't re-sell it. While Timber is still in development, it's also in-use on [hundreds of sites](http://jarednova.github.io/timber/#showcase). While much has been stabilized since the first major push back in June 2013, you should expect some breaking changes as development progresses towards a version 1.0.
83
 
84
  #### Contributing
85
- Read the [contributor guidelines](https://github.com/jarednova/timber/wiki#contributing) in the wiki.
 
 
86
 
87
  ## How To...
88
 
1
  <div style="text-align:center">
2
+ <a href="http://timber.github.io/timber"><img src="http://i.imgur.com/oM1AHrz.jpg" style="display:block; margin:auto; width:100%; max-width:100%"/></a>
3
  <div>
4
+ By Jared Novack (<a href="https://twitter.com/jarednova">@JaredNova</a>) and <a href="http://upstatement.com">Upstatement</a> (<a href="https://twitter.com/upstatement">@Upstatement</a>)</div>
5
  </div>
6
 
7
+ [![Build Status](https://img.shields.io/travis/timber/timber/master.svg?style=flat-square)](https://travis-ci.org/timber/timber)
8
+ [![Coverage Status](https://img.shields.io/coveralls/jarednova/timber.svg?style=flat-square)](https://coveralls.io/r/timber/timber?branch=master)
9
  [![Dependency Status](https://img.shields.io/versioneye/d/ruby/rails.svg?style=flat-square)](https://www.versioneye.com/user/projects/54e3c717d1ec5734f4000099)
10
+ [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/timber/timber.svg?style=flat-square)](https://scrutinizer-ci.com/g/timber/timber/?branch=master)
11
+ [![Latest Stable Version](https://img.shields.io/packagist/v/timber/timber.svg?style=flat-square)](https://packagist.org/packages/timber/timber)
12
  [![WordPress Download Count](https://img.shields.io/wordpress/plugin/dt/timber-library.svg?style=flat-square)](https://wordpress.org/plugins/timber-library/)
13
+ [![HHVM Status](https://img.shields.io/hhvm/timber/timber.svg?style=flat-square)](http://hhvm.h4cc.de/package/timber/timber)
14
+ [![Join the chat at https://gitter.im/timber/timber](https://img.shields.io/gitter/room/timber/timber.svg?style=flat-square)](https://gitter.im/timber/timber?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
15
+
16
 
17
  ### Because WordPress is awesome, but the_loop isn't
18
  Timber helps you create fully-customized WordPress themes faster with more sustainable code. With Timber, you write your HTML using the [Twig Template Engine](http://twig.sensiolabs.org/) separate from your PHP files.
35
  Once Timber is installed and activated in your plugin directory, it gives any WordPress theme the ability to take advantage of the power of Twig and other Timber features.
36
 
37
  ### Looking for docs?
38
+ * [Timber Documentation](https://github.com/timber/timber/wiki)
39
  * [Twig Reference](http://twig.sensiolabs.org/doc/templates.html)
40
+ * [Video Tutorials](https://github.com/timber/timber/wiki/Video-Tutorials)
41
+ * [Overview / Getting Started Guide](https://github.com/timber/timber/wiki/getting-started)
42
 
43
  * * *
44
 
45
  ### Installation
46
 
47
+ The GitHub version of Timber requires [Composer](https://getcomposer.org/download/). If you'd prefer one-click installation, you should use the [WordPress.org](https://wordpress.org/plugins/timber-library/) version.
48
 
49
  ```shell
50
+ composer require timber/timber
51
  ```
52
 
53
+ If your theme is not setup to pull in Composer's autoload file, you will need to
54
+
55
+ ```php
56
+ require_once(__DIR__ . '/vendor/autoload.php');
57
+ ```
58
 
59
+ At the top of your `functions.php` file.
60
  * * *
61
 
62
  ### Mission Statement
72
  Timber is great for any WordPress developer who cares about writing good, maintainable code. It helps teams of designers and developers working together. At [Upstatement](http://upstatement.com) we made Timber because while our entire team needs to participate in building WordPress sites, not everyone knows the ins-and-outs of the_loop(), codex and PHP (nor should they). With Timber your best WordPress dev can focus on building the .php files with requests from WordPress and pass the data into .twig files. Once there, designers can easily mark-up data and build out a site's look-and-feel.
73
 
74
  #### Related Projects
75
+ * [**Timber Starter Theme**](https://github.com/timber/starter-theme) The "_s" of Timber to give you an easy start to the most basic theme you can build upon and customize.
76
+ * [**Timber Debug Bar**](https://github.com/timber/debug-bar-timber) Adds a debug bar panel that will show you which template is in-use and the data sent to your twig file.
77
  * [**TimberPhoton**](https://github.com/slimndap/TimberPhoton) Plug-in to use JetPack's free Photon image manipulation and CDN with Timber.
78
+ * [**Timber CLI**](https://github.com/nclud/wp-timber-cli) A CLI for Timber.
79
+ * [**Timber Sugar**](https://github.com/timber/sugar) A catch-all for goodies to use w Timber.
80
+ * [**Timmy**](https://github.com/MINDKomm/Timmy) Advanced image manipulation for Timber.
81
  * [**Twig**](https://github.com/fabpot/Twig) The template language used by Timber.
82
 
83
  #### Projects that use Timber
84
  * [**Gantry5**](https://wordpress.org/plugins/gantry5/) a framework for theme development
85
 
86
  #### Helpful Links
87
+ * [**CSS Tricks**](https://css-tricks.com/timber-and-twig-reignited-my-love-for-wordpress/) introduction to Timber by [@tjFogarty](https://github.com/tjFogarty)
88
+ * [**Twig for Timber Cheatsheet**](http://notlaura.com/the-twig-for-timber-cheatsheet/) by [@laras126](https://github.com/laras126)
89
+ * [**TutsPlus**](http://code.tutsplus.com/articles/kick-start-wordpress-development-with-twig-introduction--cms-24781) A guide to getting started by [@ahmadawais](https://github.com/ahmadawais)
90
+
91
+ #### Support
92
+ Please post on [StackOverflow under the "Timber" tag](http://stackoverflow.com/questions/tagged/timber). Please use GitHub issues only for specific bugs, feature requests and other types of issues.
93
 
94
  #### Should I use it?
95
+ It's MIT-licensed, so please use in personal or commercial work. Just don't re-sell it. While Timber is still in development, it's also in-use on [hundreds of sites](http://timber.github.io/timber/#showcase). While much has been stabilized since the first major push back in June 2013, you should expect some breaking changes as development progresses towards a version 1.0.
96
 
97
  #### Contributing
98
+ Read the [contributor guidelines](https://github.com/timber/timber/wiki#contributing) in the wiki.
99
+
100
+
101
 
102
  ## How To...
103
 
init.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use Timber\Timber;
4
+
5
+ $timber = new Timber();
6
+ Timber::$dirname = 'views';
lib/Admin.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ class Admin {
6
+
7
+ public static function init() {
8
+ $filter = add_filter('plugin_row_meta', array( __CLASS__, 'meta_links' ), 10, 2);
9
+ $action = add_action('in_plugin_update_message-timber-library/timber.php', array('Timber\Admin', 'in_plugin_update_message'), 10, 2);
10
+ $action = add_action('in_plugin_update_message-timber/timber.php', array('Timber\Admin', 'in_plugin_update_message'), 10, 2);
11
+ if ($filter && $action) {
12
+ return true;
13
+ }
14
+ }
15
+
16
+ /**
17
+ * @param array $links
18
+ * @param string $file
19
+ * @return array
20
+ */
21
+ public static function meta_links( $links, $file ) {
22
+ if ( strstr($file, '/timber.php') ) {
23
+ unset($links[2]);
24
+ $links[] = '<a href="/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>';
25
+ $links[] = '<a href="http://upstatement.com/timber" target="_blank">Homepage</a>';
26
+ $links[] = '<a href="https://github.com/timber/timber/wiki" target="_blank">Documentation</a>';
27
+ $links[] = '<a href="https://github.com/timber/timber/wiki/getting-started" target="_blank">Starter Guide</a>';
28
+ return $links;
29
+ }
30
+ return $links;
31
+ }
32
+
33
+ /**
34
+ * in_plugin_update_message
35
+ *
36
+ * Displays an update message for plugin list screens.
37
+ * Shows only the version updates from the current until the newest version
38
+ *
39
+ * @type function
40
+ * @date 4/22/16
41
+ *
42
+ * @param {array} $plugin_data
43
+ * @param {object} $r
44
+ */
45
+ function in_plugin_update_message( $plugin_data, $r ) {
46
+
47
+
48
+ //print_r($r);
49
+ // vars
50
+ if ( version_compare("1.0", $plugin_data->Version) > 0 ) {
51
+ $m = '<p><b>Warning:</b> Timber 1.0 removed a number of features and methods. Before upgrading please test your theme on a local or staging site to ensure that your theme will work with the newest version.</p>
52
+
53
+ <p><strong>Is your theme in active development?</strong> That is, is someone actively in PHP files writing new code? If you answered "no", then <i>do not upgrade</i>. You will not benefit from Timber 1.0</p>';
54
+
55
+ $m .= '<p>Read the <strong><a href="https://github.com/timber/timber/wiki/1.0-Upgrade-Guide">Upgrade Guide</a></strong> for more information</p>';
56
+
57
+ if ( version_compare("0.22.6", $plugin_data->Version) > 0 ) {
58
+ $m .= "<p>You can also <b><a href='https://downloads.wordpress.org/plugin/timber-library.0.22.6.zip'>upgrade to version 0.22.6</a></b> if you want to upgrade, but are unsure if you're ready for 1.0";
59
+ }
60
+ }
61
+
62
+
63
+
64
+
65
+ // show message
66
+ echo '<br />'.sprintf($m, admin_url('edit.php?post_type=acf-field-group&page=acf-settings-updates'), 'http://www.advancedcustomfields.com/pro');
67
+
68
+ }
69
+
70
+ }
lib/{timber-archives.php → Archives.php} RENAMED
@@ -1,6 +1,13 @@
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * The TimberArchives class is used to generate a menu based on the date archives of your posts. The [Nieman Foundation News site](http://nieman.harvard.edu/news/) has an example of how the output can be used in a real site ([screenshot](https://cloud.githubusercontent.com/assets/1298086/9610076/3cdca596-50a5-11e5-82fd-acb74c09c482.png)).
 
4
  * @example
5
  * ```php
6
  * $context['archives'] = new TimberArchives( $args );
@@ -30,8 +37,8 @@
30
  * </ul>
31
  * ```
32
  */
33
- class TimberArchives extends TimberCore {
34
-
35
  public $base = '';
36
  /**
37
  * @api
@@ -55,6 +62,8 @@ class TimberArchives extends TimberCore {
55
  * @param string $base any additional paths that need to be prepended to the URLs that are generated, for example: "tags"
56
  */
57
  function __construct( $args = null, $base = '' ) {
 
 
58
  $this->init($args, $base);
59
  }
60
 
@@ -77,7 +86,7 @@ class TimberArchives extends TimberCore {
77
  protected function get_archives_link( $url, $text ) {
78
  $ret = array();
79
  $ret['text'] = $ret['title'] = $ret['name'] = wptexturize($text);
80
- $ret['url'] = $ret['link'] = esc_url(TimberURLHelper::prepend_to_url($url, $this->base));
81
  return $ret;
82
  }
83
 
@@ -97,13 +106,13 @@ class TimberArchives extends TimberCore {
97
  $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";
98
  $key = md5($query);
99
  $key = "wp_get_archives:$key:$last_changed";
100
- if (!$results = wp_cache_get($key, 'posts')) {
101
  $results = $wpdb->get_results($query);
102
  wp_cache_set($key, $results, 'posts');
103
  }
104
- if ($results) {
105
- foreach ( (array)$results as $result ) {
106
- $url = get_year_link( $result->year );
107
  $text = sprintf('%d', $result->year);
108
  $output[] = $this->get_archives_link($url, $text);
109
  }
@@ -137,28 +146,28 @@ class TimberArchives extends TimberCore {
137
  . "ORDER BY post_date $order $limit";
138
  $key = md5($query);
139
  $key = "wp_get_archives:$key:$last_changed";
140
- if (!$results = wp_cache_get($key, 'posts')) {
141
  $results = $wpdb->get_results($query);
142
  wp_cache_set($key, $results, 'posts');
143
  }
144
- if ($results) {
145
- foreach ((array)$results as $result) {
146
  $url = get_month_link($result->year, $result->month);
147
- if ($show_year && !$nested) {
148
  $text = sprintf(__('%1$s %2$d'), $wp_locale->get_month($result->month), $result->year);
149
  } else {
150
  $text = sprintf(__('%1$s'), $wp_locale->get_month($result->month));
151
  }
152
- if ($nested) {
153
  $output[$result->year][] = $this->get_archives_link($url, $text);
154
  } else {
155
  $output[] = $this->get_archives_link($url, $text);
156
  }
157
  }
158
  }
159
- if ($nested) {
160
  $out2 = array();
161
- foreach ($output as $year => $months) {
162
  $out2[] = array('name' => $year, 'children' => $months);
163
  }
164
  return $out2;
@@ -199,7 +208,7 @@ class TimberArchives extends TimberCore {
199
 
200
  if ( !empty($args['limit']) ) {
201
  $limit = absint($limit);
202
- $limit = ' LIMIT ' . $limit;
203
  }
204
 
205
  $order = strtoupper($order);
@@ -220,7 +229,7 @@ class TimberArchives extends TimberCore {
220
  $archive_week_start_date_format = 'Y/m/d';
221
  $archive_week_end_date_format = 'Y/m/d';
222
 
223
- if (!$archive_date_format_over_ride) {
224
  $archive_day_date_format = get_option('date_format');
225
  $archive_week_start_date_format = get_option('date_format');
226
  $archive_week_end_date_format = get_option('date_format');
@@ -232,7 +241,7 @@ class TimberArchives extends TimberCore {
232
 
233
  $output = array();
234
  $last_changed = wp_cache_get('last_changed', 'posts');
235
- if (!$last_changed) {
236
  $last_changed = microtime();
237
  wp_cache_set('last_changed', $last_changed, 'posts');
238
  }
@@ -241,24 +250,19 @@ class TimberArchives extends TimberCore {
241
  } elseif ( 'yearly' == $type ) {
242
  $output = $this->get_items_yearly($args, $last_changed, $join, $where, $order, $limit);
243
  } elseif ( 'monthly-nested' == $type ) {
244
- $years = $this->get_items_yearly($args, $last_changed, $join, $where, $order, $limit);
245
- foreach ( $years as &$year ) {
246
- $args = array('show_year' => false);
247
- $year['children'] = $this->get_items_monthly($args, $last_changed, $join, $where, $order, $limit);
248
- }
249
- $output = $years;
250
  } elseif ( 'daily' == $type ) {
251
  $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";
252
  $key = md5($query);
253
  $key = "wp_get_archives:$key:$last_changed";
254
- if (!$results = wp_cache_get($key, 'posts')) {
255
  $results = $wpdb->get_results($query);
256
  $cache = array();
257
  $cache[$key] = $results;
258
  wp_cache_set($key, $results, 'posts');
259
  }
260
  if ( $results ) {
261
- foreach ( (array)$results as $result ) {
262
  $url = get_day_link($result->year, $result->month, $result->dayofmonth);
263
  $date = sprintf('%1$d-%2$02d-%3$02d 00:00:00', $result->year, $result->month, $result->dayofmonth);
264
  $text = mysql2date($archive_day_date_format, $date);
@@ -271,13 +275,13 @@ class TimberArchives extends TimberCore {
271
  . "count( `ID` ) AS `posts` FROM `$wpdb->posts` $join $where GROUP BY $week, YEAR( `post_date` ) ORDER BY `post_date` $order $limit";
272
  $key = md5($query);
273
  $key = "wp_get_archives:$key:$last_changed";
274
- if (!$results = wp_cache_get($key, 'posts')) {
275
  $results = $wpdb->get_results($query);
276
  wp_cache_set($key, $results, 'posts');
277
  }
278
  $arc_w_last = '';
279
  if ( $results ) {
280
- foreach ( (array)$results as $result ) {
281
  if ( $result->week != $arc_w_last ) {
282
  $arc_year = $result->yr;
283
  $arc_w_last = $result->week;
@@ -285,7 +289,7 @@ class TimberArchives extends TimberCore {
285
  $arc_week_start = date_i18n($archive_week_start_date_format, $arc_week['start']);
286
  $arc_week_end = date_i18n($archive_week_end_date_format, $arc_week['end']);
287
  $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);
288
- $text = $arc_week_start . $archive_week_separator . $arc_week_end;
289
  $output[] = $this->get_archives_link($url, $text);
290
  }
291
  }
@@ -300,10 +304,10 @@ class TimberArchives extends TimberCore {
300
  wp_cache_set($key, $results, 'posts');
301
  }
302
  if ( $results ) {
303
- foreach ( (array)$results as $result ) {
304
- if ($result->post_date != '0000-00-00 00:00:00') {
305
  $url = get_permalink($result);
306
- if ($result->post_title) {
307
  /** This filter is documented in wp-includes/post-template.php */
308
  $text = strip_tags(apply_filters('the_title', $result->post_title, $result->ID));
309
  } else {
1
  <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\Core;
6
+ use Timber\URLHelper;
7
+
8
  /**
9
  * The TimberArchives class is used to generate a menu based on the date archives of your posts. The [Nieman Foundation News site](http://nieman.harvard.edu/news/) has an example of how the output can be used in a real site ([screenshot](https://cloud.githubusercontent.com/assets/1298086/9610076/3cdca596-50a5-11e5-82fd-acb74c09c482.png)).
10
+ *
11
  * @example
12
  * ```php
13
  * $context['archives'] = new TimberArchives( $args );
37
  * </ul>
38
  * ```
39
  */
40
+ class Archives extends Core {
41
+
42
  public $base = '';
43
  /**
44
  * @api
62
  * @param string $base any additional paths that need to be prepended to the URLs that are generated, for example: "tags"
63
  */
64
  function __construct( $args = null, $base = '' ) {
65
+
66
+
67
  $this->init($args, $base);
68
  }
69
 
86
  protected function get_archives_link( $url, $text ) {
87
  $ret = array();
88
  $ret['text'] = $ret['title'] = $ret['name'] = wptexturize($text);
89
+ $ret['url'] = $ret['link'] = esc_url(URLHelper::prepend_to_url($url, $this->base));
90
  return $ret;
91
  }
92
 
106
  $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";
107
  $key = md5($query);
108
  $key = "wp_get_archives:$key:$last_changed";
109
+ if ( !$results = wp_cache_get($key, 'posts') ) {
110
  $results = $wpdb->get_results($query);
111
  wp_cache_set($key, $results, 'posts');
112
  }
113
+ if ( $results ) {
114
+ foreach ( (array) $results as $result ) {
115
+ $url = get_year_link($result->year);
116
  $text = sprintf('%d', $result->year);
117
  $output[] = $this->get_archives_link($url, $text);
118
  }
146
  . "ORDER BY post_date $order $limit";
147
  $key = md5($query);
148
  $key = "wp_get_archives:$key:$last_changed";
149
+ if ( !$results = wp_cache_get($key, 'posts') ) {
150
  $results = $wpdb->get_results($query);
151
  wp_cache_set($key, $results, 'posts');
152
  }
153
+ if ( $results ) {
154
+ foreach ( (array) $results as $result ) {
155
  $url = get_month_link($result->year, $result->month);
156
+ if ( $show_year && !$nested ) {
157
  $text = sprintf(__('%1$s %2$d'), $wp_locale->get_month($result->month), $result->year);
158
  } else {
159
  $text = sprintf(__('%1$s'), $wp_locale->get_month($result->month));
160
  }
161
+ if ( $nested ) {
162
  $output[$result->year][] = $this->get_archives_link($url, $text);
163
  } else {
164
  $output[] = $this->get_archives_link($url, $text);
165
  }
166
  }
167
  }
168
+ if ( $nested ) {
169
  $out2 = array();
170
+ foreach ( $output as $year => $months ) {
171
  $out2[] = array('name' => $year, 'children' => $months);
172
  }
173
  return $out2;
208
 
209
  if ( !empty($args['limit']) ) {
210
  $limit = absint($limit);
211
+ $limit = ' LIMIT '.$limit;
212
  }
213
 
214
  $order = strtoupper($order);
229
  $archive_week_start_date_format = 'Y/m/d';
230
  $archive_week_end_date_format = 'Y/m/d';
231
 
232
+ if ( !$archive_date_format_over_ride ) {
233
  $archive_day_date_format = get_option('date_format');
234
  $archive_week_start_date_format = get_option('date_format');
235
  $archive_week_end_date_format = get_option('date_format');
241
 
242
  $output = array();
243
  $last_changed = wp_cache_get('last_changed', 'posts');
244
+ if ( !$last_changed ) {
245
  $last_changed = microtime();
246
  wp_cache_set('last_changed', $last_changed, 'posts');
247
  }
250
  } elseif ( 'yearly' == $type ) {
251
  $output = $this->get_items_yearly($args, $last_changed, $join, $where, $order, $limit);
252
  } elseif ( 'monthly-nested' == $type ) {
253
+ $output = $this->get_items_monthly($args, $last_changed, $join, $where, $order, $limit);
 
 
 
 
 
254
  } elseif ( 'daily' == $type ) {
255
  $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";
256
  $key = md5($query);
257
  $key = "wp_get_archives:$key:$last_changed";
258
+ if ( !$results = wp_cache_get($key, 'posts') ) {
259
  $results = $wpdb->get_results($query);
260
  $cache = array();
261
  $cache[$key] = $results;
262
  wp_cache_set($key, $results, 'posts');
263
  }
264
  if ( $results ) {
265
+ foreach ( (array) $results as $result ) {
266
  $url = get_day_link($result->year, $result->month, $result->dayofmonth);
267
  $date = sprintf('%1$d-%2$02d-%3$02d 00:00:00', $result->year, $result->month, $result->dayofmonth);
268
  $text = mysql2date($archive_day_date_format, $date);
275
  . "count( `ID` ) AS `posts` FROM `$wpdb->posts` $join $where GROUP BY $week, YEAR( `post_date` ) ORDER BY `post_date` $order $limit";
276
  $key = md5($query);
277
  $key = "wp_get_archives:$key:$last_changed";
278
+ if ( !$results = wp_cache_get($key, 'posts') ) {
279
  $results = $wpdb->get_results($query);
280
  wp_cache_set($key, $results, 'posts');
281
  }
282
  $arc_w_last = '';
283
  if ( $results ) {
284
+ foreach ( (array) $results as $result ) {
285
  if ( $result->week != $arc_w_last ) {
286
  $arc_year = $result->yr;
287
  $arc_w_last = $result->week;
289
  $arc_week_start = date_i18n($archive_week_start_date_format, $arc_week['start']);
290
  $arc_week_end = date_i18n($archive_week_end_date_format, $arc_week['end']);
291
  $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);
292
+ $text = $arc_week_start.$archive_week_separator.$arc_week_end;
293
  $output[] = $this->get_archives_link($url, $text);
294
  }
295
  }
304
  wp_cache_set($key, $results, 'posts');
305
  }
306
  if ( $results ) {
307
+ foreach ( (array) $results as $result ) {
308
+ if ( $result->post_date != '0000-00-00 00:00:00' ) {
309
  $url = get_permalink($result);
310
+ if ( $result->post_title ) {
311
  /** This filter is documented in wp-includes/post-template.php */
312
  $text = strip_tags(apply_filters('the_title', $result->post_title, $result->ID));
313
  } else {
lib/{cache → Cache}/KeyGenerator.php RENAMED
File without changes
lib/{cache → Cache}/TimberKeyGeneratorInterface.php RENAMED
File without changes
lib/{cache → Cache}/WPObjectCacheAdapter.php RENAMED
@@ -1,7 +1,7 @@
1
  <?php namespace Timber\Cache;
2
 
3
  use Asm89\Twig\CacheExtension\CacheProviderInterface;
4
- use TimberLoader;
5
 
6
  class WPObjectCacheAdapter implements CacheProviderInterface {
7
 
@@ -12,17 +12,17 @@ class WPObjectCacheAdapter implements CacheProviderInterface {
12
  */
13
  private $timberloader;
14
 
15
- public function __construct(TimberLoader $timberloader, $cache_group = 'timber') {
16
  $this->cache_group = $cache_group;
17
  $this->timberloader = $timberloader;
18
  }
19
 
20
- public function fetch($key) {
21
- return $this->timberloader->get_cache($key, $this->cache_group, TimberLoader::CACHE_USE_DEFAULT);
22
  }
23
 
24
- public function save($key, $value, $expire = 0) {
25
- return $this->timberloader->set_cache($key, $value, $this->cache_group, $expire, TimberLoader::CACHE_USE_DEFAULT);
26
  }
27
 
28
  }
1
  <?php namespace Timber\Cache;
2
 
3
  use Asm89\Twig\CacheExtension\CacheProviderInterface;
4
+ use Timber\Loader;
5
 
6
  class WPObjectCacheAdapter implements CacheProviderInterface {
7
 
12
  */
13
  private $timberloader;
14
 
15
+ public function __construct( Loader $timberloader, $cache_group = 'timber' ) {
16
  $this->cache_group = $cache_group;
17
  $this->timberloader = $timberloader;
18
  }
19
 
20
+ public function fetch( $key ) {
21
+ return $this->timberloader->get_cache($key, $this->cache_group, Loader::CACHE_USE_DEFAULT);
22
  }
23
 
24
+ public function save( $key, $value, $expire = 0 ) {
25
+ return $this->timberloader->set_cache($key, $value, $this->cache_group, $expire, Loader::CACHE_USE_DEFAULT);
26
  }
27
 
28
  }
lib/{timber-comment.php → Comment.php} RENAMED
@@ -1,5 +1,11 @@
1
  <?php
2
 
 
 
 
 
 
 
3
  /**
4
  * The TimberComment class is used to view the output of comments. 99% of the time this will be in the context of the comments on a post. However you can also fetch a comment directly using its comment ID.
5
  * @example
@@ -19,9 +25,9 @@
19
  * <p class="comment-attribution">- Sullivan Ballou</p>
20
  * ```
21
  */
22
- class TimberComment extends TimberCore implements TimberCoreInterface {
23
 
24
- public $PostClass = 'TimberPost';
25
  public $object_type = 'comment';
26
 
27
  public static $representation = 'comment';
@@ -40,7 +46,7 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
40
  /**
41
  * @param int $cid
42
  */
43
- function __construct($cid) {
44
  $this->init($cid);
45
  }
46
 
@@ -52,9 +58,9 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
52
  * @internal
53
  * @param integer $cid
54
  */
55
- function init($cid) {
56
  $comment_data = $cid;
57
- if (is_integer($cid)) {
58
  $comment_data = get_comment($cid);
59
  }
60
  $this->import($comment_data);
@@ -83,14 +89,14 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
83
  * <li>Rebecca Pearl, who is a author</li>
84
  * </ol>
85
  * ```
86
- * @return TimberUser
87
  */
88
  public function author() {
89
- if ($this->user_id) {
90
- return new TimberUser($this->user_id);
91
  } else {
92
- $author = new TimberUser(0);
93
- if (isset($this->comment_author) && $this->comment_author) {
94
  $author->name = $this->comment_author;
95
  } else {
96
  $author->name = 'Anonymous';
@@ -113,22 +119,22 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
113
  * @param string $default
114
  * @return bool|mixed|string
115
  */
116
- public function avatar($size = 92, $default = '') {
117
- if (!get_option('show_avatars')) {
118
  return false;
119
  }
120
- if (!is_numeric($size)) {
121
  $size = '92';
122
  }
123
 
124
  $email = $this->avatar_email();
125
  $email_hash = '';
126
- if (!empty($email)) {
127
  $email_hash = md5(strtolower(trim($email)));
128
  }
129
  $host = $this->avatar_host($email_hash);
130
  $default = $this->avatar_default($default, $email, $size, $host);
131
- if (!empty($email)) {
132
  $avatar = $this->avatar_out($default, $host, $email_hash, $size);
133
  } else {
134
  $avatar = $default;
@@ -181,7 +187,7 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
181
  */
182
  public function date( $date_format = '' ) {
183
  $df = $date_format ? $date_format : get_option('date_format');
184
- $the_date = (string)mysql2date($df, $this->comment_date);
185
  return apply_filters('get_comment_date ', $the_date, $df);
186
  }
187
 
@@ -206,7 +212,7 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
206
  */
207
  public function time( $time_format = '' ) {
208
  $tf = $time_format ? $time_format : get_option('time_format');
209
- $the_time = (string)mysql2date($tf, $this->comment_date);
210
  return apply_filters('get_comment_time', $the_time, $tf);
211
  }
212
 
@@ -214,7 +220,7 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
214
  * @param string $field_name
215
  * @return mixed
216
  */
217
- public function meta($field_name) {
218
  return $this->get_meta_field($field_name);
219
  }
220
 
@@ -231,15 +237,15 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
231
  * @param int $comment_id
232
  * @return mixed
233
  */
234
- protected function get_meta_fields($comment_id = null) {
235
- if ($comment_id === null) {
236
  $comment_id = $this->ID;
237
  }
238
  //Could not find a WP function to fetch all comment meta data, so I made one.
239
  apply_filters('timber_comment_get_meta_pre', array(), $comment_id);
240
  $comment_metas = get_comment_meta($comment_id);
241
- foreach ($comment_metas as &$cm) {
242
- if (is_array($cm) && count($cm) == 1) {
243
  $cm = $cm[0];
244
  }
245
  }
@@ -248,13 +254,14 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
248
  }
249
 
250
  /**
 
251
  * @internal
252
  * @param string $field_name
253
  * @return mixed
254
  */
255
- protected function get_meta_field($field_name) {
256
  $value = apply_filters('timber_comment_get_meta_field_pre', null, $this->ID, $field_name, $this);
257
- if ($value === null) {
258
  $value = get_comment_meta($this->ID, $field_name, true);
259
  }
260
  $value = apply_filters('timber_comment_get_meta_field', $value, $this->ID, $field_name, $this);
@@ -269,7 +276,7 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
269
  */
270
  public function reply_link( $reply_text = 'Reply' ) {
271
  if ( is_singular() && comments_open() && get_option('thread_comments') ) {
272
- wp_enqueue_script( 'comment-reply' );
273
  }
274
 
275
  // Get the comments depth option from the admin panel
@@ -284,7 +291,7 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
284
  'max_depth' => $max_depth,
285
  );
286
 
287
- return get_comment_reply_link( $args, $this->ID, $this->post_id );
288
  }
289
 
290
  /* AVATAR Stuff
@@ -295,9 +302,9 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
295
  * @return string
296
  */
297
  protected function avatar_email() {
298
- $id = (int)$this->user_id;
299
  $user = get_userdata($id);
300
- if ($user) {
301
  $email = $user->user_email;
302
  } else {
303
  $email = $this->comment_author_email;
@@ -310,11 +317,11 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
310
  * @param string $email_hash
311
  * @return string
312
  */
313
- protected function avatar_host($email_hash) {
314
- if (is_ssl()) {
315
  $host = 'https://secure.gravatar.com';
316
  } else {
317
- if (!empty($email_hash)) {
318
  $host = sprintf("http://%d.gravatar.com", (hexdec($email_hash[0]) % 2));
319
  } else {
320
  $host = 'http://0.gravatar.com';
@@ -332,30 +339,30 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
332
  * @param string $host
333
  * @return string
334
  */
335
- protected function avatar_default($default, $email, $size, $host) {
336
- if (substr($default, 0, 1) == '/') {
337
- $default = home_url() . $default;
338
  }
339
 
340
- if (empty($default)) {
341
  $avatar_default = get_option('avatar_default');
342
- if (empty($avatar_default)) {
343
  $default = 'mystery';
344
  } else {
345
  $default = $avatar_default;
346
  }
347
  }
348
- if ('mystery' == $default) {
349
- $default = $host . '/avatar/ad516503a11cd5ca435acc9bb6523536?s=' . $size;
350
  // ad516503a11cd5ca435acc9bb6523536 == md5('unknown@gravatar.com')
351
- } else if ('blank' == $default) {
352
  $default = $email ? 'blank' : includes_url('images/blank.gif');
353
- } else if (!empty($email) && 'gravatar_default' == $default) {
354
  $default = '';
355
- } else if ('gravatar_default' == $default) {
356
- $default = $host . '/avatar/?s=' . $size;
357
- } else if (empty($email) && !strstr($default, 'http://')) {
358
- $default = $host . '/avatar/?d=' . $default . '&amp;s=' . $size;
359
  }
360
  return $default;
361
  }
@@ -368,13 +375,13 @@ class TimberComment extends TimberCore implements TimberCoreInterface {
368
  * @param string $size
369
  * @return mixed
370
  */
371
- protected function avatar_out($default, $host, $email_hash, $size) {
372
- $out = $host . '/avatar/' . $email_hash . '?s=' . $size . '&amp;d=' . urlencode($default);
373
  $rating = get_option('avatar_rating');
374
- if (!empty($rating)) {
375
- $out .= '&amp;r=' . $rating;
376
  }
377
  return str_replace('&#038;', '&amp;', esc_url($out));
378
  }
379
 
380
- }
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\User;
6
+ use Timber\Core;
7
+ use Timber\CoreInterface;
8
+
9
  /**
10
  * The TimberComment class is used to view the output of comments. 99% of the time this will be in the context of the comments on a post. However you can also fetch a comment directly using its comment ID.
11
  * @example
25
  * <p class="comment-attribution">- Sullivan Ballou</p>
26
  * ```
27
  */
28
+ class Comment extends Core implements CoreInterface {
29
 
30
+ public $PostClass = 'Post';
31
  public $object_type = 'comment';
32
 
33
  public static $representation = 'comment';
46
  /**
47
  * @param int $cid
48
  */
49
+ function __construct( $cid ) {
50
  $this->init($cid);
51
  }
52
 
58
  * @internal
59
  * @param integer $cid
60
  */
61
+ function init( $cid ) {
62
  $comment_data = $cid;
63
+ if ( is_integer($cid) ) {
64
  $comment_data = get_comment($cid);
65
  }
66
  $this->import($comment_data);
89
  * <li>Rebecca Pearl, who is a author</li>
90
  * </ol>
91
  * ```
92
+ * @return User
93
  */
94
  public function author() {
95
+ if ( $this->user_id ) {
96
+ return new User($this->user_id);
97
  } else {
98
+ $author = new User(0);
99
+ if ( isset($this->comment_author) && $this->comment_author ) {
100
  $author->name = $this->comment_author;
101
  } else {
102
  $author->name = 'Anonymous';
119
  * @param string $default
120
  * @return bool|mixed|string
121
  */
122
+ public function avatar( $size = 92, $default = '' ) {
123
+ if ( !get_option('show_avatars') ) {
124
  return false;
125
  }
126
+ if ( !is_numeric($size) ) {
127
  $size = '92';
128
  }
129
 
130
  $email = $this->avatar_email();
131
  $email_hash = '';
132
+ if ( !empty($email) ) {
133
  $email_hash = md5(strtolower(trim($email)));
134
  }
135
  $host = $this->avatar_host($email_hash);
136
  $default = $this->avatar_default($default, $email, $size, $host);
137
+ if ( !empty($email) ) {
138
  $avatar = $this->avatar_out($default, $host, $email_hash, $size);
139
  } else {
140
  $avatar = $default;
187
  */
188
  public function date( $date_format = '' ) {
189
  $df = $date_format ? $date_format : get_option('date_format');
190
+ $the_date = (string) mysql2date($df, $this->comment_date);
191
  return apply_filters('get_comment_date ', $the_date, $df);
192
  }
193
 
212
  */
213
  public function time( $time_format = '' ) {
214
  $tf = $time_format ? $time_format : get_option('time_format');
215
+ $the_time = (string) mysql2date($tf, $this->comment_date);
216
  return apply_filters('get_comment_time', $the_time, $tf);
217
  }
218
 
220
  * @param string $field_name
221
  * @return mixed
222
  */
223
+ public function meta( $field_name ) {
224
  return $this->get_meta_field($field_name);
225
  }
226
 
237
  * @param int $comment_id
238
  * @return mixed
239
  */
240
+ protected function get_meta_fields( $comment_id = null ) {
241
+ if ( $comment_id === null ) {
242
  $comment_id = $this->ID;
243
  }
244
  //Could not find a WP function to fetch all comment meta data, so I made one.
245
  apply_filters('timber_comment_get_meta_pre', array(), $comment_id);
246
  $comment_metas = get_comment_meta($comment_id);
247
+ foreach ( $comment_metas as &$cm ) {
248
+ if ( is_array($cm) && count($cm) == 1 ) {
249
  $cm = $cm[0];
250
  }
251
  }
254
  }
255
 
256
  /**
257
+ *
258
  * @internal
259
  * @param string $field_name
260
  * @return mixed
261
  */
262
+ protected function get_meta_field( $field_name ) {
263
  $value = apply_filters('timber_comment_get_meta_field_pre', null, $this->ID, $field_name, $this);
264
+ if ( $value === null ) {
265
  $value = get_comment_meta($this->ID, $field_name, true);
266
  }
267
  $value = apply_filters('timber_comment_get_meta_field', $value, $this->ID, $field_name, $this);
276
  */
277
  public function reply_link( $reply_text = 'Reply' ) {
278
  if ( is_singular() && comments_open() && get_option('thread_comments') ) {
279
+ wp_enqueue_script('comment-reply');
280
  }
281
 
282
  // Get the comments depth option from the admin panel
291
  'max_depth' => $max_depth,
292
  );
293
 
294
+ return get_comment_reply_link($args, $this->ID, $this->post_id);
295
  }
296
 
297
  /* AVATAR Stuff
302
  * @return string
303
  */
304
  protected function avatar_email() {
305
+ $id = (int) $this->user_id;
306
  $user = get_userdata($id);
307
+ if ( $user ) {
308
  $email = $user->user_email;
309
  } else {
310
  $email = $this->comment_author_email;
317
  * @param string $email_hash
318
  * @return string
319
  */
320
+ protected function avatar_host( $email_hash ) {
321
+ if ( is_ssl() ) {
322
  $host = 'https://secure.gravatar.com';
323
  } else {
324
+ if ( !empty($email_hash) ) {
325
  $host = sprintf("http://%d.gravatar.com", (hexdec($email_hash[0]) % 2));
326
  } else {
327
  $host = 'http://0.gravatar.com';
339
  * @param string $host
340
  * @return string
341
  */
342
+ protected function avatar_default( $default, $email, $size, $host ) {
343
+ if ( substr($default, 0, 1) == '/' ) {
344
+ $default = home_url().$default;
345
  }
346
 
347
+ if ( empty($default) ) {
348
  $avatar_default = get_option('avatar_default');
349
+ if ( empty($avatar_default) ) {
350
  $default = 'mystery';
351
  } else {
352
  $default = $avatar_default;
353
  }
354
  }
355
+ if ( 'mystery' == $default ) {
356
+ $default = $host.'/avatar/ad516503a11cd5ca435acc9bb6523536?s='.$size;
357
  // ad516503a11cd5ca435acc9bb6523536 == md5('unknown@gravatar.com')
358
+ } else if ( 'blank' == $default ) {
359
  $default = $email ? 'blank' : includes_url('images/blank.gif');
360
+ } else if ( !empty($email) && 'gravatar_default' == $default ) {
361
  $default = '';
362
+ } else if ( 'gravatar_default' == $default ) {
363
+ $default = $host.'/avatar/?s='.$size;
364
+ } else if ( empty($email) && !strstr($default, 'http://') ) {
365
+ $default = $host.'/avatar/?d='.$default.'&amp;s='.$size;
366
  }
367
  return $default;
368
  }
375
  * @param string $size
376
  * @return mixed
377
  */
378
+ protected function avatar_out( $default, $host, $email_hash, $size ) {
379
+ $out = $host.'/avatar/'.$email_hash.'?s='.$size.'&amp;d='.urlencode($default);
380
  $rating = get_option('avatar_rating');
381
+ if ( !empty($rating) ) {
382
+ $out .= '&amp;r='.$rating;
383
  }
384
  return str_replace('&#038;', '&amp;', esc_url($out));
385
  }
386
 
387
+ }
lib/Core.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ abstract class Core {
6
+
7
+ public $id;
8
+ public $ID;
9
+ public $object_type;
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 ( property_exists($this, $field) ) {
37
+ return $this->$field;
38
+ }
39
+ if ( method_exists($this, 'meta') && $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
+ * Takes an array or object and adds the properties to the parent object
50
+ * @example
51
+ * ```php
52
+ * $data = array('airplane' => '757-200', 'flight' => '5316');
53
+ * $post = new TimberPost()
54
+ * $post->import(data);
55
+ * echo $post->airplane; //757-200
56
+ * ```
57
+ * @param array|object $info an object or array you want to grab data from to attach to the Timber object
58
+ */
59
+ function import( $info, $force = false ) {
60
+ if ( is_object($info) ) {
61
+ $info = get_object_vars($info);
62
+ }
63
+ if ( is_array($info) ) {
64
+ foreach ( $info as $key => $value ) {
65
+ if ( !empty($key) && $force ) {
66
+ $this->$key = $value;
67
+ } else if ( !empty($key) && !method_exists($this, $key) ) {
68
+ $this->$key = $value;
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+
75
+ /**
76
+ * @ignore
77
+ * @param string $key
78
+ * @param mixed $value
79
+ */
80
+ function update( $key, $value ) {
81
+ update_metadata($this->object_type, $this->ID, $key, $value);
82
+ }
83
+
84
+ /**
85
+ * Can you edit this post/term/user? Well good for you. You're no better than me.
86
+ * @example
87
+ * ```twig
88
+ * {% if post.can_edit %}
89
+ * <a href="{{ post.edit_link }}">Edit</a>
90
+ * {% endif %}
91
+ * ```
92
+ * ```html
93
+ * <a href="http://example.org/wp-admin/edit.php?p=242">Edit</a>
94
+ * ```
95
+ * @return bool
96
+ */
97
+ function can_edit() {
98
+ if ( !function_exists('current_user_can') ) {
99
+ return false;
100
+ }
101
+ if ( current_user_can('edit_post', $this->ID) ) {
102
+ return true;
103
+ }
104
+ return false;
105
+ }
106
+
107
+ /**
108
+ *
109
+ *
110
+ * @return array
111
+ */
112
+ function get_method_values() {
113
+ $ret = array();
114
+ $ret['can_edit'] = $this->can_edit();
115
+ return $ret;
116
+ }
117
+
118
+ }
lib/{timber-core-interface.php → CoreInterface.php} RENAMED
@@ -1,6 +1,8 @@
1
  <?php
2
 
3
- interface TimberCoreInterface {
 
 
4
 
5
  public function __call( $field, $args );
6
 
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ interface CoreInterface {
6
 
7
  public function __call( $field, $args );
8
 
lib/FunctionWrapper.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\Helper;
6
+
7
+ class FunctionWrapper {
8
+
9
+ private $_class;
10
+ private $_function;
11
+ private $_args;
12
+ private $_use_ob;
13
+
14
+ public function __toString() {
15
+ try {
16
+ return (string) $this->call();
17
+ } catch ( \Exception $e ) {
18
+ return 'Caught exception: '.$e->getMessage()."\n";
19
+ }
20
+ }
21
+
22
+ /**
23
+ *
24
+ *
25
+ * @param callable $function
26
+ * @param array $args
27
+ * @param bool $return_output_buffer
28
+ */
29
+ public function __construct( $function, $args = array(), $return_output_buffer = false ) {
30
+ if ( is_array($function) ) {
31
+ if ( (is_string($function[0]) && class_exists($function[0])) || gettype($function[0]) === 'object' ) {
32
+ $this->_class = $function[0];
33
+ }
34
+
35
+ if ( is_string($function[1]) ) {
36
+ $this->_function = $function[1];
37
+ }
38
+ } else {
39
+ $this->_function = $function;
40
+ }
41
+
42
+ $this->_args = $args;
43
+ $this->_use_ob = $return_output_buffer;
44
+
45
+ add_filter('get_twig', array(&$this, 'add_to_twig'));
46
+ }
47
+
48
+ /**
49
+ *
50
+ *
51
+ * @param Twig_Environment $twig
52
+ * @return Twig_Environment
53
+ */
54
+ public function add_to_twig( $twig ) {
55
+ $wrapper = $this;
56
+
57
+ $twig->addFunction(new \Twig_SimpleFunction($this->_function, function() use ($wrapper) {
58
+ return call_user_func_array(array($wrapper, 'call'), func_get_args());
59
+ } ));
60
+
61
+ return $twig;
62
+ }
63
+
64
+ /**
65
+ *
66
+ *
67
+ * @return string
68
+ */
69
+ public function call() {
70
+ $args = $this->_parse_args(func_get_args(), $this->_args);
71
+ $callable = (isset($this->_class)) ? array($this->_class, $this->_function) : $this->_function;
72
+
73
+ if ( $this->_use_ob ) {
74
+ return Helper::ob_function($callable, $args);
75
+ } else {
76
+ return call_user_func_array($callable, $args);
77
+ }
78
+ }
79
+
80
+ /**
81
+ *
82
+ *
83
+ * @param array $args
84
+ * @param array $defaults
85
+ * @return array
86
+ */
87
+ private function _parse_args( $args, $defaults ) {
88
+ $_arg = reset($defaults);
89
+
90
+ foreach ( $args as $index => $arg ) {
91
+ $defaults[$index] = is_null($arg) ? $_arg : $arg;
92
+ $_arg = next($defaults);
93
+ }
94
+
95
+ return $defaults;
96
+ }
97
+
98
+ }
lib/{timber-helper.php → Helper.php} RENAMED
@@ -1,9 +1,14 @@
1
  <?php
2
 
 
 
 
 
 
3
  /**
4
  * As the name suggests these are helpers for Timber (and you!) when developing. You can find additional (mainly internally-focused helpers) in TimberURLHelper
5
  */
6
- class TimberHelper {
7
 
8
  /**
9
  * A utility for a one-stop shop for Transients
@@ -26,16 +31,17 @@ class TimberHelper {
26
  * @return mixed
27
  */
28
  public static function transient( $slug, $callback, $transient_time = 0, $lock_timeout = 5, $force = false ) {
 
29
 
30
- $enable_transients = ( $transient_time === false || ( defined( 'WP_DISABLE_TRANSIENTS' ) && WP_DISABLE_TRANSIENTS ) ) ? false : true;
31
- $data = $enable_transients ? get_transient( $slug ) : false;
32
 
33
  if ( false === $data ) {
34
 
35
- if ( $enable_transients && self::_is_transient_locked( $slug ) ) {
36
 
37
- $force = apply_filters( 'timber_force_transients', $force );
38
- $force = apply_filters( 'timber_force_transient_' . $slug, $force );
39
 
40
  if ( !$force ) {
41
  //the server is currently executing the process.
@@ -48,14 +54,15 @@ class TimberHelper {
48
 
49
  // lock timeout shouldn't be higher than 5 seconds, unless
50
  // remote calls with high timeouts are made here
51
- if ( $enable_transients )
52
- self::_lock_transient( $slug, $lock_timeout );
 
53
 
54
  $data = $callback();
55
 
56
  if ( $enable_transients ) {
57
- set_transient( $slug, $data, $transient_time );
58
- self::_unlock_transient( $slug );
59
  }
60
 
61
  }
@@ -70,7 +77,7 @@ class TimberHelper {
70
  * @param integer $lock_timeout
71
  */
72
  static function _lock_transient( $slug, $lock_timeout ) {
73
- set_transient( $slug . '_lock', true, $lock_timeout );
74
  }
75
 
76
  /**
@@ -78,7 +85,7 @@ class TimberHelper {
78
  * @param string $slug
79
  */
80
  static function _unlock_transient( $slug ) {
81
- delete_transient( $slug . '_lock', true );
82
  }
83
 
84
  /**
@@ -86,7 +93,7 @@ class TimberHelper {
86
  * @param string $slug
87
  */
88
  static function _is_transient_locked( $slug ) {
89
- return (bool)get_transient( $slug . '_lock' );
90
  }
91
 
92
  /* These are for measuring page render time */
@@ -98,7 +105,7 @@ class TimberHelper {
98
  */
99
  public static function start_timer() {
100
  $time = microtime();
101
- $time = explode( ' ', $time );
102
  $time = $time[1] + $time[0];
103
  return $time;
104
  }
@@ -116,11 +123,11 @@ class TimberHelper {
116
  */
117
  public static function stop_timer( $start ) {
118
  $time = microtime();
119
- $time = explode( ' ', $time );
120
  $time = $time[1] + $time[0];
121
  $finish = $time;
122
- $total_time = round( ( $finish - $start ), 4 );
123
- return $total_time . ' seconds.';
124
  }
125
 
126
  /* Function Utilities
@@ -152,24 +159,22 @@ class TimberHelper {
152
  * @param array $args
153
  * @return string
154
  */
155
- public static function ob_function( $function, $args = array( null ) ) {
156
  ob_start();
157
- call_user_func_array( $function, $args );
158
  $data = ob_get_contents();
159
  ob_end_clean();
160
  return $data;
161
  }
162
 
163
  /**
164
- *
165
- *
166
- * @param string $function_name
167
- * @param integer[] $defaults
168
- * @param bool $return_output_buffer
169
- * @return TimberFunctionWrapper
170
  */
171
  public static function function_wrapper( $function_name, $defaults = array(), $return_output_buffer = false ) {
172
- return new TimberFunctionWrapper( $function_name, $defaults, $return_output_buffer );
173
  }
174
 
175
  /**
@@ -182,12 +187,20 @@ class TimberHelper {
182
  if ( !WP_DEBUG ) {
183
  return;
184
  }
185
- if ( is_object( $arg ) || is_array( $arg ) ) {
186
- $arg = print_r( $arg, true );
187
  }
188
- return error_log( $arg );
189
  }
190
 
 
 
 
 
 
 
 
 
191
  /**
192
  *
193
  *
@@ -196,8 +209,8 @@ class TimberHelper {
196
  * @return string
197
  */
198
  public static function get_wp_title( $separator = ' ', $seplocation = 'left' ) {
199
- $separator = apply_filters( 'timber_wp_title_seperator', $separator );
200
- return trim( wp_title( $separator, false, $seplocation ) );
201
  }
202
 
203
  /* Text Utilities
@@ -214,33 +227,33 @@ class TimberHelper {
214
  */
215
  public static function trim_words( $text, $num_words = 55, $more = null, $allowed_tags = 'p a span b i br blockquote' ) {
216
  if ( null === $more ) {
217
- $more = __( '&hellip;' );
218
  }
219
  $original_text = $text;
220
  $allowed_tag_string = '';
221
- foreach ( explode( ' ', apply_filters( 'timber/trim_words/allowed_tags', $allowed_tags ) ) as $tag ) {
222
- $allowed_tag_string .= '<' . $tag . '>';
223
  }
224
- $text = strip_tags( $text, $allowed_tag_string );
225
  /* translators: If your word count is based on single characters (East Asian characters), enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
226
- if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
227
- $text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
228
- preg_match_all( '/./u', $text, $words_array );
229
- $words_array = array_slice( $words_array[0], 0, $num_words + 1 );
230
  $sep = '';
231
  } else {
232
- $words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
233
  $sep = ' ';
234
  }
235
- if ( count( $words_array ) > $num_words ) {
236
- array_pop( $words_array );
237
- $text = implode( $sep, $words_array );
238
- $text = $text . $more;
239
  } else {
240
- $text = implode( $sep, $words_array );
241
  }
242
- $text = self::close_tags( $text );
243
- return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text );
244
  }
245
 
246
  /**
@@ -251,87 +264,30 @@ class TimberHelper {
251
  */
252
  public static function close_tags( $html ) {
253
  //put all opened tags into an array
254
- preg_match_all( '#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result );
255
  $openedtags = $result[1];
256
  //put all closed tags into an array
257
- preg_match_all( '#</([a-z]+)>#iU', $html, $result );
258
  $closedtags = $result[1];
259
- $len_opened = count( $openedtags );
260
  // all tags are closed
261
- if ( count( $closedtags ) == $len_opened ) {
262
  return $html;
263
  }
264
- $openedtags = array_reverse( $openedtags );
265
  // close tags
266
  for ( $i = 0; $i < $len_opened; $i++ ) {
267
- if ( !in_array( $openedtags[$i], $closedtags ) ) {
268
- $html .= '</' . $openedtags[$i] . '>';
269
  } else {
270
- unset( $closedtags[array_search( $openedtags[$i], $closedtags )] );
271
  }
272
  }
273
- $html = str_replace(array('</br>','</hr>','</wbr>'), '', $html);
274
- $html = str_replace(array('<br>','<hr>','<wbr>'), array('<br />','<hr />','<wbr />'), $html);
275
  return $html;
276
  }
277
 
278
- /* WordPress Query Utilities
279
- ======================== */
280
-
281
- /**
282
- * @param string $key
283
- * @param string $value
284
- * @return array|int
285
- * @deprecated 0.20.0
286
- */
287
- public static function get_posts_by_meta( $key, $value ) {
288
- global $wpdb;
289
- $query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s", $key, $value );
290
- $results = $wpdb->get_col( $query );
291
- $pids = array();
292
- foreach ( $results as $result ) {
293
- if ( get_post( $result ) ) {
294
- $pids[] = $result;
295
- }
296
- }
297
- if ( count( $pids ) ) {
298
- return $pids;
299
- }
300
- return 0;
301
- }
302
-
303
- /**
304
- *
305
- *
306
- * @param string $key
307
- * @param string $value
308
- * @return int
309
- * @deprecated 0.20.0
310
- */
311
- public static function get_post_by_meta( $key, $value ) {
312
- global $wpdb;
313
- $query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s ORDER BY post_id", $key, $value );
314
- $results = $wpdb->get_col( $query );
315
- foreach ( $results as $result ) {
316
- if ( $result && get_post( $result ) ) {
317
- return $result;
318
- }
319
- }
320
- return 0;
321
- }
322
-
323
- /**
324
- *
325
- * @deprecated 0.21.8
326
- * @param int $ttid
327
- * @return mixed
328
- */
329
- public static function get_term_id_by_term_taxonomy_id( $ttid ) {
330
- global $wpdb;
331
- $query = $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %s", $ttid );
332
- return $wpdb->get_var( $query );
333
- }
334
-
335
  /* Object Utilities
336
  ======================== */
337
 
@@ -343,7 +299,7 @@ class TimberHelper {
343
  * @return void
344
  */
345
  public static function osort( &$array, $prop ) {
346
- usort( $array, function ( $a, $b ) use ( $prop ) {
347
  return $a->$prop > $b->$prop ? 1 : -1;
348
  } );
349
  }
@@ -355,10 +311,10 @@ class TimberHelper {
355
  * @return bool
356
  */
357
  public static function is_array_assoc( $arr ) {
358
- if ( !is_array( $arr ) ) {
359
  return false;
360
  }
361
- return (bool)count( array_filter( array_keys( $arr ), 'is_string' ) );
362
  }
363
 
364
  /**
@@ -368,10 +324,10 @@ class TimberHelper {
368
  * @return stdClass
369
  */
370
  public static function array_to_object( $array ) {
371
- $obj = new stdClass;
372
  foreach ( $array as $k => $v ) {
373
- if ( is_array( $v ) ) {
374
- $obj->{$k} = self::array_to_object( $v ); //RECURSION
375
  } else {
376
  $obj->{$k} = $v;
377
  }
@@ -388,10 +344,10 @@ class TimberHelper {
388
  * @return bool|int
389
  */
390
  public static function get_object_index_by_property( $array, $key, $value ) {
391
- if ( is_array( $array ) ) {
392
  $i = 0;
393
  foreach ( $array as $arr ) {
394
- if ( is_array( $arr ) ) {
395
  if ( $arr[$key] == $value ) {
396
  return $i;
397
  }
@@ -416,15 +372,15 @@ class TimberHelper {
416
  * @throws Exception
417
  */
418
  public static function get_object_by_property( $array, $key, $value ) {
419
- if ( is_array( $array ) ) {
420
  foreach ( $array as $arr ) {
421
  if ( $arr->$key == $value ) {
422
  return $arr;
423
  }
424
  }
425
  } else {
426
- throw new InvalidArgumentException( '$array is not an array, got:' );
427
- TimberHelper::error_log( $array );
428
  }
429
  }
430
 
@@ -436,8 +392,8 @@ class TimberHelper {
436
  * @return array
437
  */
438
  public static function array_truncate( $array, $len ) {
439
- if ( sizeof( $array ) > $len ) {
440
- $array = array_splice( $array, 0, $len );
441
  }
442
  return $array;
443
  }
@@ -452,11 +408,11 @@ class TimberHelper {
452
  * @return bool
453
  */
454
  public static function is_true( $value ) {
455
- if ( isset( $value ) ) {
456
- if (is_string($value)) {
457
  $value = strtolower($value);
458
  }
459
- if ( ($value == 'true' || $value === 1 || $value === '1' || $value == true) && $value !== false && $value !== 'false') {
460
  return true;
461
  }
462
  }
@@ -470,7 +426,7 @@ class TimberHelper {
470
  * @return bool
471
  */
472
  public static function iseven( $i ) {
473
- return ( $i % 2 ) == 0;
474
  }
475
 
476
  /**
@@ -480,24 +436,12 @@ class TimberHelper {
480
  * @return bool
481
  */
482
  public static function isodd( $i ) {
483
- return ( $i % 2 ) != 0;
484
  }
485
 
486
  /* Links, Forms, Etc. Utilities
487
  ======================== */
488
 
489
- /**
490
- *
491
- * Gets the comment form for use on a single article page
492
- * @deprecated 0.21.8 use `{{ function('comment_form') }}` instead
493
- * @param int $post_id which post_id should the form be tied to?
494
- * @param array $args this $args thing is a fucking mess, [fix at some point](http://codex.wordpress.org/Function_Reference/comment_form)
495
- * @return string
496
- */
497
- public static function get_comment_form( $post_id = null, $args = array() ) {
498
- return self::ob_function( 'comment_form', array( $args, $post_id ) );
499
- }
500
-
501
  /**
502
  *
503
  *
@@ -512,28 +456,28 @@ class TimberHelper {
512
  'current' => 0,
513
  'show_all' => false,
514
  'prev_next' => false,
515
- 'prev_text' => __( '&laquo; Previous' ),
516
- 'next_text' => __( 'Next &raquo;' ),
517
  'end_size' => 1,
518
  'mid_size' => 2,
519
  'type' => 'array',
520
  'add_args' => false, // array of query args to add
521
  'add_fragment' => ''
522
  );
523
- $args = wp_parse_args( $args, $defaults );
524
  // Who knows what else people pass in $args
525
- $args['total'] = intval( (int)$args['total'] );
526
  if ( $args['total'] < 2 ) {
527
  return array();
528
  }
529
- $args['current'] = (int)$args['current'];
530
- $args['end_size'] = 0 < (int)$args['end_size'] ? (int)$args['end_size'] : 1; // Out of bounds? Make it the default.
531
- $args['mid_size'] = 0 <= (int)$args['mid_size'] ? (int)$args['mid_size'] : 2;
532
- $args['add_args'] = is_array( $args['add_args'] ) ? $args['add_args'] : false;
533
  $page_links = array();
534
  $dots = false;
535
  for ( $n = 1; $n <= $args['total']; $n++ ) {
536
- $n_display = number_format_i18n( $n );
537
  if ( $n == $args['current'] ) {
538
  $page_links[] = array(
539
  'class' => 'page-number page-numbers current',
@@ -544,18 +488,18 @@ class TimberHelper {
544
  );
545
  $dots = true;
546
  } else {
547
- 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'] ) ) {
548
- $link = str_replace( '%_%', 1 == $n ? '' : $args['format'], $args['base'] );
549
- $link = str_replace( '%#%', $n, $link );
550
- $link = trailingslashit( $link ) . ltrim( $args['add_fragment'], '/' );
551
  if ( $args['add_args'] ) {
552
- $link = rtrim( add_query_arg( $args['add_args'], $link ), '/' );
553
  }
554
  $link = str_replace(' ', '+', $link);
555
- $link = untrailingslashit( $link );
556
  $page_links[] = array(
557
  'class' => 'page-number page-numbers',
558
- 'link' => esc_url( apply_filters( 'paginate_links', $link ) ),
559
  'title' => $n_display,
560
  'name' => $n_display,
561
  'current' => $args['current'] == $n
@@ -564,7 +508,7 @@ class TimberHelper {
564
  } elseif ( $dots && !$args['show_all'] ) {
565
  $page_links[] = array(
566
  'class' => 'dots',
567
- 'title' => __( '&hellip;' )
568
  );
569
  $dots = false;
570
  }
@@ -574,94 +518,10 @@ class TimberHelper {
574
  }
575
 
576
  /**
577
- * @deprecated 0.18.0
578
- */
579
- static function get_current_url() {
580
- return TimberURLHelper::get_current_url();
581
- }
582
-
583
- /**
584
- * @deprecated 0.18.0
585
- */
586
- static function is_url( $url ) {
587
- return TimberURLHelper::is_url( $url );
588
- }
589
-
590
- /**
591
- * @deprecated 0.18.0
592
- */
593
- static function get_path_base() {
594
- return TimberURLHelper::get_path_base();
595
- }
596
-
597
- /**
598
- * @deprecated 0.18.0
599
- */
600
- static function get_rel_url( $url, $force = false ) {
601
- return TimberURLHelper::get_rel_url( $url, $force );
602
- }
603
-
604
- /**
605
- * @deprecated 0.18.0
606
- */
607
- static function is_local( $url ) {
608
- return TimberURLHelper::is_local( $url );
609
- }
610
-
611
- /**
612
- * @deprecated 0.18.0
613
- */
614
- static function get_full_path( $src ) {
615
- return TimberURLHelper::get_full_path( $src );
616
- }
617
-
618
- /**
619
- * @deprecated 0.18.0
620
- */
621
- static function get_rel_path( $src ) {
622
- return TimberURLHelper::get_rel_path( $src );
623
- }
624
-
625
- /**
626
- * @deprecated 0.18.0
627
- */
628
- static function remove_double_slashes( $url ) {
629
- return TimberURLHelper::remove_double_slashes( $url );
630
- }
631
-
632
- /**
633
- * @deprecated 0.18.0
634
- */
635
- static function prepend_to_url( $url, $path ) {
636
- return TimberURLHelper::prepend_to_url( $url, $path );
637
- }
638
-
639
- /**
640
- * @deprecated 0.18.0
641
- */
642
- static function preslashit( $path ) {
643
- return TimberURLHelper::preslashit( $path );
644
- }
645
-
646
- /**
647
- * @deprecated 0.18.0
648
- */
649
- static function is_external( $url ) {
650
- return TimberURLHelper::is_external( $url );
651
- }
652
-
653
- /**
654
- * @deprecated 0.18.0
655
- */
656
- static function download_url( $url, $timeout = 300 ) {
657
- return TimberURLHelper::download_url( $url, $timeout );
658
- }
659
-
660
- /**
661
- * @deprecated 0.18.0
662
  */
663
- static function get_params( $i = -1 ) {
664
- return TimberURLHelper::get_params( $i );
 
665
  }
666
-
667
- }
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\FunctionWrapper;
6
+ use Timber\URLHelper;
7
+
8
  /**
9
  * As the name suggests these are helpers for Timber (and you!) when developing. You can find additional (mainly internally-focused helpers) in TimberURLHelper
10
  */
11
+ class Helper {
12
 
13
  /**
14
  * A utility for a one-stop shop for Transients
31
  * @return mixed
32
  */
33
  public static function transient( $slug, $callback, $transient_time = 0, $lock_timeout = 5, $force = false ) {
34
+ $slug = apply_filters('timber/transient/slug', $slug);
35
 
36
+ $enable_transients = ($transient_time === false || (defined('WP_DISABLE_TRANSIENTS') && WP_DISABLE_TRANSIENTS)) ? false : true;
37
+ $data = $enable_transients ? get_transient($slug) : false;
38
 
39
  if ( false === $data ) {
40
 
41
+ if ( $enable_transients && self::_is_transient_locked($slug) ) {
42
 
43
+ $force = apply_filters('timber_force_transients', $force);
44
+ $force = apply_filters('timber_force_transient_'.$slug, $force);
45
 
46
  if ( !$force ) {
47
  //the server is currently executing the process.
54
 
55
  // lock timeout shouldn't be higher than 5 seconds, unless
56
  // remote calls with high timeouts are made here
57
+ if ( $enable_transients ) {
58
+ self::_lock_transient($slug, $lock_timeout);
59
+ }
60
 
61
  $data = $callback();
62
 
63
  if ( $enable_transients ) {
64
+ set_transient($slug, $data, $transient_time);
65
+ self::_unlock_transient($slug);
66
  }
67
 
68
  }
77
  * @param integer $lock_timeout
78
  */
79
  static function _lock_transient( $slug, $lock_timeout ) {
80
+ set_transient($slug.'_lock', true, $lock_timeout);
81
  }
82
 
83
  /**
85
  * @param string $slug
86
  */
87
  static function _unlock_transient( $slug ) {
88
+ delete_transient($slug.'_lock', true);
89
  }
90
 
91
  /**
93
  * @param string $slug
94
  */
95
  static function _is_transient_locked( $slug ) {
96
+ return (bool) get_transient($slug.'_lock');
97
  }
98
 
99
  /* These are for measuring page render time */
105
  */
106
  public static function start_timer() {
107
  $time = microtime();
108
+ $time = explode(' ', $time);
109
  $time = $time[1] + $time[0];
110
  return $time;
111
  }
123
  */
124
  public static function stop_timer( $start ) {
125
  $time = microtime();
126
+ $time = explode(' ', $time);
127
  $time = $time[1] + $time[0];
128
  $finish = $time;
129
+ $total_time = round(($finish - $start), 4);
130
+ return $total_time.' seconds.';
131
  }
132
 
133
  /* Function Utilities
159
  * @param array $args
160
  * @return string
161
  */
162
+ public static function ob_function( $function, $args = array(null) ) {
163
  ob_start();
164
+ call_user_func_array($function, $args);
165
  $data = ob_get_contents();
166
  ob_end_clean();
167
  return $data;
168
  }
169
 
170
  /**
171
+ * @param mixed $function_name or array( $class( string|object ), $function_name )
172
+ * @param array (optional) $defaults
173
+ * @param bool (optional) $return_output_buffer Return function output instead of return value (default: false)
174
+ * @return \TimberFunctionWrapper
 
 
175
  */
176
  public static function function_wrapper( $function_name, $defaults = array(), $return_output_buffer = false ) {
177
+ return new FunctionWrapper($function_name, $defaults, $return_output_buffer);
178
  }
179
 
180
  /**
187
  if ( !WP_DEBUG ) {
188
  return;
189
  }
190
+ if ( is_object($arg) || is_array($arg) ) {
191
+ $arg = print_r($arg, true);
192
  }
193
+ return error_log($arg);
194
  }
195
 
196
+ /**
197
+ * @param string $message that you want to output
198
+ * @return void
199
+ */
200
+ public static function warn( $message ) {
201
+ return trigger_error($message, E_USER_WARNING);
202
+ }
203
+
204
  /**
205
  *
206
  *
209
  * @return string
210
  */
211
  public static function get_wp_title( $separator = ' ', $seplocation = 'left' ) {
212
+ $separator = apply_filters('timber_wp_title_seperator', $separator);
213
+ return trim(wp_title($separator, false, $seplocation));
214
  }
215
 
216
  /* Text Utilities
227
  */
228
  public static function trim_words( $text, $num_words = 55, $more = null, $allowed_tags = 'p a span b i br blockquote' ) {
229
  if ( null === $more ) {
230
+ $more = __('&hellip;');
231
  }
232
  $original_text = $text;
233
  $allowed_tag_string = '';
234
+ foreach ( explode(' ', apply_filters('timber/trim_words/allowed_tags', $allowed_tags)) as $tag ) {
235
+ $allowed_tag_string .= '<'.$tag.'>';
236
  }
237
+ $text = strip_tags($text, $allowed_tag_string);
238
  /* translators: If your word count is based on single characters (East Asian characters), enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
239
+ if ( 'characters' == _x('words', 'word count: words or characters?') && preg_match('/^utf\-?8$/i', get_option('blog_charset')) ) {
240
+ $text = trim(preg_replace("/[\n\r\t ]+/", ' ', $text), ' ');
241
+ preg_match_all('/./u', $text, $words_array);
242
+ $words_array = array_slice($words_array[0], 0, $num_words + 1);
243
  $sep = '';
244
  } else {
245
+ $words_array = preg_split("/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY);
246
  $sep = ' ';
247
  }
248
+ if ( count($words_array) > $num_words ) {
249
+ array_pop($words_array);
250
+ $text = implode($sep, $words_array);
251
+ $text = $text.$more;
252
  } else {
253
+ $text = implode($sep, $words_array);
254
  }
255
+ $text = self::close_tags($text);
256
+ return apply_filters('wp_trim_words', $text, $num_words, $more, $original_text);
257
  }
258
 
259
  /**
264
  */
265
  public static function close_tags( $html ) {
266
  //put all opened tags into an array
267
+ preg_match_all('#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
268
  $openedtags = $result[1];
269
  //put all closed tags into an array
270
+ preg_match_all('#</([a-z]+)>#iU', $html, $result);
271
  $closedtags = $result[1];
272
+ $len_opened = count($openedtags);
273
  // all tags are closed
274
+ if ( count($closedtags) == $len_opened ) {
275
  return $html;
276
  }
277
+ $openedtags = array_reverse($openedtags);
278
  // close tags
279
  for ( $i = 0; $i < $len_opened; $i++ ) {
280
+ if ( !in_array($openedtags[$i], $closedtags) ) {
281
+ $html .= '</'.$openedtags[$i].'>';
282
  } else {
283
+ unset($closedtags[array_search($openedtags[$i], $closedtags)]);
284
  }
285
  }
286
+ $html = str_replace(array('</br>', '</hr>', '</wbr>'), '', $html);
287
+ $html = str_replace(array('<br>', '<hr>', '<wbr>'), array('<br />', '<hr />', '<wbr />'), $html);
288
  return $html;
289
  }
290
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  /* Object Utilities
292
  ======================== */
293
 
299
  * @return void
300
  */
301
  public static function osort( &$array, $prop ) {
302
+ usort($array, function( $a, $b ) use ($prop) {
303
  return $a->$prop > $b->$prop ? 1 : -1;
304
  } );
305
  }
311
  * @return bool
312
  */
313
  public static function is_array_assoc( $arr ) {
314
+ if ( !is_array($arr) ) {
315
  return false;
316
  }
317
+ return (bool) count(array_filter(array_keys($arr), 'is_string'));
318
  }
319
 
320
  /**
324
  * @return stdClass
325
  */
326
  public static function array_to_object( $array ) {
327
+ $obj = new \stdClass;
328
  foreach ( $array as $k => $v ) {
329
+ if ( is_array($v) ) {
330
+ $obj->{$k} = self::array_to_object($v); //RECURSION
331
  } else {
332
  $obj->{$k} = $v;
333
  }
344
  * @return bool|int
345
  */
346
  public static function get_object_index_by_property( $array, $key, $value ) {
347
+ if ( is_array($array) ) {
348
  $i = 0;
349
  foreach ( $array as $arr ) {
350
+ if ( is_array($arr) ) {
351
  if ( $arr[$key] == $value ) {
352
  return $i;
353
  }
372
  * @throws Exception
373
  */
374
  public static function get_object_by_property( $array, $key, $value ) {
375
+ if ( is_array($array) ) {
376
  foreach ( $array as $arr ) {
377
  if ( $arr->$key == $value ) {
378
  return $arr;
379
  }
380
  }
381
  } else {
382
+ throw new \InvalidArgumentException('$array is not an array, got:');
383
+ Helper::error_log($array);
384
  }
385
  }
386
 
392
  * @return array
393
  */
394
  public static function array_truncate( $array, $len ) {
395
+ if ( sizeof($array) > $len ) {
396
+ $array = array_splice($array, 0, $len);
397
  }
398
  return $array;
399
  }
408
  * @return bool
409
  */
410
  public static function is_true( $value ) {
411
+ if ( isset($value) ) {
412
+ if ( is_string($value) ) {
413
  $value = strtolower($value);
414
  }
415
+ if ( ($value == 'true' || $value === 1 || $value === '1' || $value == true) && $value !== false && $value !== 'false' ) {
416
  return true;
417
  }
418
  }
426
  * @return bool
427
  */
428
  public static function iseven( $i ) {
429
+ return ($i % 2) == 0;
430
  }
431
 
432
  /**
436
  * @return bool
437
  */
438
  public static function isodd( $i ) {
439
+ return ($i % 2) != 0;
440
  }
441
 
442
  /* Links, Forms, Etc. Utilities
443
  ======================== */
444
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  /**
446
  *
447
  *
456
  'current' => 0,
457
  'show_all' => false,
458
  'prev_next' => false,
459
+ 'prev_text' => __('&laquo; Previous'),
460
+ 'next_text' => __('Next &raquo;'),
461
  'end_size' => 1,
462
  'mid_size' => 2,
463
  'type' => 'array',
464
  'add_args' => false, // array of query args to add
465
  'add_fragment' => ''
466
  );
467
+ $args = wp_parse_args($args, $defaults);
468
  // Who knows what else people pass in $args
469
+ $args['total'] = intval((int) $args['total']);
470
  if ( $args['total'] < 2 ) {
471
  return array();
472
  }
473
+ $args['current'] = (int) $args['current'];
474
+ $args['end_size'] = 0 < (int) $args['end_size'] ? (int) $args['end_size'] : 1; // Out of bounds? Make it the default.
475
+ $args['mid_size'] = 0 <= (int) $args['mid_size'] ? (int) $args['mid_size'] : 2;
476
+ $args['add_args'] = is_array($args['add_args']) ? $args['add_args'] : false;
477
  $page_links = array();
478
  $dots = false;
479
  for ( $n = 1; $n <= $args['total']; $n++ ) {
480
+ $n_display = number_format_i18n($n);
481
  if ( $n == $args['current'] ) {
482
  $page_links[] = array(
483
  'class' => 'page-number page-numbers current',
488
  );
489
  $dots = true;
490
  } else {
491
+ 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']) ) {
492
+ $link = str_replace('%_%', 1 == $n ? '' : $args['format'], $args['base']);
493
+ $link = str_replace('%#%', $n, $link);
494
+ $link = trailingslashit($link).ltrim($args['add_fragment'], '/');
495
  if ( $args['add_args'] ) {
496
+ $link = rtrim(add_query_arg($args['add_args'], $link), '/');
497
  }
498
  $link = str_replace(' ', '+', $link);
499
+ $link = untrailingslashit($link);
500
  $page_links[] = array(
501
  'class' => 'page-number page-numbers',
502
+ 'link' => esc_url(apply_filters('paginate_links', $link)),
503
  'title' => $n_display,
504
  'name' => $n_display,
505
  'current' => $args['current'] == $n
508
  } elseif ( $dots && !$args['show_all'] ) {
509
  $page_links[] = array(
510
  'class' => 'dots',
511
+ 'title' => __('&hellip;')
512
  );
513
  $dots = false;
514
  }
518
  }
519
 
520
  /**
521
+ *
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  */
523
+ function get_current_url() {
524
+ Helper::warn('TimberHelper::get_current_url() is deprecated and will be removed in future versions, use Timber\URLHelper::get_current_url()');
525
+ return URLHelper::get_current_url();
526
  }
527
+ }
 
lib/{timber-image.php → Image.php} RENAMED
@@ -1,5 +1,13 @@
1
  <?php
2
 
 
 
 
 
 
 
 
 
3
  /**
4
  * If TimberPost is the class you're going to spend the most time, TimberImage is the class you're going to have the most fun with.
5
  * @example
@@ -37,7 +45,7 @@
37
  * </article>
38
  * ```
39
  */
40
- class TimberImage extends TimberPost implements TimberCoreInterface {
41
 
42
  protected $_can_edit;
43
  protected $_dimensions;
@@ -50,12 +58,21 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
50
  * @var string $representation what does this class represent in WordPress terms?
51
  */
52
  public static $representation = 'image';
 
 
 
 
53
  /**
54
  * @api
55
  * @var string $file_loc the location of the image file in the filesystem (ex: `/var/www/htdocs/wp-content/uploads/2015/08/my-pic.jpg`)
56
  */
57
  public $file_loc;
58
  public $file;
 
 
 
 
 
59
  public $sizes = array();
60
  /**
61
  * @api
@@ -79,7 +96,7 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
79
  * ```
80
  * @param int|string $iid
81
  */
82
- public function __construct($iid) {
83
  $this->init($iid);
84
  }
85
 
@@ -87,8 +104,8 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
87
  * @return string the src of the file
88
  */
89
  public function __toString() {
90
- if ( $this->get_src() ) {
91
- return $this->get_src();
92
  }
93
  return '';
94
  }
@@ -106,7 +123,7 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
106
  * @param string $dim
107
  * @return array|int
108
  */
109
- protected function get_dimensions($dim = null) {
110
  if ( isset($this->_dimensions) ) {
111
  return $this->get_dimensions_loaded($dim);
112
  }
@@ -124,7 +141,7 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
124
  * @param string|null $dim
125
  * @return array|int
126
  */
127
- protected function get_dimensions_loaded($dim) {
128
  if ( $dim === null ) {
129
  return $this->_dimensions;
130
  }
@@ -143,25 +160,25 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
143
  */
144
  protected function get_image_info( $iid ) {
145
  $image_info = $iid;
146
- if (is_numeric($iid)) {
147
  $image_info = wp_get_attachment_metadata($iid);
148
- if (!is_array($image_info)) {
149
  $image_info = array();
150
  }
151
  $image_custom = get_post_custom($iid);
152
  $basic = get_post($iid);
153
- if ($basic) {
154
- if (isset($basic->post_excerpt)) {
155
  $this->caption = $basic->post_excerpt;
156
  }
157
  $image_custom = array_merge($image_custom, get_object_vars($basic));
158
  }
159
  return array_merge($image_info, $image_custom);
160
  }
161
- if (is_array($image_info) && isset($image_info['image'])) {
162
  return $image_info['image'];
163
  }
164
- if (is_object($image_info)) {
165
  return get_object_vars($image_info);
166
  }
167
  return $iid;
@@ -172,9 +189,9 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
172
  * @param string $url for evaluation
173
  * @return string with http/https corrected depending on what's appropriate for server
174
  */
175
- protected static function _maybe_secure_url($url) {
176
  if ( is_ssl() && strpos($url, 'https') !== 0 && strpos($url, 'http') === 0 ) {
177
- $url = 'https' . substr($url, strlen('http'));
178
  }
179
  return $url;
180
  }
@@ -194,8 +211,10 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
194
  * @param int $iid
195
  */
196
  function init( $iid = false ) {
197
- if ( !is_numeric( $iid ) && is_string( $iid ) ) {
198
- if (strstr($iid, '://')) {
 
 
199
  $this->init_with_url($iid);
200
  return;
201
  }
@@ -203,10 +222,28 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
203
  $this->init_with_file_path($iid);
204
  return;
205
  }
206
- if ( strstr(strtolower($iid), '.jpg') ) {
 
 
 
 
207
  $this->init_with_relative_path($iid);
208
  return;
209
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  }
211
 
212
  $image_info = $this->get_image_info($iid);
@@ -215,10 +252,10 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
215
  $basedir = self::wp_upload_dir();
216
  $basedir = $basedir['basedir'];
217
  if ( isset($this->file) ) {
218
- $this->file_loc = $basedir . DIRECTORY_SEPARATOR . $this->file;
219
  } else if ( isset($this->_wp_attached_file) ) {
220
  $this->file = reset($this->_wp_attached_file);
221
- $this->file_loc = $basedir . DIRECTORY_SEPARATOR . $this->file;
222
  }
223
  if ( isset($image_info['id']) ) {
224
  $this->ID = $image_info['id'];
@@ -227,15 +264,16 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
227
  }
228
  if ( isset($this->ID) ) {
229
  $custom = get_post_custom($this->ID);
230
- foreach ($custom as $key => $value) {
231
  $this->$key = $value[0];
232
  }
 
233
  } else {
234
  if ( is_array($iid) || is_object($iid) ) {
235
- TimberHelper::error_log('Not able to init in TimberImage with iid=');
236
- TimberHelper::error_log($iid);
237
  } else {
238
- TimberHelper::error_log('Not able to init in TimberImage with iid=' . $iid);
239
  }
240
  }
241
  }
@@ -245,8 +283,8 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
245
  * @param string $relative_path
246
  */
247
  protected function init_with_relative_path( $relative_path ) {
248
- $this->abs_url = home_url( $relative_path );
249
- $file_path = TimberURLHelper::get_full_path( $relative_path );
250
  $this->file_loc = $file_path;
251
  $this->file = $file_path;
252
  }
@@ -256,7 +294,7 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
256
  * @param string $file_path
257
  */
258
  protected function init_with_file_path( $file_path ) {
259
- $url = TimberURLHelper::file_system_to_url( $file_path );
260
  $this->abs_url = $url;
261
  $this->file_loc = $file_path;
262
  $this->file = $file_path;
@@ -266,11 +304,11 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
266
  * @internal
267
  * @param string $url
268
  */
269
- protected function init_with_url($url) {
270
  $this->abs_url = $url;
271
- if ( TimberURLHelper::is_local($url) ) {
272
- $this->file = ABSPATH . TimberURLHelper::get_rel_url($url);
273
- $this->file_loc = ABSPATH . TimberURLHelper::get_rel_url($url);
274
  }
275
  }
276
 
@@ -365,7 +403,7 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
365
  * @return string the /relative/path/to/the/file
366
  */
367
  public function path() {
368
- return TimberURLHelper::get_rel_path($this->src());
369
  }
370
 
371
  /**
@@ -381,7 +419,7 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
381
  * ```
382
  * @return bool|string
383
  */
384
- public function src($size = '') {
385
  if ( isset($this->abs_url) ) {
386
  return $this->_maybe_secure_url($this->abs_url);
387
  }
@@ -402,19 +440,11 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
402
  $dir = self::wp_upload_dir();
403
  $base = $dir['baseurl'];
404
 
405
- $src = trailingslashit($this->_maybe_secure_url($base)) . $this->file;
406
  $src = apply_filters('timber/image/src', $src, $this->ID);
407
  return apply_filters('timber_image_src', $src, $this->ID);
408
  }
409
 
410
- /**
411
- * @deprecated use src() instead
412
- * @return string
413
- */
414
- function url() {
415
- return $this->get_src();
416
- }
417
-
418
  /**
419
  * @api
420
  * @example
@@ -430,69 +460,34 @@ class TimberImage extends TimberPost implements TimberCoreInterface {
430
  return $this->get_dimensions('width');
431
  }
432
 
433
-
434
- /**
435
- * @deprecated 0.21.9 use TimberImage::width() instead
436
- * @internal
437
- * @return int
438
- */
439
- function get_width() {
440
- return $this->width();
441
- }
442
-
443
- /**
444
- * @deprecated 0.21.9 use TimberImage::height() instead
445
- * @internal
446
- * @return int
447
- */
448
- function get_height() {
449
- return $this->height();
450
- }
451
-
452
  /**
453
  * @deprecated 0.21.9 use TimberImage::src
454
  * @internal
455
  * @param string $size
456
  * @return bool|string
457
  */
458
- function get_src( $size = '' ) {
459
- return $this->src( $size );
 
460
  }
461
 
462
- /**
463
- * @deprecated 0.21.9 use TimberImage::path()
464
- * @internal
465
- * @return string
466
- */
467
- function get_path() {
468
- return $this->link();
469
- }
470
 
471
  /**
472
- * @deprecated use src() instead
473
  * @return string
474
  */
475
- function get_url() {
476
- return $this->get_src();
 
477
  }
478
 
479
- /**
480
- * @internal
481
- * @deprecated 0.21.8
482
- * @return bool|TimberPost
483
- */
484
- function get_parent() {
485
- return $this->parent();
486
- }
487
 
488
  /**
489
- * @internal
490
- * @deprecated 0.21.9
491
- * @see TimberImage::alt
492
  * @return string
493
  */
494
- function get_alt() {
495
- return $this->alt();
 
496
  }
497
-
498
- }
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\CoreInterface;
6
+ use Timber\Helper;
7
+ use Timber\Post;
8
+ use Timber\URLHelper;
9
+
10
+
11
  /**
12
  * If TimberPost is the class you're going to spend the most time, TimberImage is the class you're going to have the most fun with.
13
  * @example
45
  * </article>
46
  * ```
47
  */
48
+ class Image extends Post implements CoreInterface {
49
 
50
  protected $_can_edit;
51
  protected $_dimensions;
58
  * @var string $representation what does this class represent in WordPress terms?
59
  */
60
  public static $representation = 'image';
61
+ /**
62
+ * @var array of supported relative file types
63
+ */
64
+ private $file_types = array('jpg', 'jpeg', 'png', 'svg', 'bmp', 'ico', 'gif', 'tiff', 'pdf');
65
  /**
66
  * @api
67
  * @var string $file_loc the location of the image file in the filesystem (ex: `/var/www/htdocs/wp-content/uploads/2015/08/my-pic.jpg`)
68
  */
69
  public $file_loc;
70
  public $file;
71
+ /**
72
+ * @api
73
+ * @var integer the ID of the image (which is a WP_Post)
74
+ */
75
+ public $id;
76
  public $sizes = array();
77
  /**
78
  * @api
96
  * ```
97
  * @param int|string $iid
98
  */
99
+ public function __construct( $iid ) {
100
  $this->init($iid);
101
  }
102
 
104
  * @return string the src of the file
105
  */
106
  public function __toString() {
107
+ if ( $this->src() ) {
108
+ return $this->src();
109
  }
110
  return '';
111
  }
123
  * @param string $dim
124
  * @return array|int
125
  */
126
+ protected function get_dimensions( $dim = null ) {
127
  if ( isset($this->_dimensions) ) {
128
  return $this->get_dimensions_loaded($dim);
129
  }
141
  * @param string|null $dim
142
  * @return array|int
143
  */
144
+ protected function get_dimensions_loaded( $dim ) {
145
  if ( $dim === null ) {
146
  return $this->_dimensions;
147
  }
160
  */
161
  protected function get_image_info( $iid ) {
162
  $image_info = $iid;
163
+ if ( is_numeric($iid) ) {
164
  $image_info = wp_get_attachment_metadata($iid);
165
+ if ( !is_array($image_info) ) {
166
  $image_info = array();
167
  }
168
  $image_custom = get_post_custom($iid);
169
  $basic = get_post($iid);
170
+ if ( $basic ) {
171
+ if ( isset($basic->post_excerpt) ) {
172
  $this->caption = $basic->post_excerpt;
173
  }
174
  $image_custom = array_merge($image_custom, get_object_vars($basic));
175
  }
176
  return array_merge($image_info, $image_custom);
177
  }
178
+ if ( is_array($image_info) && isset($image_info['image']) ) {
179
  return $image_info['image'];
180
  }
181
+ if ( is_object($image_info) ) {
182
  return get_object_vars($image_info);
183
  }
184
  return $iid;
189
  * @param string $url for evaluation
190
  * @return string with http/https corrected depending on what's appropriate for server
191
  */
192
+ protected static function _maybe_secure_url( $url ) {
193
  if ( is_ssl() && strpos($url, 'https') !== 0 && strpos($url, 'http') === 0 ) {
194
+ $url = 'https'.substr($url, strlen('http'));
195
  }
196
  return $url;
197
  }
211
  * @param int $iid
212
  */
213
  function init( $iid = false ) {
214
+ if ( !$iid ) { Helper::error_log('Initalized TimberImage without providing first parameter.'); return; }
215
+
216
+ if ( !is_numeric($iid) && is_string($iid) ) {
217
+ if ( strstr($iid, '://') ) {
218
  $this->init_with_url($iid);
219
  return;
220
  }
222
  $this->init_with_file_path($iid);
223
  return;
224
  }
225
+
226
+ $relative = false;
227
+ $iid_lower = strtolower($iid);
228
+ foreach ( $this->file_types as $type ) { if ( strstr($iid_lower, $type) ) { $relative = true; break; } };
229
+ if ( $relative ) {
230
  $this->init_with_relative_path($iid);
231
  return;
232
  }
233
+ } else if ( $iid instanceof \WP_Post ) {
234
+ $ref = new \ReflectionClass($this);
235
+ $post = $ref->getParentClass()->newInstance($iid->ID);
236
+ if ( isset($post->_thumbnail_id) && $post->_thumbnail_id ) {
237
+ return $this->init((int) $post->_thumbnail_id);
238
+ }
239
+ return $this->init($iid->ID);
240
+ } else if ( $iid instanceof Post ) {
241
+ /**
242
+ * This will catch TimberPost and any post classes that extend TimberPost,
243
+ * see http://php.net/manual/en/internals2.opcodes.instanceof.php#109108
244
+ * and https://github.com/timber/timber/wiki/Extending-Timber
245
+ */
246
+ $iid = (int) $iid->_thumbnail_id;
247
  }
248
 
249
  $image_info = $this->get_image_info($iid);
252
  $basedir = self::wp_upload_dir();
253
  $basedir = $basedir['basedir'];
254
  if ( isset($this->file) ) {
255
+ $this->file_loc = $basedir.DIRECTORY_SEPARATOR.$this->file;
256
  } else if ( isset($this->_wp_attached_file) ) {
257
  $this->file = reset($this->_wp_attached_file);
258
+ $this->file_loc = $basedir.DIRECTORY_SEPARATOR.$this->file;
259
  }
260
  if ( isset($image_info['id']) ) {
261
  $this->ID = $image_info['id'];
264
  }
265
  if ( isset($this->ID) ) {
266
  $custom = get_post_custom($this->ID);
267
+ foreach ( $custom as $key => $value ) {
268
  $this->$key = $value[0];
269
  }
270
+ $this->id = $this->ID;
271
  } else {
272
  if ( is_array($iid) || is_object($iid) ) {
273
+ Helper::error_log('Not able to init in TimberImage with iid=');
274
+ Helper::error_log($iid);
275
  } else {
276
+ Helper::error_log('Not able to init in TimberImage with iid='.$iid);
277
  }
278
  }
279
  }
283
  * @param string $relative_path
284
  */
285
  protected function init_with_relative_path( $relative_path ) {
286
+ $this->abs_url = home_url($relative_path);
287
+ $file_path = URLHelper::get_full_path($relative_path);
288
  $this->file_loc = $file_path;
289
  $this->file = $file_path;
290
  }
294
  * @param string $file_path
295
  */
296
  protected function init_with_file_path( $file_path ) {
297
+ $url = URLHelper::file_system_to_url($file_path);
298
  $this->abs_url = $url;
299
  $this->file_loc = $file_path;
300
  $this->file = $file_path;
304
  * @internal
305
  * @param string $url
306
  */
307
+ protected function init_with_url( $url ) {
308
  $this->abs_url = $url;
309
+ if ( URLHelper::is_local($url) ) {
310
+ $this->file = URLHelper::remove_double_slashes(ABSPATH.URLHelper::get_rel_url($url));
311
+ $this->file_loc = URLHelper::remove_double_slashes(ABSPATH.URLHelper::get_rel_url($url));
312
  }
313
  }
314
 
403
  * @return string the /relative/path/to/the/file
404
  */
405
  public function path() {
406
+ return URLHelper::get_rel_path($this->src());
407
  }
408
 
409
  /**
419
  * ```
420
  * @return bool|string
421
  */
422
+ public function src( $size = '' ) {
423
  if ( isset($this->abs_url) ) {
424
  return $this->_maybe_secure_url($this->abs_url);
425
  }
440
  $dir = self::wp_upload_dir();
441
  $base = $dir['baseurl'];
442
 
443
+ $src = trailingslashit($this->_maybe_secure_url($base)).$this->file;
444
  $src = apply_filters('timber/image/src', $src, $this->ID);
445
  return apply_filters('timber_image_src', $src, $this->ID);
446
  }
447
 
 
 
 
 
 
 
 
 
448
  /**
449
  * @api
450
  * @example
460
  return $this->get_dimensions('width');
461
  }
462
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  /**
464
  * @deprecated 0.21.9 use TimberImage::src
465
  * @internal
466
  * @param string $size
467
  * @return bool|string
468
  */
469
+ public function get_src( $size = '' ) {
470
+ Helper::warn('{{image.get_src}} is deprecated and will be removed in 1.1; use {{image.src}}');
471
+ return $this->src($size);
472
  }
473
 
 
 
 
 
 
 
 
 
474
 
475
  /**
476
+ * @deprecated since 0.21.9 use src() instead
477
  * @return string
478
  */
479
+ public function url( $size = '' ) {
480
+ Helper::warn('{{image.url}} is deprecated and will be removed in 1.1; use {{image.src}}');
481
+ return $this->src($size);
482
  }
483
 
 
 
 
 
 
 
 
 
484
 
485
  /**
486
+ * @deprecated since 0.21.9 use src() instead
 
 
487
  * @return string
488
  */
489
+ public function get_url( $size = '' ) {
490
+ Helper::warn('{{image.get_url}} is deprecated and will be removed in 1.1; use {{image.src}}');
491
+ return $this->src($size);
492
  }
493
+ }
 
lib/{image/timber-image-operation.php → Image/Operation.php} RENAMED
@@ -1,4 +1,7 @@
1
  <?php
 
 
 
2
  /**
3
  * Each image filter is represented by a subclass of this class,m
4
  * and each filter call is a new instance, with call arguments as properties.
@@ -8,8 +11,9 @@
8
  * - filename
9
  * - run
10
  */
11
- abstract class TimberImageOperation {
12
  /**
 
13
  * Builds the result filename, based on source filename and extension
14
  *
15
  * @param string $src_filename source filename (excluding extension and path)
@@ -17,7 +21,7 @@ abstract class TimberImageOperation {
17
  * @return string resulting filename (including extension but excluding path)
18
  * ex: my-awesome-file.jpg
19
  */
20
- public abstract function filename($src_filename, $src_extension);
21
 
22
  /**
23
  * Performs the actual image manipulation,
@@ -27,7 +31,7 @@ abstract class TimberImageOperation {
27
  * @param string $save_filename filepath (not URL) where result file should be saved
28
  * @return bool true if everything went fine, false otherwise
29
  */
30
- public abstract function run($load_filename, $save_filename);
31
 
32
  /**
33
  * Helper method to convert hex string to rgb array
@@ -37,13 +41,13 @@ abstract class TimberImageOperation {
37
  * ex: array('red' => 255, 'green' => 20, 'blue' => 85);
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
  }
1
  <?php
2
+
3
+ namespace Timber\Image;
4
+
5
  /**
6
  * Each image filter is represented by a subclass of this class,m
7
  * and each filter call is a new instance, with call arguments as properties.
11
  * - filename
12
  * - run
13
  */
14
+ abstract class Operation {
15
  /**
16
+ *
17
  * Builds the result filename, based on source filename and extension
18
  *
19
  * @param string $src_filename source filename (excluding extension and path)
21
  * @return string resulting filename (including extension but excluding path)
22
  * ex: my-awesome-file.jpg
23
  */
24
+ public abstract function filename( $src_filename, $src_extension );
25
 
26
  /**
27
  * Performs the actual image manipulation,
31
  * @param string $save_filename filepath (not URL) where result file should be saved
32
  * @return bool true if everything went fine, false otherwise
33
  */
34
+ public abstract function run( $load_filename, $save_filename );
35
 
36
  /**
37
  * Helper method to convert hex string to rgb array
41
  * ex: array('red' => 255, 'green' => 20, 'blue' => 85);
42
  */
43
  public static function hexrgb( $hexstr ) {
44
+ if ( !strstr($hexstr, '#') ) {
45
+ $hexstr = '#'.$hexstr;
46
  }
47
+ if ( strlen($hexstr) == 4 ) {
48
+ $hexstr = '#'.$hexstr[1].$hexstr[1].$hexstr[2].$hexstr[2].$hexstr[3].$hexstr[3];
49
  }
50
+ $int = hexdec($hexstr);
51
+ return array("red" => 0xFF & ($int >> 0x10), "green" => 0xFF & ($int >> 0x8), "blue" => 0xFF & $int);
52
  }
53
  }
lib/{image/timber-image-operation-letterbox.php → Image/Operation/Letterbox.php} RENAMED
@@ -1,15 +1,21 @@
1
  <?php
 
 
 
 
 
 
2
  /*
3
  * Changes image to new size, by shrinking/enlarging then padding with colored bands,
4
  * so that no part of the image is cropped or stretched.
5
- *
6
  * Arguments:
7
  * - width of new image
8
  * - height of new image
9
- * - color of padding
10
  */
11
- class TimberImageOperationLetterbox extends TimberImageOperation {
12
-
13
  private $w, $h, $color;
14
 
15
  /**
@@ -17,7 +23,7 @@ class TimberImageOperationLetterbox extends TimberImageOperation {
17
  * @param int $h height
18
  * @param string $color hex string, for color of padding bands
19
  */
20
- function __construct($w, $h, $color) {
21
  $this->w = $w;
22
  $this->h = $h;
23
  $this->color = $color;
@@ -26,36 +32,36 @@ class TimberImageOperationLetterbox extends TimberImageOperation {
26
  /**
27
  * @param string $src_filename the basename of the file (ex: my-awesome-pic)
28
  * @param string $src_extension the extension (ex: .jpg)
29
- * @return string the final filename to be used
30
- * (ex: my-awesome-pic-lbox-300x200-FF3366.jpg)
31
  */
32
- public function filename($src_filename, $src_extension) {
33
- $color = str_replace( '#', '', $this->color );
34
- $newbase = $src_filename . '-lbox-' . $this->w . 'x' . $this->h . '-' . $color;
35
- $new_name = $newbase . '.' . $src_extension;
36
  return $new_name;
37
  }
38
 
39
  /**
40
  * Performs the actual image manipulation,
41
  * including saving the target file.
42
- *
43
- * @param string $load_filename filepath (not URL) to source file
44
  * (ex: /src/var/www/wp-content/uploads/my-pic.jpg)
45
- * @param string $save_filename filepath (not URL) where result file should be saved
46
  * (ex: /src/var/www/wp-content/uploads/my-pic-lbox-300x200-FF3366.jpg)
47
  * @return bool true if everything went fine, false otherwise
48
  */
49
- public function run($load_filename, $save_filename) {
50
  $w = $this->w;
51
  $h = $this->h;
52
 
53
- $bg = imagecreatetruecolor( $w, $h );
54
- $c = self::hexrgb( $this->color );
55
- $bgColor = imagecolorallocate( $bg, $c['red'], $c['green'], $c['blue'] );
56
- imagefill( $bg, 0, 0, $bgColor );
57
- $image = wp_get_image_editor( $load_filename );
58
- if ( !is_wp_error( $image ) ) {
59
  $current_size = $image->get_size();
60
  $quality = $image->get_quality();
61
  $ow = $current_size['width'];
@@ -69,29 +75,38 @@ class TimberImageOperationLetterbox extends TimberImageOperation {
69
  $y = 0;
70
  $x = $w / 2 - $owt / 2;
71
  $oht = $h;
72
- $image->crop( 0, 0, $ow, $oh, $owt, $oht );
73
  } else {
74
  $w_scale = $w / $ow;
75
  $oht = $oh * $w_scale;
76
  $x = 0;
77
  $y = $h / 2 - $oht / 2;
78
  $owt = $w;
79
- $image->crop( 0, 0, $ow, $oh, $owt, $oht );
80
  }
81
- $image->save( $save_filename );
82
  $func = 'imagecreatefromjpeg';
83
- $ext = pathinfo( $save_filename, PATHINFO_EXTENSION );
 
84
  if ( $ext == 'gif' ) {
85
  $func = 'imagecreatefromgif';
 
86
  } else if ( $ext == 'png' ) {
87
  $func = 'imagecreatefrompng';
 
 
 
 
 
 
 
 
 
 
88
  }
89
- $image = $func( $save_filename );
90
- imagecopy( $bg, $image, $x, $y, 0, 0, $owt, $oht );
91
- imagejpeg( $bg, $save_filename, $quality );
92
- return true;
93
  } else {
94
- TimberHelper::error_log( $image );
95
  }
96
  return false;
97
  }
1
  <?php
2
+
3
+ namespace Timber\Image\Operation;
4
+
5
+ use Timber\Helper;
6
+ use Timber\Image\Operation as ImageOperation;
7
+
8
  /*
9
  * Changes image to new size, by shrinking/enlarging then padding with colored bands,
10
  * so that no part of the image is cropped or stretched.
11
+ *
12
  * Arguments:
13
  * - width of new image
14
  * - height of new image
15
+ * - color of padding
16
  */
17
+ class Letterbox extends ImageOperation {
18
+
19
  private $w, $h, $color;
20
 
21
  /**
23
  * @param int $h height
24
  * @param string $color hex string, for color of padding bands
25
  */
26
+ function __construct( $w, $h, $color ) {
27
  $this->w = $w;
28
  $this->h = $h;
29
  $this->color = $color;
32
  /**
33
  * @param string $src_filename the basename of the file (ex: my-awesome-pic)
34
  * @param string $src_extension the extension (ex: .jpg)
35
+ * @return string the final filename to be used
36
+ * (ex: my-awesome-pic-lbox-300x200-FF3366.jpg)
37
  */
38
+ public function filename( $src_filename, $src_extension ) {
39
+ $color = str_replace('#', '', $this->color);
40
+ $newbase = $src_filename.'-lbox-'.$this->w.'x'.$this->h.'-'.$color;
41
+ $new_name = $newbase.'.'.$src_extension;
42
  return $new_name;
43
  }
44
 
45
  /**
46
  * Performs the actual image manipulation,
47
  * including saving the target file.
48
+ *
49
+ * @param string $load_filename filepath (not URL) to source file
50
  * (ex: /src/var/www/wp-content/uploads/my-pic.jpg)
51
+ * @param string $save_filename filepath (not URL) where result file should be saved
52
  * (ex: /src/var/www/wp-content/uploads/my-pic-lbox-300x200-FF3366.jpg)
53
  * @return bool true if everything went fine, false otherwise
54
  */
55
+ public function run( $load_filename, $save_filename ) {
56
  $w = $this->w;
57
  $h = $this->h;
58
 
59
+ $bg = imagecreatetruecolor($w, $h);
60
+ $c = self::hexrgb($this->color);
61
+ $bgColor = imagecolorallocate($bg, $c['red'], $c['green'], $c['blue']);
62
+ imagefill($bg, 0, 0, $bgColor);
63
+ $image = wp_get_image_editor($load_filename);
64
+ if ( !is_wp_error($image) ) {
65
  $current_size = $image->get_size();
66
  $quality = $image->get_quality();
67
  $ow = $current_size['width'];
75
  $y = 0;
76
  $x = $w / 2 - $owt / 2;
77
  $oht = $h;
78
+ $image->crop(0, 0, $ow, $oh, $owt, $oht);
79
  } else {
80
  $w_scale = $w / $ow;
81
  $oht = $oh * $w_scale;
82
  $x = 0;
83
  $y = $h / 2 - $oht / 2;
84
  $owt = $w;
85
+ $image->crop(0, 0, $ow, $oh, $owt, $oht);
86
  }
87
+ $result = $image->save($save_filename);
88
  $func = 'imagecreatefromjpeg';
89
+ $save_func = 'imagejpeg';
90
+ $ext = pathinfo($save_filename, PATHINFO_EXTENSION);
91
  if ( $ext == 'gif' ) {
92
  $func = 'imagecreatefromgif';
93
+ $save_func = 'imagegif';
94
  } else if ( $ext == 'png' ) {
95
  $func = 'imagecreatefrompng';
96
+ $save_func = 'imagepng';
97
+ if ( $quality > 9 ) {
98
+ $quality = $quality / 10;
99
+ $quality = round(10 - $quality);
100
+ }
101
+ }
102
+ $image = $func($save_filename);
103
+ imagecopy($bg, $image, $x, $y, 0, 0, $owt, $oht);
104
+ if ( $save_func === 'imagegif' ) {
105
+ return $save_func($bg, $save_filename);
106
  }
107
+ return $save_func($bg, $save_filename, $quality);
 
 
 
108
  } else {
109
+ Helper::error_log($image);
110
  }
111
  return false;
112
  }
lib/{image/timber-image-operation-resize.php → Image/Operation/Resize.php} RENAMED
@@ -1,4 +1,10 @@
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * Changes image to new size, by shrinking/enlarging
4
  * then cropping to respect new ratio.
@@ -8,21 +14,21 @@
8
  * - height of new image
9
  * - crop method
10
  */
11
- class TimberImageOperationResize extends TimberImageOperation {
12
 
13
  private $w, $h, $crop;
14
 
15
  /**
16
  * @param int $w width of new image
17
  * @param int $h height of new image
18
- * @param string $crop cropping method, one of: 'default', 'center', 'top', 'bottom', 'left', 'right'.
19
  */
20
- function __construct($w, $h, $crop) {
21
  $this->w = $w;
22
  $this->h = $h;
23
  // Sanitize crop position
24
- $allowed_crop_positions = array( 'default', 'center', 'top', 'bottom', 'left', 'right' );
25
- if ( $crop !== false && !in_array( $crop, $allowed_crop_positions ) ) {
26
  $crop = $allowed_crop_positions[0];
27
  }
28
  $this->crop = $crop;
@@ -33,7 +39,7 @@ class TimberImageOperationResize extends TimberImageOperation {
33
  * @param string $src_extension the extension (ex: .jpg)
34
  * @return string the final filename to be used (ex: my-awesome-pic-300x200-c-default.jpg)
35
  */
36
- public function filename($src_filename, $src_extension) {
37
  $w = 0;
38
  $h = 0;
39
  if ( $this->w ) {
@@ -42,8 +48,8 @@ class TimberImageOperationResize extends TimberImageOperation {
42
  if ( $this->h ) {
43
  $h = $this->h;
44
  }
45
- $result = $src_filename . '-' . $w . 'x' . $h . '-c-' . ( $this->crop ? $this->crop : 'f' ); // Crop will be either user named or f (false)
46
- if($src_extension) {
47
  $result .= '.'.$src_extension;
48
  }
49
  return $result;
@@ -54,7 +60,7 @@ class TimberImageOperationResize extends TimberImageOperation {
54
  * @param string $save_filename
55
  */
56
  protected function run_animated_gif( $load_filename, $save_filename ) {
57
- $image = wp_get_image_editor( $load_filename );
58
  $current_size = $image->get_size();
59
  $src_w = $current_size['width'];
60
  $src_h = $current_size['height'];
@@ -63,10 +69,10 @@ class TimberImageOperationResize extends TimberImageOperation {
63
  if ( !class_exists('Imagick') ) {
64
  return false;
65
  }
66
- $image = new Imagick($load_filename);
67
  $image = $image->coalesceImages();
68
- $crop = self::get_target_sizes( $load_filename );
69
- foreach ($image as $frame) {
70
  $frame->cropImage($crop['src_w'], $crop['src_h'], $crop['x'], $crop['y']);
71
  $frame->thumbnailImage($w, $h);
72
  $frame->setImagePage($w, $h, 0, 0);
@@ -75,8 +81,11 @@ class TimberImageOperationResize extends TimberImageOperation {
75
  return $image->writeImages($save_filename, true);
76
  }
77
 
 
 
 
78
  protected function get_target_sizes( $load_filename ) {
79
- $image = wp_get_image_editor( $load_filename );
80
  $w = $this->w;
81
  $h = $this->h;
82
  $crop = $this->crop;
@@ -86,11 +95,11 @@ class TimberImageOperationResize extends TimberImageOperation {
86
  $src_h = $current_size['height'];
87
  $src_ratio = $src_w / $src_h;
88
  if ( !$h ) {
89
- $h = round( $w / $src_ratio );
90
  }
91
  if ( !$w ) {
92
  //the user wants to resize based on constant height
93
- $w = round( $h * $src_ratio );
94
  }
95
  if ( !$crop ) {
96
  return array(
@@ -104,34 +113,51 @@ class TimberImageOperationResize extends TimberImageOperation {
104
  $src_wt = $src_h * $dest_ratio;
105
  $src_ht = $src_w / $dest_ratio;
106
  $src_x = $src_w / 2 - $src_wt / 2;
107
- $src_y = ( $src_h - $src_ht ) / 6;
108
  //now specific overrides based on options:
109
- if ( $crop == 'center' ) {
110
- // Get source x and y
111
- $src_x = round( ( $src_w - $src_wt ) / 2 );
112
- $src_y = round( ( $src_h - $src_ht ) / 2 );
113
- } else if ( $crop == 'top' ) {
114
- $src_y = 0;
115
- } else if ( $crop == 'bottom' ) {
116
- $src_y = $src_h - $src_ht;
117
- } else if ( $crop == 'left' ) {
118
- $src_x = 0;
119
- } else if ( $crop == 'right' ) {
120
- $src_x = $src_w - $src_wt;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
122
  // Crop the image
123
- if ( $dest_ratio > $src_ratio ) {
124
- return array(
125
  'x' => 0, 'y' => $src_y,
126
  'src_w' => $src_w, 'src_h' => $src_ht,
127
  'target_w' => $w, 'target_h' => $h
 
 
 
 
 
128
  );
129
- }
130
- return array(
131
- 'x' => $src_x, 'y' => 0,
132
- 'src_w' => $src_wt, 'src_h' => $src_h,
133
- 'target_w' => $w, 'target_h' => $h
134
- );
135
  }
136
 
137
  /**
@@ -142,44 +168,44 @@ class TimberImageOperationResize extends TimberImageOperation {
142
  * (ex: /src/var/www/wp-content/uploads/my-pic.jpg)
143
  * @param string $save_filename filepath (not URL) where result file should be saved
144
  * (ex: /src/var/www/wp-content/uploads/my-pic-300x200-c-default.jpg)
145
- * @return bool true if everything went fine, false otherwise
146
  */
147
- public function run($load_filename, $save_filename) {
148
  //should be resized by gif resizer
149
- if ( TimberImageHelper::is_animated_gif($load_filename) ) {
150
  //attempt to resize
151
  //return if successful
152
  //proceed if not
153
  $gif = self::run_animated_gif($load_filename, $save_filename);
154
- if ($gif) {
155
  return true;
156
  }
157
  }
158
- $image = wp_get_image_editor( $load_filename );
159
- if ( !is_wp_error( $image ) ) {
160
- $crop = self::get_target_sizes( $load_filename );
161
- $image->crop( $crop['x'],
162
  $crop['y'],
163
  $crop['src_w'],
164
  $crop['src_h'],
165
  $crop['target_w'],
166
  $crop['target_h']
167
  );
168
- $result = $image->save( $save_filename );
169
- if ( is_wp_error( $result ) ) {
170
  // @codeCoverageIgnoreStart
171
- TimberHelper::error_log( 'Error resizing image' );
172
- TimberHelper::error_log( $result );
173
  return false;
174
  // @codeCoverageIgnoreEnd
175
  } else {
176
  return true;
177
  }
178
- } else if ( isset( $image->error_data['error_loading_image'] ) ) {
179
  // @codeCoverageIgnoreStart
180
- TimberHelper::error_log( 'Error loading ' . $image->error_data['error_loading_image'] );
181
  } else {
182
- TimberHelper::error_log( $image );
183
  // @codeCoverageIgnoreEnd
184
  }
185
  }
1
  <?php
2
+
3
+ namespace Timber\Image\Operation;
4
+
5
+ use Timber\Helper;
6
+ use Timber\Image\Operation as ImageOperation;
7
+
8
  /**
9
  * Changes image to new size, by shrinking/enlarging
10
  * then cropping to respect new ratio.
14
  * - height of new image
15
  * - crop method
16
  */
17
+ class Resize extends ImageOperation {
18
 
19
  private $w, $h, $crop;
20
 
21
  /**
22
  * @param int $w width of new image
23
  * @param int $h height of new image
24
+ * @param string $crop cropping method, one of: 'default', 'center', 'top', 'bottom', 'left', 'right', 'top-center', 'bottom-center'.
25
  */
26
+ function __construct( $w, $h, $crop ) {
27
  $this->w = $w;
28
  $this->h = $h;
29
  // Sanitize crop position
30
+ $allowed_crop_positions = array('default', 'center', 'top', 'bottom', 'left', 'right', 'top-center', 'bottom-center');
31
+ if ( $crop !== false && !in_array($crop, $allowed_crop_positions) ) {
32
  $crop = $allowed_crop_positions[0];
33
  }
34
  $this->crop = $crop;
39
  * @param string $src_extension the extension (ex: .jpg)
40
  * @return string the final filename to be used (ex: my-awesome-pic-300x200-c-default.jpg)
41
  */
42
+ public function filename( $src_filename, $src_extension ) {
43
  $w = 0;
44
  $h = 0;
45
  if ( $this->w ) {
48
  if ( $this->h ) {
49
  $h = $this->h;
50
  }
51
+ $result = $src_filename.'-'.$w.'x'.$h.'-c-'.($this->crop ? $this->crop : 'f'); // Crop will be either user named or f (false)
52
+ if ( $src_extension ) {
53
  $result .= '.'.$src_extension;
54
  }
55
  return $result;
60
  * @param string $save_filename
61
  */
62
  protected function run_animated_gif( $load_filename, $save_filename ) {
63
+ $image = wp_get_image_editor($load_filename);
64
  $current_size = $image->get_size();
65
  $src_w = $current_size['width'];
66
  $src_h = $current_size['height'];
69
  if ( !class_exists('Imagick') ) {
70
  return false;
71
  }
72
+ $image = new \Imagick($load_filename);
73
  $image = $image->coalesceImages();
74
+ $crop = self::get_target_sizes($load_filename);
75
+ foreach ( $image as $frame ) {
76
  $frame->cropImage($crop['src_w'], $crop['src_h'], $crop['x'], $crop['y']);
77
  $frame->thumbnailImage($w, $h);
78
  $frame->setImagePage($w, $h, 0, 0);
81
  return $image->writeImages($save_filename, true);
82
  }
83
 
84
+ /**
85
+ * @param string $load_filename
86
+ */
87
  protected function get_target_sizes( $load_filename ) {
88
+ $image = wp_get_image_editor($load_filename);
89
  $w = $this->w;
90
  $h = $this->h;
91
  $crop = $this->crop;
95
  $src_h = $current_size['height'];
96
  $src_ratio = $src_w / $src_h;
97
  if ( !$h ) {
98
+ $h = round($w / $src_ratio);
99
  }
100
  if ( !$w ) {
101
  //the user wants to resize based on constant height
102
+ $w = round($h * $src_ratio);
103
  }
104
  if ( !$crop ) {
105
  return array(
113
  $src_wt = $src_h * $dest_ratio;
114
  $src_ht = $src_w / $dest_ratio;
115
  $src_x = $src_w / 2 - $src_wt / 2;
116
+ $src_y = ($src_h - $src_ht) / 6;
117
  //now specific overrides based on options:
118
+ switch ( $crop ) {
119
+ case 'center':
120
+ // Get source x and y
121
+ $src_x = round(($src_w - $src_wt) / 2);
122
+ $src_y = round(($src_h - $src_ht) / 2);
123
+ break;
124
+
125
+ case 'top':
126
+ $src_y = 0;
127
+ break;
128
+
129
+ case 'bottom':
130
+ $src_y = $src_h - $src_ht;
131
+ break;
132
+
133
+ case 'top-center':
134
+ $src_y = round(($src_h - $src_ht) / 4);
135
+ break;
136
+
137
+ case 'bottom-center':
138
+ $src_y = $src_h - $src_ht - round(($src_h - $src_ht) / 4);
139
+ break;
140
+
141
+ case 'left':
142
+ $src_x = 0;
143
+ break;
144
+
145
+ case 'right':
146
+ $src_x = $src_w - $src_wt;
147
+ break;
148
  }
149
  // Crop the image
150
+ return ($dest_ratio > $src_ratio)
151
+ ? array(
152
  'x' => 0, 'y' => $src_y,
153
  'src_w' => $src_w, 'src_h' => $src_ht,
154
  'target_w' => $w, 'target_h' => $h
155
+ )
156
+ : array(
157
+ 'x' => $src_x, 'y' => 0,
158
+ 'src_w' => $src_wt, 'src_h' => $src_h,
159
+ 'target_w' => $w, 'target_h' => $h
160
  );
 
 
 
 
 
 
161
  }
162
 
163
  /**
168
  * (ex: /src/var/www/wp-content/uploads/my-pic.jpg)
169
  * @param string $save_filename filepath (not URL) where result file should be saved
170
  * (ex: /src/var/www/wp-content/uploads/my-pic-300x200-c-default.jpg)
171
+ * @return boolean|null true if everything went fine, false otherwise
172
  */
173
+ public function run( $load_filename, $save_filename ) {
174
  //should be resized by gif resizer
175
+ if ( \Timber\ImageHelper::is_animated_gif($load_filename) ) {
176
  //attempt to resize
177
  //return if successful
178
  //proceed if not
179
  $gif = self::run_animated_gif($load_filename, $save_filename);
180
+ if ( $gif ) {
181
  return true;
182
  }
183
  }
184
+ $image = wp_get_image_editor($load_filename);
185
+ if ( !is_wp_error($image) ) {
186
+ $crop = self::get_target_sizes($load_filename);
187
+ $image->crop($crop['x'],
188
  $crop['y'],
189
  $crop['src_w'],
190
  $crop['src_h'],
191
  $crop['target_w'],
192
  $crop['target_h']
193
  );
194
+ $result = $image->save($save_filename);
195
+ if ( is_wp_error($result) ) {
196
  // @codeCoverageIgnoreStart
197
+ Helper::error_log('Error resizing image');
198
+ Helper::error_log($result);
199
  return false;
200
  // @codeCoverageIgnoreEnd
201
  } else {
202
  return true;
203
  }
204
+ } else if ( isset($image->error_data['error_loading_image']) ) {
205
  // @codeCoverageIgnoreStart
206
+ Helper::error_log('Error loading '.$image->error_data['error_loading_image']);
207
  } else {
208
+ Helper::error_log($image);
209
  // @codeCoverageIgnoreEnd
210
  }
211
  }
lib/Image/Operation/Retina.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber\Image\Operation;
4
+
5
+ use Timber\Helper;
6
+ use Timber\Image\Operation as ImageOperation;
7
+
8
+ /**
9
+ * Contains the class for running image retina-izing operations
10
+ */
11
+
12
+ /**
13
+ * Increases image size by a given factor
14
+ * Arguments:
15
+ * - factor by which to multiply image dimensions
16
+ * @property float $factor the factor (ex: 2, 1.5, 1.75) to multiply dimension by
17
+ */
18
+ class Retina extends ImageOperation {
19
+
20
+ private $factor;
21
+
22
+ /**
23
+ * Construct our operation
24
+ * @param float $factor to multiply original dimensions by
25
+ */
26
+ function __construct( $factor ) {
27
+ $this->factor = $factor;
28
+ }
29
+
30
+ /**
31
+ * Generates the final filename based on the source's name and extension
32
+ *
33
+ * @param string $src_filename the basename of the file (ex: my-awesome-pic)
34
+ * @param string $src_extension the extension (ex: .jpg)
35
+ * @return string the final filename to be used (ex: my-awesome-pic@2x.jpg)
36
+ */
37
+ function filename( $src_filename, $src_extension ) {
38
+ $newbase = $src_filename.'@'.$this->factor.'x'; // add @2x, @3x, @1.5x, etc.
39
+ $new_name = $newbase.'.'.$src_extension;
40
+ return $new_name;
41
+ }
42
+
43
+ /**
44
+ * Performs the actual image manipulation,
45
+ * including saving the target file.
46
+ *
47
+ * @param string $load_filename filepath (not URL) to source file
48
+ * (ex: /src/var/www/wp-content/uploads/my-pic.jpg)
49
+ * @param string $save_filename filepath (not URL) where result file should be saved
50
+ * (ex: /src/var/www/wp-content/uploads/my-pic@2x.jpg)
51
+ * @return bool true if everything went fine, false otherwise
52
+ */
53
+ function run( $load_filename, $save_filename ) {
54
+ $image = wp_get_image_editor($load_filename);
55
+ if ( !is_wp_error($image) ) {
56
+ $current_size = $image->get_size();
57
+ $src_w = $current_size['width'];
58
+ $src_h = $current_size['height'];
59
+ // Get ratios
60
+ $w = $src_w * $this->factor;
61
+ $h = $src_h * $this->factor;
62
+ $image->crop(0, 0, $src_w, $src_h, $w, $h);
63
+ $result = $image->save($save_filename);
64
+ if ( is_wp_error($result) ) {
65
+ // @codeCoverageIgnoreStart
66
+ Helper::error_log('Error resizing image');
67
+ Helper::error_log($result);
68
+ return false;
69
+ // @codeCoverageIgnoreEnd
70
+ } else {
71
+ return true;
72
+ }
73
+ } else if ( isset($image->error_data['error_loading_image']) ) {
74
+ Helper::error_log('Error loading '.$image->error_data['error_loading_image']);
75
+ } else {
76
+ Helper::error_log($image);
77
+ }
78
+ return false;
79
+ }
80
+ }
lib/{image/timber-image-operation-tojpg.php → Image/Operation/ToJpg.php} RENAMED
@@ -1,18 +1,22 @@
1
  <?php
2
 
 
 
 
 
3
  /**
4
  * Implements converting a PNG file to JPG.
5
  * Argument:
6
  * - color to fill transparent zones
7
  */
8
- class TimberImageOperationToJpg extends TimberImageOperation {
9
 
10
  private $color;
11
 
12
  /**
13
  * @param string $color hex string of color to use for transparent zones
14
  */
15
- function __construct($color) {
16
  $this->color = $color;
17
  }
18
 
@@ -21,8 +25,8 @@ class TimberImageOperationToJpg extends TimberImageOperation {
21
  * @param string $src_extension ignored
22
  * @return string the final filename to be used (ex: my-awesome-pic.jpg)
23
  */
24
- function filename($src_filename, $src_extension = 'jpg') {
25
- $new_name = $src_filename . '.jpg';
26
  return $new_name;
27
  }
28
 
@@ -35,15 +39,15 @@ class TimberImageOperationToJpg extends TimberImageOperation {
35
  * (ex: /src/var/www/wp-content/uploads/my-pic.png)
36
  * @return bool true if everything went fine, false otherwise
37
  */
38
- function run($load_filename, $save_filename) {
39
- $input = self::image_create( $load_filename );
40
- list( $width, $height ) = getimagesize( $load_filename );
41
- $output = imagecreatetruecolor( $width, $height );
42
- $c = self::hexrgb( $this->color );
43
- $color = imagecolorallocate( $output, $c['red'], $c['green'], $c['blue'] );
44
- imagefilledrectangle( $output, 0, 0, $width, $height, $color );
45
- imagecopy( $output, $input, 0, 0, 0, 0, $width, $height );
46
- imagejpeg( $output, $save_filename );
47
  return true;
48
  }
49
 
@@ -55,7 +59,7 @@ class TimberImageOperationToJpg extends TimberImageOperation {
55
  function image_create( $filename, $ext = 'auto' ) {
56
  if ( $ext == 'auto' ) {
57
  $ext = wp_check_filetype($filename);
58
- if (isset($ext['ext'])) {
59
  $ext = $ext['ext'];
60
  }
61
  }
@@ -69,6 +73,6 @@ class TimberImageOperationToJpg extends TimberImageOperation {
69
  if ( $ext == 'jpg' || $ext == 'jpeg' ) {
70
  return imagecreatefromjpeg($filename);
71
  }
72
- throw new InvalidArgumentException( 'image_create only accepts PNG, GIF and JPGs. File extension was: '.$ext );
73
  }
74
  }
1
  <?php
2
 
3
+ namespace Timber\Image\Operation;
4
+
5
+ use Timber\Image\Operation as ImageOperation;
6
+
7
  /**
8
  * Implements converting a PNG file to JPG.
9
  * Argument:
10
  * - color to fill transparent zones
11
  */
12
+ class ToJpg extends ImageOperation {
13
 
14
  private $color;
15
 
16
  /**
17
  * @param string $color hex string of color to use for transparent zones
18
  */
19
+ function __construct( $color ) {
20
  $this->color = $color;
21
  }
22
 
25
  * @param string $src_extension ignored
26
  * @return string the final filename to be used (ex: my-awesome-pic.jpg)
27
  */
28
+ function filename( $src_filename, $src_extension = 'jpg' ) {
29
+ $new_name = $src_filename.'.jpg';
30
  return $new_name;
31
  }
32
 
39
  * (ex: /src/var/www/wp-content/uploads/my-pic.png)
40
  * @return bool true if everything went fine, false otherwise
41
  */
42
+ function run( $load_filename, $save_filename ) {
43
+ $input = self::image_create($load_filename);
44
+ list($width, $height) = getimagesize($load_filename);
45
+ $output = imagecreatetruecolor($width, $height);
46
+ $c = self::hexrgb($this->color);
47
+ $color = imagecolorallocate($output, $c['red'], $c['green'], $c['blue']);
48
+ imagefilledrectangle($output, 0, 0, $width, $height, $color);
49
+ imagecopy($output, $input, 0, 0, 0, 0, $width, $height);
50
+ imagejpeg($output, $save_filename);
51
  return true;
52
  }
53
 
59
  function image_create( $filename, $ext = 'auto' ) {
60
  if ( $ext == 'auto' ) {
61
  $ext = wp_check_filetype($filename);
62
+ if ( isset($ext['ext']) ) {
63
  $ext = $ext['ext'];
64
  }
65
  }
73
  if ( $ext == 'jpg' || $ext == 'jpeg' ) {
74
  return imagecreatefromjpeg($filename);
75
  }
76
+ throw new \InvalidArgumentException('image_create only accepts PNG, GIF and JPGs. File extension was: '.$ext);
77
  }
78
  }
lib/{timber-image-helper.php → ImageHelper.php} RENAMED
@@ -1,8 +1,18 @@
1
  <?php
2
 
 
 
 
 
 
 
 
 
 
 
3
  /**
4
  * Implements the Twig image filters:
5
- * https://github.com/jarednova/timber/wiki/Image-cookbook#arbitrary-resizing-of-images
6
  * - resize
7
  * - retina
8
  * - letterbox
@@ -13,7 +23,7 @@
13
  * - most of the work is common to all filters (URL analysis, directory gymnastics, file caching, error management) and done by private static functions
14
  * - the specific part (actual image processing) is delegated to dedicated subclasses of TimberImageOperation
15
  */
16
- class TimberImageHelper {
17
 
18
  const BASE_UPLOADS = 1;
19
  const BASE_CONTENT = 2;
@@ -30,8 +40,8 @@ class TimberImageHelper {
30
  *
31
  * @api
32
  * @param string $src an URL (absolute or relative) to the original image
33
- * @param int|string $w target width(int) or WordPress image size (WP-set or user-defined)
34
- * @param int $h target height (ignored if $w is WP image size)
35
  * @param string $crop your choices are 'default', 'center', 'top', 'bottom', 'left', 'right'
36
  * @param bool $force
37
  * @example
@@ -44,15 +54,15 @@ class TimberImageHelper {
44
  * @return string (ex: )
45
  */
46
  public static function resize( $src, $w, $h = 0, $crop = 'default', $force = false ) {
47
- if (!is_numeric($w) && is_string($w)) {
48
- if ($sizes = self::find_wp_dimensions($w)) {
49
  $w = $sizes['w'];
50
  $h = $sizes['h'];
51
  } else {
52
  return $src;
53
  }
54
  }
55
- $op = new TimberImageOperationResize($w, $h, $crop);
56
  return self::_operate($src, $op, $force);
57
  }
58
 
@@ -61,21 +71,21 @@ class TimberImageHelper {
61
  * @param string $size the image size to search for
62
  * can be WordPress-defined ("medium")
63
  * or user-defined ("my-awesome-size")
64
- * @return array {
65
  * @type int w
66
  * @type int h
67
  * }
68
  */
69
  private static function find_wp_dimensions( $size ) {
70
  global $_wp_additional_image_sizes;
71
- if (isset($_wp_additional_image_sizes[$size])) {
72
  $w = $_wp_additional_image_sizes[$size]['width'];
73
  $h = $_wp_additional_image_sizes[$size]['height'];
74
- } else if (in_array($size, array('thumbnail', 'medium', 'large'))) {
75
  $w = get_option($size.'_size_w');
76
  $h = get_option($size.'_size_h');
77
  }
78
- if (isset($w) && isset($h) && ($w || $h)) {
79
  return array('w' => $w, 'h' => $h);
80
  }
81
  return false;
@@ -91,7 +101,7 @@ class TimberImageHelper {
91
  * @return string url to the new image
92
  */
93
  public static function retina_resize( $src, $multiplier = 2, $force = false ) {
94
- $op = new TimberImageOperationRetina($multiplier);
95
  return self::_operate($src, $op, $force);
96
  }
97
 
@@ -106,25 +116,25 @@ class TimberImageHelper {
106
  return false;
107
  }
108
  //its a gif so test
109
- if( !($fh = @fopen($file, 'rb')) ) {
110
  return false;
111
- }
112
- $count = 0;
113
- //an animated gif contains multiple "frames", with each frame having a
114
- //header made up of:
115
- // * a static 4-byte sequence (\x00\x21\xF9\x04)
116
- // * 4 variable bytes
117
- // * a static 2-byte sequence (\x00\x2C)
118
 
119
- // We read through the file til we reach the end of the file, or we've found
120
- // at least 2 frame headers
121
- while(!feof($fh) && $count < 2) {
122
- $chunk = fread($fh, 1024 * 100); //read 100kb at a time
123
- $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00[\x2C\x21]#s', $chunk, $matches);
124
- }
125
 
126
- fclose($fh);
127
- return $count > 1;
128
  }
129
 
130
  /**
@@ -139,7 +149,7 @@ class TimberImageHelper {
139
  * @return mixed|null|string
140
  */
141
  public static function letterbox( $src, $w, $h, $color = '#000000', $force = false ) {
142
- $op = new TimberImageOperationLetterbox($w, $h, $color);
143
  return self::_operate($src, $op, $force);
144
  }
145
 
@@ -151,7 +161,7 @@ class TimberImageHelper {
151
  * @return string
152
  */
153
  public static function img_to_jpg( $src, $bghex = '#FFFFFF', $force = false ) {
154
- $op = new TimberImageOperationToJpg($bghex);
155
  return self::_operate($src, $op, $force);
156
  }
157
 
@@ -159,13 +169,13 @@ class TimberImageHelper {
159
  * Deletes all resized versions of an image when the source is deleted
160
  */
161
  protected static function add_actions() {
162
- add_action( 'delete_attachment', function ( $post_id ) {
163
- $post = get_post( $post_id );
164
- $image_types = array( 'image/jpeg', 'image/png', 'image/gif', 'image/jpg' );
165
- if ( in_array( $post->post_mime_type, $image_types ) ) {
166
- $attachment = new TimberImage( $post_id );
167
  if ( $attachment->file_loc ) {
168
- TimberImageHelper::delete_generated_files( $attachment->file_loc );
169
  }
170
  }
171
  } );
@@ -176,9 +186,9 @@ class TimberImageHelper {
176
  * for example /wp-content or /content
177
  */
178
  protected static function add_constants() {
179
- if ( !defined( 'WP_CONTENT_SUBDIR' ) ) {
180
- $wp_content_path = str_replace( home_url(), '', WP_CONTENT_URL );
181
- define( 'WP_CONTENT_SUBDIR', $wp_content_path );
182
  }
183
  }
184
 
@@ -188,8 +198,8 @@ class TimberImageHelper {
188
  * @return void
189
  */
190
  static function add_filters() {
191
- add_filter( 'upload_dir', function ( $arr ) {
192
- $arr['relative'] = str_replace( home_url(), '', $arr['baseurl'] );
193
  return $arr;
194
  } );
195
  }
@@ -198,18 +208,20 @@ class TimberImageHelper {
198
  /**
199
  * Deletes the auto-generated files for resize and letterboxing created by Timber
200
  * @param string $local_file ex: /var/www/wp-content/uploads/2015/my-pic.jpg
201
- * or: http://example.org/wp-content/uploads/2015/my-pic.jpg
202
  */
203
  static function delete_generated_files( $local_file ) {
204
- if (TimberURLHelper::is_absolute( $local_file ) ) {
205
- $local_file = TimberURLHelper::url_to_file_system( $local_file );
206
  }
207
- $info = pathinfo( $local_file );
208
  $dir = $info['dirname'];
209
  $ext = $info['extension'];
210
  $filename = $info['filename'];
211
- self::process_delete_generated_files( $filename, $ext, $dir, '-[0-9999999]*', '-[0-9]*x[0-9]*-c-[a-z]*.' );
212
- self::process_delete_generated_files( $filename, $ext, $dir, '-lbox-[0-9999999]*', '-lbox-[0-9]*x[0-9]*-[a-zA-Z0-9]*.' );
 
 
213
  }
214
 
215
  /**
@@ -226,19 +238,18 @@ class TimberImageHelper {
226
  * @param string $search_pattern pattern of files to pluck from
227
  * @param string $match_pattern pattern of files to go forth and delete
228
  */
229
- protected static function process_delete_generated_files( $filename, $ext, $dir, $search_pattern, $match_pattern ) {
230
- $searcher = '/' . $filename . $search_pattern;
231
- foreach ( glob( $dir . $searcher ) as $found_file ) {
232
- $regexdir = str_replace( '/', '\/', $dir );
233
- $pattern = '/' . ( $regexdir ) . '\/' . $filename . $match_pattern . $ext . '/';
234
- $match = preg_match( $pattern, $found_file );
235
- if ( $match ) {
236
- unlink( $found_file );
237
  }
238
  }
239
  }
240
 
241
-
242
  /**
243
  * Determines the filepath corresponding to a given URL
244
  *
@@ -247,7 +258,7 @@ class TimberImageHelper {
247
  */
248
  public static function get_server_location( $url ) {
249
  // if we're already an absolute dir, just return
250
- if ( 0 === strpos( $url, ABSPATH ) ) {
251
  return $url;
252
  }
253
  // otherwise, analyze URL then build mapping path
@@ -266,14 +277,14 @@ class TimberImageHelper {
266
  $upload = wp_upload_dir();
267
  $dir = $upload['path'];
268
  $filename = $file;
269
- $file = parse_url( $file );
270
- $path_parts = pathinfo( $file['path'] );
271
- $basename = md5( $filename );
272
  $ext = 'jpg';
273
- if ( isset( $path_parts['extension'] ) ) {
274
  $ext = $path_parts['extension'];
275
  }
276
- return $dir . '/' . $basename . '.' . $ext;
277
  }
278
 
279
  /**
@@ -283,27 +294,27 @@ class TimberImageHelper {
283
  * @return string the URL to the downloaded file
284
  */
285
  public static function sideload_image( $file ) {
286
- $loc = self::get_sideloaded_file_loc( $file );
287
- if ( file_exists( $loc ) ) {
288
- return TimberURLHelper::preslashit( TimberURLHelper::get_rel_path( $loc ) );
289
  }
290
  // Download file to temp location
291
- if ( !function_exists( 'download_url' ) ) {
292
- require_once ABSPATH . '/wp-admin/includes/file.php';
293
  }
294
- $tmp = download_url( $file );
295
- preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches );
296
  $file_array = array();
297
- $file_array['name'] = basename( $matches[0] );
298
  $file_array['tmp_name'] = $tmp;
299
  // If error storing temporarily, unlink
300
- if ( is_wp_error( $tmp ) ) {
301
- @unlink( $file_array['tmp_name'] );
302
  $file_array['tmp_name'] = '';
303
  }
304
  // do the validation and storage stuff
305
- $locinfo = pathinfo( $loc );
306
- $file = wp_upload_bits( $locinfo['basename'], null, file_get_contents( $file_array['tmp_name'] ) );
307
  return $file['url'];
308
  }
309
 
@@ -315,10 +326,10 @@ class TimberImageHelper {
315
  * @param string $url an URL (absolute or relative) pointing to an image
316
  * @return array an array (see keys in code below)
317
  */
318
- private static function analyze_url($url) {
319
  $result = array(
320
  'url' => $url, // the initial url
321
- 'absolute' => TimberURLHelper::is_absolute($url), // is the url absolute or relative (to home_url)
322
  'base' => 0, // is the image in uploads dir, or in content dir (theme or plugin)
323
  'subdir' => '', // the path between base (uploads or content) and file
324
  'filename' => '', // the filename, without extension
@@ -327,31 +338,32 @@ class TimberImageHelper {
327
  );
328
  $upload_dir = wp_upload_dir();
329
  $tmp = $url;
330
- if ( 0 === strpos($tmp, ABSPATH) ) { // we've been given a dir, not an url
 
331
  $result['absolute'] = true;
332
  if ( 0 === strpos($tmp, $upload_dir['basedir']) ) {
333
- $result['base']= self::BASE_UPLOADS; // upload based
334
  $tmp = str_replace($upload_dir['basedir'], '', $tmp);
335
  }
336
  if ( 0 === strpos($tmp, WP_CONTENT_DIR) ) {
337
- $result['base']= self::BASE_CONTENT; // content based
338
  $tmp = str_replace(WP_CONTENT_DIR, '', $tmp);
339
  }
340
  } else {
341
- if (!$result['absolute']) {
342
  $tmp = home_url().$tmp;
343
  }
344
- if (0 === strpos($tmp, $upload_dir['baseurl'])) {
345
- $result['base']= self::BASE_UPLOADS; // upload based
346
  $tmp = str_replace($upload_dir['baseurl'], '', $tmp);
347
  }
348
- if (0 === strpos($tmp, content_url())) {
349
- $result['base']= self::BASE_CONTENT; // content-based
350
  $tmp = str_replace(content_url(), '', $tmp);
351
  }
352
  }
353
  $parts = pathinfo($tmp);
354
- $result['subdir'] = $parts['dirname'];
355
  $result['filename'] = $parts['filename'];
356
  $result['extension'] = $parts['extension'];
357
  $result['basename'] = $parts['basename'];
@@ -368,20 +380,20 @@ class TimberImageHelper {
368
  * @param bool $absolute should the returned URL be absolute (include protocol+host), or relative
369
  * @return string the URL
370
  */
371
- private static function _get_file_url($base, $subdir, $filename, $absolute) {
372
  $url = '';
373
- if( self::BASE_UPLOADS == $base ) {
374
  $upload_dir = wp_upload_dir();
375
  $url = $upload_dir['baseurl'];
376
  }
377
- if( self::BASE_CONTENT == $base ) {
378
  $url = content_url();
379
  }
380
- if(!empty($subdir)) {
381
  $url .= $subdir;
382
  }
383
  $url .= '/'.$filename;
384
- if(!$absolute) {
385
  $url = str_replace(home_url(), '', $url);
386
  }
387
  // $url = TimberURLHelper::remove_double_slashes( $url);
@@ -396,16 +408,16 @@ class TimberImageHelper {
396
  * @param string $filename file name, including extension (but no path)
397
  * @return string the file location
398
  */
399
- private static function _get_file_path($base, $subdir, $filename) {
400
  $path = '';
401
- if(self::BASE_UPLOADS == $base) {
402
  $upload_dir = wp_upload_dir();
403
  $path = $upload_dir['basedir'];
404
  }
405
- if(self::BASE_CONTENT == $base) {
406
  $path = WP_CONTENT_DIR;
407
  }
408
- if(!empty($subdir)) {
409
  $path .= $subdir;
410
  }
411
  $path .= '/'.$filename;
@@ -427,12 +439,15 @@ class TimberImageHelper {
427
  *
428
  */
429
  private static function _operate( $src, $op, $force = false ) {
430
- if ( empty( $src ) ) {
431
  return '';
432
  }
 
 
433
  // if external image, load it first
434
- if ( TimberURLHelper::is_external_content( $src ) ) {
435
- $src = self::sideload_image( $src );
 
436
  }
437
  // break down URL into components
438
  $au = self::analyze_url($src);
@@ -453,18 +468,25 @@ class TimberImageHelper {
453
  $au['subdir'],
454
  $au['basename']
455
  );
 
 
 
 
456
  // if already exists...
457
- if ( file_exists( $new_server_path ) ) {
458
  if ( $force ) {
459
  // Force operation - warning: will regenerate the image on every pageload, use for testing purposes only!
460
- unlink( $new_server_path );
461
  } else {
462
  // return existing file (caching)
463
  return $new_url;
464
  }
465
  }
466
  // otherwise generate result file
467
- if($op->run($old_server_path, $new_server_path)) {
 
 
 
468
  return $new_url;
469
  } else {
470
  // in case of error, we return source file itself
@@ -475,9 +497,9 @@ class TimberImageHelper {
475
 
476
  // -- the below methods are just used for unit testing the URL generation code
477
  //
478
- static function get_letterbox_file_url($url, $w, $h, $color) {
479
  $au = self::analyze_url($url);
480
- $op = new TimberImageOperationLetterbox($w, $h, $color);
481
  $new_url = self::_get_file_url(
482
  $au['base'],
483
  $au['subdir'],
@@ -486,9 +508,9 @@ class TimberImageHelper {
486
  );
487
  return $new_url;
488
  }
489
- public static function get_letterbox_file_path($url, $w, $h, $color ) {
490
  $au = self::analyze_url($url);
491
- $op = new TimberImageOperationLetterbox($w, $h, $color);
492
  $new_path = self::_get_file_path(
493
  $au['base'],
494
  $au['subdir'],
@@ -496,9 +518,9 @@ class TimberImageHelper {
496
  );
497
  return $new_path;
498
  }
499
- static function get_resize_file_url($url, $w, $h, $crop) {
500
  $au = self::analyze_url($url);
501
- $op = new TimberImageOperationResize($w, $h, $crop);
502
  $new_url = self::_get_file_url(
503
  $au['base'],
504
  $au['subdir'],
@@ -507,9 +529,9 @@ class TimberImageHelper {
507
  );
508
  return $new_url;
509
  }
510
- static function get_resize_file_path($url, $w, $h, $crop) {
511
  $au = self::analyze_url($url);
512
- $op = new TimberImageOperationResize($w, $h, $crop);
513
  $new_path = self::_get_file_path(
514
  $au['base'],
515
  $au['subdir'],
@@ -517,6 +539,4 @@ class TimberImageHelper {
517
  );
518
  return $new_path;
519
  }
520
-
521
-
522
- }
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\Image;
6
+ use Timber\Image\Operation\ToJpg;
7
+ use Timber\Image\Operation\Resize;
8
+ use Timber\Image\Operation\Retina;
9
+ use Timber\Image\Operation\Letterbox;
10
+
11
+ use Timber\URLHelper;
12
+
13
  /**
14
  * Implements the Twig image filters:
15
+ * https://github.com/timber/timber/wiki/Image-cookbook#arbitrary-resizing-of-images
16
  * - resize
17
  * - retina
18
  * - letterbox
23
  * - most of the work is common to all filters (URL analysis, directory gymnastics, file caching, error management) and done by private static functions
24
  * - the specific part (actual image processing) is delegated to dedicated subclasses of TimberImageOperation
25
  */
26
+ class ImageHelper {
27
 
28
  const BASE_UPLOADS = 1;
29
  const BASE_CONTENT = 2;
40
  *
41
  * @api
42
  * @param string $src an URL (absolute or relative) to the original image
43
+ * @param int|string $w target width(int) or WordPress image size (WP-set or user-defined).
44
+ * @param int $h target height (ignored if $w is WP image size). If not set, will ignore and resize based on $w only.
45
  * @param string $crop your choices are 'default', 'center', 'top', 'bottom', 'left', 'right'
46
  * @param bool $force
47
  * @example
54
  * @return string (ex: )
55
  */
56
  public static function resize( $src, $w, $h = 0, $crop = 'default', $force = false ) {
57
+ if ( !is_numeric($w) && is_string($w) ) {
58
+ if ( $sizes = self::find_wp_dimensions($w) ) {
59
  $w = $sizes['w'];
60
  $h = $sizes['h'];
61
  } else {
62
  return $src;
63
  }
64
  }
65
+ $op = new Image\Operation\Resize($w, $h, $crop);
66
  return self::_operate($src, $op, $force);
67
  }
68
 
71
  * @param string $size the image size to search for
72
  * can be WordPress-defined ("medium")
73
  * or user-defined ("my-awesome-size")
74
+ * @return false|array {
75
  * @type int w
76
  * @type int h
77
  * }
78
  */
79
  private static function find_wp_dimensions( $size ) {
80
  global $_wp_additional_image_sizes;
81
+ if ( isset($_wp_additional_image_sizes[$size]) ) {
82
  $w = $_wp_additional_image_sizes[$size]['width'];
83
  $h = $_wp_additional_image_sizes[$size]['height'];
84
+ } else if ( in_array($size, array('thumbnail', 'medium', 'large')) ) {
85
  $w = get_option($size.'_size_w');
86
  $h = get_option($size.'_size_h');
87
  }
88
+ if ( isset($w) && isset($h) && ($w || $h) ) {
89
  return array('w' => $w, 'h' => $h);
90
  }
91
  return false;
101
  * @return string url to the new image
102
  */
103
  public static function retina_resize( $src, $multiplier = 2, $force = false ) {
104
+ $op = new Image\Operation\Retina($multiplier);
105
  return self::_operate($src, $op, $force);
106
  }
107
 
116
  return false;
117
  }
118
  //its a gif so test
119
+ if ( !($fh = @fopen($file, 'rb')) ) {
120
  return false;
121
+ }
122
+ $count = 0;
123
+ //an animated gif contains multiple "frames", with each frame having a
124
+ //header made up of:
125
+ // * a static 4-byte sequence (\x00\x21\xF9\x04)
126
+ // * 4 variable bytes
127
+ // * a static 2-byte sequence (\x00\x2C)
128
 
129
+ // We read through the file til we reach the end of the file, or we've found
130
+ // at least 2 frame headers
131
+ while ( !feof($fh) && $count < 2 ) {
132
+ $chunk = fread($fh, 1024 * 100); //read 100kb at a time
133
+ $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00[\x2C\x21]#s', $chunk, $matches);
134
+ }
135
 
136
+ fclose($fh);
137
+ return $count > 1;
138
  }
139
 
140
  /**
149
  * @return mixed|null|string
150
  */
151
  public static function letterbox( $src, $w, $h, $color = '#000000', $force = false ) {
152
+ $op = new Letterbox($w, $h, $color);
153
  return self::_operate($src, $op, $force);
154
  }
155
 
161
  * @return string
162
  */
163
  public static function img_to_jpg( $src, $bghex = '#FFFFFF', $force = false ) {
164
+ $op = new Image\Operation\ToJpg($bghex);
165
  return self::_operate($src, $op, $force);
166
  }
167
 
169
  * Deletes all resized versions of an image when the source is deleted
170
  */
171
  protected static function add_actions() {
172
+ add_action('delete_attachment', function( $post_id ) {
173
+ $post = get_post($post_id);
174
+ $image_types = array('image/jpeg', 'image/png', 'image/gif', 'image/jpg');
175
+ if ( in_array($post->post_mime_type, $image_types) ) {
176
+ $attachment = new Image($post_id);
177
  if ( $attachment->file_loc ) {
178
+ self::delete_generated_files($attachment->file_loc);
179
  }
180
  }
181
  } );
186
  * for example /wp-content or /content
187
  */
188
  protected static function add_constants() {
189
+ if ( !defined('WP_CONTENT_SUBDIR') ) {
190
+ $wp_content_path = str_replace(home_url(), '', WP_CONTENT_URL);
191
+ define('WP_CONTENT_SUBDIR', $wp_content_path);
192
  }
193
  }
194
 
198
  * @return void
199
  */
200
  static function add_filters() {
201
+ add_filter('upload_dir', function( $arr ) {
202
+ $arr['relative'] = str_replace(home_url(), '', $arr['baseurl']);
203
  return $arr;
204
  } );
205
  }
208
  /**
209
  * Deletes the auto-generated files for resize and letterboxing created by Timber
210
  * @param string $local_file ex: /var/www/wp-content/uploads/2015/my-pic.jpg
211
+ * or: http://example.org/wp-content/uploads/2015/my-pic.jpg
212
  */
213
  static function delete_generated_files( $local_file ) {
214
+ if ( URLHelper::is_absolute($local_file) ) {
215
+ $local_file = URLHelper::url_to_file_system($local_file);
216
  }
217
+ $info = pathinfo($local_file);
218
  $dir = $info['dirname'];
219
  $ext = $info['extension'];
220
  $filename = $info['filename'];
221
+ self::process_delete_generated_files($filename, $ext, $dir, '-[0-9999999]*', '-[0-9]*x[0-9]*-c-[a-z]*.');
222
+ self::process_delete_generated_files($filename, $ext, $dir, '-lbox-[0-9999999]*', '-lbox-[0-9]*x[0-9]*-[a-zA-Z0-9]*.');
223
+ self::process_delete_generated_files($filename, 'jpg', $dir, '-tojpg.*');
224
+ self::process_delete_generated_files($filename, 'jpg', $dir, '-tojpg-[0-9999999]*');
225
  }
226
 
227
  /**
238
  * @param string $search_pattern pattern of files to pluck from
239
  * @param string $match_pattern pattern of files to go forth and delete
240
  */
241
+ protected static function process_delete_generated_files( $filename, $ext, $dir, $search_pattern, $match_pattern = null ) {
242
+ $searcher = '/'.$filename.$search_pattern;
243
+ foreach ( glob($dir.$searcher) as $found_file ) {
244
+ $regexdir = str_replace('/', '\/', $dir);
245
+ $pattern = '/'.($regexdir).'\/'.$filename.$match_pattern.$ext.'/';
246
+ $match = preg_match($pattern, $found_file);
247
+ if ( !$match_pattern || $match ) {
248
+ unlink($found_file);
249
  }
250
  }
251
  }
252
 
 
253
  /**
254
  * Determines the filepath corresponding to a given URL
255
  *
258
  */
259
  public static function get_server_location( $url ) {
260
  // if we're already an absolute dir, just return
261
+ if ( 0 === strpos($url, ABSPATH) ) {
262
  return $url;
263
  }
264
  // otherwise, analyze URL then build mapping path
277
  $upload = wp_upload_dir();
278
  $dir = $upload['path'];
279
  $filename = $file;
280
+ $file = parse_url($file);
281
+ $path_parts = pathinfo($file['path']);
282
+ $basename = md5($filename);
283
  $ext = 'jpg';
284
+ if ( isset($path_parts['extension']) ) {
285
  $ext = $path_parts['extension'];
286
  }
287
+ return $dir.'/'.$basename.'.'.$ext;
288
  }
289
 
290
  /**
294
  * @return string the URL to the downloaded file
295
  */
296
  public static function sideload_image( $file ) {
297
+ $loc = self::get_sideloaded_file_loc($file);
298
+ if ( file_exists($loc) ) {
299
+ return URLHelper::preslashit(URLHelper::get_rel_path($loc));
300
  }
301
  // Download file to temp location
302
+ if ( !function_exists('download_url') ) {
303
+ require_once ABSPATH.'/wp-admin/includes/file.php';
304
  }
305
+ $tmp = download_url($file);
306
+ preg_match('/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches);
307
  $file_array = array();
308
+ $file_array['name'] = basename($matches[0]);
309
  $file_array['tmp_name'] = $tmp;
310
  // If error storing temporarily, unlink
311
+ if ( is_wp_error($tmp) ) {
312
+ @unlink($file_array['tmp_name']);
313
  $file_array['tmp_name'] = '';
314
  }
315
  // do the validation and storage stuff
316
+ $locinfo = pathinfo($loc);
317
+ $file = wp_upload_bits($locinfo['basename'], null, file_get_contents($file_array['tmp_name']));
318
  return $file['url'];
319
  }
320
 
326
  * @param string $url an URL (absolute or relative) pointing to an image
327
  * @return array an array (see keys in code below)
328
  */
329
+ private static function analyze_url( $url ) {
330
  $result = array(
331
  'url' => $url, // the initial url
332
+ 'absolute' => URLHelper::is_absolute($url), // is the url absolute or relative (to home_url)
333
  'base' => 0, // is the image in uploads dir, or in content dir (theme or plugin)
334
  'subdir' => '', // the path between base (uploads or content) and file
335
  'filename' => '', // the filename, without extension
338
  );
339
  $upload_dir = wp_upload_dir();
340
  $tmp = $url;
341
+ if ( 0 === strpos($tmp, ABSPATH) ) {
342
+ // we've been given a dir, not an url
343
  $result['absolute'] = true;
344
  if ( 0 === strpos($tmp, $upload_dir['basedir']) ) {
345
+ $result['base'] = self::BASE_UPLOADS; // upload based
346
  $tmp = str_replace($upload_dir['basedir'], '', $tmp);
347
  }
348
  if ( 0 === strpos($tmp, WP_CONTENT_DIR) ) {
349
+ $result['base'] = self::BASE_CONTENT; // content based
350
  $tmp = str_replace(WP_CONTENT_DIR, '', $tmp);
351
  }
352
  } else {
353
+ if ( !$result['absolute'] ) {
354
  $tmp = home_url().$tmp;
355
  }
356
+ if ( 0 === strpos($tmp, $upload_dir['baseurl']) ) {
357
+ $result['base'] = self::BASE_UPLOADS; // upload based
358
  $tmp = str_replace($upload_dir['baseurl'], '', $tmp);
359
  }
360
+ if ( 0 === strpos($tmp, content_url()) ) {
361
+ $result['base'] = self::BASE_CONTENT; // content-based
362
  $tmp = str_replace(content_url(), '', $tmp);
363
  }
364
  }
365
  $parts = pathinfo($tmp);
366
+ $result['subdir'] = ($parts['dirname'] === '/') ? '' : $parts['dirname'];
367
  $result['filename'] = $parts['filename'];
368
  $result['extension'] = $parts['extension'];
369
  $result['basename'] = $parts['basename'];
380
  * @param bool $absolute should the returned URL be absolute (include protocol+host), or relative
381
  * @return string the URL
382
  */
383
+ private static function _get_file_url( $base, $subdir, $filename, $absolute ) {
384
  $url = '';
385
+ if ( self::BASE_UPLOADS == $base ) {
386
  $upload_dir = wp_upload_dir();
387
  $url = $upload_dir['baseurl'];
388
  }
389
+ if ( self::BASE_CONTENT == $base ) {
390
  $url = content_url();
391
  }
392
+ if ( !empty($subdir) ) {
393
  $url .= $subdir;
394
  }
395
  $url .= '/'.$filename;
396
+ if ( !$absolute ) {
397
  $url = str_replace(home_url(), '', $url);
398
  }
399
  // $url = TimberURLHelper::remove_double_slashes( $url);
408
  * @param string $filename file name, including extension (but no path)
409
  * @return string the file location
410
  */
411
+ private static function _get_file_path( $base, $subdir, $filename ) {
412
  $path = '';
413
+ if ( self::BASE_UPLOADS == $base ) {
414
  $upload_dir = wp_upload_dir();
415
  $path = $upload_dir['basedir'];
416
  }
417
+ if ( self::BASE_CONTENT == $base ) {
418
  $path = WP_CONTENT_DIR;
419
  }
420
+ if ( !empty($subdir) ) {
421
  $path .= $subdir;
422
  }
423
  $path .= '/'.$filename;
439
  *
440
  */
441
  private static function _operate( $src, $op, $force = false ) {
442
+ if ( empty($src) ) {
443
  return '';
444
  }
445
+ $external = false;
446
+
447
  // if external image, load it first
448
+ if ( URLHelper::is_external_content($src) ) {
449
+ $src = self::sideload_image($src);
450
+ $external = true;
451
  }
452
  // break down URL into components
453
  $au = self::analyze_url($src);
468
  $au['subdir'],
469
  $au['basename']
470
  );
471
+
472
+ $new_url = apply_filters( 'timber/image/new_url', $new_url );
473
+ $new_server_path = apply_filters( 'timber/image/new_path', $new_server_path );
474
+
475
  // if already exists...
476
+ if ( file_exists($new_server_path) ) {
477
  if ( $force ) {
478
  // Force operation - warning: will regenerate the image on every pageload, use for testing purposes only!
479
+ unlink($new_server_path);
480
  } else {
481
  // return existing file (caching)
482
  return $new_url;
483
  }
484
  }
485
  // otherwise generate result file
486
+ if ( $op->run($old_server_path, $new_server_path) ) {
487
+ if ( get_class($op) === 'TimberImageOperationResize' && $external ) {
488
+ $new_url = strtolower($new_url);
489
+ }
490
  return $new_url;
491
  } else {
492
  // in case of error, we return source file itself
497
 
498
  // -- the below methods are just used for unit testing the URL generation code
499
  //
500
+ static function get_letterbox_file_url( $url, $w, $h, $color ) {
501
  $au = self::analyze_url($url);
502
+ $op = new Image\Operation\Letterbox($w, $h, $color);
503
  $new_url = self::_get_file_url(
504
  $au['base'],
505
  $au['subdir'],
508
  );
509
  return $new_url;
510
  }
511
+ public static function get_letterbox_file_path( $url, $w, $h, $color ) {
512
  $au = self::analyze_url($url);
513
+ $op = new Image\Operation\Letterbox($w, $h, $color);
514
  $new_path = self::_get_file_path(
515
  $au['base'],
516
  $au['subdir'],
518
  );
519
  return $new_path;
520
  }
521
+ static function get_resize_file_url( $url, $w, $h, $crop ) {
522
  $au = self::analyze_url($url);
523
+ $op = new Image\Operation\Resize($w, $h, $crop);
524
  $new_url = self::_get_file_url(
525
  $au['base'],
526
  $au['subdir'],
529
  );
530
  return $new_url;
531
  }
532
+ static function get_resize_file_path( $url, $w, $h, $crop ) {
533
  $au = self::analyze_url($url);
534
+ $op = new Image\Operation\Resize($w, $h, $crop);
535
  $new_path = self::_get_file_path(
536
  $au['base'],
537
  $au['subdir'],
539
  );
540
  return $new_path;
541
  }
542
+ }
 
 
lib/Integrations.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\Integrations\ACF;
6
+
7
+ /**
8
+ * This is for integrating external plugins into timber
9
+ * @package timber
10
+ */
11
+ class Integrations {
12
+
13
+ public static function init() {
14
+
15
+ add_action( 'init', array( __CLASS__, 'maybe_init_acftimber' ) );
16
+
17
+ if ( class_exists( 'WP_CLI_Command' ) ) {
18
+ \WP_CLI::add_command( 'timber', 'Timber\Integrations\Timber_WP_CLI_Command' );
19
+ }
20
+ }
21
+
22
+ public static function maybe_init_acftimber() {
23
+
24
+ if ( class_exists( 'ACF' ) ) {
25
+ new ACF();
26
+ }
27
+
28
+ }
29
+ }
lib/Integrations/ACF.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber\Integrations;
4
+
5
+ class ACF {
6
+
7
+ function __construct() {
8
+ add_filter('timber_post_get_meta', array($this, 'post_get_meta'), 10, 2);
9
+ add_filter('timber_post_get_meta_field', array($this, 'post_get_meta_field'), 10, 3);
10
+ add_filter('timber_term_get_meta', array($this, 'term_get_meta'), 10, 3);
11
+ add_filter('timber_term_get_meta_field', array($this, 'term_get_meta_field'), 10, 4);
12
+ add_filter('timber_user_get_meta_field_pre', array($this, 'user_get_meta_field'), 10, 3);
13
+ add_filter('timber_term_set_meta', array($this, 'term_set_meta'), 10, 4);
14
+ }
15
+
16
+ function post_get_meta( $customs, $post_id ) {
17
+ return $customs;
18
+ }
19
+
20
+ function post_get_meta_field( $value, $post_id, $field_name ) {
21
+ return get_field($field_name, $post_id);
22
+ }
23
+
24
+ function term_get_meta_field( $value, $term_id, $field_name, $term ) {
25
+ $searcher = $term->taxonomy."_".$term->ID;
26
+ return get_field($field_name, $searcher);
27
+ }
28
+
29
+ function term_set_meta( $value, $field, $term_id, $term ) {
30
+ $searcher = $term->taxonomy."_".$term->ID;
31
+ update_field($field, $value, $searcher);
32
+ return $value;
33
+ }
34
+
35
+ function term_get_meta( $fields, $term_id, $term ) {
36
+ $searcher = $term->taxonomy."_".$term->ID; // save to a specific category
37
+ $fds = get_fields($searcher);
38
+ if ( is_array($fds) ) {
39
+ foreach ( $fds as $key => $value ) {
40
+ $key = preg_replace('/_/', '', $key, 1);
41
+ $key = str_replace($searcher, '', $key);
42
+ $key = preg_replace('/_/', '', $key, 1);
43
+ $field = get_field($key, $searcher);
44
+ $fields[$key] = $field;
45
+ }
46
+ $fields = array_merge($fields, $fds);
47
+ }
48
+ return $fields;
49
+ }
50
+
51
+ function user_get_meta( $fields, $user_id ) {
52
+ return $fields;
53
+ }
54
+
55
+ function user_get_meta_field( $value, $uid, $field ) {
56
+ return get_field($field, 'user_'.$uid);
57
+ }
58
+ }
59
+
60
+
lib/Integrations/Command.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber\Integrations;
4
+
5
+ use Timber\Loader;
6
+
7
+ /**
8
+ * These are methods that can be executed by WPCLI, other CLI mechanism or other external controllers
9
+ * @package timber
10
+ */
11
+ class Command {
12
+
13
+ public static function clear_cache( $mode = 'all' ) {
14
+ if ( is_array($mode) ) {
15
+ $mode = reset($mode);
16
+ }
17
+ if ( $mode == 'all' ) {
18
+ $twig_cache = self::clear_cache_twig();
19
+ $timber_cache = self::clear_cache_timber();
20
+ if ( $twig_cache && $timber_cache ) {
21
+ return true;
22
+ }
23
+ } else if ( $mode == 'twig' ) {
24
+ return self::clear_cache_twig();
25
+ } else if ( $mode == 'timber' ) {
26
+ return self::clear_cache_timber();
27
+ }
28
+ }
29
+
30
+ static function clear_cache_timber() {
31
+ $loader = new Loader();
32
+ return $loader->clear_cache_timber();
33
+ }
34
+
35
+ static function clear_cache_twig() {
36
+ $loader = new Loader();
37
+ return $loader->clear_cache_twig();
38
+ }
39
+
40
+ }
lib/Integrations/Timber_WP_CLI_Command.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber\Integrations;
4
+
5
+ use Timber\Integrations\Command;
6
+
7
+ if ( !class_exists('WP_CLI_Command') ) {
8
+ return;
9
+ }
10
+
11
+ class Timber_WP_CLI_Command extends \WP_CLI_Command {
12
+
13
+ /**
14
+ * Clears Timber and Twig's Cache
15
+ *
16
+ * ## EXAMPLES
17
+ *
18
+ * wp timber clear_cache
19
+ *
20
+ */
21
+ public function clear_cache( $mode = 'all' ) {
22
+ Command::clear_cache($mode);
23
+ }
24
+
25
+ /**
26
+ * Clears Twig's Cache
27
+ *
28
+ * ## EXAMPLES
29
+ *
30
+ * wp timber clear_cache_twig
31
+ *
32
+ */
33
+ function clear_cache_twig() {
34
+ $clear = Command::clear_cache_twig();
35
+ if ( $clear ) {
36
+ WP_CLI::success('Cleared contents of twig cache');
37
+ } else {
38
+ WP_CLI::warning('Failed to clear twig cache');
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Clears Timber's Cache
44
+ *
45
+ * ## EXAMPLES
46
+ *
47
+ * wp timber clear_cache_timber
48
+ *
49
+ */
50
+ function clear_cache_timber() {
51
+ $clear = Command::clear_cache_timber();
52
+ $message = 'Failed to clear timber cache';
53
+ if ( $clear ) {
54
+ $message = "Cleared contents of Timber's Cache";
55
+ WP_CLI::success($message);
56
+ } else {
57
+ WP_CLI::warning($message);
58
+ }
59
+ return $message;
60
+ }
61
+
62
+ }
lib/{timber-loader.php → Loader.php} RENAMED
@@ -1,6 +1,8 @@
1
  <?php
2
 
3
- class TimberLoader {
 
 
4
 
5
  const CACHEGROUP = 'timberloader';
6
 
@@ -26,7 +28,7 @@ class TimberLoader {
26
  /**
27
  * @param bool|string $caller the calling directory or false
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
  $this->cache_mode = apply_filters('timber/cache/mode', $this->cache_mode);
@@ -39,10 +41,10 @@ class TimberLoader {
39
  * @param string $cache_mode
40
  * @return bool|string
41
  */
42
- function render($file, $data = null, $expires = false, $cache_mode = self::CACHE_USE_DEFAULT) {
43
  // Different $expires if user is anonymous or logged in
44
- if (is_array($expires)) {
45
- if (is_user_logged_in() && isset($expires[1])) {
46
  $expires = $expires[1];
47
  } else {
48
  $expires = $expires[0];
@@ -51,15 +53,15 @@ class TimberLoader {
51
 
52
  $key = null;
53
  $output = false;
54
- if (false !== $expires) {
55
  ksort($data);
56
- $key = md5($file . json_encode($data));
57
  $output = $this->get_cache($key, self::CACHEGROUP, $cache_mode);
58
  }
59
 
60
- if (false === $output || null === $output) {
61
  $twig = $this->get_twig();
62
- if (strlen($file)) {
63
  $loader = $this->get_loader();
64
  $result = $loader->getCacheKey($file);
65
  do_action('timber_loader_render_file', $result);
@@ -69,7 +71,7 @@ class TimberLoader {
69
  $output = $twig->render($file, $data);
70
  }
71
 
72
- if (false !== $output && false !== $expires && null !== $key) {
73
  $this->set_cache($key, $output, self::CACHEGROUP, $expires, $cache_mode);
74
  }
75
  $output = apply_filters('timber_output', $output);
@@ -80,11 +82,11 @@ class TimberLoader {
80
  * @param array $filenames
81
  * @return bool
82
  */
83
- public function choose_template($filenames) {
84
- if (is_array($filenames)) {
85
  /* its an array so we have to figure out which one the dev wants */
86
- foreach ($filenames as $filename) {
87
- if (self::template_exists($filename)) {
88
  return $filename;
89
  }
90
  }
@@ -97,10 +99,10 @@ class TimberLoader {
97
  * @param string $file
98
  * @return bool
99
  */
100
- protected function template_exists($file) {
101
- foreach ($this->locations as $dir) {
102
- $look_for = trailingslashit($dir) . $file;
103
- if (file_exists($look_for)) {
104
  return true;
105
  }
106
  }
@@ -114,18 +116,18 @@ class TimberLoader {
114
  $theme_locs = array();
115
  $child_loc = get_stylesheet_directory();
116
  $parent_loc = get_template_directory();
117
- if (DIRECTORY_SEPARATOR == '\\') {
118
  $child_loc = str_replace('/', '\\', $child_loc);
119
  $parent_loc = str_replace('/', '\\', $parent_loc);
120
  }
121
  $theme_locs[] = $child_loc;
122
- foreach ($this->get_locations_theme_dir() as $dirname) {
123
- $theme_locs[] = trailingslashit($child_loc) . trailingslashit($dirname);
124
  }
125
- if ($child_loc != $parent_loc) {
126
  $theme_locs[] = $parent_loc;
127
- foreach ($this->get_locations_theme_dir() as $dirname) {
128
- $theme_locs[] = trailingslashit($parent_loc) . trailingslashit($dirname);
129
  }
130
  }
131
  //now make sure theres a trailing slash on everything
@@ -138,24 +140,25 @@ class TimberLoader {
138
  * @return string[] the names of directores, ie: array('templats', 'views');
139
  */
140
  private function get_locations_theme_dir() {
141
- if (is_string(Timber::$dirname)) {
142
  return array(Timber::$dirname);
143
  }
144
  return Timber::$dirname;
145
  }
146
 
147
  /**
 
148
  * @return array
149
  */
150
  function get_locations_user() {
151
  $locs = array();
152
- if (isset(Timber::$locations)) {
153
- if (is_string(Timber::$locations)) {
154
  Timber::$locations = array(Timber::$locations);
155
  }
156
- foreach (Timber::$locations as $tloc) {
157
  $tloc = realpath($tloc);
158
- if (is_dir($tloc)) {
159
  $locs[] = $tloc;
160
  }
161
  }
@@ -167,16 +170,16 @@ class TimberLoader {
167
  * @param bool|string $caller the calling directory
168
  * @return array
169
  */
170
- function get_locations_caller($caller = false) {
171
  $locs = array();
172
- if ($caller && is_string($caller)) {
173
  $caller = trailingslashit($caller);
174
- if (is_dir($caller)) {
175
  $locs[] = $caller;
176
  }
177
- foreach ($this->get_locations_theme_dir() as $dirname) {
178
- $caller_sub = $caller . trailingslashit($dirname);
179
- if (is_dir($caller_sub)) {
180
  $locs[] = $caller_sub;
181
  }
182
  }
@@ -188,7 +191,7 @@ class TimberLoader {
188
  * @param bool|string $caller the calling directory (or false)
189
  * @return array
190
  */
191
- function get_locations($caller = false) {
192
  //prioirty: user locations, caller (but not theme), child theme, parent theme, caller
193
  $locs = array();
194
  $locs = array_merge($locs, $this->get_locations_user());
@@ -204,26 +207,26 @@ class TimberLoader {
204
  }
205
 
206
  /**
207
- * @return Twig_Loader_Filesystem
208
  */
209
  function get_loader() {
210
  $paths = array();
211
- foreach ($this->locations as $loc) {
212
  $loc = realpath($loc);
213
- if (is_dir($loc)) {
214
  $loc = realpath($loc);
215
  $paths[] = $loc;
216
  } else {
217
  //error_log($loc.' is not a directory');
218
  }
219
  }
220
- if (!ini_get('open_basedir')) {
221
  $paths[] = '/';
222
  } else {
223
  $paths[] = ABSPATH;
224
  }
225
  $paths = apply_filters('timber/loader/paths', $paths);
226
- $loader = new Twig_Loader_Filesystem($paths);
227
  return $loader;
228
  }
229
 
@@ -233,22 +236,22 @@ class TimberLoader {
233
  function get_twig() {
234
  $loader = $this->get_loader();
235
  $params = array('debug' => WP_DEBUG, 'autoescape' => false);
236
- if (isset(Timber::$autoescape)) {
237
  $params['autoescape'] = Timber::$autoescape;
238
  }
239
- if (Timber::$cache === true) {
240
  Timber::$twig_cache = true;
241
  }
242
- if (Timber::$twig_cache) {
243
- $twig_cache_loc = apply_filters( 'timber/cache/location', TIMBER_LOC . '/cache/twig' );
244
- if (!file_exists($twig_cache_loc)) {
245
  mkdir($twig_cache_loc, 0777, true);
246
  }
247
  $params['cache'] = $twig_cache_loc;
248
  }
249
- $twig = new Twig_Environment($loader, $params);
250
  if ( WP_DEBUG ) {
251
- $twig->addExtension(new Twig_Extension_Debug());
252
  }
253
  $twig->addExtension($this->_get_cache_extension());
254
 
@@ -258,26 +261,26 @@ class TimberLoader {
258
  return $twig;
259
  }
260
 
261
- public function clear_cache_timber($cache_mode = self::CACHE_USE_DEFAULT){
262
  //_transient_timberloader
263
  $object_cache = false;
264
- if (isset($GLOBALS['wp_object_cache']) && is_object($GLOBALS['wp_object_cache'])) {
265
  $object_cache = true;
266
  }
267
  $cache_mode = $this->_get_cache_mode($cache_mode);
268
- if (self::CACHE_TRANSIENT === $cache_mode) {
269
  global $wpdb;
270
  $query = $wpdb->prepare("DELETE FROM $wpdb->options WHERE option_name LIKE '%s'", '_transient_timberloader_%');
271
- $wpdb->query( $query );
272
  return true;
273
- } else if (self::CACHE_SITE_TRANSIENT === $cache_mode) {
274
  global $wpdb;
275
  $query = $wpdb->prepare("DELETE FROM $wpdb->options WHERE option_name LIKE '%s'", '_transient_timberloader_%');
276
- $wpdb->query( $query );
277
  return true;
278
- } else if (self::CACHE_OBJECT === $cache_mode && $object_cache) {
279
  global $wp_object_cache;
280
- if (isset($wp_object_cache->cache[self::CACHEGROUP])){
281
  unset($wp_object_cache->cache[self::CACHEGROUP]);
282
  return true;
283
  }
@@ -289,7 +292,7 @@ class TimberLoader {
289
  $twig = $this->get_twig();
290
  $twig->clearCacheFiles();
291
  $cache = $twig->getCache();
292
- if ($cache){
293
  self::rrmdir($twig->getCache());
294
  return true;
295
  }
@@ -299,16 +302,16 @@ class TimberLoader {
299
  /**
300
  * @param string|false $dirPath
301
  */
302
- public static function rrmdir($dirPath) {
303
- if (! is_dir($dirPath)) {
304
- throw new InvalidArgumentException("$dirPath must be a directory");
305
  }
306
- if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
307
  $dirPath .= '/';
308
  }
309
- $files = glob($dirPath . '*', GLOB_MARK);
310
- foreach ($files as $file) {
311
- if (is_dir($file)) {
312
  self::rrmdir($file);
313
  } else {
314
  unlink($file);
@@ -323,9 +326,9 @@ class TimberLoader {
323
  private function _get_cache_extension() {
324
 
325
  $key_generator = new \Timber\Cache\KeyGenerator();
326
- $cache_provider = new \Timber\Cache\WPObjectCacheAdapter( $this );
327
- $cache_strategy = new \Asm89\Twig\CacheExtension\CacheStrategy\GenerationalCacheStrategy( $cache_provider, $key_generator );
328
- $cache_extension = new \Asm89\Twig\CacheExtension\Extension( $cache_strategy );
329
 
330
  return $cache_extension;
331
  }
@@ -336,10 +339,10 @@ class TimberLoader {
336
  * @param string $cache_mode
337
  * @return bool
338
  */
339
- public function get_cache($key, $group = self::CACHEGROUP, $cache_mode = self::CACHE_USE_DEFAULT) {
340
  $object_cache = false;
341
 
342
- if (isset($GLOBALS['wp_object_cache']) && is_object($GLOBALS['wp_object_cache'])) {
343
  $object_cache = true;
344
  }
345
 
@@ -347,15 +350,14 @@ class TimberLoader {
347
 
348
  $value = false;
349
 
350
- $trans_key = substr($group . '_' . $key, 0, self::TRANS_KEY_LEN);
351
- if (self::CACHE_TRANSIENT === $cache_mode)
352
- $value = get_transient($trans_key);
353
-
354
- elseif (self::CACHE_SITE_TRANSIENT === $cache_mode)
355
- $value = get_site_transient($trans_key);
356
-
357
- elseif (self::CACHE_OBJECT === $cache_mode && $object_cache)
358
- $value = wp_cache_get($key, $group);
359
 
360
  return $value;
361
  }
@@ -368,27 +370,27 @@ class TimberLoader {
368
  * @param string $cache_mode
369
  * @return string|boolean
370
  */
371
- public function set_cache($key, $value, $group = self::CACHEGROUP, $expires = 0, $cache_mode = self::CACHE_USE_DEFAULT) {
372
  $object_cache = false;
373
 
374
- if (isset($GLOBALS['wp_object_cache']) && is_object($GLOBALS['wp_object_cache'])) {
375
  $object_cache = true;
376
  }
377
 
378
- if ((int)$expires < 1)
379
- $expires = 0;
 
380
 
381
  $cache_mode = self::_get_cache_mode($cache_mode);
382
- $trans_key = substr($group . '_' . $key, 0, self::TRANS_KEY_LEN);
383
-
384
- if (self::CACHE_TRANSIENT === $cache_mode)
385
- set_transient($trans_key, $value, $expires);
386
-
387
- elseif (self::CACHE_SITE_TRANSIENT === $cache_mode)
388
- set_site_transient($trans_key, $value, $expires);
389
-
390
- elseif (self::CACHE_OBJECT === $cache_mode && $object_cache)
391
- wp_cache_set($key, $value, $group, $expires);
392
 
393
  return $value;
394
  }
@@ -397,13 +399,13 @@ class TimberLoader {
397
  * @param string $cache_mode
398
  * @return string
399
  */
400
- private function _get_cache_mode($cache_mode) {
401
- if (empty($cache_mode) || self::CACHE_USE_DEFAULT === $cache_mode) {
402
  $cache_mode = $this->cache_mode;
403
  }
404
 
405
  // Fallback if self::$cache_mode did not get a valid value
406
- if (!in_array($cache_mode, self::$cache_modes)) {
407
  $cache_mode = self::CACHE_OBJECT;
408
  }
409
 
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ class Loader {
6
 
7
  const CACHEGROUP = 'timberloader';
8
 
28
  /**
29
  * @param bool|string $caller the calling directory or false
30
  */
31
+ function __construct( $caller = false ) {
32
  $this->locations = $this->get_locations($caller);
33
  $this->cache_mode = apply_filters('timber_cache_mode', $this->cache_mode);
34
  $this->cache_mode = apply_filters('timber/cache/mode', $this->cache_mode);
41
  * @param string $cache_mode
42
  * @return bool|string
43
  */
44
+ function render( $file, $data = null, $expires = false, $cache_mode = self::CACHE_USE_DEFAULT ) {
45
  // Different $expires if user is anonymous or logged in
46
+ if ( is_array($expires) ) {
47
+ if ( is_user_logged_in() && isset($expires[1]) ) {
48
  $expires = $expires[1];
49
  } else {
50
  $expires = $expires[0];
53
 
54
  $key = null;
55
  $output = false;
56
+ if ( false !== $expires ) {
57
  ksort($data);
58
+ $key = md5($file.json_encode($data));
59
  $output = $this->get_cache($key, self::CACHEGROUP, $cache_mode);
60
  }
61
 
62
+ if ( false === $output || null === $output ) {
63
  $twig = $this->get_twig();
64
+ if ( strlen($file) ) {
65
  $loader = $this->get_loader();
66
  $result = $loader->getCacheKey($file);
67
  do_action('timber_loader_render_file', $result);
71
  $output = $twig->render($file, $data);
72
  }
73
 
74
+ if ( false !== $output && false !== $expires && null !== $key ) {
75
  $this->set_cache($key, $output, self::CACHEGROUP, $expires, $cache_mode);
76
  }
77
  $output = apply_filters('timber_output', $output);
82
  * @param array $filenames
83
  * @return bool
84
  */
85
+ public function choose_template( $filenames ) {
86
+ if ( is_array($filenames) ) {
87
  /* its an array so we have to figure out which one the dev wants */
88
+ foreach ( $filenames as $filename ) {
89
+ if ( self::template_exists($filename) ) {
90
  return $filename;
91
  }
92
  }
99
  * @param string $file
100
  * @return bool
101
  */
102
+ protected function template_exists( $file ) {
103
+ foreach ( $this->locations as $dir ) {
104
+ $look_for = trailingslashit($dir).$file;
105
+ if ( file_exists($look_for) ) {
106
  return true;
107
  }
108
  }
116
  $theme_locs = array();
117
  $child_loc = get_stylesheet_directory();
118
  $parent_loc = get_template_directory();
119
+ if ( DIRECTORY_SEPARATOR == '\\' ) {
120
  $child_loc = str_replace('/', '\\', $child_loc);
121
  $parent_loc = str_replace('/', '\\', $parent_loc);
122
  }
123
  $theme_locs[] = $child_loc;
124
+ foreach ( $this->get_locations_theme_dir() as $dirname ) {
125
+ $theme_locs[] = trailingslashit($child_loc).trailingslashit($dirname);
126
  }
127
+ if ( $child_loc != $parent_loc ) {
128
  $theme_locs[] = $parent_loc;
129
+ foreach ( $this->get_locations_theme_dir() as $dirname ) {
130
+ $theme_locs[] = trailingslashit($parent_loc).trailingslashit($dirname);
131
  }
132
  }
133
  //now make sure theres a trailing slash on everything
140
  * @return string[] the names of directores, ie: array('templats', 'views');
141
  */
142
  private function get_locations_theme_dir() {
143
+ if ( is_string(Timber::$dirname) ) {
144
  return array(Timber::$dirname);
145
  }
146
  return Timber::$dirname;
147
  }
148
 
149
  /**
150
+ *
151
  * @return array
152
  */
153
  function get_locations_user() {
154
  $locs = array();
155
+ if ( isset(Timber::$locations) ) {
156
+ if ( is_string(Timber::$locations) ) {
157
  Timber::$locations = array(Timber::$locations);
158
  }
159
+ foreach ( Timber::$locations as $tloc ) {
160
  $tloc = realpath($tloc);
161
+ if ( is_dir($tloc) ) {
162
  $locs[] = $tloc;
163
  }
164
  }
170
  * @param bool|string $caller the calling directory
171
  * @return array
172
  */
173
+ function get_locations_caller( $caller = false ) {
174
  $locs = array();
175
+ if ( $caller && is_string($caller) ) {
176
  $caller = trailingslashit($caller);
177
+ if ( is_dir($caller) ) {
178
  $locs[] = $caller;
179
  }
180
+ foreach ( $this->get_locations_theme_dir() as $dirname ) {
181
+ $caller_sub = $caller.trailingslashit($dirname);
182
+ if ( is_dir($caller_sub) ) {
183
  $locs[] = $caller_sub;
184
  }
185
  }
191
  * @param bool|string $caller the calling directory (or false)
192
  * @return array
193
  */
194
+ function get_locations( $caller = false ) {
195
  //prioirty: user locations, caller (but not theme), child theme, parent theme, caller
196
  $locs = array();
197
  $locs = array_merge($locs, $this->get_locations_user());
207
  }
208
 
209
  /**
210
+ * @return \Twig_Loader_Filesystem
211
  */
212
  function get_loader() {
213
  $paths = array();
214
+ foreach ( $this->locations as $loc ) {
215
  $loc = realpath($loc);
216
+ if ( is_dir($loc) ) {
217
  $loc = realpath($loc);
218
  $paths[] = $loc;
219
  } else {
220
  //error_log($loc.' is not a directory');
221
  }
222
  }
223
+ if ( !ini_get('open_basedir') ) {
224
  $paths[] = '/';
225
  } else {
226
  $paths[] = ABSPATH;
227
  }
228
  $paths = apply_filters('timber/loader/paths', $paths);
229
+ $loader = new \Twig_Loader_Filesystem($paths);
230
  return $loader;
231
  }
232
 
236
  function get_twig() {
237
  $loader = $this->get_loader();
238
  $params = array('debug' => WP_DEBUG, 'autoescape' => false);
239
+ if ( isset(Timber::$autoescape) ) {
240
  $params['autoescape'] = Timber::$autoescape;
241
  }
242
+ if ( Timber::$cache === true ) {
243
  Timber::$twig_cache = true;
244
  }
245
+ if ( Timber::$twig_cache ) {
246
+ $twig_cache_loc = apply_filters('timber/cache/location', TIMBER_LOC.'/cache/twig');
247
+ if ( !file_exists($twig_cache_loc) ) {
248
  mkdir($twig_cache_loc, 0777, true);
249
  }
250
  $params['cache'] = $twig_cache_loc;
251
  }
252
+ $twig = new \Twig_Environment($loader, $params);
253
  if ( WP_DEBUG ) {
254
+ $twig->addExtension(new \Twig_Extension_Debug());
255
  }
256
  $twig->addExtension($this->_get_cache_extension());
257
 
261
  return $twig;
262
  }
263
 
264
+ public function clear_cache_timber( $cache_mode = self::CACHE_USE_DEFAULT ) {
265
  //_transient_timberloader
266
  $object_cache = false;
267
+ if ( isset($GLOBALS['wp_object_cache']) && is_object($GLOBALS['wp_object_cache']) ) {
268
  $object_cache = true;
269
  }
270
  $cache_mode = $this->_get_cache_mode($cache_mode);
271
+ if ( self::CACHE_TRANSIENT === $cache_mode ) {
272
  global $wpdb;
273
  $query = $wpdb->prepare("DELETE FROM $wpdb->options WHERE option_name LIKE '%s'", '_transient_timberloader_%');
274
+ $wpdb->query($query);
275
  return true;
276
+ } else if ( self::CACHE_SITE_TRANSIENT === $cache_mode ) {
277
  global $wpdb;
278
  $query = $wpdb->prepare("DELETE FROM $wpdb->options WHERE option_name LIKE '%s'", '_transient_timberloader_%');
279
+ $wpdb->query($query);
280
  return true;
281
+ } else if ( self::CACHE_OBJECT === $cache_mode && $object_cache ) {
282
  global $wp_object_cache;
283
+ if ( isset($wp_object_cache->cache[self::CACHEGROUP]) ) {
284
  unset($wp_object_cache->cache[self::CACHEGROUP]);
285
  return true;
286
  }
292
  $twig = $this->get_twig();
293
  $twig->clearCacheFiles();
294
  $cache = $twig->getCache();
295
+ if ( $cache ) {
296
  self::rrmdir($twig->getCache());
297
  return true;
298
  }
302
  /**
303
  * @param string|false $dirPath
304
  */
305
+ public static function rrmdir( $dirPath ) {
306
+ if ( !is_dir($dirPath) ) {
307
+ throw new \InvalidArgumentException("$dirPath must be a directory");
308
  }
309
+ if ( substr($dirPath, strlen($dirPath) - 1, 1) != '/' ) {
310
  $dirPath .= '/';
311
  }
312
+ $files = glob($dirPath.'*', GLOB_MARK);
313
+ foreach ( $files as $file ) {
314
+ if ( is_dir($file) ) {
315
  self::rrmdir($file);
316
  } else {
317
  unlink($file);
326
  private function _get_cache_extension() {
327
 
328
  $key_generator = new \Timber\Cache\KeyGenerator();
329
+ $cache_provider = new \Timber\Cache\WPObjectCacheAdapter($this);
330
+ $cache_strategy = new \Asm89\Twig\CacheExtension\CacheStrategy\GenerationalCacheStrategy($cache_provider, $key_generator);
331
+ $cache_extension = new \Asm89\Twig\CacheExtension\Extension($cache_strategy);
332
 
333
  return $cache_extension;
334
  }
339
  * @param string $cache_mode
340
  * @return bool
341
  */
342
+ public function get_cache( $key, $group = self::CACHEGROUP, $cache_mode = self::CACHE_USE_DEFAULT ) {
343
  $object_cache = false;
344
 
345
+ if ( isset($GLOBALS['wp_object_cache']) && is_object($GLOBALS['wp_object_cache']) ) {
346
  $object_cache = true;
347
  }
348
 
350
 
351
  $value = false;
352
 
353
+ $trans_key = substr($group.'_'.$key, 0, self::TRANS_KEY_LEN);
354
+ if ( self::CACHE_TRANSIENT === $cache_mode ) {
355
+ $value = get_transient($trans_key);
356
+ } elseif ( self::CACHE_SITE_TRANSIENT === $cache_mode ) {
357
+ $value = get_site_transient($trans_key);
358
+ } elseif ( self::CACHE_OBJECT === $cache_mode && $object_cache ) {
359
+ $value = wp_cache_get($key, $group);
360
+ }
 
361
 
362
  return $value;
363
  }
370
  * @param string $cache_mode
371
  * @return string|boolean
372
  */
373
+ public function set_cache( $key, $value, $group = self::CACHEGROUP, $expires = 0, $cache_mode = self::CACHE_USE_DEFAULT ) {
374
  $object_cache = false;
375
 
376
+ if ( isset($GLOBALS['wp_object_cache']) && is_object($GLOBALS['wp_object_cache']) ) {
377
  $object_cache = true;
378
  }
379
 
380
+ if ( (int) $expires < 1 ) {
381
+ $expires = 0;
382
+ }
383
 
384
  $cache_mode = self::_get_cache_mode($cache_mode);
385
+ $trans_key = substr($group.'_'.$key, 0, self::TRANS_KEY_LEN);
386
+
387
+ if ( self::CACHE_TRANSIENT === $cache_mode ) {
388
+ set_transient($trans_key, $value, $expires);
389
+ } elseif ( self::CACHE_SITE_TRANSIENT === $cache_mode ) {
390
+ set_site_transient($trans_key, $value, $expires);
391
+ } elseif ( self::CACHE_OBJECT === $cache_mode && $object_cache ) {
392
+ wp_cache_set($key, $value, $group, $expires);
393
+ }
 
394
 
395
  return $value;
396
  }
399
  * @param string $cache_mode
400
  * @return string
401
  */
402
+ private function _get_cache_mode( $cache_mode ) {
403
+ if ( empty($cache_mode) || self::CACHE_USE_DEFAULT === $cache_mode ) {
404
  $cache_mode = $this->cache_mode;
405
  }
406
 
407
  // Fallback if self::$cache_mode did not get a valid value
408
+ if ( !in_array($cache_mode, self::$cache_modes) ) {
409
  $cache_mode = self::CACHE_OBJECT;
410
  }
411
 
lib/{timber-menu.php → Menu.php} RENAMED
@@ -1,5 +1,10 @@
1
  <?php
2
 
 
 
 
 
 
3
  /**
4
  * In Timber, you can use TimberMenu() to make a standard Wordpress menu available to the Twig template as an object you can loop through. And once the menu becomes available to the context, you can get items from it in a way that is a little smoother and more versatile than Wordpress's wp_nav_menu. (You need never again rely on a crazy "Walker Function!"). The first thing to do is to initialize the menu using TimberMenu(). This will make the menu available as an object to work with in the context. (TimberMenu can include a Wordpress menu slug or ID, or it can be sent with no parameter--and guess the right menu.)
5
  * @example
@@ -27,11 +32,11 @@
27
  * <nav>
28
  * <ul class="main-nav">
29
  * {% for item in menu.get_items %}
30
- * <li class="nav-main-item {{item.classes | join(' ')}}"><a class="nav-main-link" href="{{item.get_link}}">{{item.title}}</a>
31
  * {% if item.get_children %}
32
  * <ul class="nav-drop">
33
  * {% for child in item.get_children %}
34
- * <li class="nav-drop-item"><a href="{{child.get_link}}">{{child.title}}</a></li>
35
  * {% endfor %}
36
  * </ul>
37
  * {% endif %}
@@ -41,10 +46,10 @@
41
  * </nav>
42
  * ```
43
  */
44
- class TimberMenu extends TimberCore {
45
 
46
- public $MenuItemClass = 'TimberMenuItem';
47
- public $PostClass = 'TimberPost';
48
 
49
  /**
50
  * @api
@@ -75,18 +80,18 @@ class TimberMenu extends TimberCore {
75
  /**
76
  * @param int|string $slug
77
  */
78
- function __construct($slug = 0) {
79
  $locations = get_nav_menu_locations();
80
- if ($slug != 0 && is_numeric($slug)) {
81
  $menu_id = $slug;
82
- } else if (is_array($locations) && count($locations)) {
83
  $menu_id = $this->get_menu_id_from_locations($slug, $locations);
84
- } else if ($slug === false) {
85
  $menu_id = false;
86
  } else {
87
  $menu_id = $this->get_menu_id_from_terms($slug);
88
  }
89
- if ($menu_id) {
90
  $this->init($menu_id);
91
  } else {
92
  $this->init_as_page_menu();
@@ -97,11 +102,11 @@ class TimberMenu extends TimberCore {
97
  * @internal
98
  * @param int $menu_id
99
  */
100
- protected function init($menu_id) {
101
  $menu = wp_get_nav_menu_items($menu_id);
102
- if ($menu) {
103
  _wp_menu_item_classes_by_context($menu);
104
- if (is_array($menu)){
105
  $menu = self::order_children($menu);
106
  }
107
  $this->items = $menu;
@@ -118,12 +123,12 @@ class TimberMenu extends TimberCore {
118
  */
119
  protected function init_as_page_menu() {
120
  $menu = get_pages();
121
- if ($menu) {
122
- foreach($menu as $mi) {
123
  $mi->__title = $mi->post_title;
124
  }
125
  _wp_menu_item_classes_by_context($menu);
126
- if (is_array($menu)){
127
  $menu = self::order_children($menu);
128
  }
129
  $this->items = $menu;
@@ -136,14 +141,14 @@ class TimberMenu extends TimberCore {
136
  * @param array $locations
137
  * @return integer
138
  */
139
- protected function get_menu_id_from_locations($slug, $locations) {
140
- if ($slug === 0) {
141
  $slug = $this->get_menu_id_from_terms($slug);
142
  }
143
- if (is_numeric($slug)) {
144
  $slug = array_search($slug, $locations);
145
  }
146
- if (isset($locations[$slug])) {
147
  $menu_id = $locations[$slug];
148
  return $menu_id;
149
  }
@@ -154,21 +159,21 @@ class TimberMenu extends TimberCore {
154
  * @param int $slug
155
  * @return int
156
  */
157
- protected function get_menu_id_from_terms($slug = 0) {
158
- if (!is_numeric($slug) && is_string($slug)) {
159
  //we have a string so lets search for that
160
  $menu_id = get_term_by('slug', $slug, 'nav_menu');
161
- if ($menu_id) {
162
  return $menu_id;
163
  }
164
  $menu_id = get_term_by('name', $slug, 'nav_menu');
165
- if ($menu_id) {
166
  return $menu_id;
167
  }
168
  }
169
  $menus = get_terms('nav_menu', array('hide_empty' => true));
170
- if (is_array($menus) && count($menus)) {
171
- if (isset($menus[0]->term_id)) {
172
  return $menus[0]->term_id;
173
  }
174
  }
@@ -180,9 +185,9 @@ class TimberMenu extends TimberCore {
180
  * @param int $parent_id
181
  * @return TimberMenuItem|null
182
  */
183
- function find_parent_item_in_menu($menu_items, $parent_id) {
184
- foreach ($menu_items as &$item) {
185
- if ($item->ID == $parent_id) {
186
  return $item;
187
  }
188
  }
@@ -193,29 +198,29 @@ class TimberMenu extends TimberCore {
193
  * @param array $items
194
  * @return array
195
  */
196
- protected function order_children($items) {
197
  $index = array();
198
  $menu = array();
199
- foreach ($items as $item) {
200
- if (isset($item->title)) {
201
  //items from wp can come with a $title property which conflicts with methods
202
  $item->__title = $item->title;
203
  unset($item->title);
204
  }
205
- if(isset($item->ID)){
206
- if (is_object($item) && get_class($item) == 'WP_Post'){
207
  $old_menu_item = $item;
208
  $item = new $this->PostClass($item);
209
  }
210
  $menu_item = new $this->MenuItemClass($item);
211
- if (isset($old_menu_item)){
212
  $menu_item->import_classes($old_menu_item);
213
  }
214
  $index[$item->ID] = $menu_item;
215
  }
216
  }
217
- foreach ($index as $item) {
218
- if (isset($item->menu_item_parent) && $item->menu_item_parent && isset($index[$item->menu_item_parent])) {
219
  $index[$item->menu_item_parent]->add_child($item);
220
  } else {
221
  $menu[] = $item;
@@ -228,7 +233,7 @@ class TimberMenu extends TimberCore {
228
  * @return array
229
  */
230
  function get_items() {
231
- if (is_array($this->items)) {
232
  return $this->items;
233
  }
234
  return array();
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\Core;
6
+ use Timber\Post;
7
+
8
  /**
9
  * In Timber, you can use TimberMenu() to make a standard Wordpress menu available to the Twig template as an object you can loop through. And once the menu becomes available to the context, you can get items from it in a way that is a little smoother and more versatile than Wordpress's wp_nav_menu. (You need never again rely on a crazy "Walker Function!"). The first thing to do is to initialize the menu using TimberMenu(). This will make the menu available as an object to work with in the context. (TimberMenu can include a Wordpress menu slug or ID, or it can be sent with no parameter--and guess the right menu.)
10
  * @example
32
  * <nav>
33
  * <ul class="main-nav">
34
  * {% for item in menu.get_items %}
35
+ * <li class="nav-main-item {{item.classes | join(' ')}}"><a class="nav-main-link" href="{{item.link}}">{{item.title}}</a>
36
  * {% if item.get_children %}
37
  * <ul class="nav-drop">
38
  * {% for child in item.get_children %}
39
+ * <li class="nav-drop-item"><a href="{{child.link}}">{{child.title}}</a></li>
40
  * {% endfor %}
41
  * </ul>
42
  * {% endif %}
46
  * </nav>
47
  * ```
48
  */
49
+ class Menu extends Core {
50
 
51
+ public $MenuItemClass = 'Timber\MenuItem';
52
+ public $PostClass = 'Timber\Post';
53
 
54
  /**
55
  * @api
80
  /**
81
  * @param int|string $slug
82
  */
83
+ function __construct( $slug = 0 ) {
84
  $locations = get_nav_menu_locations();
85
+ if ( $slug != 0 && is_numeric($slug) ) {
86
  $menu_id = $slug;
87
+ } else if ( is_array($locations) && count($locations) ) {
88
  $menu_id = $this->get_menu_id_from_locations($slug, $locations);
89
+ } else if ( $slug === false ) {
90
  $menu_id = false;
91
  } else {
92
  $menu_id = $this->get_menu_id_from_terms($slug);
93
  }
94
+ if ( $menu_id ) {
95
  $this->init($menu_id);
96
  } else {
97
  $this->init_as_page_menu();
102
  * @internal
103
  * @param int $menu_id
104
  */
105
+ protected function init( $menu_id ) {
106
  $menu = wp_get_nav_menu_items($menu_id);
107
+ if ( $menu ) {
108
  _wp_menu_item_classes_by_context($menu);
109
+ if ( is_array($menu) ) {
110
  $menu = self::order_children($menu);
111
  }
112
  $this->items = $menu;
123
  */
124
  protected function init_as_page_menu() {
125
  $menu = get_pages();
126
+ if ( $menu ) {
127
+ foreach ( $menu as $mi ) {
128
  $mi->__title = $mi->post_title;
129
  }
130
  _wp_menu_item_classes_by_context($menu);
131
+ if ( is_array($menu) ) {
132
  $menu = self::order_children($menu);
133
  }
134
  $this->items = $menu;
141
  * @param array $locations
142
  * @return integer
143
  */
144
+ protected function get_menu_id_from_locations( $slug, $locations ) {
145
+ if ( $slug === 0 ) {
146
  $slug = $this->get_menu_id_from_terms($slug);
147
  }
148
+ if ( is_numeric($slug) ) {
149
  $slug = array_search($slug, $locations);
150
  }
151
+ if ( isset($locations[$slug]) ) {
152
  $menu_id = $locations[$slug];
153
  return $menu_id;
154
  }
159
  * @param int $slug
160
  * @return int
161
  */
162
+ protected function get_menu_id_from_terms( $slug = 0 ) {
163
+ if ( !is_numeric($slug) && is_string($slug) ) {
164
  //we have a string so lets search for that
165
  $menu_id = get_term_by('slug', $slug, 'nav_menu');
166
+ if ( $menu_id ) {
167
  return $menu_id;
168
  }
169
  $menu_id = get_term_by('name', $slug, 'nav_menu');
170
+ if ( $menu_id ) {
171
  return $menu_id;
172
  }
173
  }
174
  $menus = get_terms('nav_menu', array('hide_empty' => true));
175
+ if ( is_array($menus) && count($menus) ) {
176
+ if ( isset($menus[0]->term_id) ) {
177
  return $menus[0]->term_id;
178
  }
179
  }
185
  * @param int $parent_id
186
  * @return TimberMenuItem|null
187
  */
188
+ function find_parent_item_in_menu( $menu_items, $parent_id ) {
189
+ foreach ( $menu_items as &$item ) {
190
+ if ( $item->ID == $parent_id ) {
191
  return $item;
192
  }
193
  }
198
  * @param array $items
199
  * @return array
200
  */
201
+ protected function order_children( $items ) {
202
  $index = array();
203
  $menu = array();
204
+ foreach ( $items as $item ) {
205
+ if ( isset($item->title) ) {
206
  //items from wp can come with a $title property which conflicts with methods
207
  $item->__title = $item->title;
208
  unset($item->title);
209
  }
210
+ if ( isset($item->ID) ) {
211
+ if ( is_object($item) && get_class($item) == 'WP_Post' ) {
212
  $old_menu_item = $item;
213
  $item = new $this->PostClass($item);
214
  }
215
  $menu_item = new $this->MenuItemClass($item);
216
+ if ( isset($old_menu_item) ) {
217
  $menu_item->import_classes($old_menu_item);
218
  }
219
  $index[$item->ID] = $menu_item;
220
  }
221
  }
222
+ foreach ( $index as $item ) {
223
+ if ( isset($item->menu_item_parent) && $item->menu_item_parent && isset($index[$item->menu_item_parent]) ) {
224
  $index[$item->menu_item_parent]->add_child($item);
225
  } else {
226
  $menu[] = $item;
233
  * @return array
234
  */
235
  function get_items() {
236
+ if ( is_array($this->items) ) {
237
  return $this->items;
238
  }
239
  return array();
lib/{timber-menu-item.php → MenuItem.php} RENAMED
@@ -1,6 +1,13 @@
1
  <?php
2
 
3
- class TimberMenuItem extends TimberCore implements TimberCoreInterface {
 
 
 
 
 
 
 
4
 
5
  public $children;
6
  public $has_child_class = false;
@@ -26,13 +33,13 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
26
  */
27
  public function __construct( $data ) {
28
  $data = (object) $data;
29
- $this->import( $data );
30
- $this->import_classes( $data );
31
- if ( isset( $this->name ) ) {
32
  $this->_name = $this->name;
33
  }
34
  $this->name = $this->name();
35
- $this->add_class( 'menu-item-' . $this->ID );
36
  $this->menu_object = $data;
37
  }
38
 
@@ -49,7 +56,7 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
49
  */
50
  public function add_class( $class_name ) {
51
  $this->classes[] = $class_name;
52
- $this->class .= ' ' . $class_name;
53
  }
54
 
55
  /**
@@ -61,7 +68,7 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
61
  if ( $title = $this->title() ) {
62
  return $title;
63
  }
64
- if ( isset( $this->_name ) ) {
65
  return $this->_name;
66
  }
67
  return '';
@@ -82,10 +89,10 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
82
  * @return string the slug of the menu item kinda-like-this
83
  */
84
  public function slug() {
85
- if ( !isset( $this->master_object ) ) {
86
  $this->master_object = $this->get_master_object();
87
  }
88
- if ( isset( $this->master_object->post_name ) && $this->master_object->post_name ) {
89
  return $this->master_object->post_name;
90
  }
91
  return $this->post_name;
@@ -96,34 +103,29 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
96
  * @return mixed whatever object (Post, Term, etc.) the menu item represents
97
  */
98
  protected function get_master_object() {
99
- if ( isset( $this->_menu_item_object_id ) ) {
100
- return new $this->PostClass( $this->_menu_item_object_id );
101
  }
102
  }
103
 
104
  /**
105
  * @internal
106
  * @see TimberMenuItem::link
 
107
  * @return string an absolute URL http://example.org/my-page
108
  */
109
  function get_link() {
110
- if ( !isset( $this->url ) || !$this->url ) {
111
- if ( isset( $this->_menu_item_type ) && $this->_menu_item_type == 'custom' ) {
112
- $this->url = $this->_menu_item_url;
113
- } else if ( isset( $this->menu_object ) && method_exists( $this->menu_object, 'get_link' ) ) {
114
- $this->url = $this->menu_object->get_link();
115
- }
116
- }
117
- return $this->url;
118
  }
119
 
120
  /**
121
  * @internal
122
  * @see TimberMenuItem::path()
 
123
  * @return string a relative url /my-page
124
  */
125
  function get_path() {
126
- return TimberURLHelper::get_rel_url( $this->get_link() );
127
  }
128
 
129
  /**
@@ -133,15 +135,15 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
133
  */
134
  function add_child( $item ) {
135
  if ( !$this->has_child_class ) {
136
- $this->add_class( 'menu-item-has-children' );
137
  $this->has_child_class = true;
138
  }
139
- if ( !isset( $this->children ) ) {
140
  $this->children = array();
141
  }
142
  $this->children[] = $item;
143
  $item->level = $this->level + 1;
144
- if ($item->children) {
145
  $this->update_child_levels();
146
  }
147
  }
@@ -152,8 +154,8 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
152
  * @return bool
153
  */
154
  function update_child_levels() {
155
- if (is_array($this->children)) {
156
- foreach( $this->children as $child ) {
157
  $child->level = $this->level + 1;
158
  $child->update_child_levels();
159
  }
@@ -170,10 +172,10 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
170
  if ( is_array($data) ) {
171
  $data = (object) $data;
172
  }
173
- $this->classes = array_merge( $this->classes, $data->classes );
174
- $this->classes = array_unique( $this->classes );
175
- $this->classes = apply_filters( 'nav_menu_css_class', $this->classes, $this );
176
- $this->class = trim( implode( ' ', $this->classes ) );
177
  }
178
 
179
  /**
@@ -182,7 +184,7 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
182
  * @return array|bool
183
  */
184
  function get_children() {
185
- if ( isset( $this->children ) ) {
186
  return $this->children;
187
  }
188
  return false;
@@ -201,7 +203,7 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
201
  if ( $this->type != 'custom' ) {
202
  return false;
203
  }
204
- return TimberURLHelper::is_external( $this->url );
205
  }
206
 
207
  /**
@@ -209,10 +211,10 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
209
  * @return mixed whatever value is storied in the database
210
  */
211
  public function meta( $key ) {
212
- if ( is_object( $this->menu_object ) && method_exists( $this->menu_object, 'meta' ) ) {
213
- return $this->menu_object->meta( $key );
214
  }
215
- if ( isset( $this->$key ) ) {
216
  return $this->$key;
217
  }
218
  }
@@ -249,22 +251,14 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
249
  * @return string a full URL like http://mysite.com/thing/
250
  */
251
  public function link() {
252
- return $this->get_link();
253
- }
254
-
255
- /**
256
- * Return the relative path of a Menu Item's link
257
- * @example
258
- * ```twig
259
- * {% for item in menu.items %}
260
- * <li><a href="{{ item.path }}">{{ item.title }}</a></li>
261
- * {% endfor %}
262
- * ```
263
- * @see get_path()
264
- * @return string the path of a URL like /foo
265
- */
266
- public function path() {
267
- return $this->get_path();
268
  }
269
 
270
  /**
@@ -275,17 +269,23 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
275
  * @return string a full URL like http://mysite.com/thing/
276
  */
277
  public function permalink() {
278
- return $this->get_link();
 
279
  }
280
 
281
  /**
282
- * @internal
283
- * @deprecated since 0.21.7, use link instead
284
- * @see link()
285
- * @return string a full URL like http://mysite.com/thing/
 
 
 
 
 
286
  */
287
- public function get_permalink() {
288
- return $this->get_link();
289
  }
290
 
291
  /**
@@ -299,9 +299,8 @@ class TimberMenuItem extends TimberCore implements TimberCoreInterface {
299
  * @return string the public label like Foo
300
  */
301
  public function title() {
302
- if ( isset( $this->__title ) ) {
303
  return $this->__title;
304
  }
305
  }
306
-
307
- }
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\Core;
6
+ use Timber\CoreInterface;
7
+
8
+ use Timber\URLHelper;
9
+
10
+ class MenuItem extends Core implements CoreInterface {
11
 
12
  public $children;
13
  public $has_child_class = false;
33
  */
34
  public function __construct( $data ) {
35
  $data = (object) $data;
36
+ $this->import($data);
37
+ $this->import_classes($data);
38
+ if ( isset($this->name) ) {
39
  $this->_name = $this->name;
40
  }
41
  $this->name = $this->name();
42
+ $this->add_class('menu-item-'.$this->ID);
43
  $this->menu_object = $data;
44
  }
45
 
56
  */
57
  public function add_class( $class_name ) {
58
  $this->classes[] = $class_name;
59
+ $this->class .= ' '.$class_name;
60
  }
61
 
62
  /**
68
  if ( $title = $this->title() ) {
69
  return $title;
70
  }
71
+ if ( isset($this->_name) ) {
72
  return $this->_name;
73
  }
74
  return '';
89
  * @return string the slug of the menu item kinda-like-this
90
  */
91
  public function slug() {
92
+ if ( !isset($this->master_object) ) {
93
  $this->master_object = $this->get_master_object();
94
  }
95
+ if ( isset($this->master_object->post_name) && $this->master_object->post_name ) {
96
  return $this->master_object->post_name;
97
  }
98
  return $this->post_name;
103
  * @return mixed whatever object (Post, Term, etc.) the menu item represents
104
  */
105
  protected function get_master_object() {
106
+ if ( isset($this->_menu_item_object_id) ) {
107
+ return new $this->PostClass($this->_menu_item_object_id);
108
  }
109
  }
110
 
111
  /**
112
  * @internal
113
  * @see TimberMenuItem::link
114
+ * @deprecated 1.0
115
  * @return string an absolute URL http://example.org/my-page
116
  */
117
  function get_link() {
118
+ return $this->link();
 
 
 
 
 
 
 
119
  }
120
 
121
  /**
122
  * @internal
123
  * @see TimberMenuItem::path()
124
+ * @deprecated 1.0
125
  * @return string a relative url /my-page
126
  */
127
  function get_path() {
128
+ return $this->path();
129
  }
130
 
131
  /**
135
  */
136
  function add_child( $item ) {
137
  if ( !$this->has_child_class ) {
138
+ $this->add_class('menu-item-has-children');
139
  $this->has_child_class = true;
140
  }
141
+ if ( !isset($this->children) ) {
142
  $this->children = array();
143
  }
144
  $this->children[] = $item;
145
  $item->level = $this->level + 1;
146
+ if ( $item->children ) {
147
  $this->update_child_levels();
148
  }
149
  }
154
  * @return bool
155
  */
156
  function update_child_levels() {
157
+ if ( is_array($this->children) ) {
158
+ foreach ( $this->children as $child ) {
159
  $child->level = $this->level + 1;
160
  $child->update_child_levels();
161
  }
172
  if ( is_array($data) ) {
173
  $data = (object) $data;
174
  }
175
+ $this->classes = array_merge($this->classes, $data->classes);
176
+ $this->classes = array_unique($this->classes);
177
+ $this->classes = apply_filters('nav_menu_css_class', $this->classes, $this);
178
+ $this->class = trim(implode(' ', $this->classes));
179
  }
180
 
181
  /**
184
  * @return array|bool
185
  */
186
  function get_children() {
187
+ if ( isset($this->children) ) {
188
  return $this->children;
189
  }
190
  return false;
203
  if ( $this->type != 'custom' ) {
204
  return false;
205
  }
206
+ return URLHelper::is_external($this->url);
207
  }
208
 
209
  /**
211
  * @return mixed whatever value is storied in the database
212
  */
213
  public function meta( $key ) {
214
+ if ( is_object($this->menu_object) && method_exists($this->menu_object, 'meta') ) {
215
+ return $this->menu_object->meta($key);
216
  }
217
+ if ( isset($this->$key) ) {
218
  return $this->$key;
219
  }
220
  }
251
  * @return string a full URL like http://mysite.com/thing/
252
  */
253
  public function link() {
254
+ if ( !isset($this->url) || !$this->url ) {
255
+ if ( isset($this->_menu_item_type) && $this->_menu_item_type == 'custom' ) {
256
+ $this->url = $this->_menu_item_url;
257
+ } else if ( isset($this->menu_object) && method_exists($this->menu_object, 'get_link') ) {
258
+ $this->url = $this->menu_object->get_link();
259
+ }
260
+ }
261
+ return $this->url;
 
 
 
 
 
 
 
 
262
  }
263
 
264
  /**
269
  * @return string a full URL like http://mysite.com/thing/
270
  */
271
  public function permalink() {
272
+ Helper::warn('{{item.permalink}} is deprecated, use {{item.link}} instead');
273
+ return $this->link();
274
  }
275
 
276
  /**
277
+ * Return the relative path of a Menu Item's link
278
+ * @example
279
+ * ```twig
280
+ * {% for item in menu.items %}
281
+ * <li><a href="{{ item.path }}">{{ item.title }}</a></li>
282
+ * {% endfor %}
283
+ * ```
284
+ * @see get_path()
285
+ * @return string the path of a URL like /foo
286
  */
287
+ public function path() {
288
+ return URLHelper::get_rel_url($this->link());
289
  }
290
 
291
  /**
299
  * @return string the public label like Foo
300
  */
301
  public function title() {
302
+ if ( isset($this->__title) ) {
303
  return $this->__title;
304
  }
305
  }
306
+ }
 
lib/{timber-post.php → Post.php} RENAMED
@@ -1,5 +1,17 @@
1
  <?php
2
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  /**
4
  * This is the object you use to access or extend WordPress posts. Think of it as Timber's (more accessible) version of WP_Post. This is used throughout Timber to represent posts retrieved from WordPress making them available to Twig templates. See the PHP and Twig examples for an example of what it's like to work with this object in your code.
5
  * @example
@@ -7,7 +19,7 @@
7
  * <?php
8
  * // single.php, see connected twig example
9
  * $context = Timber::get_context();
10
- * $context['post'] = new TimberPost(); // It's a new TimberPost object, but an existing post from WordPress.
11
  * Timber::render('single.twig', $context);
12
  * ?>
13
  * ```
@@ -32,22 +44,22 @@
32
  *
33
  * @package Timber
34
  */
35
- class TimberPost extends TimberCore implements TimberCoreInterface {
36
-
37
  /**
38
  * @var string $ImageClass the name of the class to handle images by default
39
  */
40
- public $ImageClass = 'TimberImage';
41
 
42
  /**
43
  * @var string $PostClass the name of the class to handle posts by default
44
  */
45
- public $PostClass = 'TimberPost';
46
 
47
  /**
48
  * @var string $TermClass the name of the class to handle terms by default
49
  */
50
- public $TermClass = 'TimberTerm';
51
 
52
  /**
53
  * @var string $object_type what does this class represent in WordPress terms?
@@ -65,25 +77,18 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
65
  */
66
  protected $_content;
67
 
68
- /**
69
- * @internal
70
- * @var array $_get_terms stores the results of a get_terms method call
71
- * @deprecated
72
- */
73
- protected $_get_terms;
74
-
75
  /**
76
  * @var string $_permalink the returned permalink from WP's get_permalink function
77
  */
78
  protected $_permalink;
79
 
80
  /**
81
- * @var array $_next stores the results of the next TimberPost in a set inside an array (in order to manage by-taxonomy)
82
  */
83
  protected $_next = array();
84
 
85
  /**
86
- * @var array $_prev stores the results of the previous TimberPost in a set inside an array (in order to manage by-taxonomy)
87
  */
88
  protected $_prev = array();
89
 
@@ -93,12 +98,6 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
93
  */
94
  public $class;
95
 
96
- /**
97
- * @deprecated since 0.21.7
98
- * @var string $display_date @deprecated stores the display date (ex: "October 6, 1984"),
99
- */
100
- public $display_date;
101
-
102
  /**
103
  * @api
104
  * @var string $id the numeric WordPress id of a post
@@ -162,13 +161,15 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
162
  * If you send the constructor nothing it will try to figure out the current post id based on being inside The_Loop
163
  * @example
164
  * ```php
165
- * $post = new TimberPost();
166
- * $other_post = new TimberPost($random_post_id);
167
  * ```
168
  * @param mixed $pid
169
  */
170
- public function __construct($pid = null) {
171
- $pid = $this->determine_id( $pid );
 
 
172
  $this->init($pid);
173
  }
174
 
@@ -178,7 +179,7 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
178
  * @param mixed a value to test against
179
  * @return int the numberic id we should be using for this post object
180
  */
181
- protected function determine_id($pid) {
182
  global $wp_query;
183
  if ( $pid === null &&
184
  isset($wp_query->queried_object_id)
@@ -187,14 +188,18 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
187
  && is_object($wp_query->queried_object)
188
  && get_class($wp_query->queried_object) == 'WP_Post'
189
  ) {
190
- $pid = $wp_query->queried_object_id;
191
- } else if ( $pid === null && $wp_query->is_home && isset($wp_query->queried_object_id) && $wp_query->queried_object_id ) {
 
 
 
 
192
  //hack for static page as home page
193
  $pid = $wp_query->queried_object_id;
194
  } else if ( $pid === null ) {
195
  $gtid = false;
196
  $maybe_post = get_post();
197
- if ( isset($maybe_post->ID) ){
198
  $gtid = true;
199
  }
200
  if ( $gtid ) {
@@ -207,7 +212,7 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
207
  }
208
  }
209
  }
210
- if ( $pid === null && ($pid_from_loop = TimberPostGetter::loop_to_id()) ) {
211
  $pid = $pid_from_loop;
212
  }
213
  return $pid;
@@ -221,13 +226,43 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
221
  return $this->title();
222
  }
223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
  /**
226
- * Initializes a TimberPost
227
  * @internal
228
  * @param int|bool $pid
229
  */
230
- protected function init($pid = false) {
231
  if ( $pid === false ) {
232
  $pid = get_the_ID();
233
  }
@@ -236,8 +271,6 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
236
  }
237
  $post_info = $this->get_info($pid);
238
  $this->import($post_info);
239
- /* deprecated, adding for support for older themes */
240
- $this->display_date = $this->date();
241
  //cant have a function, so gots to do it this way
242
  $post_class = $this->post_class();
243
  $this->class = $post_class;
@@ -246,13 +279,13 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
246
  /**
247
  * Get the URL that will edit the current post/object
248
  * @internal
249
- * @see TimberPost::edit_link
 
 
250
  * @return bool|string
251
  */
252
  function get_edit_url() {
253
- if ( $this->can_edit() ) {
254
- return get_edit_post_link($this->ID);
255
- }
256
  }
257
 
258
  /**
@@ -316,11 +349,11 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
316
  * @param string $post_name
317
  * @return int
318
  */
319
- static function get_post_id_by_name($post_name) {
320
  global $wpdb;
321
  $query = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_name = %s LIMIT 1", $post_name);
322
  $result = $wpdb->get_row($query);
323
- if (!$result) {
324
  return null;
325
  }
326
  return $result->ID;
@@ -338,15 +371,16 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
338
  * @param int $len The number of words that WP should use to make the tease. (Isn't this better than [this mess](http://wordpress.org/support/topic/changing-the-default-length-of-the_excerpt-1?replies=14)?). If you've set a post_excerpt on a post, we'll use that for the preview text; otherwise the first X words of the post_content
339
  * @param bool $force What happens if your custom post excerpt is longer then the length requested? By default (`$force = false`) it will use the full `post_excerpt`. However, you can set this to true to *force* your excerpt to be of the desired length
340
  * @param string $readmore The text you want to use on the 'readmore' link
341
- * @param bool $strip Strip tags? yes or no. tell me!
 
342
  * @return string of the post preview
343
  */
344
- function get_preview($len = 50, $force = false, $readmore = 'Read More', $strip = true) {
345
  $text = '';
346
  $trimmed = false;
347
  if ( isset($this->post_excerpt) && strlen($this->post_excerpt) ) {
348
  if ( $force ) {
349
- $text = TimberHelper::trim_words($this->post_excerpt, $len, false);
350
  $trimmed = true;
351
  } else {
352
  $text = $this->post_excerpt;
@@ -356,26 +390,27 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
356
  $pieces = explode($readmore_matches[0], $this->post_content);
357
  $text = $pieces[0];
358
  if ( $force ) {
359
- $text = TimberHelper::trim_words($text, $len, false);
360
  $trimmed = true;
361
  }
362
- $text = do_shortcode( $text );
363
  }
364
  if ( !strlen($text) ) {
365
- $text = TimberHelper::trim_words($this->get_content(), $len, false);
366
  $trimmed = true;
367
  }
368
  if ( !strlen(trim($text)) ) {
369
  return trim($text);
370
  }
371
  if ( $strip ) {
372
- $text = trim(strip_tags($text));
 
373
  }
374
  if ( strlen($text) ) {
375
  $text = trim($text);
376
  $last = $text[strlen($text) - 1];
377
  if ( $last != '.' && $trimmed ) {
378
- $text .= ' &hellip;';
379
  }
380
  if ( !$strip ) {
381
  $last_p_tag = strrpos($text, '</p>');
@@ -383,16 +418,16 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
383
  $text = substr($text, 0, $last_p_tag);
384
  }
385
  if ( $last != '.' && $trimmed ) {
386
- $text .= ' &hellip; ';
387
  }
388
  }
389
  $read_more_class = apply_filters('timber/post/get_preview/read_more_class', "read-more");
390
  if ( $readmore && isset($readmore_matches) && !empty($readmore_matches[1]) ) {
391
- $text .= ' <a href="' . $this->get_permalink() . '" class="'.$read_more_class .'">' . trim($readmore_matches[1]) . '</a>';
392
  } elseif ( $readmore ) {
393
- $text .= ' <a href="' . $this->get_permalink() . '" class="'.$read_more_class .'">' . trim($readmore) . '</a>';
394
  }
395
- if ( !$strip && $last_p_tag && ( strpos($text, '<p>') || strpos($text, '<p ') ) ) {
396
  $text .= '</p>';
397
  }
398
  }
@@ -436,263 +471,313 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
436
  }
437
 
438
  /**
439
- * @internal
440
- * @see TimberPost::thumbnail
441
- * @return null|TimberImage
442
  */
443
- function get_thumbnail() {
444
- if ( function_exists('get_post_thumbnail_id') ) {
445
- $tid = get_post_thumbnail_id($this->ID);
446
- if ( $tid ) {
447
- return new $this->ImageClass($tid);
448
- }
449
  }
 
450
  }
451
 
452
  /**
 
453
  * @internal
454
- * @see TimberPost::link
455
- * @return string
456
  */
457
- function get_permalink() {
458
- if ( isset($this->_permalink) ) {
459
- return $this->_permalink;
 
460
  }
461
- $this->_permalink = get_permalink($this->ID);
462
- return $this->_permalink;
 
 
 
 
 
463
  }
464
 
465
- /**
466
- * get the permalink for a post object
467
- * In your templates you should use link:
468
- * <a href="{{post.link}}">Read my post</a>
469
- * @internal
470
- * @return string
471
- */
472
- function get_link() {
473
- return $this->get_permalink();
474
- }
475
 
476
  /**
477
- * Get the next post in WordPress's ordering
478
- * @internal
479
- * @param bool $taxonomy
480
- * @return TimberPost|boolean
 
 
481
  */
482
- function get_next( $taxonomy = false ) {
483
- if ( !isset($this->_next) || !isset($this->_next[$taxonomy]) ) {
484
- global $post;
485
- $this->_next = array();
486
- $old_global = $post;
487
- $post = $this;
488
- if ( $taxonomy ) {
489
- $adjacent = get_adjacent_post(true, '', false, $taxonomy);
490
- } else {
491
- $adjacent = get_adjacent_post(false, '', false);
492
- }
493
 
494
- if ( $adjacent ) {
495
- $this->_next[$taxonomy] = new $this->PostClass($adjacent);
 
 
 
 
 
 
 
 
 
496
  } else {
497
- $this->_next[$taxonomy] = false;
498
  }
499
- $post = $old_global;
500
  }
501
- return $this->_next[$taxonomy];
502
- }
503
 
504
- /**
505
- * Get a data array of pagination so you can navigate to the previous/next for a paginated post
506
- * @return array
507
- */
508
- public function get_pagination() {
509
- global $post, $page, $numpages, $multipage;
510
- $post = $this;
511
- $ret = array();
512
- if ( $multipage ) {
513
- for ( $i = 1; $i <= $numpages; $i++ ) {
514
- $link = self::get_wp_link_page($i);
515
- $data = array('name' => $i, 'title' => $i, 'text' => $i, 'link' => $link);
516
- if ( $i == $page ) {
517
- $data['current'] = true;
518
- }
519
- $ret['pages'][] = $data;
520
  }
521
- $i = $page - 1;
522
- if ( $i ) {
523
- $link = self::get_wp_link_page($i);
524
- $ret['prev'] = array('link' => $link);
525
  }
526
- $i = $page + 1;
527
- if ( $i <= $numpages ) {
528
- $link = self::get_wp_link_page($i);
529
- $ret['next'] = array('link' => $link);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
  }
531
  }
532
- return $ret;
533
  }
534
 
535
  /**
536
- * @param int $i
537
- * @return string
 
538
  */
539
- protected static function get_wp_link_page($i) {
540
- $link = _wp_link_page($i);
541
- $link = new SimpleXMLElement($link . '</a>');
542
- if ( isset($link['href']) ) {
543
- return $link['href'];
 
 
 
 
 
 
544
  }
545
- return '';
546
  }
547
 
548
  /**
549
- * Get the permalink for a post, but as a relative path
550
- * For example, where {{post.link}} would return "http://example.org/2015/07/04/my-cool-post"
551
- * this will return the relative version: "/2015/07/04/my-cool-post"
552
- * @internal
553
  * @return string
554
  */
555
- function get_path() {
556
- return TimberURLHelper::get_rel_url($this->get_link());
557
  }
558
-
559
  /**
560
- * Get the next post in WordPress's ordering
561
- * @internal
562
- * @param bool $taxonomy
563
- * @return TimberPost|boolean
 
 
 
 
 
 
 
564
  */
565
- function get_prev( $taxonomy = false ) {
566
- if ( isset($this->_prev) && isset($this->_prev[$taxonomy]) ) {
567
- return $this->_prev[$taxonomy];
568
- }
569
- global $post;
570
- $old_global = $post;
571
- $post = $this;
572
- $within_taxonomy = ($taxonomy) ? $taxonomy : 'category';
573
- $adjacent = get_adjacent_post(($taxonomy), '', true, $within_taxonomy);
574
- $prev_in_taxonomy = false;
575
- if ( $adjacent ) {
576
- $prev_in_taxonomy = new $this->PostClass($adjacent);
577
- }
578
- $this->_prev[$taxonomy] = $prev_in_taxonomy;
579
- $post = $old_global;
580
- return $this->_prev[$taxonomy];
581
  }
582
 
583
  /**
584
- * Get the parent post of the post
585
- * @internal
586
- * @return bool|TimberPost
587
  */
588
- function get_parent() {
589
- if ( !$this->post_parent ) {
590
- return false;
591
- }
592
- return new $this->PostClass($this->post_parent);
593
  }
594
 
595
  /**
596
- * Gets a User object from the author of the post
597
- * @internal
598
- * @see TimberPost::author
599
- * @return bool|TimberUser
600
  */
601
- function get_author() {
602
- if ( isset($this->post_author) ) {
603
- return new TimberUser($this->post_author);
 
 
 
 
 
 
 
604
  }
 
 
 
605
  }
606
 
607
  /**
608
- * @internal
609
- * @return bool|TimberUser
610
  */
611
- function get_modified_author() {
612
- $user_id = get_post_meta($this->ID, '_edit_last', true);
613
- return ($user_id ? new TimberUser($user_id) : $this->get_author());
614
  }
615
 
616
  /**
617
- * Used internally by init, etc. to build TimberPost object
618
  * @internal
619
- * @param int $pid
620
- * @return null|object|WP_Post
621
- */
622
- protected function get_info($pid) {
623
- $post = $this->prepare_post_info($pid);
624
- if ( !isset($post->post_status) ) {
625
- return null;
626
- }
627
- $post->status = $post->post_status;
628
- $post->id = $post->ID;
629
- $post->slug = $post->post_name;
630
- $customs = $this->get_post_custom($post->ID);
631
- $post->custom = $customs;
632
- $post = (object) array_merge((array)$customs, (array)$post);
633
- return $post;
 
 
 
 
 
 
 
 
 
 
 
634
  }
635
 
 
 
636
  /**
637
- * Get the human-friendly date that should actually display in a .twig template
638
- * @deprecated since 0.20.0
639
- * @see TimberPost::date
640
- * @param string $use
641
- * @return string
642
  */
643
- function get_display_date( $use = 'post_date' ) {
644
- return date(get_option('date_format'), strtotime($this->$use));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645
  }
646
 
647
  /**
648
- * @internal
649
- * @see TimberPost::date
650
- * @param string $date_format
651
- * @return string
 
 
 
 
 
 
652
  */
653
- function get_date( $date_format = '' ) {
654
- $df = $date_format ? $date_format : get_option('date_format');
655
- $the_date = (string)mysql2date($df, $this->post_date);
656
- return apply_filters('get_the_date', $the_date, $df);
657
  }
658
 
659
  /**
660
- * @internal
661
- * @param string $date_format
662
- * @return string
 
 
 
 
 
 
663
  */
664
- function get_modified_date( $date_format = '' ) {
665
- $df = $date_format ? $date_format : get_option('date_format');
666
- $the_time = $this->get_modified_time($df);
667
- return apply_filters('get_the_modified_date', $the_time, $date_format);
668
  }
669
 
670
  /**
671
- * @internal
672
- * @param string $time_format
673
- * @return string
674
  */
675
- function get_modified_time( $time_format = '' ) {
676
- $tf = $time_format ? $time_format : get_option('time_format');
677
- $the_time = get_post_modified_time($tf, false, $this->ID, true);
678
- return apply_filters('get_the_modified_time', $the_time, $time_format);
679
  }
680
 
681
  /**
682
- * @internal
683
- * @see TimberPost::children
684
- * @param string $post_type
685
- * @param bool|string $childPostClass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
  * @return array
687
  */
688
- function get_children( $post_type = 'any', $childPostClass = false ) {
689
  if ( $childPostClass === false ) {
690
  $childPostClass = $this->PostClass;
691
  }
692
  if ( $post_type == 'parent' ) {
693
  $post_type = $this->post_type;
694
  }
695
- $children = get_children('post_parent=' . $this->ID . '&post_type=' . $post_type . '&numberposts=-1&orderby=menu_order title&order=ASC');
696
  foreach ( $children as &$child ) {
697
  $child = new $childPostClass($child->ID);
698
  }
@@ -700,21 +785,28 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
700
  return $children;
701
  }
702
 
703
-
704
  /**
705
- * Get the comments for a post
706
- * @internal
707
- * @see TimberPost::comments
708
- * @param int $ct
709
- * @param string $order
710
- * @param string $type
711
- * @param string $status
712
- * @param string $CommentClass
713
- * @return array|mixed
 
 
 
 
 
 
 
 
 
 
714
  */
715
-
716
- function get_comments($ct = 0, $order = 'wp', $type = 'comment', $status = 'approve', $CommentClass = 'TimberComment') {
717
-
718
  global $overridden_cpage, $user_ID;
719
  $overridden_cpage = false;
720
 
@@ -722,50 +814,50 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
722
  $comment_author_email = $commenter['comment_author_email'];
723
 
724
  $args = array('post_id' => $this->ID, 'status' => $status, 'order' => $order);
725
- if ( $ct > 0 ) {
726
- $args['number'] = $ct;
727
  }
728
  if ( strtolower($order) == 'wp' || strtolower($order) == 'wordpress' ) {
729
  $args['order'] = get_option('comment_order');
730
  }
731
 
732
  if ( $user_ID ) {
733
- $args['include_unapproved'] = array( $user_ID );
734
- } elseif ( ! empty( $comment_author_email ) ) {
735
- $args['include_unapproved'] = array( $comment_author_email );
736
  }
737
 
738
  $comments = get_comments($args);
739
  $timber_comments = array();
740
 
741
  if ( '' == get_query_var('cpage') && get_option('page_comments') ) {
742
- set_query_var( 'cpage', 'newest' == get_option('default_comments_page') ? get_comment_pages_count() : 1 );
743
  $overridden_cpage = true;
744
  }
745
 
746
- foreach($comments as $key => &$comment) {
747
  $timber_comment = new $CommentClass($comment);
748
  $timber_comments[$timber_comment->id] = $timber_comment;
749
  }
750
 
751
  // Build a flattened (depth=1) comment tree
752
  $comments_tree = array();
753
- foreach( $timber_comments as $key => $comment ) {
754
- if ( ! $comment->is_child() ) {
755
  continue;
756
  }
757
 
758
  $tree_element = $comment;
759
  do {
760
  $tree_element = $timber_comments[$tree_element->comment_parent];
761
- } while( $tree_element->is_child() );
762
 
763
  $comments_tree[$tree_element->id][] = $comment->id;
764
  }
765
 
766
  // Add child comments to the relative "super parents"
767
- foreach($comments_tree as $comment_parent => $comment_children) {
768
- foreach($comment_children as $comment_child) {
769
  $timber_comments[$comment_parent]->children[] = $timber_comments[$comment_child];
770
  unset($timber_comments[$comment_child]);
771
  }
@@ -777,649 +869,664 @@ class TimberPost extends TimberCore implements TimberCoreInterface {
777
  }
778
 
779
  /**
780
- * Get the categories for a post
781
- * @internal
782
- * @see TimberPost::categories
783
- * @return array of TimberTerms
784
- */
785
- function get_categories() {
786
- return $this->get_terms('category');
787
- }
788
-
789
- /**
790
- * @internal
791
- * @see TimberPost::category
792
- * @return mixed
793
- */
794
- function get_category( ) {
795
- $cats = $this->get_categories();
796
- if ( count($cats) && isset($cats[0]) ) {
797
- return $cats[0];
798
- }
799
- }
800
-
801
- /**
802
- * @internal
803
- * @param string|array $tax
804
- * @param bool $merge
805
- * @param string $TermClass
806
- * @return array
807
  */
808
- function get_terms( $tax = '', $merge = true, $TermClass = '' ) {
809
-
810
- $TermClass = $TermClass ?: $this->TermClass;
811
-
812
- if ( is_string($merge) && class_exists($merge) ) {
813
- $TermClass = $merge;
814
  }
815
- if ( is_array($tax) ) {
816
- $taxonomies = $tax;
 
817
  }
818
- if ( is_string($tax) ) {
819
- if ( in_array($tax, array('all','any','')) ) {
820
- $taxonomies = get_object_taxonomies($this->post_type);
821
- } else {
822
- $taxonomies = array($tax);
823
  }
824
  }
825
-
826
- $term_class_objects = array();
827
-
828
- foreach ( $taxonomies as $taxonomy ) {
829
- if ( in_array($taxonomy, array('tag','tags')) ) {
830
- $taxonomy = 'post_tag';
831
- }
832
- if ( $taxonomy == 'categories' ) {
833
- $taxonomy = 'category';
834
- }
835
-
836
- $terms = wp_get_post_terms($this->ID, $taxonomy);
837
-
838
- if ( is_wp_error($terms) ) {
839
- /* @var $terms WP_Error */
840
- TimberHelper::error_log("Error retrieving terms for taxonomy '$taxonomy' on a post in timber-post.php");
841
- TimberHelper::error_log('tax = ' . print_r($tax, true));
842
- TimberHelper::error_log('WP_Error: ' . $terms->get_error_message());
843
-
844
- return $term_class_objects;
845
- }
846
-
847
- // map over array of wordpress terms, and transform them into instances of the TermClass
848
- $terms = array_map(function($term) use ($TermClass, $taxonomy) {
849
- return call_user_func(array($TermClass, 'from'), $term->term_id, $taxonomy);
850
- }, $terms);
851
-
852
- if ( $merge && is_array($terms) ) {
853
- $term_class_objects = array_merge($term_class_objects, $terms);
854
- } else if ( count($terms) ) {
855
- $term_class_objects[$taxonomy] = $terms;
856
- }
857
  }
858
- return $term_class_objects;
859
  }
860
 
861
  /**
862
- * @param string|int $term_name_or_id
863
- * @param string $taxonomy
864
- * @return bool
865
  */
866
- function has_term( $term_name_or_id, $taxonomy = 'all' ) {
867
- if ( $taxonomy == 'all' || $taxonomy == 'any' ) {
868
- $taxes = get_object_taxonomies($this->post_type, 'names');
869
- $ret = false;
870
- foreach ( $taxes as $tax ) {
871
- if ( has_term($term_name_or_id, $tax, $this->ID) ) {
872
- $ret = true;
873
- break;
874
- }
875
- }
876
- return $ret;
877
- }
878
- return has_term($term_name_or_id, $taxonomy, $this->ID);
879
  }
880
 
881
  /**
882
- * @param string $field
883
- * @return TimberImage
884
- */
885
- function get_image( $field ) {
886
- return new $this->ImageClass($this->$field);
887
- }
888
-
889
- /**
890
- * Gets an array of tags for you to use
891
- * @internal
892
  * @example
893
  * ```twig
894
- * <ul class="tags">
895
- * {% for tag in post.tags %}
896
- * <li>{{tag.name}}</li>
897
- * {% endfor %}
898
- * </ul>
899
  * ```
900
- * @return array
 
 
 
 
 
 
 
901
  */
902
- function get_tags() {
903
- return $this->get_terms('post_tag');
 
 
904
  }
905
 
906
  /**
907
- * Outputs the title with filters applied
908
- * @internal
909
  * @example
910
  * ```twig
911
- * <h1>{{post.get_title}}</h1>
 
 
912
  * ```
 
913
  * ```html
914
- * <h1>Hello World!</h1>
 
 
915
  * ```
 
916
  * @return string
917
  */
918
- function get_title() {
919
- return apply_filters('the_title', $this->post_title, $this->ID);
 
 
920
  }
921
 
922
  /**
923
- * Displays the content of the post with filters, shortcodes and wpautop applied
924
- * @example
925
- * ```twig
926
- * <div class="article-text">{{post.get_content}}</div>
927
- * ```
928
- * ```html
929
- * <div class="article-text"><p>Blah blah blah</p><p>More blah blah blah.</p></div>
930
- * ```
931
- * @param int $len
932
- * @param int $page
933
- * @return string
934
  */
935
- function get_content( $len = 0, $page = 0 ) {
936
- if ( $len == 0 && $page == 0 && $this->_content ) {
937
- return $this->_content;
938
- }
939
- $content = $this->post_content;
940
- if ( $len ) {
941
- $content = wp_trim_words($content, $len);
942
- }
943
- if ( $page ) {
944
- $contents = explode('<!--nextpage-->', $content);
945
- $page--;
946
- if ( count($contents) > $page ) {
947
- $content = $contents[$page];
948
- }
949
- }
950
- $content = apply_filters('the_content', ($content));
951
- if ( $len == 0 && $page == 0 ) {
952
- $this->_content = $content;
953
  }
954
- return $content;
955
  }
956
 
957
  /**
958
- * @return string
 
959
  */
960
- function get_paged_content() {
961
- global $page;
962
- return $this->get_content(0, $page);
963
  }
 
964
  /**
965
- *
966
- * Here is my summary
967
  * @example
968
  * ```twig
969
- * This post is from <span>{{ post.get_post_type.labels.plural }}</span>
970
- * ```
971
- *
972
- * ```html
973
- * This post is from <span>Recipes</span>
974
  * ```
 
 
 
 
 
 
 
 
 
 
 
 
975
  * @return mixed
976
  */
977
- public function get_post_type() {
978
- return get_post_type_object($this->post_type);
 
 
 
 
979
  }
980
 
981
  /**
982
- * @return int the number of comments on a post
983
  */
984
- public function get_comment_count() {
985
- return get_comments_number($this->ID);
986
  }
987
 
988
  /**
989
- * @param string $field_name
990
- * @return mixed
991
  */
992
- public function get_field( $field_name ) {
993
- $value = apply_filters('timber_post_get_meta_field_pre', null, $this->ID, $field_name, $this);
994
- if ( $value === null ) {
995
- $value = get_post_meta($this->ID, $field_name);
996
- if ( is_array($value) && count($value) == 1 ) {
997
- $value = $value[0];
998
- }
999
- if ( is_array($value) && count($value) == 0 ) {
1000
- $value = null;
1001
- }
1002
- }
1003
- $value = apply_filters('timber_post_get_meta_field', $value, $this->ID, $field_name, $this);
1004
- return $value;
1005
  }
1006
 
1007
  /**
1008
- * @param string $field_name
 
1009
  */
1010
- function import_field( $field_name ) {
1011
- $this->$field_name = $this->get_field($field_name);
1012
  }
1013
 
1014
  /**
1015
- * @internal
 
1016
  * @return mixed
1017
  */
1018
- function get_format() {
1019
- return get_post_format($this->ID);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1020
  }
1021
 
1022
  /**
1023
- * Get the CSS classes for a post. For usage you should use `{{post.class}}` instead of `{{post.post_class}}`
1024
- * @internal
1025
- * @param string $class additional classes you want to add
1026
- * @see TimberPost::$class
1027
- * @example
1028
- * ```twig
1029
- * <article class="{{ post.class }}">
1030
- * {# Some stuff here #}
1031
- * </article>
1032
- * ```
1033
- *
1034
- * ```html
1035
- * <article class="post-2612 post type-post status-publish format-standard has-post-thumbnail hentry category-data tag-charleston-church-shooting tag-dylann-roof tag-gun-violence tag-hate-crimes tag-national-incident-based-reporting-system">
1036
- * {# Some stuff here #}
1037
- * </article>
1038
- * ```
1039
- * @return string a space-seperated list of classes
1040
  */
1041
- public function post_class( $class='' ) {
1042
- global $post;
1043
- $old_global_post = $post;
1044
  $post = $this;
1045
- $class_array = get_post_class($class, $this->ID);
1046
- $post = $old_global_post;
1047
- if ( is_array($class_array) ){
1048
- return implode(' ', $class_array);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1049
  }
1050
- return $class_array;
1051
  }
1052
 
1053
- // Docs
1054
 
1055
  /**
1056
- * @return array
1057
- * @codeCoverageIgnore
1058
  */
1059
- public function get_method_values() {
1060
- $ret = parent::get_method_values();
1061
- $ret['author'] = $this->author();
1062
- $ret['categories'] = $this->categories();
1063
- $ret['category'] = $this->category();
1064
- $ret['children'] = $this->children();
1065
- $ret['comments'] = $this->comments();
1066
- $ret['content'] = $this->content();
1067
- $ret['edit_link'] = $this->edit_link();
1068
- $ret['format'] = $this->format();
1069
- $ret['link'] = $this->link();
1070
- $ret['next'] = $this->next();
1071
- $ret['pagination'] = $this->pagination();
1072
- $ret['parent'] = $this->parent();
1073
- $ret['path'] = $this->path();
1074
- $ret['prev'] = $this->prev();
1075
- $ret['terms'] = $this->terms();
1076
- $ret['tags'] = $this->tags();
1077
- $ret['thumbnail'] = $this->thumbnail();
1078
- $ret['title'] = $this->title();
1079
- return $ret;
1080
  }
1081
 
1082
  /**
1083
- * Return the author of a post
1084
  * @api
1085
  * @example
1086
  * ```twig
1087
- * <h1>{{post.title}}</h1>
1088
- * <p class="byline">
1089
- * <a href="{{post.author.link}}">{{post.author.name}}</a>
1090
- * </p>
1091
  * ```
1092
- * @return TimberUser|bool A TimberUser object if found, false if not
 
1093
  */
1094
- public function author() {
1095
- return $this->get_author();
 
 
 
1096
  }
1097
 
1098
  /**
1099
- * Get the author (WordPress user) who last modified the post
 
 
1100
  * @example
1101
  * ```twig
1102
- * Last updated by {{ post.modified_author.name }}
1103
- * ```
1104
- * ```html
1105
- * Last updated by Harper Lee
1106
  * ```
1107
- * @return TimberUser|bool A TimberUser object if found, false if not
1108
  */
1109
- public function modified_author() {
1110
- return $this->get_modified_author();
1111
  }
1112
 
1113
  /**
1114
- * Get the categoires on a particular post
1115
  * @api
1116
- * @return array of TimberTerms
 
 
 
 
 
 
 
1117
  */
1118
- public function categories() {
1119
- return $this->get_terms('category');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1120
  }
1121
 
1122
  /**
1123
- * Returns a category attached to a post
1124
  * @api
1125
- * If mulitpuile categories are set, it will return just the first one
1126
- * @return TimberTerm|null
1127
  */
1128
- public function category() {
1129
- return $this->get_category();
1130
  }
1131
 
1132
  /**
1133
- * Returns an array of children on the post as TimberPosts
1134
- * (or other claass as you define).
1135
  * @api
1136
  * @example
1137
  * ```twig
1138
- * {% if post.children %}
1139
- * Here are the child pages:
1140
- * {% for child in page.children %}
1141
- * <a href="{{ child.link }}">{{ child.title }}</a>
1142
- * {% endfor %}
1143
- * {% endif %}
1144
  * ```
1145
- * @param string $post_type _optional_ use to find children of a particular post type (attachment vs. page for example). You might want to restrict to certain types of children in case other stuff gets all mucked in there. You can use 'parent' to use the parent's post type
1146
- * @param string|bool $childPostClass _optional_ a custom post class (ex: 'MyTimberPost') to return the objects as. By default (false) it will use TimberPost::$post_class value.
1147
- * @return array
1148
  */
1149
- public function children( $post_type = 'any', $childPostClass = false ) {
1150
- return $this->get_children( $post_type, $childPostClass );
 
 
 
 
 
 
1151
  }
1152
 
1153
  /**
1154
- * Gets the comments on a TimberPost and returns them as an array of [TimberComments](#TimberComment) (or whatever comment class you set).
1155
  * @api
1156
- * @param int $count Set the number of comments you want to get. `0` is analogous to "all"
1157
- * @param string $order use ordering set in WordPress admin, or a different scheme
1158
- * @param string $type For when other plugins use the comments table for their own special purposes, might be set to 'liveblog' or other depending on what's stored in yr comments table
1159
- * @param string $status Could be 'pending', etc.
1160
- * @param string $CommentClass What class to use when returning Comment objects. As you become a Timber pro, you might find yourself extending TimberComment for your site or app (obviously, totally optional)
1161
  * @example
1162
  * ```twig
1163
- * {# single.twig #}
1164
- * <h4>Comments:</h4>
1165
- * {% for comment in post.comments %}
1166
- * <div class="comment-{{comment.ID}} comment-order-{{loop.index}}">
1167
- * <p>{{comment.author.name}} said:</p>
1168
- * <p>{{comment.content}}</p>
1169
- * </div>
1170
- * {% endfor %}
1171
  * ```
1172
- * @return bool|array
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1173
  */
1174
- public function comments( $count = 0, $order = 'wp', $type = 'comment', $status = 'approve', $CommentClass = 'TimberComment' ) {
1175
- return $this->get_comments($count, $order, $type, $status, $CommentClass);
1176
  }
1177
 
1178
  /**
1179
- * Gets the actual content of a WP Post, as opposed to post_content this will run the hooks/filters attached to the_content. \This guy will return your posts content with WordPress filters run on it (like for shortcodes and wpautop).
1180
- * @api
1181
- * @example
1182
- * ```twig
1183
- * <div class="article">
1184
- * <h2>{{post.title}}</h2>
1185
- * <div class="content">{{ post.content }}</div>
1186
- * </div>
1187
- * ```
1188
- * @param int $page
1189
- * @return string
1190
  */
1191
- public function content( $page = 0 ) {
1192
- return $this->get_content(0, $page);
 
 
 
1193
  }
1194
 
1195
  /**
1196
- * @return string
 
1197
  */
1198
- public function paged_content() {
1199
- return $this->get_paged_content();
1200
  }
1201
 
1202
  /**
1203
- * Get the date to use in your template!
1204
- * @api
 
 
1205
  * @example
1206
  * ```twig
1207
- * Published on {{ post.date }} // Uses WP's formatting set in Admin
1208
- * OR
1209
- * Published on {{ post.date | date('F jS') }} // Jan 12th
1210
- * ```
1211
- *
1212
- * ```html
1213
- * Published on January 12, 2015
1214
- * OR
1215
- * Published on Jan 12th
1216
  * ```
1217
- * @param string $date_format
1218
- * @return string
1219
  */
1220
- public function date( $date_format = '' ) {
1221
- return $this->get_date($date_format);
1222
  }
1223
 
1224
  /**
1225
- * Get the time to use in your template
1226
- * @api
 
 
1227
  * @example
1228
  * ```twig
1229
- * Published at {{ post.time }} // Uses WP's formatting set in Admin
1230
- * OR
1231
- * Published at {{ post.time | time('G:i') }} // 13:25
1232
  * ```
1233
- *
1234
  * ```html
1235
- * Published at 1:25 pm
1236
- * OR
1237
- * Published at 13:25
1238
  * ```
1239
- * @param string $time_format
1240
  * @return string
1241
  */
1242
- public function time( $time_format = '' ) {
1243
- $tf = $time_format ? $time_format : get_option('time_format');
1244
- $the_time = (string)mysql2date($tf, $this->post_date);
1245
- return apply_filters('get_the_time', $the_time, $tf);
1246
  }
1247
 
1248
- /**
1249
- * @return bool|string
1250
- */
1251
- public function edit_link() {
1252
- return $this->get_edit_url();
 
 
 
 
 
 
 
 
 
 
 
 
 
1253
  }
1254
 
1255
  /**
1256
- * @api
 
1257
  * @return mixed
1258
  */
1259
- public function format() {
1260
- return $this->get_format();
1261
  }
1262
 
1263
  /**
1264
- * get the permalink for a post object
1265
- * @api
1266
- * @example
1267
- * ```twig
1268
- * <a href="{{post.link}}">Read my post</a>
1269
- * ```
1270
- * @return string ex: http://example.org/2015/07/my-awesome-post
 
1271
  */
1272
- public function link() {
1273
- return $this->get_permalink();
1274
  }
1275
 
1276
  /**
1277
- * @param string $field_name
1278
- * @return mixed
 
1279
  */
1280
- public function meta( $field_name = null ) {
1281
- if ( $field_name === null ) {
1282
- //on the off-chance the field is actually named meta
1283
- $field_name = 'meta';
1284
- }
1285
- return $this->get_field($field_name);
 
 
 
 
 
 
 
 
 
1286
  }
1287
 
1288
  /**
 
 
 
 
 
1289
  * @return string
1290
  */
1291
- public function name(){
1292
- return $this->title();
1293
  }
1294
 
1295
  /**
1296
- * @param string $date_format
 
1297
  * @return string
1298
  */
1299
- public function modified_date( $date_format = '' ) {
1300
- return $this->get_modified_date($date_format);
 
 
1301
  }
1302
 
1303
  /**
1304
- * @param string $time_format
1305
- * @return string
 
 
 
 
 
1306
  */
1307
- public function modified_time( $time_format = '' ) {
1308
- return $this->get_modified_time($time_format);
 
 
 
 
 
 
 
 
 
 
 
 
 
1309
  }
1310
 
1311
  /**
1312
- * @api
1313
- * @param bool $in_same_cat
1314
- * @return mixed
 
 
 
1315
  */
1316
- public function next( $in_same_cat = false ) {
1317
- return $this->get_next($in_same_cat);
1318
  }
1319
 
1320
  /**
1321
- * @return array
 
 
 
 
1322
  */
1323
- public function pagination() {
1324
- return $this->get_pagination();
1325
  }
1326
 
1327
  /**
1328
- * Gets the parent (if one exists) from a post as a TimberPost object (or whatever is set in TimberPost::$PostClass)
1329
- * @api
1330
- * @example
1331
- * ```twig
1332
- * Parent page: <a href="{{ post.parent.link }}">{{ post.parent.title }}</a>
1333
- * ```
1334
- * @return bool|TimberPost
1335
  */
1336
- public function parent() {
1337
- return $this->get_parent();
 
 
1338
  }
1339
 
1340
  /**
1341
- * Gets the relative path of a WP Post, so while link() will return http://example.org/2015/07/my-cool-post
1342
- * this will return just /2015/07/my-cool-post
1343
- * @api
1344
- * @example
1345
- * ```twig
1346
- * <a href="{{post.path}}">{{post.title}}</a>
1347
- * ```
1348
- * @return string
1349
  */
1350
- public function path() {
1351
- return $this->get_path();
 
 
 
 
 
 
 
 
 
 
 
1352
  }
1353
 
1354
  /**
1355
- * @deprecated 0.20.0 use link() instead
 
 
 
1356
  * @return string
1357
  */
1358
- public function permalink() {
1359
- return $this->get_permalink();
1360
  }
1361
 
1362
  /**
1363
- * Get the previous post in a set
1364
- * @api
1365
- * @example
1366
- * ```twig
1367
- * <h4>Prior Entry:</h4>
1368
- * <h3>{{post.prev.title}}</h3>
1369
- * <p>{{post.prev.get_preview(25)}}</p>
1370
- * ```
1371
- * @param bool $in_same_cat
1372
- * @return mixed
1373
  */
1374
- public function prev( $in_same_cat = false ) {
1375
- return $this->get_prev($in_same_cat);
1376
  }
1377
 
1378
  /**
1379
- * Get the terms associated with the post
1380
- * This goes across all taxonomies by default
1381
- * @api
1382
- * @param string|array $tax What taxonom(y|ies) to pull from. Defaults to all registered taxonomies for the post type. You can use custom ones, or built-in WordPress taxonomies (category, tag). Timber plays nice and figures out that tag/tags/post_tag are all the same (and categories/category), for custom taxonomies you're on your own.
1383
- * @param bool $merge Should the resulting array be one big one (true)? Or should it be an array of sub-arrays for each taxonomy (false)?
1384
- * @return array
1385
  */
1386
- public function terms( $tax = '', $merge = true ) {
1387
- return $this->get_terms($tax, $merge);
1388
  }
1389
 
1390
  /**
1391
- * Gets the tags on a post, uses WP's post_tag taxonomy
1392
- * @api
 
 
 
1393
  * @return array
1394
  */
1395
- public function tags() {
1396
- return $this->get_tags();
1397
  }
1398
 
1399
- /**
1400
- * get the featured image as a TimberImage
1401
- * @api
1402
- * @example
1403
- * ```twig
1404
- * <img src="{{post.thumbnail.src}}" />
1405
- * ```
1406
- * @return TimberImage|null of your thumbnail
1407
- */
1408
- public function thumbnail() {
1409
- return $this->get_thumbnail();
1410
- }
1411
 
1412
  /**
1413
- * Returns the processed title to be used in templates. This returns the title of the post after WP's filters have run. This is analogous to `the_title()` in standard WP template tags.
1414
- * @api
1415
- * @example
1416
- * ```twig
1417
- * <h1>{{ post.title }}</h1>
1418
- * ```
1419
- * @return string
 
 
1420
  */
1421
- public function title() {
1422
- return $this->get_title();
1423
  }
1424
 
1425
  }
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\Core;
6
+ use Timber\CoreInterface;
7
+
8
+ use Timber\Term;
9
+ use Timber\User;
10
+ use Timber\Image;
11
+ use Timber\Helper;
12
+ use Timber\URLHelper;
13
+ use Timber\PostGetter;
14
+
15
  /**
16
  * This is the object you use to access or extend WordPress posts. Think of it as Timber's (more accessible) version of WP_Post. This is used throughout Timber to represent posts retrieved from WordPress making them available to Twig templates. See the PHP and Twig examples for an example of what it's like to work with this object in your code.
17
  * @example
19
  * <?php
20
  * // single.php, see connected twig example
21
  * $context = Timber::get_context();
22
+ * $context['post'] = new Timber\Post(); // It's a new Timber\Post object, but an existing post from WordPress.
23
  * Timber::render('single.twig', $context);
24
  * ?>
25
  * ```
44
  *
45
  * @package Timber
46
  */
47
+ class Post extends Core implements CoreInterface {
48
+
49
  /**
50
  * @var string $ImageClass the name of the class to handle images by default
51
  */
52
+ public $ImageClass = 'Timber\Image';
53
 
54
  /**
55
  * @var string $PostClass the name of the class to handle posts by default
56
  */
57
+ public $PostClass = 'Timber\Post';
58
 
59
  /**
60
  * @var string $TermClass the name of the class to handle terms by default
61
  */
62
+ public $TermClass = 'Timber\Term';
63
 
64
  /**
65
  * @var string $object_type what does this class represent in WordPress terms?
77
  */
78
  protected $_content;
79
 
 
 
 
 
 
 
 
80
  /**
81
  * @var string $_permalink the returned permalink from WP's get_permalink function
82
  */
83
  protected $_permalink;
84
 
85
  /**
86
+ * @var array $_next stores the results of the next Timber\Post in a set inside an array (in order to manage by-taxonomy)
87
  */
88
  protected $_next = array();
89
 
90
  /**
91
+ * @var array $_prev stores the results of the previous Timber\Post in a set inside an array (in order to manage by-taxonomy)
92
  */
93
  protected $_prev = array();
94
 
98
  */
99
  public $class;
100
 
 
 
 
 
 
 
101
  /**
102
  * @api
103
  * @var string $id the numeric WordPress id of a post
161
  * If you send the constructor nothing it will try to figure out the current post id based on being inside The_Loop
162
  * @example
163
  * ```php
164
+ * $post = new Timber\Post();
165
+ * $other_post = new Timber\Post($random_post_id);
166
  * ```
167
  * @param mixed $pid
168
  */
169
+ public function __construct( $pid = null ) {
170
+ $this->namespacing();
171
+
172
+ $pid = $this->determine_id($pid);
173
  $this->init($pid);
174
  }
175
 
179
  * @param mixed a value to test against
180
  * @return int the numberic id we should be using for this post object
181
  */
182
+ protected function determine_id( $pid ) {
183
  global $wp_query;
184
  if ( $pid === null &&
185
  isset($wp_query->queried_object_id)
188
  && is_object($wp_query->queried_object)
189
  && get_class($wp_query->queried_object) == 'WP_Post'
190
  ) {
191
+ if ( isset($_GET['preview']) && isset($_GET['preview_nonce']) && wp_verify_nonce($_GET['preview_nonce'], 'post_preview_'.$wp_query->queried_object_id) ) {
192
+ $pid = $this->get_post_preview_id($wp_query);
193
+ } else if ( !$pid ) {
194
+ $pid = $wp_query->queried_object_id;
195
+ }
196
+ } else if ( $pid === null && $wp_query->is_home && isset($wp_query->queried_object_id) && $wp_query->queried_object_id ) {
197
  //hack for static page as home page
198
  $pid = $wp_query->queried_object_id;
199
  } else if ( $pid === null ) {
200
  $gtid = false;
201
  $maybe_post = get_post();
202
+ if ( isset($maybe_post->ID) ) {
203
  $gtid = true;
204
  }
205
  if ( $gtid ) {
212
  }
213
  }
214
  }
215
+ if ( $pid === null && ($pid_from_loop = PostGetter::loop_to_id()) ) {
216
  $pid = $pid_from_loop;
217
  }
218
  return $pid;
226
  return $this->title();
227
  }
228
 
229
+ protected function get_post_preview_id( $query ) {
230
+ $can = array(
231
+ 'edit_'.$query->queried_object->post_type.'s',
232
+ );
233
+
234
+ if ( $query->queried_object->author_id !== get_current_user_id() ) {
235
+ $can[] = 'edit_others_'.$query->queried_object->post_type.'s';
236
+ }
237
+
238
+ $can_preview = array();
239
+
240
+ foreach ( $can as $type ) {
241
+ if ( current_user_can($type) ) {
242
+ $can_preview[] = true;
243
+ }
244
+ }
245
+
246
+ if ( count($can_preview) !== count($can) ) {
247
+ return;
248
+ }
249
+
250
+ $revisions = wp_get_post_revisions($query->queried_object_id);
251
+
252
+ if ( !empty($revisions) ) {
253
+ $last = end($revisions);
254
+ return $last->ID;
255
+ }
256
+
257
+ return false;
258
+ }
259
 
260
  /**
261
+ * Initializes a Post
262
  * @internal
263
  * @param int|bool $pid
264
  */
265
+ protected function init( $pid = false ) {
266
  if ( $pid === false ) {
267
  $pid = get_the_ID();
268
  }
271
  }
272
  $post_info = $this->get_info($pid);
273
  $this->import($post_info);
 
 
274
  //cant have a function, so gots to do it this way
275
  $post_class = $this->post_class();
276
  $this->class = $post_class;
279
  /**
280
  * Get the URL that will edit the current post/object
281
  * @internal
282
+ * @deprecated since 1.0
283
+ * @codeCoverageIgnore
284
+ * @see Timber\Post::edit_link
285
  * @return bool|string
286
  */
287
  function get_edit_url() {
288
+ return $this->edit_link();
 
 
289
  }
290
 
291
  /**
349
  * @param string $post_name
350
  * @return int
351
  */
352
+ static function get_post_id_by_name( $post_name ) {
353
  global $wpdb;
354
  $query = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_name = %s LIMIT 1", $post_name);
355
  $result = $wpdb->get_row($query);
356
+ if ( !$result ) {
357
  return null;
358
  }
359
  return $result->ID;
371
  * @param int $len The number of words that WP should use to make the tease. (Isn't this better than [this mess](http://wordpress.org/support/topic/changing-the-default-length-of-the_excerpt-1?replies=14)?). If you've set a post_excerpt on a post, we'll use that for the preview text; otherwise the first X words of the post_content
372
  * @param bool $force What happens if your custom post excerpt is longer then the length requested? By default (`$force = false`) it will use the full `post_excerpt`. However, you can set this to true to *force* your excerpt to be of the desired length
373
  * @param string $readmore The text you want to use on the 'readmore' link
374
+ * @param bool|string $strip true for default, false for none, string for list of custom attributes
375
+ * @param string $end The text to end the preview with (defaults to ...)
376
  * @return string of the post preview
377
  */
378
+ function get_preview( $len = 50, $force = false, $readmore = 'Read More', $strip = true, $end = '&hellip;' ) {
379
  $text = '';
380
  $trimmed = false;
381
  if ( isset($this->post_excerpt) && strlen($this->post_excerpt) ) {
382
  if ( $force ) {
383
+ $text = Helper::trim_words($this->post_excerpt, $len, false);
384
  $trimmed = true;
385
  } else {
386
  $text = $this->post_excerpt;
390
  $pieces = explode($readmore_matches[0], $this->post_content);
391
  $text = $pieces[0];
392
  if ( $force ) {
393
+ $text = Helper::trim_words($text, $len, false);
394
  $trimmed = true;
395
  }
396
+ $text = do_shortcode($text);
397
  }
398
  if ( !strlen($text) ) {
399
+ $text = Helper::trim_words($this->get_content(), $len, false);
400
  $trimmed = true;
401
  }
402
  if ( !strlen(trim($text)) ) {
403
  return trim($text);
404
  }
405
  if ( $strip ) {
406
+ $allowable_tags = (is_string($strip)) ? $strip : null;
407
+ $text = trim(strip_tags($text, $allowable_tags));
408
  }
409
  if ( strlen($text) ) {
410
  $text = trim($text);
411
  $last = $text[strlen($text) - 1];
412
  if ( $last != '.' && $trimmed ) {
413
+ $text .= $end;
414
  }
415
  if ( !$strip ) {
416
  $last_p_tag = strrpos($text, '</p>');
418
  $text = substr($text, 0, $last_p_tag);
419
  }
420
  if ( $last != '.' && $trimmed ) {
421
+ $text .= $end.' ';
422
  }
423
  }
424
  $read_more_class = apply_filters('timber/post/get_preview/read_more_class', "read-more");
425
  if ( $readmore && isset($readmore_matches) && !empty($readmore_matches[1]) ) {
426
+ $text .= ' <a href="'.$this->link().'" class="'.$read_more_class.'">'.trim($readmore_matches[1]).'</a>';
427
  } elseif ( $readmore ) {
428
+ $text .= ' <a href="'.$this->link().'" class="'.$read_more_class.'">'.trim($readmore).'</a>';
429
  }
430
+ if ( !$strip && $last_p_tag && (strpos($text, '<p>') || strpos($text, '<p ')) ) {
431
  $text .= '</p>';
432
  }
433
  }
471
  }
472
 
473
  /**
474
+ * @param int $i
475
+ * @return string
 
476
  */
477
+ protected static function get_wp_link_page( $i ) {
478
+ $link = _wp_link_page($i);
479
+ $link = new \SimpleXMLElement($link.'</a>');
480
+ if ( isset($link['href']) ) {
481
+ return $link['href'];
 
482
  }
483
+ return '';
484
  }
485
 
486
  /**
487
+ * Used internally by init, etc. to build TimberPost object
488
  * @internal
489
+ * @param int $pid
490
+ * @return null|object|WP_Post
491
  */
492
+ protected function get_info( $pid ) {
493
+ $post = $this->prepare_post_info($pid);
494
+ if ( !isset($post->post_status) ) {
495
+ return null;
496
  }
497
+ $post->status = $post->post_status;
498
+ $post->id = $post->ID;
499
+ $post->slug = $post->post_name;
500
+ $customs = $this->get_post_custom($post->ID);
501
+ $post->custom = $customs;
502
+ $post = (object) array_merge((array) $customs, (array) $post);
503
+ return $post;
504
  }
505
 
 
 
 
 
 
 
 
 
 
 
506
 
507
  /**
508
+ * Get the terms associated with the post
509
+ * This goes across all taxonomies by default
510
+ * @api
511
+ * @param string|array $tax What taxonom(y|ies) to pull from. Defaults to all registered taxonomies for the post type. You can use custom ones, or built-in WordPress taxonomies (category, tag). Timber plays nice and figures out that tag/tags/post_tag are all the same (and categories/category), for custom taxonomies you're on your own.
512
+ * @param bool $merge Should the resulting array be one big one (true)? Or should it be an array of sub-arrays for each taxonomy (false)?
513
+ * @return array
514
  */
515
+ public function terms( $tax = '', $merge = true, $TermClass = '' ) {
 
 
 
 
 
 
 
 
 
 
516
 
517
+ $TermClass = $TermClass ?: $this->TermClass;
518
+
519
+ if ( is_string($merge) && class_exists($merge) ) {
520
+ $TermClass = $merge;
521
+ }
522
+ if ( is_array($tax) ) {
523
+ $taxonomies = $tax;
524
+ }
525
+ if ( is_string($tax) ) {
526
+ if ( in_array($tax, array('all', 'any', '')) ) {
527
+ $taxonomies = get_object_taxonomies($this->post_type);
528
  } else {
529
+ $taxonomies = array($tax);
530
  }
 
531
  }
 
 
532
 
533
+ $term_class_objects = array();
534
+
535
+ foreach ( $taxonomies as $taxonomy ) {
536
+ if ( in_array($taxonomy, array('tag', 'tags')) ) {
537
+ $taxonomy = 'post_tag';
 
 
 
 
 
 
 
 
 
 
 
538
  }
539
+ if ( $taxonomy == 'categories' ) {
540
+ $taxonomy = 'category';
 
 
541
  }
542
+
543
+ $terms = wp_get_post_terms($this->ID, $taxonomy);
544
+
545
+ if ( is_wp_error($terms) ) {
546
+ /* @var $terms WP_Error */
547
+ Helper::error_log("Error retrieving terms for taxonomy '$taxonomy' on a post in timber-post.php");
548
+ Helper::error_log('tax = '.print_r($tax, true));
549
+ Helper::error_log('WP_Error: '.$terms->get_error_message());
550
+
551
+ return $term_class_objects;
552
+ }
553
+
554
+ // map over array of wordpress terms, and transform them into instances of the TermClass
555
+ $terms = array_map(function( $term ) use ($TermClass, $taxonomy) {
556
+ return call_user_func(array($TermClass, 'from'), $term->term_id, $taxonomy);
557
+ }, $terms);
558
+
559
+ if ( $merge && is_array($terms) ) {
560
+ $term_class_objects = array_merge($term_class_objects, $terms);
561
+ } else if ( count($terms) ) {
562
+ $term_class_objects[$taxonomy] = $terms;
563
  }
564
  }
565
+ return $term_class_objects;
566
  }
567
 
568
  /**
569
+ * @param string|int $term_name_or_id
570
+ * @param string $taxonomy
571
+ * @return bool
572
  */
573
+ function has_term( $term_name_or_id, $taxonomy = 'all' ) {
574
+ if ( $taxonomy == 'all' || $taxonomy == 'any' ) {
575
+ $taxes = get_object_taxonomies($this->post_type, 'names');
576
+ $ret = false;
577
+ foreach ( $taxes as $tax ) {
578
+ if ( has_term($term_name_or_id, $tax, $this->ID) ) {
579
+ $ret = true;
580
+ break;
581
+ }
582
+ }
583
+ return $ret;
584
  }
585
+ return has_term($term_name_or_id, $taxonomy, $this->ID);
586
  }
587
 
588
  /**
 
 
 
 
589
  * @return string
590
  */
591
+ function get_paged_content() {
592
+ return $this->paged_content();
593
  }
 
594
  /**
595
+ *
596
+ * Here is my summary
597
+ * @example
598
+ * ```twig
599
+ * This post is from <span>{{ post.get_post_type.labels.plural }}</span>
600
+ * ```
601
+ *
602
+ * ```html
603
+ * This post is from <span>Recipes</span>
604
+ * ```
605
+ * @return mixed
606
  */
607
+ public function get_post_type() {
608
+ return get_post_type_object($this->post_type);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
609
  }
610
 
611
  /**
612
+ * @return int the number of comments on a post
 
 
613
  */
614
+ public function get_comment_count() {
615
+ return get_comments_number($this->ID);
 
 
 
616
  }
617
 
618
  /**
619
+ * @param string $field_name
620
+ * @return mixed
 
 
621
  */
622
+ public function get_field( $field_name ) {
623
+ $value = apply_filters('timber_post_get_meta_field_pre', null, $this->ID, $field_name, $this);
624
+ if ( $value === null ) {
625
+ $value = get_post_meta($this->ID, $field_name);
626
+ if ( is_array($value) && count($value) == 1 ) {
627
+ $value = $value[0];
628
+ }
629
+ if ( is_array($value) && count($value) == 0 ) {
630
+ $value = null;
631
+ }
632
  }
633
+ $value = apply_filters('timber_post_get_meta_field', $value, $this->ID, $field_name, $this);
634
+ $value = $this->convert($value, __CLASS__);
635
+ return $value;
636
  }
637
 
638
  /**
639
+ * @param string $field_name
 
640
  */
641
+ function import_field( $field_name ) {
642
+ $this->$field_name = $this->get_field($field_name);
 
643
  }
644
 
645
  /**
646
+ * Get the CSS classes for a post. For usage you should use `{{post.class}}` instead of `{{post.post_class}}`
647
  * @internal
648
+ * @param string $class additional classes you want to add
649
+ * @see Timber\Post::$class
650
+ * @example
651
+ * ```twig
652
+ * <article class="{{ post.class }}">
653
+ * {# Some stuff here #}
654
+ * </article>
655
+ * ```
656
+ *
657
+ * ```html
658
+ * <article class="post-2612 post type-post status-publish format-standard has-post-thumbnail hentry category-data tag-charleston-church-shooting tag-dylann-roof tag-gun-violence tag-hate-crimes tag-national-incident-based-reporting-system">
659
+ * {# Some stuff here #}
660
+ * </article>
661
+ * ```
662
+ * @return string a space-seperated list of classes
663
+ */
664
+ public function post_class( $class = '' ) {
665
+ global $post;
666
+ $old_global_post = $post;
667
+ $post = $this;
668
+ $class_array = get_post_class($class, $this->ID);
669
+ $post = $old_global_post;
670
+ if ( is_array($class_array) ) {
671
+ return implode(' ', $class_array);
672
+ }
673
+ return $class_array;
674
  }
675
 
676
+ // Docs
677
+
678
  /**
679
+ * @return array
680
+ * @codeCoverageIgnore
 
 
 
681
  */
682
+ public function get_method_values() {
683
+ $ret = parent::get_method_values();
684
+ $ret['author'] = $this->author();
685
+ $ret['categories'] = $this->categories();
686
+ $ret['category'] = $this->category();
687
+ $ret['children'] = $this->children();
688
+ $ret['comments'] = $this->comments();
689
+ $ret['content'] = $this->content();
690
+ $ret['edit_link'] = $this->edit_link();
691
+ $ret['format'] = $this->format();
692
+ $ret['link'] = $this->link();
693
+ $ret['next'] = $this->next();
694
+ $ret['pagination'] = $this->pagination();
695
+ $ret['parent'] = $this->parent();
696
+ $ret['path'] = $this->path();
697
+ $ret['prev'] = $this->prev();
698
+ $ret['terms'] = $this->terms();
699
+ $ret['tags'] = $this->tags();
700
+ $ret['thumbnail'] = $this->thumbnail();
701
+ $ret['title'] = $this->title();
702
+ return $ret;
703
  }
704
 
705
  /**
706
+ * Return the author of a post
707
+ * @api
708
+ * @example
709
+ * ```twig
710
+ * <h1>{{post.title}}</h1>
711
+ * <p class="byline">
712
+ * <a href="{{post.author.link}}">{{post.author.name}}</a>
713
+ * </p>
714
+ * ```
715
+ * @return TimberUser|bool A TimberUser object if found, false if not
716
  */
717
+ public function author() {
718
+ return $this->get_author();
 
 
719
  }
720
 
721
  /**
722
+ * Get the author (WordPress user) who last modified the post
723
+ * @example
724
+ * ```twig
725
+ * Last updated by {{ post.modified_author.name }}
726
+ * ```
727
+ * ```html
728
+ * Last updated by Harper Lee
729
+ * ```
730
+ * @return TimberUser|bool A TimberUser object if found, false if not
731
  */
732
+ public function modified_author() {
733
+ $user_id = get_post_meta($this->ID, '_edit_last', true);
734
+ return ($user_id ? new User($user_id) : $this->get_author());
 
735
  }
736
 
737
  /**
738
+ * Get the categoires on a particular post
739
+ * @api
740
+ * @return array of TimberTerms
741
  */
742
+ public function categories() {
743
+ return $this->get_terms('category');
 
 
744
  }
745
 
746
  /**
747
+ * Returns a category attached to a post
748
+ * @api
749
+ * If mulitpuile categories are set, it will return just the first one
750
+ * @return TimberTerm|null
751
+ */
752
+ public function category() {
753
+ return $this->get_category();
754
+ }
755
+
756
+ /**
757
+ * Returns an array of children on the post as Timber\Posts
758
+ * (or other claass as you define).
759
+ * @api
760
+ * @example
761
+ * ```twig
762
+ * {% if post.children %}
763
+ * Here are the child pages:
764
+ * {% for child in page.children %}
765
+ * <a href="{{ child.link }}">{{ child.title }}</a>
766
+ * {% endfor %}
767
+ * {% endif %}
768
+ * ```
769
+ * @param string $post_type _optional_ use to find children of a particular post type (attachment vs. page for example). You might want to restrict to certain types of children in case other stuff gets all mucked in there. You can use 'parent' to use the parent's post type
770
+ * @param string|bool $childPostClass _optional_ a custom post class (ex: 'MyTimber\Post') to return the objects as. By default (false) it will use Timber\Post::$post_class value.
771
  * @return array
772
  */
773
+ public function children( $post_type = 'any', $childPostClass = false ) {
774
  if ( $childPostClass === false ) {
775
  $childPostClass = $this->PostClass;
776
  }
777
  if ( $post_type == 'parent' ) {
778
  $post_type = $this->post_type;
779
  }
780
+ $children = get_children('post_parent='.$this->ID.'&post_type='.$post_type.'&numberposts=-1&orderby=menu_order title&order=ASC&post_status=publish');
781
  foreach ( $children as &$child ) {
782
  $child = new $childPostClass($child->ID);
783
  }
785
  return $children;
786
  }
787
 
 
788
  /**
789
+ * Gets the comments on a Timber\Post and returns them as an array of [TimberComments](#TimberComment) (or whatever comment class you set).
790
+ * @api
791
+ * @param int $count Set the number of comments you want to get. `0` is analogous to "all"
792
+ * @param string $order use ordering set in WordPress admin, or a different scheme
793
+ * @param string $type For when other plugins use the comments table for their own special purposes, might be set to 'liveblog' or other depending on what's stored in yr comments table
794
+ * @param string $status Could be 'pending', etc.
795
+ * @param string $CommentClass What class to use when returning Comment objects. As you become a Timber pro, you might find yourself extending TimberComment for your site or app (obviously, totally optional)
796
+ * @example
797
+ * ```twig
798
+ * {# single.twig #}
799
+ * <h4>Comments:</h4>
800
+ * {% for comment in post.comments %}
801
+ * <div class="comment-{{comment.ID}} comment-order-{{loop.index}}">
802
+ * <p>{{comment.author.name}} said:</p>
803
+ * <p>{{comment.content}}</p>
804
+ * </div>
805
+ * {% endfor %}
806
+ * ```
807
+ * @return bool|array
808
  */
809
+ public function comments( $count = 0, $order = 'wp', $type = 'comment', $status = 'approve', $CommentClass = 'TimberComment' ) {
 
 
810
  global $overridden_cpage, $user_ID;
811
  $overridden_cpage = false;
812
 
814
  $comment_author_email = $commenter['comment_author_email'];
815
 
816
  $args = array('post_id' => $this->ID, 'status' => $status, 'order' => $order);
817
+ if ( $count > 0 ) {
818
+ $args['number'] = $count;
819
  }
820
  if ( strtolower($order) == 'wp' || strtolower($order) == 'wordpress' ) {
821
  $args['order'] = get_option('comment_order');
822
  }
823
 
824
  if ( $user_ID ) {
825
+ $args['include_unapproved'] = array($user_ID);
826
+ } elseif ( !empty($comment_author_email) ) {
827
+ $args['include_unapproved'] = array($comment_author_email);
828
  }
829
 
830
  $comments = get_comments($args);
831
  $timber_comments = array();
832
 
833
  if ( '' == get_query_var('cpage') && get_option('page_comments') ) {
834
+ set_query_var('cpage', 'newest' == get_option('default_comments_page') ? get_comment_pages_count() : 1);
835
  $overridden_cpage = true;
836
  }
837
 
838
+ foreach ( $comments as $key => &$comment ) {
839
  $timber_comment = new $CommentClass($comment);
840
  $timber_comments[$timber_comment->id] = $timber_comment;
841
  }
842
 
843
  // Build a flattened (depth=1) comment tree
844
  $comments_tree = array();
845
+ foreach ( $timber_comments as $key => $comment ) {
846
+ if ( !$comment->is_child() ) {
847
  continue;
848
  }
849
 
850
  $tree_element = $comment;
851
  do {
852
  $tree_element = $timber_comments[$tree_element->comment_parent];
853
+ } while ( $tree_element->is_child() );
854
 
855
  $comments_tree[$tree_element->id][] = $comment->id;
856
  }
857
 
858
  // Add child comments to the relative "super parents"
859
+ foreach ( $comments_tree as $comment_parent => $comment_children ) {
860
+ foreach ( $comment_children as $comment_child ) {
861
  $timber_comments[$comment_parent]->children[] = $timber_comments[$comment_child];
862
  unset($timber_comments[$comment_child]);
863
  }
869
  }
870
 
871
  /**
872
+ * Gets the actual content of a WP Post, as opposed to post_content this will run the hooks/filters attached to the_content. \This guy will return your posts content with WordPress filters run on it (like for shortcodes and wpautop).
873
+ * @api
874
+ * @example
875
+ * ```twig
876
+ * <div class="article">
877
+ * <h2>{{post.title}}</h2>
878
+ * <div class="content">{{ post.content }}</div>
879
+ * </div>
880
+ * ```
881
+ * @param int $page
882
+ * @return string
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883
  */
884
+ public function content( $page = 0, $len = -1 ) {
885
+ if ( $len == -1 && $page == 0 && $this->_content ) {
886
+ return $this->_content;
 
 
 
887
  }
888
+ $content = $this->post_content;
889
+ if ( $len > 0 ) {
890
+ $content = wp_trim_words($content, $len);
891
  }
892
+ if ( $page ) {
893
+ $contents = explode('<!--nextpage-->', $content);
894
+ $page--;
895
+ if ( count($contents) > $page ) {
896
+ $content = $contents[$page];
897
  }
898
  }
899
+ $content = apply_filters('the_content', ($content));
900
+ if ( $len == -1 && $page == 0 ) {
901
+ $this->_content = $content;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
902
  }
903
+ return $content;
904
  }
905
 
906
  /**
907
+ * @return string
 
 
908
  */
909
+ public function paged_content() {
910
+ global $page;
911
+ return $this->content($page, -1);
 
 
 
 
 
 
 
 
 
 
912
  }
913
 
914
  /**
915
+ * Get the date to use in your template!
916
+ * @api
 
 
 
 
 
 
 
 
917
  * @example
918
  * ```twig
919
+ * Published on {{ post.date }} // Uses WP's formatting set in Admin
920
+ * OR
921
+ * Published on {{ post.date | date('F jS') }} // Jan 12th
 
 
922
  * ```
923
+ *
924
+ * ```html
925
+ * Published on January 12, 2015
926
+ * OR
927
+ * Published on Jan 12th
928
+ * ```
929
+ * @param string $date_format
930
+ * @return string
931
  */
932
+ public function date( $date_format = '' ) {
933
+ $df = $date_format ? $date_format : get_option('date_format');
934
+ $the_date = (string) mysql2date($df, $this->post_date);
935
+ return apply_filters('get_the_date', $the_date, $df);
936
  }
937
 
938
  /**
939
+ * Get the time to use in your template
940
+ * @api
941
  * @example
942
  * ```twig
943
+ * Published at {{ post.time }} // Uses WP's formatting set in Admin
944
+ * OR
945
+ * Published at {{ post.time | time('G:i') }} // 13:25
946
  * ```
947
+ *
948
  * ```html
949
+ * Published at 1:25 pm
950
+ * OR
951
+ * Published at 13:25
952
  * ```
953
+ * @param string $time_format
954
  * @return string
955
  */
956
+ public function time( $time_format = '' ) {
957
+ $tf = $time_format ? $time_format : get_option('time_format');
958
+ $the_time = (string) mysql2date($tf, $this->post_date);
959
+ return apply_filters('get_the_time', $the_time, $tf);
960
  }
961
 
962
  /**
963
+ * Returns the edit URL of a post if the user has access to it
964
+ * @return bool|string the edit URL of a post in the WordPress admin
 
 
 
 
 
 
 
 
 
965
  */
966
+ public function edit_link() {
967
+ if ( $this->can_edit() ) {
968
+ return get_edit_post_link($this->ID);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
969
  }
 
970
  }
971
 
972
  /**
973
+ * @api
974
+ * @return mixed
975
  */
976
+ public function format() {
977
+ return get_post_format($this->ID);
 
978
  }
979
+
980
  /**
981
+ * get the permalink for a post object
982
+ * @api
983
  * @example
984
  * ```twig
985
+ * <a href="{{post.link}}">Read my post</a>
 
 
 
 
986
  * ```
987
+ * @return string ex: http://example.org/2015/07/my-awesome-post
988
+ */
989
+ public function link() {
990
+ if ( isset($this->_permalink) ) {
991
+ return $this->_permalink;
992
+ }
993
+ $this->_permalink = get_permalink($this->ID);
994
+ return $this->_permalink;
995
+ }
996
+
997
+ /**
998
+ * @param string $field_name
999
  * @return mixed
1000
  */
1001
+ public function meta( $field_name = null ) {
1002
+ if ( $field_name === null ) {
1003
+ //on the off-chance the field is actually named meta
1004
+ $field_name = 'meta';
1005
+ }
1006
+ return $this->get_field($field_name);
1007
  }
1008
 
1009
  /**
1010
+ * @return string
1011
  */
1012
+ public function name() {
1013
+ return $this->title();
1014
  }
1015
 
1016
  /**
1017
+ * @param string $date_format
1018
+ * @return string
1019
  */
1020
+ public function modified_date( $date_format = '' ) {
1021
+ $df = $date_format ? $date_format : get_option('date_format');
1022
+ $the_time = $this->get_modified_time($df);
1023
+ return apply_filters('get_the_modified_date', $the_time, $date_format);
 
 
 
 
 
 
 
 
 
1024
  }
1025
 
1026
  /**
1027
+ * @param string $time_format
1028
+ * @return string
1029
  */
1030
+ public function modified_time( $time_format = '' ) {
1031
+ return $this->get_modified_time($time_format);
1032
  }
1033
 
1034
  /**
1035
+ * @api
1036
+ * @param bool $in_same_cat
1037
  * @return mixed
1038
  */
1039
+ public function next( $in_same_term = false ) {
1040
+ if ( !isset($this->_next) || !isset($this->_next[$in_same_term]) ) {
1041
+ global $post;
1042
+ $this->_next = array();
1043
+ $old_global = $post;
1044
+ $post = $this;
1045
+ if ( $in_same_term ) {
1046
+ $adjacent = get_adjacent_post(true, '', false, $in_same_term);
1047
+ } else {
1048
+ $adjacent = get_adjacent_post(false, '', false);
1049
+ }
1050
+
1051
+ if ( $adjacent ) {
1052
+ $this->_next[$in_same_term] = new $this->PostClass($adjacent);
1053
+ } else {
1054
+ $this->_next[$in_same_term] = false;
1055
+ }
1056
+ $post = $old_global;
1057
+ }
1058
+ return $this->_next[$in_same_term];
1059
  }
1060
 
1061
  /**
1062
+ * Get a data array of pagination so you can navigate to the previous/next for a paginated post
1063
+ * @return array
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1064
  */
1065
+ public function pagination() {
1066
+ global $post, $page, $numpages, $multipage;
 
1067
  $post = $this;
1068
+ $ret = array();
1069
+ if ( $multipage ) {
1070
+ for ( $i = 1; $i <= $numpages; $i++ ) {
1071
+ $link = self::get_wp_link_page($i);
1072
+ $data = array('name' => $i, 'title' => $i, 'text' => $i, 'link' => $link);
1073
+ if ( $i == $page ) {
1074
+ $data['current'] = true;
1075
+ }
1076
+ $ret['pages'][] = $data;
1077
+ }
1078
+ $i = $page - 1;
1079
+ if ( $i ) {
1080
+ $link = self::get_wp_link_page($i);
1081
+ $ret['prev'] = array('link' => $link);
1082
+ }
1083
+ $i = $page + 1;
1084
+ if ( $i <= $numpages ) {
1085
+ $link = self::get_wp_link_page($i);
1086
+ $ret['next'] = array('link' => $link);
1087
+ }
1088
  }
1089
+ return $ret;
1090
  }
1091
 
1092
+
1093
 
1094
  /**
1095
+ * Finds any WP_Post objects and converts them to Timber\Posts
1096
+ * @param array $data
1097
  */
1098
+ public function convert( $data, $class ) {
1099
+ if ( is_array($data) ) {
1100
+ $func = __FUNCTION__;
1101
+ foreach ( $data as &$ele ) {
1102
+ if ( gettype($ele) === 'array' ) {
1103
+ $ele = $this->$func($ele, $class);
1104
+ } else {
1105
+ if ( $ele instanceof WP_Post ) {
1106
+ $ele = new $class($ele);
1107
+ }
1108
+ }
1109
+ }
1110
+ }
1111
+
1112
+ return $data;
 
 
 
 
 
 
1113
  }
1114
 
1115
  /**
1116
+ * Gets the parent (if one exists) from a post as a Timber\Post object (or whatever is set in Timber\Post::$PostClass)
1117
  * @api
1118
  * @example
1119
  * ```twig
1120
+ * Parent page: <a href="{{ post.parent.link }}">{{ post.parent.title }}</a>
 
 
 
1121
  * ```
1122
+ * @return bool|Timber\Post
1123
+ * @param string $field_name
1124
  */
1125
+ public function parent() {
1126
+ if ( !$this->post_parent ) {
1127
+ return false;
1128
+ }
1129
+ return new $this->PostClass($this->post_parent);
1130
  }
1131
 
1132
  /**
1133
+ * Gets the relative path of a WP Post, so while link() will return http://example.org/2015/07/my-cool-post
1134
+ * this will return just /2015/07/my-cool-post
1135
+ * @api
1136
  * @example
1137
  * ```twig
1138
+ * <a href="{{post.path}}">{{post.title}}</a>
 
 
 
1139
  * ```
1140
+ * @return string
1141
  */
1142
+ public function path() {
1143
+ return URLHelper::get_rel_url($this->get_link());
1144
  }
1145
 
1146
  /**
1147
+ * Get the previous post in a set
1148
  * @api
1149
+ * @example
1150
+ * ```twig
1151
+ * <h4>Prior Entry:</h4>
1152
+ * <h3>{{post.prev.title}}</h3>
1153
+ * <p>{{post.prev.get_preview(25)}}</p>
1154
+ * ```
1155
+ * @param bool $in_same_term
1156
+ * @return mixed
1157
  */
1158
+ public function prev( $in_same_term = false ) {
1159
+ if ( isset($this->_prev) && isset($this->_prev[$in_same_term]) ) {
1160
+ return $this->_prev[$in_same_term];
1161
+ }
1162
+ global $post;
1163
+ $old_global = $post;
1164
+ $post = $this;
1165
+ $within_taxonomy = ($in_same_term) ? $in_same_term : 'category';
1166
+ $adjacent = get_adjacent_post(($in_same_term), '', true, $within_taxonomy);
1167
+ $prev_in_taxonomy = false;
1168
+ if ( $adjacent ) {
1169
+ $prev_in_taxonomy = new $this->PostClass($adjacent);
1170
+ }
1171
+ $this->_prev[$in_same_term] = $prev_in_taxonomy;
1172
+ $post = $old_global;
1173
+ return $this->_prev[$in_same_term];
1174
  }
1175
 
1176
  /**
1177
+ * Gets the tags on a post, uses WP's post_tag taxonomy
1178
  * @api
1179
+ * @return array
 
1180
  */
1181
+ public function tags() {
1182
+ return $this->get_tags();
1183
  }
1184
 
1185
  /**
1186
+ * get the featured image as a TimberImage
 
1187
  * @api
1188
  * @example
1189
  * ```twig
1190
+ * <img src="{{post.thumbnail.src}}" />
 
 
 
 
 
1191
  * ```
1192
+ * @return TimberImage|null of your thumbnail
 
 
1193
  */
1194
+ public function thumbnail() {
1195
+ if ( function_exists('get_post_thumbnail_id') ) {
1196
+ $tid = get_post_thumbnail_id($this->ID);
1197
+ if ( $tid ) {
1198
+ //return new Image($tid);
1199
+ return new $this->ImageClass($tid);
1200
+ }
1201
+ }
1202
  }
1203
 
1204
  /**
1205
+ * Returns the processed title to be used in templates. This returns the title of the post after WP's filters have run. This is analogous to `the_title()` in standard WP template tags.
1206
  * @api
 
 
 
 
 
1207
  * @example
1208
  * ```twig
1209
+ * <h1>{{ post.title }}</h1>
 
 
 
 
 
 
 
1210
  * ```
1211
+ * @return string
1212
+ */
1213
+ public function title() {
1214
+ return apply_filters('the_title', $this->post_title, $this->ID);
1215
+ }
1216
+
1217
+ /**
1218
+ *
1219
+ * ===================================
1220
+ * DEPRECATED FUNCTIONS LIVE DOWN HERE
1221
+ * ===================================
1222
+ *
1223
+ */
1224
+
1225
+ /**
1226
+ * Get the categories for a post
1227
+ * @internal
1228
+ * @deprecated since 1.0
1229
+ * @codeCoverageIgnore
1230
+ * @see Timber\Post::categories
1231
+ * @return array of TimberTerms
1232
  */
1233
+ function get_categories() {
1234
+ return $this->get_terms('category');
1235
  }
1236
 
1237
  /**
1238
+ * @internal
1239
+ * @deprecated since 1.0
1240
+ * @codeCoverageIgnore
1241
+ * @see Timber\Post::category
1242
+ * @return mixed
 
 
 
 
 
 
1243
  */
1244
+ function get_category( ) {
1245
+ $cats = $this->get_categories();
1246
+ if ( count($cats) && isset($cats[0]) ) {
1247
+ return $cats[0];
1248
+ }
1249
  }
1250
 
1251
  /**
1252
+ * @param string $field
1253
+ * @return TimberImage
1254
  */
1255
+ function get_image( $field ) {
1256
+ return new $this->ImageClass($this->$field);
1257
  }
1258
 
1259
  /**
1260
+ * Gets an array of tags for you to use
1261
+ * @internal
1262
+ * @deprecated since 1.0
1263
+ * @codeCoverageIgnore
1264
  * @example
1265
  * ```twig
1266
+ * <ul class="tags">
1267
+ * {% for tag in post.tags %}
1268
+ * <li>{{tag.name}}</li>
1269
+ * {% endfor %}
1270
+ * </ul>
 
 
 
 
1271
  * ```
1272
+ * @return array
 
1273
  */
1274
+ function get_tags() {
1275
+ return $this->get_terms('post_tag');
1276
  }
1277
 
1278
  /**
1279
+ * Outputs the title with filters applied
1280
+ * @internal
1281
+ * @deprecated since 1.0
1282
+ * @codeCoverageIgnore
1283
  * @example
1284
  * ```twig
1285
+ * <h1>{{post.get_title}}</h1>
 
 
1286
  * ```
 
1287
  * ```html
1288
+ * <h1>Hello World!</h1>
 
 
1289
  * ```
 
1290
  * @return string
1291
  */
1292
+ function get_title() {
1293
+ return $this->title();
 
 
1294
  }
1295
 
1296
+ /**
1297
+ * Displays the content of the post with filters, shortcodes and wpautop applied
1298
+ * @example
1299
+ * ```twig
1300
+ * <div class="article-text">{{post.get_content}}</div>
1301
+ * ```
1302
+ * ```html
1303
+ * <div class="article-text"><p>Blah blah blah</p><p>More blah blah blah.</p></div>
1304
+ * ```
1305
+ * @param int $len
1306
+ * @param int $page
1307
+ * @return string
1308
+ */
1309
+ function get_content( $len = -1, $page = 0 ) {
1310
+ if ( $len === 0 ) {
1311
+ $len = -1;
1312
+ }
1313
+ return $this->content($page, $len);
1314
  }
1315
 
1316
  /**
1317
+ * @internal
1318
+ * @deprecated since 1.0
1319
  * @return mixed
1320
  */
1321
+ function get_format() {
1322
+ return $this->format();
1323
  }
1324
 
1325
  /**
1326
+ * Get the terms associated with the post
1327
+ * This goes across all taxonomies by default
1328
+ * @internal
1329
+ * @deprecated since 1.0
1330
+ * @codeCoverageIgnore
1331
+ * @param string|array $tax What taxonom(y|ies) to pull from. Defaults to all registered taxonomies for the post type. You can use custom ones, or built-in WordPress taxonomies (category, tag). Timber plays nice and figures out that tag/tags/post_tag are all the same (and categories/category), for custom taxonomies you're on your own.
1332
+ * @param bool $merge Should the resulting array be one big one (true)? Or should it be an array of sub-arrays for each taxonomy (false)?
1333
+ * @return array
1334
  */
1335
+ public function get_terms( $tax = '', $merge = true ) {
1336
+ return $this->terms($tax, $merge);
1337
  }
1338
 
1339
  /**
1340
+ * @deprecated 0.20.0 use link() instead
1341
+ * @codeCoverageIgnore
1342
+ * @return string
1343
  */
1344
+ public function permalink() {
1345
+ Helper::warn('post.permalink has been removed, please use post.link');
1346
+ return $this->link();
1347
+ }
1348
+
1349
+ /**
1350
+ * @internal
1351
+ * @see Timber\Post::date
1352
+ * @deprecated since 1.0
1353
+ * @codeCoverageIgnore
1354
+ * @param string $date_format
1355
+ * @return string
1356
+ */
1357
+ function get_date( $date_format = '' ) {
1358
+ return $this->date($date_format);
1359
  }
1360
 
1361
  /**
1362
+ * @internal
1363
+ * @see Timber\Post::modified_date
1364
+ * @deprecated since 1.0
1365
+ * @codeCoverageIgnore
1366
+ * @param string $date_format
1367
  * @return string
1368
  */
1369
+ function get_modified_date( $date_format = '' ) {
1370
+ return $this->modified_date($date_format);
1371
  }
1372
 
1373
  /**
1374
+ * @internal
1375
+ * @param string $time_format
1376
  * @return string
1377
  */
1378
+ function get_modified_time( $time_format = '' ) {
1379
+ $tf = $time_format ? $time_format : get_option('time_format');
1380
+ $the_time = get_post_modified_time($tf, false, $this->ID, true);
1381
+ return apply_filters('get_the_modified_time', $the_time, $time_format);
1382
  }
1383
 
1384
  /**
1385
+ * @internal
1386
+ * @see Timber\Post::children
1387
+ * @deprecated since 1.0
1388
+ * @codeCoverageIgnore
1389
+ * @param string $post_type
1390
+ * @param bool|string $childPostClass
1391
+ * @return array
1392
  */
1393
+ function get_children( $post_type = 'any', $childPostClass = false ) {
1394
+ return $this->children($post_type, $childPostClass);
1395
+ }
1396
+
1397
+ /**
1398
+ * Get the permalink for a post, but as a relative path
1399
+ * For example, where {{post.link}} would return "http://example.org/2015/07/04/my-cool-post"
1400
+ * this will return the relative version: "/2015/07/04/my-cool-post"
1401
+ * @internal
1402
+ * @deprecated since 1.0
1403
+ * @codeCoverageIgnore
1404
+ * @return string
1405
+ */
1406
+ function get_path() {
1407
+ return $this->path();
1408
  }
1409
 
1410
  /**
1411
+ * Get the next post in WordPress's ordering
1412
+ * @internal
1413
+ * @deprecated since 1.0
1414
+ * @codeCoverageIgnore
1415
+ * @param bool $taxonomy
1416
+ * @return TimberPost|boolean
1417
  */
1418
+ function get_prev( $in_same_term = false ) {
1419
+ return $this->prev($in_same_term);
1420
  }
1421
 
1422
  /**
1423
+ * Get the parent post of the post
1424
+ * @internal
1425
+ * @deprecated since 1.0
1426
+ * @codeCoverageIgnore
1427
+ * @return bool|TimberPost
1428
  */
1429
+ function get_parent() {
1430
+ return $this->parent();
1431
  }
1432
 
1433
  /**
1434
+ * Gets a User object from the author of the post
1435
+ * @internal
1436
+ * @deprecated since 1.0
1437
+ * @codeCoverageIgnore
1438
+ * @see TimberPost::author
1439
+ * @return bool|TimberUser
 
1440
  */
1441
+ function get_author() {
1442
+ if ( isset($this->post_author) ) {
1443
+ return new User($this->post_author);
1444
+ }
1445
  }
1446
 
1447
  /**
1448
+ * @internal
1449
+ * @deprecated since 1.0
1450
+ * @codeCoverageIgnore
1451
+ * @return bool|TimberUser
 
 
 
 
1452
  */
1453
+ function get_modified_author() {
1454
+ return $this->modified_author();
1455
+ }
1456
+
1457
+ /**
1458
+ * @internal
1459
+ * @see TimberPost::thumbnail
1460
+ * @deprecated since 1.0
1461
+ * @codeCoverageIgnore
1462
+ * @return null|TimberImage
1463
+ */
1464
+ function get_thumbnail() {
1465
+ return $this->thumbnail();
1466
  }
1467
 
1468
  /**
1469
+ * @internal
1470
+ * @see TimberPost::link
1471
+ * @deprecated since 1.0
1472
+ * @codeCoverageIgnore
1473
  * @return string
1474
  */
1475
+ function get_permalink() {
1476
+ return $this->link();
1477
  }
1478
 
1479
  /**
1480
+ * get the permalink for a post object
1481
+ * In your templates you should use link:
1482
+ * <a href="{{post.link}}">Read my post</a>
1483
+ * @internal
1484
+ * @deprecated since 1.0
1485
+ * @codeCoverageIgnore
1486
+ * @return string
 
 
 
1487
  */
1488
+ function get_link() {
1489
+ return $this->get_permalink();
1490
  }
1491
 
1492
  /**
1493
+ * Get the next post in WordPress's ordering
1494
+ * @internal
1495
+ * @deprecated since 1.0
1496
+ * @codeCoverageIgnore
1497
+ * @param bool $taxonomy
1498
+ * @return TimberPost|boolean
1499
  */
1500
+ function get_next( $taxonomy = false ) {
1501
+ return $this->next($taxonomy);
1502
  }
1503
 
1504
  /**
1505
+ * Get a data array of pagination so you can navigate to the previous/next for a paginated post
1506
+ * @internal
1507
+ * @see Timber\Post::pagination();
1508
+ * @deprecated since 1.0
1509
+ * @codeCoverageIgnore
1510
  * @return array
1511
  */
1512
+ public function get_pagination() {
1513
+ return $this->pagination();
1514
  }
1515
 
 
 
 
 
 
 
 
 
 
 
 
 
1516
 
1517
  /**
1518
+ * Get the comments for a post
1519
+ * @internal
1520
+ * @see Timber\Post::comments
1521
+ * @param int $count
1522
+ * @param string $order
1523
+ * @param string $type
1524
+ * @param string $status
1525
+ * @param string $CommentClass
1526
+ * @return array|mixed
1527
  */
1528
+ function get_comments( $count = 0, $order = 'wp', $type = 'comment', $status = 'approve', $CommentClass = 'TimberComment' ) {
1529
+ return $this->comments($count, $order, $type, $status, $CommentClass);
1530
  }
1531
 
1532
  }
lib/PostGetter.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\PostsCollection;
6
+ use Timber\QueryIterator;
7
+
8
+ class PostGetter {
9
+
10
+ /**
11
+ * @param mixed $query
12
+ * @param string $PostClass
13
+ * @return array|bool|null
14
+ */
15
+ static function get_post( $query = false, $PostClass = 'TimberPost' ) {
16
+ $posts = self::get_posts($query, $PostClass);
17
+ if ( $post = reset($posts) ) {
18
+ return $post;
19
+ }
20
+ }
21
+
22
+ static function get_posts( $query = false, $PostClass = 'TimberPost', $return_collection = false ) {
23
+ $posts = self::query_posts($query, $PostClass);
24
+ return apply_filters('timber_post_getter_get_posts', $posts->get_posts($return_collection));
25
+ }
26
+
27
+ static function query_post( $query = false, $PostClass = 'TimberPost' ) {
28
+ $posts = self::query_posts($query, $PostClass);
29
+ if ( method_exists($posts, 'current') && $post = $posts->current() ) {
30
+ return $post;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * @param mixed $query
36
+ * @param string $PostClass
37
+ * @return array|bool|null
38
+ */
39
+ static function query_posts( $query = false, $PostClass = 'TimberPost' ) {
40
+ if ( $type = self::get_class_for_use_as_timber_post($query) ) {
41
+ $PostClass = $type;
42
+ if ( self::is_post_class_or_class_map($query) ) {
43
+ $query = false;
44
+ }
45
+ }
46
+
47
+ if ( is_object($query) && !is_a($query, 'WP_Query') ) {
48
+ // The only object other than a query is a type of post object
49
+ $query = array($query);
50
+ }
51
+
52
+ if ( is_array($query) && count($query) && isset($query[0]) && is_object($query[0]) ) {
53
+ // We have an array of post objects that already have data
54
+ return new PostsCollection($query, $PostClass);
55
+ } else {
56
+ // We have a query (of sorts) to work with
57
+ $tqi = new QueryIterator($query, $PostClass);
58
+ return $tqi;
59
+ }
60
+ }
61
+
62
+ static function get_pids( $query ) {
63
+ $posts = self::get_posts($query);
64
+ $pids = array();
65
+ foreach ( $posts as $post ) {
66
+ if ( isset($post->ID) ) {
67
+ $pids[] = $post->ID;
68
+ }
69
+ }
70
+ return $pids;
71
+ }
72
+
73
+ static function loop_to_id() {
74
+ if ( !self::wp_query_has_posts() ) { return false; }
75
+
76
+ global $wp_query;
77
+ $post_num = property_exists($wp_query, 'current_post')
78
+ ? $wp_query->current_post + 1
79
+ : 0
80
+ ;
81
+
82
+ if ( !isset($wp_query->posts[$post_num]) ) { return false; }
83
+
84
+ return $wp_query->posts[$post_num]->ID;
85
+ }
86
+
87
+ /**
88
+ * @return bool
89
+ */
90
+ static function wp_query_has_posts() {
91
+ global $wp_query;
92
+ return ($wp_query && property_exists($wp_query, 'posts') && $wp_query->posts);
93
+ }
94
+
95
+ /**
96
+ * @param string|array $arg
97
+ * @return bool
98
+ */
99
+ static function is_post_class_or_class_map( $arg ) {
100
+ $maybe_type = self::get_class_for_use_as_timber_post($arg);
101
+ if ( is_array($arg) && isset($arg['post_type']) ) {
102
+ //the user has passed a true WP_Query-style query array that needs to be used later, so the $arg is not a class map or post class
103
+ return false;
104
+ }
105
+ if ( $maybe_type ) {
106
+ return true;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @param string|array $arg
112
+ * @return string|bool if a $type is found; false if not
113
+ */
114
+ static function get_class_for_use_as_timber_post( $arg ) {
115
+ $type = false;
116
+
117
+ if ( is_string($arg) ) {
118
+ $type = $arg;
119
+ } else if ( is_array($arg) && isset($arg['post_type']) ) {
120
+ $type = $arg['post_type'];
121
+ }
122
+
123
+ if ( !$type ) {
124
+ return false;
125
+ }
126
+
127
+ if ( class_exists($type) && is_subclass_of($type, 'TimberPost') ) {
128
+ return $type;
129
+ }
130
+ }
131
+ }
lib/PostsCollection.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\Helper;
6
+ use Timber\Post;
7
+
8
+ // Exit if accessed directly
9
+ if ( !defined('ABSPATH') )
10
+ exit;
11
+
12
+ class PostsCollection extends \ArrayObject {
13
+
14
+ public function __construct( $posts = array(), $post_class = '\Timber\Post' ) {
15
+ $returned_posts = array();
16
+ if ( is_null($posts) ) {
17
+ $posts = array();
18
+ }
19
+ foreach ( $posts as $post_object ) {
20
+ $post_class_use = $post_class;
21
+ if ( is_array($post_class) ) {
22
+ $post_type = get_post_type($post_object);
23
+ $post_class_use = '\Timber\Post';
24
+
25
+ if ( isset($post_class[$post_type]) ) {
26
+ $post_class_use = $post_class[$post_type];
27
+
28
+ } else {
29
+ if ( is_array($post_class) ) {
30
+ Helper::error_log($post_type.' of '.$post_object->ID.' not found in '.print_r($post_class, true));
31
+ } else {
32
+ Helper::error_log($post_type.' not found in '.$post_class);
33
+ }
34
+ }
35
+ }
36
+ // Don't create yet another object if $post_object is already of the right type
37
+ if ( is_a($post_object, $post_class_use) ) {
38
+ $post = $post_object;
39
+ } else {
40
+ $post = new $post_class_use($post_object);
41
+ }
42
+
43
+ if ( isset($post->ID) ) {
44
+ $returned_posts[] = $post;
45
+ }
46
+ }
47
+
48
+ $returned_posts = self::maybe_set_preview($returned_posts);
49
+
50
+ parent::__construct($returned_posts, $flags = 0, 'Timber\PostsIterator');
51
+ }
52
+
53
+ public function get_posts() {
54
+ return $this->getArrayCopy();
55
+ }
56
+
57
+ /**
58
+ * @param array $posts
59
+ * @return array
60
+ */
61
+ static function maybe_set_preview( $posts ) {
62
+ if ( is_array($posts) && isset($_GET['preview']) && $_GET['preview']
63
+ && isset($_GET['preview_id']) && $_GET['preview_id']
64
+ && current_user_can('edit_post', $_GET['preview_id']) ) {
65
+ // No need to check the nonce, that already happened in _show_post_preview on init
66
+
67
+ $preview_id = $_GET['preview_id'];
68
+ foreach ( $posts as &$post ) {
69
+ if ( is_object($post) && $post->ID == $preview_id ) {
70
+ // Based on _set_preview( $post ), but adds import_custom
71
+ $preview = wp_get_post_autosave($preview_id);
72
+ if ( is_object($preview) ) {
73
+
74
+ $preview = sanitize_post($preview);
75
+
76
+ $post->post_content = $preview->post_content;
77
+ $post->post_title = $preview->post_title;
78
+ $post->post_excerpt = $preview->post_excerpt;
79
+ $post->import_custom($preview_id);
80
+
81
+ add_filter('get_the_terms', '_wp_preview_terms_filter', 10, 3);
82
+ }
83
+ }
84
+ }
85
+
86
+ }
87
+
88
+ return $posts;
89
+ }
90
+
91
+ }
92
+
93
+ class PostsIterator extends \ArrayIterator {
94
+
95
+ public function current() {
96
+ global $post;
97
+ $post = parent::current();
98
+ return $post;
99
+ }
100
+ }
lib/QueryIterator.php ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\Helper;
6
+ use Timber\PostsCollection;
7
+
8
+ // Exit if accessed directly
9
+ if ( !defined('ABSPATH') )
10
+ exit;
11
+
12
+ class QueryIterator implements \Iterator {
13
+
14
+ /**
15
+ *
16
+ *
17
+ * @var WP_Query
18
+ */
19
+ private $_query = null;
20
+ private $_posts_class = 'TimberPost';
21
+
22
+ public function __construct( $query = false, $posts_class = 'TimberPost' ) {
23
+ add_action('pre_get_posts', array($this, 'fix_number_posts_wp_quirk'));
24
+ if ( $posts_class )
25
+ $this->_posts_class = $posts_class;
26
+
27
+ if ( is_a($query, 'WP_Query') ) {
28
+ // We got a full-fledged WP Query, look no further!
29
+ $the_query = $query;
30
+
31
+ } elseif ( false === $query ) {
32
+ // If query is explicitly set to false, use the main loop
33
+ global $wp_query;
34
+ $the_query = & $wp_query;
35
+ //if we're on a custom posts page?
36
+ $the_query = self::handle_maybe_custom_posts_page($the_query);
37
+ } elseif ( Helper::is_array_assoc($query) || (is_string($query) && strstr($query, '=')) ) {
38
+ // We have a regularly formed WP query string or array to use
39
+ $the_query = new \WP_Query($query);
40
+
41
+ } elseif ( is_numeric($query) || is_string($query) ) {
42
+ // We have what could be a post name or post ID to pull out
43
+ $the_query = self::get_query_from_string($query);
44
+
45
+ } elseif ( is_array($query) && count($query) && (is_integer($query[0]) || is_string($query[0])) ) {
46
+ // We have a list of pids (post IDs) to extract from
47
+ $the_query = self::get_query_from_array_of_ids($query);
48
+ } elseif ( is_array($query) && empty($query) ) {
49
+ // it's an empty array
50
+ $the_query = array();
51
+ } else {
52
+ Helper::error_log('I have failed you! in '.basename(__FILE__).'::'.__LINE__);
53
+ Helper::error_log($query);
54
+
55
+ // We have failed hard, at least let get something.
56
+ $the_query = new \WP_Query();
57
+ }
58
+
59
+ $this->_query = $the_query;
60
+
61
+ }
62
+
63
+ public function get_posts( $return_collection = false ) {
64
+ if ( isset($this->_query->posts) ) {
65
+ $posts = new PostsCollection($this->_query->posts, $this->_posts_class);
66
+ return ($return_collection) ? $posts : $posts->get_posts();
67
+ }
68
+ }
69
+
70
+ //
71
+ // GET POSTS
72
+ //
73
+ public static function get_query_from_array_of_ids( $query = array() ) {
74
+ if ( !is_array($query) || !count($query) )
75
+ return null;
76
+
77
+ return new \WP_Query(array(
78
+ 'post_type'=> 'any',
79
+ 'ignore_sticky_posts' => true,
80
+ 'post__in' => $query,
81
+ 'orderby' => 'post__in',
82
+ 'nopaging' => true
83
+ ));
84
+ }
85
+
86
+ public static function get_query_from_string( $string = '' ) {
87
+ $post_type = false;
88
+
89
+ if ( is_string($string) && strstr($string, '#') ) {
90
+ //we have a post_type directive here
91
+ list($post_type, $string) = explode('#', $string);
92
+ }
93
+
94
+ $query = array(
95
+ 'post_type' => ($post_type) ? $post_type : 'any'
96
+ );
97
+
98
+ if ( is_numeric($string) ) {
99
+ $query['p'] = $string;
100
+
101
+ } else {
102
+ $query['name'] = $string;
103
+ }
104
+
105
+ return new \WP_Query($query);
106
+ }
107
+
108
+ //
109
+ // Iterator Interface
110
+ //
111
+
112
+ public function valid() {
113
+ return $this->_query->have_posts();
114
+ }
115
+
116
+ public function current() {
117
+ global $post;
118
+
119
+ $this->_query->the_post();
120
+
121
+ // Sets up the global post, but also return the post, for use in Twig template
122
+ $posts_class = $this->_posts_class;
123
+ return new $posts_class($post);
124
+ }
125
+
126
+ /**
127
+ * Don't implement next, because current already advances the loop
128
+ */
129
+ final public function next() {}
130
+
131
+ public function rewind() {
132
+ $this->_query->rewind_posts();
133
+ }
134
+
135
+ public function key() {
136
+ $this->_query->current_post;
137
+ }
138
+
139
+ //get_posts users numberposts
140
+ static function fix_number_posts_wp_quirk( $query ) {
141
+ if ( isset($query->query) && isset($query->query['numberposts'])
142
+ && !isset($query->query['posts_per_page']) ) {
143
+ $query->set('posts_per_page', $query->query['numberposts']);
144
+ }
145
+ return $query;
146
+ }
147
+
148
+ /**
149
+ * this will test for whether a custom page to display posts is active, and if so, set the query to the default
150
+ * @param WP_Query $query the original query recived from WordPress
151
+ * @return WP_Query
152
+ */
153
+ static function handle_maybe_custom_posts_page( $query ) {
154
+ if ( $custom_posts_page = get_option('page_for_posts') ) {
155
+ if ( isset($query->query['p']) && $query->query['p'] == $custom_posts_page ) {
156
+ return new \WP_Query(array('post_type' => 'post'));
157
+ }
158
+ }
159
+ return $query;
160
+ }
161
+
162
+ }
lib/Request.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\Core;
6
+ use Timber\CoreInterface;
7
+
8
+ /**
9
+ * TimberRequest exposes $_GET and $_POST to the context
10
+ */
11
+
12
+ class Request extends Core implements CoreInterface {
13
+ public $post = array();
14
+ public $get = array();
15
+
16
+ /**
17
+ * Constructs a TimberRequest object
18
+ * @example
19
+ */
20
+ function __construct() {
21
+ $this->init();
22
+ }
23
+ /**
24
+ * @internal
25
+ */
26
+ protected function init() {
27
+ $this->post = $_POST;
28
+ $this->get = $_GET;
29
+ }
30
+
31
+ public function __call( $field, $args ) {}
32
+
33
+ public function __get( $field ) {}
34
+
35
+ /**
36
+ * @return boolean
37
+ */
38
+ public function __isset( $field ) {}
39
+
40
+ public function meta( $key ) {}
41
+ }
lib/{timber-site.php → Site.php} RENAMED
@@ -1,5 +1,13 @@
1
  <?php
2
 
 
 
 
 
 
 
 
 
3
  /**
4
  * TimberSite gives you access to information you need about your site. In Multisite setups, you can get info on other sites in your network.
5
  * @example
@@ -16,7 +24,7 @@
16
  * My site is called Jared's blog, another site on my network is Upstatement.com
17
  * ```
18
  */
19
- class TimberSite extends TimberCore implements TimberCoreInterface {
20
 
21
  /**
22
  * @api
@@ -64,7 +72,7 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
64
 
65
  /** @api
66
  * @var string for people who like trackback spam
67
- */
68
  public $pingback_url;
69
  public $siteurl;
70
  /**
@@ -88,7 +96,7 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
88
  public $rss;
89
  public $rss2;
90
  public $atom;
91
-
92
  /**
93
  * Constructs a TimberSite object
94
  * @example
@@ -104,7 +112,7 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
104
  function __construct( $site_name_or_id = null ) {
105
  $this->init();
106
  if ( is_multisite() ) {
107
- $this->init_as_multisite( $site_name_or_id );
108
  } else {
109
  $this->init_as_singlesite();
110
  }
@@ -122,17 +130,17 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
122
  $site_name_or_id = get_current_blog_id();
123
  }
124
  }
125
- $info = get_blog_details( $site_name_or_id );
126
- $this->import( $info );
127
  $this->ID = $info->blog_id;
128
  $this->id = $this->ID;
129
  $this->name = $this->blogname;
130
  $this->title = $this->blogname;
131
  $this->url = $this->siteurl;
132
- $theme_slug = get_blog_option( $info->blog_id, 'stylesheet' );
133
- $this->theme = new TimberTheme( $theme_slug );
134
- $this->description = get_blog_option( $info->blog_id, 'blogdescription' );
135
- $this->admin_email = get_blog_option( $info->blog_id, 'admin_email' );
136
  $this->multisite = true;
137
  }
138
 
@@ -141,13 +149,13 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
141
  * @internal
142
  */
143
  protected function init_as_singlesite() {
144
- $this->admin_email = get_bloginfo( 'admin_email' );
145
- $this->name = get_bloginfo( 'name' );
146
  $this->title = $this->name;
147
- $this->description = get_bloginfo( 'description' );
148
- $this->url = get_bloginfo( 'url' );
149
- $this->theme = new TimberTheme();
150
- $this->language_attributes = TimberHelper::function_wrapper( 'language_attributes' );
151
  $this->multisite = false;
152
  }
153
 
@@ -156,16 +164,14 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
156
  * @internal
157
  */
158
  protected function init() {
159
- $this->rdf = get_bloginfo( 'rdf_url' );
160
- $this->rss = get_bloginfo( 'rss_url' );
161
- $this->rss2 = get_bloginfo( 'rss2_url' );
162
- $this->atom = get_bloginfo( 'atom_url' );
163
- $this->language = get_bloginfo( 'language' );
164
- $this->charset = get_bloginfo( 'charset' );
165
- $this->pingback = get_bloginfo( 'pingback_url' );
166
- $this->language_attributes = TimberHelper::function_wrapper( 'language_attributes' );
167
- /* deprecated benath this comment */
168
- $this->pingback_url = get_bloginfo( 'pingback_url' );
169
  }
170
 
171
  /**
@@ -175,34 +181,16 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
175
  * @return mixed
176
  */
177
  function __get( $field ) {
178
- if ( !isset( $this->$field ) ) {
179
  if ( is_multisite() ) {
180
- $this->$field = get_blog_option( $this->ID, $field );
181
  } else {
182
- $this->$field = get_option( $field );
183
  }
184
  }
185
  return $this->$field;
186
  }
187
 
188
- /**
189
- * @deprecated 0.21.9
190
- * @internal
191
- * @return string
192
- */
193
- function get_link() {
194
- return $this->link();
195
- }
196
-
197
- /**
198
- * @deprecated 0.21.9
199
- * @internal
200
- * @return string
201
- */
202
- function get_url() {
203
- return $this->get_link();
204
- }
205
-
206
  /**
207
  * Returns the link to the site's home.
208
  * @example
@@ -223,11 +211,22 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
223
  return $this->url;
224
  }
225
 
 
 
 
 
 
 
 
 
 
 
 
226
  /**
227
  * @ignore
228
  */
229
  public function meta( $field ) {
230
- return $this->__get( $field );
231
  }
232
 
233
  /**
@@ -237,11 +236,11 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
237
  * @param mixed $value
238
  */
239
  public function update( $key, $value ) {
240
- $value = apply_filters( 'timber_site_set_meta', $value, $key, $this->ID, $this );
241
  if ( is_multisite() ) {
242
- update_blog_option( $this->ID, $key, $value );
243
  } else {
244
- update_option( $key, $value );
245
  }
246
  $this->$key = $value;
247
  }
@@ -253,7 +252,17 @@ class TimberSite extends TimberCore implements TimberCoreInterface {
253
  * @return string
254
  */
255
  function url() {
256
- return $this->get_link();
 
 
 
 
 
 
 
 
 
 
257
  }
258
 
259
  }
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\Core;
6
+ use Timber\CoreInterface;
7
+
8
+ use Timber\Theme;
9
+ use Timber\Helper;
10
+
11
  /**
12
  * TimberSite gives you access to information you need about your site. In Multisite setups, you can get info on other sites in your network.
13
  * @example
24
  * My site is called Jared's blog, another site on my network is Upstatement.com
25
  * ```
26
  */
27
+ class Site extends Core implements CoreInterface {
28
 
29
  /**
30
  * @api
72
 
73
  /** @api
74
  * @var string for people who like trackback spam
75
+ */
76
  public $pingback_url;
77
  public $siteurl;
78
  /**
96
  public $rss;
97
  public $rss2;
98
  public $atom;
99
+
100
  /**
101
  * Constructs a TimberSite object
102
  * @example
112
  function __construct( $site_name_or_id = null ) {
113
  $this->init();
114
  if ( is_multisite() ) {
115
+ $this->init_as_multisite($site_name_or_id);
116
  } else {
117
  $this->init_as_singlesite();
118
  }
130
  $site_name_or_id = get_current_blog_id();
131
  }
132
  }
133
+ $info = get_blog_details($site_name_or_id);
134
+ $this->import($info);
135
  $this->ID = $info->blog_id;
136
  $this->id = $this->ID;
137
  $this->name = $this->blogname;
138
  $this->title = $this->blogname;
139
  $this->url = $this->siteurl;
140
+ $theme_slug = get_blog_option($info->blog_id, 'stylesheet');
141
+ $this->theme = new Theme($theme_slug);
142
+ $this->description = get_blog_option($info->blog_id, 'blogdescription');
143
+ $this->admin_email = get_blog_option($info->blog_id, 'admin_email');
144
  $this->multisite = true;
145
  }
146
 
149
  * @internal
150
  */
151
  protected function init_as_singlesite() {
152
+ $this->admin_email = get_bloginfo('admin_email');
153
+ $this->name = get_bloginfo('name');
154
  $this->title = $this->name;
155
+ $this->description = get_bloginfo('description');
156
+ $this->url = get_bloginfo('url');
157
+ $this->theme = new Theme();
158
+ $this->language_attributes = Helper::function_wrapper('language_attributes');
159
  $this->multisite = false;
160
  }
161
 
164
  * @internal
165
  */
166
  protected function init() {
167
+ $this->rdf = get_bloginfo('rdf_url');
168
+ $this->rss = get_bloginfo('rss_url');
169
+ $this->rss2 = get_bloginfo('rss2_url');
170
+ $this->atom = get_bloginfo('atom_url');
171
+ $this->language = get_bloginfo('language');
172
+ $this->charset = get_bloginfo('charset');
173
+ $this->pingback = get_bloginfo('pingback_url');
174
+ $this->language_attributes = Helper::function_wrapper('language_attributes');
 
 
175
  }
176
 
177
  /**
181
  * @return mixed
182
  */
183
  function __get( $field ) {
184
+ if ( !isset($this->$field) ) {
185
  if ( is_multisite() ) {
186
+ $this->$field = get_blog_option($this->ID, $field);
187
  } else {
188
+ $this->$field = get_option($field);
189
  }
190
  }
191
  return $this->$field;
192
  }
193
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  /**
195
  * Returns the link to the site's home.
196
  * @example
211
  return $this->url;
212
  }
213
 
214
+ /**
215
+ * @deprecated 0.21.9
216
+ * @internal
217
+ * @return string
218
+ */
219
+ function get_link() {
220
+ Helper::warn('{{site.get_link}} is deprecated, use {{site.link}}');
221
+ return $this->link();
222
+ }
223
+
224
+
225
  /**
226
  * @ignore
227
  */
228
  public function meta( $field ) {
229
+ return $this->__get($field);
230
  }
231
 
232
  /**
236
  * @param mixed $value
237
  */
238
  public function update( $key, $value ) {
239
+ $value = apply_filters('timber_site_set_meta', $value, $key, $this->ID, $this);
240
  if ( is_multisite() ) {
241
+ update_blog_option($this->ID, $key, $value);
242
  } else {
243
+ update_option($key, $value);
244
  }
245
  $this->$key = $value;
246
  }
252
  * @return string
253
  */
254
  function url() {
255
+ return $this->link();
256
+ }
257
+
258
+ /**
259
+ * @deprecated 0.21.9
260
+ * @internal
261
+ * @return string
262
+ */
263
+ function get_url() {
264
+ Helper::warn('{{site.get_url}} is deprecated, use {{site.link}} instead');
265
+ return $this->link();
266
  }
267
 
268
  }
lib/{timber-term.php → Term.php} RENAMED
@@ -1,4 +1,15 @@
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
2
  /**
3
  * Terms: WordPress has got 'em, you want 'em. Categories. Tags. Custom Taxonomies. You don't care, you're a fiend. Well let's get this under control
4
  * @example
@@ -30,10 +41,10 @@
30
  * </ul>
31
  * ```
32
  */
33
- class TimberTerm extends TimberCore implements TimberCoreInterface {
34
 
35
- public $PostClass = 'TimberPost';
36
- public $TermClass = 'TimberTerm';
37
 
38
  public $object_type = 'term';
39
  public static $representation = 'term';
@@ -55,10 +66,10 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
55
  * @param string $tax
56
  */
57
  public function __construct( $tid = null, $tax = '' ) {
58
- if ($tid === null) {
59
  $tid = $this->get_term_from_query();
60
  }
61
- if (strlen($tax)) {
62
  $this->taxonomy = $tax;
63
  }
64
  $this->init($tid);
@@ -93,7 +104,7 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
93
  global $wp_query;
94
  if ( isset($wp_query->queried_object) ) {
95
  $qo = $wp_query->queried_object;
96
- if (isset($qo->term_id)) {
97
  return $qo->term_id;
98
  }
99
  }
@@ -114,9 +125,9 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
114
  $term->ID = $term->term_id;
115
  } else if ( is_string($tid) ) {
116
  //echo 'bad call using '.$tid;
117
- //TimberHelper::error_log(debug_backtrace());
118
  }
119
- if ( isset($term->ID) ){
120
  $term->id = $term->ID;
121
  $this->import($term);
122
  if ( isset($term->term_id) ) {
@@ -131,7 +142,7 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
131
  * @param int $tid
132
  * @return array
133
  */
134
- protected function get_term_meta($tid) {
135
  $customs = array();
136
  $customs = apply_filters('timber_term_get_meta', $customs, $tid, $this);
137
  return apply_filters('timber/term/meta', $customs, $tid, $this);
@@ -154,7 +165,7 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
154
  global $wpdb;
155
  $query = $wpdb->prepare("SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d LIMIT 1", $tid);
156
  $tax = $wpdb->get_var($query);
157
- if (isset($tax) && strlen($tax)) {
158
  $this->taxonomy = $tax;
159
  return get_term($tid, $tax);
160
  }
@@ -206,7 +217,7 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
206
  * @return string
207
  */
208
  public function get_meta_field( $field_name ) {
209
- if (!isset($this->$field_name)) {
210
  $field_value = '';
211
  $field_value = apply_filters('timber_term_get_meta_field', $field_value, $this->ID, $field_name, $this);
212
  $field_value = apply_filters('timber/term/meta/field', $field_value, $this->ID, $field_name, $this);
@@ -217,23 +228,20 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
217
 
218
  /**
219
  * @internal
 
220
  * @return string
221
  */
222
  public function get_path() {
223
- $link = $this->get_link();
224
- $rel = TimberURLHelper::get_rel_url($link, true);
225
- $rel = apply_filters('timber_term_path', $rel, $this);
226
- return apply_filters('timber/term/path', $rel, $this);
227
  }
228
 
229
  /**
230
  * @internal
 
231
  * @return string
232
  */
233
  public function get_link() {
234
- $link = get_term_link($this);
235
- $link = apply_filters('timber_term_link', $link, $this);
236
- return apply_filters('timber/term/link', $link, $this);
237
  }
238
 
239
  /**
@@ -245,7 +253,7 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
245
  * @return array|bool|null
246
  */
247
  public function get_posts( $numberposts = 10, $post_type = 'any', $PostClass = '' ) {
248
- if (!strlen($PostClass)) {
249
  $PostClass = $this->PostClass;
250
  }
251
  $default_tax_query = array(array(
@@ -259,10 +267,10 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
259
  parse_str($args, $new_args);
260
  $args = $new_args;
261
  $args['tax_query'] = $default_tax_query;
262
- if (!isset($args['post_type'])) {
263
  $args['post_type'] = 'any';
264
  }
265
- if (class_exists($post_type)) {
266
  $PostClass = $post_type;
267
  }
268
  } else if ( is_array($numberposts) ) {
@@ -294,8 +302,8 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
294
  public function get_children() {
295
  if ( !isset($this->_children) ) {
296
  $children = get_term_children($this->ID, $this->taxonomy);
297
- foreach ($children as &$child) {
298
- $child = new TimberTerm($child);
299
  }
300
  $this->_children = $children;
301
  }
@@ -309,7 +317,7 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
309
  * @param mixed $value
310
  */
311
  function update( $key, $value ) {
312
- $value = apply_filters( 'timber_term_set_meta', $value, $key, $this->ID, $this );
313
  $this->$key = $value;
314
  }
315
 
@@ -331,11 +339,11 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
331
  public function description() {
332
  $prefix = '<p>';
333
  $suffix = '</p>';
334
- $desc = term_description( $this->ID, $this->taxonomy );
335
- if (substr($desc, 0, strlen($prefix)) == $prefix) {
336
  $desc = substr($desc, strlen($prefix));
337
  }
338
- $desc = preg_replace('/'. preg_quote('</p>', '/') . '$/', '', $desc);
339
  return trim($desc);
340
  }
341
 
@@ -347,21 +355,15 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
347
  return $this->get_edit_url();
348
  }
349
 
350
- /**
351
- * @internal
352
- * @deprecated 0.21.8 use TimberTerm::link() instead
353
- * @return string
354
- */
355
- public function get_url() {
356
- return $this->get_link();
357
- }
358
 
359
  /**
360
  * @api
361
  * @return string
362
  */
363
  public function link() {
364
- return $this->get_link();
 
 
365
  }
366
 
367
  /**
@@ -378,7 +380,10 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
378
  * @return string
379
  */
380
  public function path() {
381
- return $this->get_path();
 
 
 
382
  }
383
 
384
  /**
@@ -408,22 +413,4 @@ class TimberTerm extends TimberCore implements TimberCoreInterface {
408
  public function title() {
409
  return $this->name;
410
  }
411
-
412
- /**
413
- * @deprecated 0.21.9 use TimberTerm::link() instead
414
- * @return string
415
- */
416
- public function url() {
417
- return $this->get_url();
418
- }
419
-
420
- /**
421
- * @deprecated 0.20.0 this was a dumb idea
422
- * @param int $i
423
- * @return string
424
- */
425
- function get_page( $i ) {
426
- return $this->get_path() . '/page/' . $i;
427
- }
428
-
429
- }
1
  <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\Core;
6
+ use Timber\CoreInterface;
7
+
8
+ use Timber\Post;
9
+ use TImber\Term;
10
+ use Timber\Helper;
11
+ use Timber\URLHelper;
12
+
13
  /**
14
  * Terms: WordPress has got 'em, you want 'em. Categories. Tags. Custom Taxonomies. You don't care, you're a fiend. Well let's get this under control
15
  * @example
41
  * </ul>
42
  * ```
43
  */
44
+ class Term extends Core implements CoreInterface {
45
 
46
+ public $PostClass = 'Timber\Post';
47
+ public $TermClass = 'Term';
48
 
49
  public $object_type = 'term';
50
  public static $representation = 'term';
66
  * @param string $tax
67
  */
68
  public function __construct( $tid = null, $tax = '' ) {
69
+ if ( $tid === null ) {
70
  $tid = $this->get_term_from_query();
71
  }
72
+ if ( strlen($tax) ) {
73
  $this->taxonomy = $tax;
74
  }
75
  $this->init($tid);
104
  global $wp_query;
105
  if ( isset($wp_query->queried_object) ) {
106
  $qo = $wp_query->queried_object;
107
+ if ( isset($qo->term_id) ) {
108
  return $qo->term_id;
109
  }
110
  }
125
  $term->ID = $term->term_id;
126
  } else if ( is_string($tid) ) {
127
  //echo 'bad call using '.$tid;
128
+ //Helper::error_log(debug_backtrace());
129
  }
130
+ if ( isset($term->ID) ) {
131
  $term->id = $term->ID;
132
  $this->import($term);
133
  if ( isset($term->term_id) ) {
142
  * @param int $tid
143
  * @return array
144
  */
145
+ protected function get_term_meta( $tid ) {
146
  $customs = array();
147
  $customs = apply_filters('timber_term_get_meta', $customs, $tid, $this);
148
  return apply_filters('timber/term/meta', $customs, $tid, $this);
165
  global $wpdb;
166
  $query = $wpdb->prepare("SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d LIMIT 1", $tid);
167
  $tax = $wpdb->get_var($query);
168
+ if ( isset($tax) && strlen($tax) ) {
169
  $this->taxonomy = $tax;
170
  return get_term($tid, $tax);
171
  }
217
  * @return string
218
  */
219
  public function get_meta_field( $field_name ) {
220
+ if ( !isset($this->$field_name) ) {
221
  $field_value = '';
222
  $field_value = apply_filters('timber_term_get_meta_field', $field_value, $this->ID, $field_name, $this);
223
  $field_value = apply_filters('timber/term/meta/field', $field_value, $this->ID, $field_name, $this);
228
 
229
  /**
230
  * @internal
231
+ * @deprecated since 1.0
232
  * @return string
233
  */
234
  public function get_path() {
235
+ return $this->path();
 
 
 
236
  }
237
 
238
  /**
239
  * @internal
240
+ * @deprecated since 1.0
241
  * @return string
242
  */
243
  public function get_link() {
244
+ return $this->link();
 
 
245
  }
246
 
247
  /**
253
  * @return array|bool|null
254
  */
255
  public function get_posts( $numberposts = 10, $post_type = 'any', $PostClass = '' ) {
256
+ if ( !strlen($PostClass) ) {
257
  $PostClass = $this->PostClass;
258
  }
259
  $default_tax_query = array(array(
267
  parse_str($args, $new_args);
268
  $args = $new_args;
269
  $args['tax_query'] = $default_tax_query;
270
+ if ( !isset($args['post_type']) ) {
271
  $args['post_type'] = 'any';
272
  }
273
+ if ( class_exists($post_type) ) {
274
  $PostClass = $post_type;
275
  }
276
  } else if ( is_array($numberposts) ) {
302
  public function get_children() {
303
  if ( !isset($this->_children) ) {
304
  $children = get_term_children($this->ID, $this->taxonomy);
305
+ foreach ( $children as &$child ) {
306
+ $child = new Term($child);
307
  }
308
  $this->_children = $children;
309
  }
317
  * @param mixed $value
318
  */
319
  function update( $key, $value ) {
320
+ $value = apply_filters('timber_term_set_meta', $value, $key, $this->ID, $this);
321
  $this->$key = $value;
322
  }
323
 
339
  public function description() {
340
  $prefix = '<p>';
341
  $suffix = '</p>';
342
+ $desc = term_description($this->ID, $this->taxonomy);
343
+ if ( substr($desc, 0, strlen($prefix)) == $prefix ) {
344
  $desc = substr($desc, strlen($prefix));
345
  }
346
+ $desc = preg_replace('/'.preg_quote('</p>', '/').'$/', '', $desc);
347
  return trim($desc);
348
  }
349
 
355
  return $this->get_edit_url();
356
  }
357
 
 
 
 
 
 
 
 
 
358
 
359
  /**
360
  * @api
361
  * @return string
362
  */
363
  public function link() {
364
+ $link = get_term_link($this);
365
+ $link = apply_filters('timber_term_link', $link, $this);
366
+ return apply_filters('timber/term/link', $link, $this);
367
  }
368
 
369
  /**
380
  * @return string
381
  */
382
  public function path() {
383
+ $link = $this->get_link();
384
+ $rel = URLHelper::get_rel_url($link, true);
385
+ $rel = apply_filters('timber_term_path', $rel, $this);
386
+ return apply_filters('timber/term/path', $rel, $this);
387
  }
388
 
389
  /**
413
  public function title() {
414
  return $this->name;
415
  }
416
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/{timber-term-getter.php → TermGetter.php} RENAMED
@@ -1,6 +1,11 @@
1
  <?php
2
 
3
- class TimberTermGetter {
 
 
 
 
 
4
 
5
  /**
6
  * @param string|array $args
@@ -8,7 +13,7 @@ class TimberTermGetter {
8
  * @param string $TermClass
9
  * @return mixed
10
  */
11
- public static function get_terms($args = null, $maybe_args = array(), $TermClass = 'TimberTerm') {
12
  if ( is_string($maybe_args) && !strstr($maybe_args, '=') ) {
13
  //the user is sending the $TermClass in the second argument
14
  $TermClass = $maybe_args;
@@ -30,7 +35,7 @@ class TimberTermGetter {
30
  $parsed->args = array_merge($parsed->args, $maybe_args);
31
  }
32
  return self::handle_term_query($parsed->taxonomies, $parsed->args, $TermClass);
33
- } else if ( is_array($args) && TimberHelper::is_array_assoc($args) ) {
34
  //its an associative array, like a good ole query
35
  $parsed = self::get_term_query_from_assoc_array($args);
36
  return self::handle_term_query($parsed->taxonomies, $parsed->args, $TermClass);
@@ -53,7 +58,7 @@ class TimberTermGetter {
53
  * @param string $TermClass
54
  * @return mixed
55
  */
56
- public static function handle_term_query($taxonomies, $args, $TermClass) {
57
  if ( !isset($args['hide_empty']) ) {
58
  $args['hide_empty'] = false;
59
  }
@@ -64,7 +69,7 @@ class TimberTermGetter {
64
  $args['include'] = $args['term_id'];
65
  }
66
  $terms = get_terms($taxonomies, $args);
67
- foreach ($terms as &$term) {
68
  $term = new $TermClass($term->term_id, $term->taxonomy);
69
  }
70
  return $terms;
@@ -74,7 +79,7 @@ class TimberTermGetter {
74
  * @param string $query_string
75
  * @return stdClass
76
  */
77
- protected static function get_term_query_from_query_string($query_string) {
78
  $args = array();
79
  parse_str($query_string, $args);
80
  $ret = self::get_term_query_from_assoc_array($args);
@@ -85,8 +90,8 @@ class TimberTermGetter {
85
  * @param string $taxs
86
  * @return stdClass
87
  */
88
- protected static function get_term_query_from_string($taxs) {
89
- $ret = new stdClass();
90
  $ret->args = array();
91
  if ( is_string($taxs) ) {
92
  $taxs = array($taxs);
@@ -99,8 +104,8 @@ class TimberTermGetter {
99
  * @param array $args
100
  * @return stdClass
101
  */
102
- public static function get_term_query_from_assoc_array($args) {
103
- $ret = new stdClass();
104
  $ret->args = $args;
105
  if ( isset($ret->args['tax']) ) {
106
  $ret->taxonomies = $ret->args['tax'];
@@ -126,7 +131,7 @@ class TimberTermGetter {
126
  * @param array $args
127
  * @return stdClass
128
  */
129
- public static function get_term_query_from_array($args) {
130
  if ( is_array($args) && !empty($args) ) {
131
  //okay its an array with content
132
  if ( is_int($args[0]) ) {
@@ -142,8 +147,8 @@ class TimberTermGetter {
142
  * @param integer[] $args
143
  * @return stdClass
144
  */
145
- public static function get_term_query_from_array_of_ids($args) {
146
- $ret = new stdClass();
147
  $ret->taxonomies = get_taxonomies();
148
  $ret->args['include'] = $args;
149
  return $ret;
@@ -153,8 +158,8 @@ class TimberTermGetter {
153
  * @param string[] $args
154
  * @return stdClass
155
  */
156
- public static function get_term_query_from_array_of_strings($args) {
157
- $ret = new stdClass();
158
  $ret->taxonomies = self::correct_taxonomy_names($args);
159
  $ret->args = array();
160
  return $ret;
@@ -164,11 +169,11 @@ class TimberTermGetter {
164
  * @param string|array $taxs
165
  * @return array
166
  */
167
- private static function correct_taxonomy_names($taxs) {
168
  if ( is_string($taxs) ) {
169
  $taxs = array($taxs);
170
  }
171
- foreach ($taxs as &$tax) {
172
  if ( $tax == 'tags' || $tax == 'tag' ) {
173
  $tax = 'post_tag';
174
  } else if ( $tax == 'categories' ) {
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\Term;
6
+ use Timber\Helper;
7
+
8
+ class TermGetter {
9
 
10
  /**
11
  * @param string|array $args
13
  * @param string $TermClass
14
  * @return mixed
15
  */
16
+ public static function get_terms( $args = null, $maybe_args = array(), $TermClass = 'Term' ) {
17
  if ( is_string($maybe_args) && !strstr($maybe_args, '=') ) {
18
  //the user is sending the $TermClass in the second argument
19
  $TermClass = $maybe_args;
35
  $parsed->args = array_merge($parsed->args, $maybe_args);
36
  }
37
  return self::handle_term_query($parsed->taxonomies, $parsed->args, $TermClass);
38
+ } else if ( is_array($args) && Helper::is_array_assoc($args) ) {
39
  //its an associative array, like a good ole query
40
  $parsed = self::get_term_query_from_assoc_array($args);
41
  return self::handle_term_query($parsed->taxonomies, $parsed->args, $TermClass);
58
  * @param string $TermClass
59
  * @return mixed
60
  */
61
+ public static function handle_term_query( $taxonomies, $args, $TermClass ) {
62
  if ( !isset($args['hide_empty']) ) {
63
  $args['hide_empty'] = false;
64
  }
69
  $args['include'] = $args['term_id'];
70
  }
71
  $terms = get_terms($taxonomies, $args);
72
+ foreach ( $terms as &$term ) {
73
  $term = new $TermClass($term->term_id, $term->taxonomy);
74
  }
75
  return $terms;
79
  * @param string $query_string
80
  * @return stdClass
81
  */
82
+ protected static function get_term_query_from_query_string( $query_string ) {
83
  $args = array();
84
  parse_str($query_string, $args);
85
  $ret = self::get_term_query_from_assoc_array($args);
90
  * @param string $taxs
91
  * @return stdClass
92
  */
93
+ protected static function get_term_query_from_string( $taxs ) {
94
+ $ret = new \stdClass();
95
  $ret->args = array();
96
  if ( is_string($taxs) ) {
97
  $taxs = array($taxs);
104
  * @param array $args
105
  * @return stdClass
106
  */
107
+ public static function get_term_query_from_assoc_array( $args ) {
108
+ $ret = new \stdClass();
109
  $ret->args = $args;
110
  if ( isset($ret->args['tax']) ) {
111
  $ret->taxonomies = $ret->args['tax'];
131
  * @param array $args
132
  * @return stdClass
133
  */
134
+ public static function get_term_query_from_array( $args ) {
135
  if ( is_array($args) && !empty($args) ) {
136
  //okay its an array with content
137
  if ( is_int($args[0]) ) {
147
  * @param integer[] $args
148
  * @return stdClass
149
  */
150
+ public static function get_term_query_from_array_of_ids( $args ) {
151
+ $ret = new \stdClass();
152
  $ret->taxonomies = get_taxonomies();
153
  $ret->args['include'] = $args;
154
  return $ret;
158
  * @param string[] $args
159
  * @return stdClass
160
  */
161
+ public static function get_term_query_from_array_of_strings( $args ) {
162
+ $ret = new \stdClass();
163
  $ret->taxonomies = self::correct_taxonomy_names($args);
164
  $ret->args = array();
165
  return $ret;
169
  * @param string|array $taxs
170
  * @return array
171
  */
172
+ private static function correct_taxonomy_names( $taxs ) {
173
  if ( is_string($taxs) ) {
174
  $taxs = array($taxs);
175
  }
176
+ foreach ( $taxs as &$tax ) {
177
  if ( $tax == 'tags' || $tax == 'tag' ) {
178
  $tax = 'post_tag';
179
  } else if ( $tax == 'categories' ) {
lib/{timber-theme.php → Theme.php} RENAMED
@@ -1,5 +1,11 @@
1
  <?php
2
 
 
 
 
 
 
 
3
  /**
4
  * Need to display info about your theme? Well you've come to the right place. By default info on the current theme comes for free with what's fetched by `Timber::get_context()` in which case you can access it your theme like so:
5
  * @example
@@ -17,7 +23,7 @@
17
  * ```
18
  * @package Timber
19
  */
20
- class TimberTheme extends TimberCore {
21
 
22
  /**
23
  * @api
@@ -62,7 +68,7 @@ class TimberTheme extends TimberCore {
62
  * We are currently using the My Theme theme.
63
  * ```
64
  */
65
- function __construct($slug = null) {
66
  $this->init($slug);
67
  }
68
 
@@ -70,14 +76,14 @@ class TimberTheme extends TimberCore {
70
  * @internal
71
  * @param string $slug
72
  */
73
- protected function init($slug = null) {
74
  $data = wp_get_theme($slug);
75
  $this->name = $data->get('Name');
76
  $ss = $data->get_stylesheet();
77
  $this->slug = $ss;
78
 
79
- if ( ! function_exists( 'get_home_path' ) ) {
80
- require_once(ABSPATH . 'wp-admin/includes/file.php');
81
  }
82
 
83
  $this->uri = get_stylesheet_directory_uri();
@@ -86,7 +92,7 @@ class TimberTheme extends TimberCore {
86
  $this->uri = get_template_directory_uri();
87
  }
88
  if ( $this->parent_slug && $this->parent_slug != $this->slug ) {
89
- $this->parent = new TimberTheme($this->parent_slug);
90
  }
91
  }
92
 
@@ -103,7 +109,7 @@ class TimberTheme extends TimberCore {
103
  * @return string the relative path to the theme (ex: `/wp-content/themes/my-timber-theme`)
104
  */
105
  public function path() {
106
- return TimberURLHelper::get_rel_url( $this->link() );
107
  }
108
 
109
  /**
@@ -111,7 +117,7 @@ class TimberTheme extends TimberCore {
111
  * @param bool $default
112
  * @return string
113
  */
114
- public function theme_mod($name, $default = false) {
115
  return get_theme_mod($name, $default);
116
  }
117
 
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\Core;
6
+ use Timber\Theme;
7
+ use Timber\URLHelper;
8
+
9
  /**
10
  * Need to display info about your theme? Well you've come to the right place. By default info on the current theme comes for free with what's fetched by `Timber::get_context()` in which case you can access it your theme like so:
11
  * @example
23
  * ```
24
  * @package Timber
25
  */
26
+ class Theme extends Core {
27
 
28
  /**
29
  * @api
68
  * We are currently using the My Theme theme.
69
  * ```
70
  */
71
+ function __construct( $slug = null ) {
72
  $this->init($slug);
73
  }
74
 
76
  * @internal
77
  * @param string $slug
78
  */
79
+ protected function init( $slug = null ) {
80
  $data = wp_get_theme($slug);
81
  $this->name = $data->get('Name');
82
  $ss = $data->get_stylesheet();
83
  $this->slug = $ss;
84
 
85
+ if ( !function_exists('get_home_path') ) {
86
+ require_once(ABSPATH.'wp-admin/includes/file.php');
87
  }
88
 
89
  $this->uri = get_stylesheet_directory_uri();
92
  $this->uri = get_template_directory_uri();
93
  }
94
  if ( $this->parent_slug && $this->parent_slug != $this->slug ) {
95
+ $this->parent = new Theme($this->parent_slug);
96
  }
97
  }
98
 
109
  * @return string the relative path to the theme (ex: `/wp-content/themes/my-timber-theme`)
110
  */
111
  public function path() {
112
+ return URLHelper::get_rel_url($this->link());
113
  }
114
 
115
  /**
117
  * @param bool $default
118
  * @return string
119
  */
120
+ public function theme_mod( $name, $default = false ) {
121
  return get_theme_mod($name, $default);
122
  }
123
 
lib/Timber.php ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\Twig;
6
+ use Timber\ImageHelper;
7
+ use Timber\Admin;
8
+ use Timber\Integrations;
9
+ use Timber\PostGetter;
10
+ use Timber\TermGetter;
11
+ use Timber\Site;
12
+ use Timber\URLHelper;
13
+ use Timber\Helper;
14
+ use Timber\Request;
15
+ use Timber\User;
16
+ use Timber\Loader;
17
+
18
+ /**
19
+ * Timber Class.
20
+ *
21
+ * Main class called Timber for this plugin.
22
+ *
23
+ * Usage:
24
+ * $posts = Timber::get_posts();
25
+ * $posts = Timber::get_posts('post_type = article')
26
+ * $posts = Timber::get_posts(array('post_type' => 'article', 'category_name' => 'sports')); // uses wp_query format.
27
+ * $posts = Timber::get_posts(array(23,24,35,67), 'InkwellArticle');
28
+ *
29
+ * $context = Timber::get_context(); // returns wp favorites!
30
+ * $context['posts'] = $posts;
31
+ * Timber::render('index.twig', $context);
32
+ */
33
+ class Timber {
34
+
35
+ public static $locations;
36
+ public static $dirname;
37
+ public static $twig_cache = false;
38
+ public static $cache = false;
39
+ public static $auto_meta = true;
40
+ public static $autoescape = false;
41
+
42
+ /**
43
+ * @codeCoverageIgnore
44
+ */
45
+ public function __construct() {
46
+ if ( !defined('ABSPATH') ) {
47
+ return;
48
+ }
49
+ $this->test_compatibility();
50
+ $this->backwards_compatibility();
51
+ $this->init_constants();
52
+ $this->init();
53
+ }
54
+
55
+ /**
56
+ * Tests whether we can use Timber
57
+ * @codeCoverageIgnore
58
+ * @return
59
+ */
60
+ protected function test_compatibility() {
61
+ if ( is_admin() || $_SERVER['PHP_SELF'] == '/wp-login.php' ) {
62
+ return;
63
+ }
64
+ if ( version_compare(phpversion(), '5.3.0', '<') && !is_admin() ) {
65
+ trigger_error('Timber requires PHP 5.3.0 or greater. You have '.phpversion(), E_USER_ERROR);
66
+ }
67
+ if ( !class_exists('Twig_Autoloader') ) {
68
+ trigger_error('You have not run "composer install" to download required dependencies for Timber, you can read more on https://github.com/timber/timber#installation', E_USER_ERROR);
69
+ }
70
+ }
71
+
72
+ private function backwards_compatibility() {
73
+ if ( class_exists('TimberArchives') ) {
74
+ //already run, so bail
75
+ return;
76
+ }
77
+ $names = array('Archives', 'Comment', 'Core', 'FunctionWrapper', 'Helper', 'Image', 'ImageHelper', 'Integrations', 'Loader', 'Menu', 'MenuItem', 'Post', 'PostGetter', 'PostsCollection', 'QueryIterator', 'Request', 'Site', 'Term', 'TermGetter', 'Theme', 'Twig', 'URLHelper', 'User');
78
+ class_alias(get_class($this), 'Timber');
79
+ foreach ( $names as $name ) {
80
+ class_alias('Timber\\'.$name, 'Timber'.$name);
81
+ }
82
+ }
83
+
84
+ function init_constants() {
85
+ defined("TIMBER_LOC") or define("TIMBER_LOC", realpath(dirname(__DIR__)));
86
+ }
87
+
88
+ /**
89
+ * @codeCoverageIgnore
90
+ */
91
+ protected function init() {
92
+ Twig::init();
93
+ ImageHelper::init();
94
+ Admin::init();
95
+ Integrations::init();
96
+ }
97
+
98
+ /* Post Retrieval Routine
99
+ ================================ */
100
+
101
+ /**
102
+ * Get post.
103
+ *
104
+ * @param mixed $query
105
+ * @param string $PostClass
106
+ * @return array|bool|null
107
+ */
108
+ public static function get_post( $query = false, $PostClass = 'TimberPost' ) {
109
+ return PostGetter::get_post($query, $PostClass);
110
+ }
111
+
112
+ /**
113
+ * Get posts.
114
+ * @example
115
+ * ```php
116
+ * $posts = Timber::get_posts();
117
+ * $posts = Timber::get_posts('post_type = article')
118
+ * $posts = Timber::get_posts(array('post_type' => 'article', 'category_name' => 'sports')); // uses wp_query format.
119
+ * $posts = Timber::get_posts('post_type=any', array('portfolio' => 'MyPortfolioClass', 'alert' => 'MyAlertClass')); //use a classmap for the $PostClass
120
+ * ```
121
+ * @param mixed $query
122
+ * @param string|array $PostClass
123
+ * @return array|bool|null
124
+ */
125
+ public static function get_posts( $query = false, $PostClass = 'TimberPost', $return_collection = false ) {
126
+ return PostGetter::get_posts($query, $PostClass, $return_collection);
127
+ }
128
+
129
+ /**
130
+ * Query post.
131
+ *
132
+ * @param mixed $query
133
+ * @param string $PostClass
134
+ * @return array|bool|null
135
+ */
136
+ public static function query_post( $query = false, $PostClass = 'TimberPost' ) {
137
+ return PostGetter::query_post($query, $PostClass);
138
+ }
139
+
140
+ /**
141
+ * Query posts.
142
+ *
143
+ * @param mixed $query
144
+ * @param string $PostClass
145
+ * @return array|bool|null
146
+ */
147
+ public static function query_posts( $query = false, $PostClass = 'TimberPost' ) {
148
+ return PostGetter::query_posts($query, $PostClass);
149
+ }
150
+
151
+ /* Term Retrieval
152
+ ================================ */
153
+
154
+ /**
155
+ * Get terms.
156
+ *
157
+ * @param string|array $args
158
+ * @param array $maybe_args
159
+ * @param string $TermClass
160
+ * @return mixed
161
+ */
162
+ public static function get_terms( $args = null, $maybe_args = array(), $TermClass = 'TimberTerm' ) {
163
+ return TermGetter::get_terms($args, $maybe_args, $TermClass);
164
+ }
165
+
166
+ /* Site Retrieval
167
+ ================================ */
168
+
169
+ /**
170
+ * Get sites.
171
+ *
172
+ * @param array|bool $blog_ids
173
+ * @return array
174
+ */
175
+ public static function get_sites( $blog_ids = false ) {
176
+ if ( !is_array($blog_ids) ) {
177
+ global $wpdb;
178
+ $blog_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs ORDER BY blog_id ASC");
179
+ }
180
+ $return = array();
181
+ foreach ( $blog_ids as $blog_id ) {
182
+ $return[] = new Site($blog_id);
183
+ }
184
+ return $return;
185
+ }
186
+
187
+
188
+ /* Template Setup and Display
189
+ ================================ */
190
+
191
+ /**
192
+ * Get context.
193
+ *
194
+ * @return array
195
+ */
196
+ public static function get_context() {
197
+ $data = array();
198
+ $data['http_host'] = 'http://'.URLHelper::get_host();
199
+ $data['wp_title'] = Helper::get_wp_title();
200
+ $data['wp_head'] = Helper::function_wrapper('wp_head');
201
+ $data['wp_footer'] = Helper::function_wrapper('wp_footer');
202
+ $data['body_class'] = implode(' ', get_body_class());
203
+
204
+ $data['site'] = new Site();
205
+ $data['request'] = new Request();
206
+ $user = new User();
207
+ $data['user'] = ($user->ID) ? $user : false;
208
+ $data['theme'] = $data['site']->theme;
209
+
210
+ $data['posts'] = Timber::query_posts();
211
+
212
+ $data = apply_filters('timber_context', $data);
213
+ $data = apply_filters('timber/context', $data);
214
+ return $data;
215
+ }
216
+
217
+ /**
218
+ * Compile function.
219
+ *
220
+ * @param array $filenames
221
+ * @param array $data
222
+ * @param bool $expires
223
+ * @param string $cache_mode
224
+ * @param bool $via_render
225
+ * @return bool|string
226
+ */
227
+ public static function compile( $filenames, $data = array(), $expires = false, $cache_mode = Loader::CACHE_USE_DEFAULT, $via_render = false ) {
228
+ $caller = self::get_calling_script_dir();
229
+ $caller_file = self::get_calling_script_file();
230
+ $caller_file = apply_filters('timber_calling_php_file', $caller_file);
231
+ $loader = new Loader($caller);
232
+ $file = $loader->choose_template($filenames);
233
+ $output = '';
234
+ if ( is_null($data) ) {
235
+ $data = array();
236
+ }
237
+ if ( strlen($file) ) {
238
+ if ( $via_render ) {
239
+ $file = apply_filters('timber_render_file', $file);
240
+ $data = apply_filters('timber_render_data', $data);
241
+ } else {
242
+ $file = apply_filters('timber_compile_file', $file);
243
+ $data = apply_filters('timber_compile_data', $data);
244
+ }
245
+ $output = $loader->render($file, $data, $expires, $cache_mode);
246
+ }
247
+ do_action('timber_compile_done');
248
+ return $output;
249
+ }
250
+
251
+ /**
252
+ * Compile string.
253
+ *
254
+ * @param string $string a string with twig variables.
255
+ * @param array $data an array with data in it.
256
+ * @return bool|string
257
+ */
258
+ public static function compile_string( $string, $data = array() ) {
259
+ $dummy_loader = new Loader();
260
+ $twig = $dummy_loader->get_twig();
261
+ $template = $twig->createTemplate($string);
262
+ return $template->render($data);
263
+ }
264
+
265
+ /**
266
+ * Fetch function.
267
+ *
268
+ * @param array $filenames
269
+ * @param array $data
270
+ * @param bool $expires
271
+ * @param string $cache_mode
272
+ * @return bool|string
273
+ */
274
+ public static function fetch( $filenames, $data = array(), $expires = false, $cache_mode = Loader::CACHE_USE_DEFAULT ) {
275
+ if ( $expires === true ) {
276
+ //if this is reading as true; the user probably is using the old $echo param
277
+ //so we should move all vars up by a spot
278
+ $expires = $cache_mode;
279
+ $cache_mode = Loader::CACHE_USE_DEFAULT;
280
+ }
281
+ $output = self::compile($filenames, $data, $expires, $cache_mode, true);
282
+ $output = apply_filters('timber_compile_result', $output);
283
+ return $output;
284
+ }
285
+
286
+ /**
287
+ * Render function.
288
+ *
289
+ * @param array $filenames
290
+ * @param array $data
291
+ * @param bool $expires
292
+ * @param string $cache_mode
293
+ * @return bool|string
294
+ */
295
+ public static function render( $filenames, $data = array(), $expires = false, $cache_mode = Loader::CACHE_USE_DEFAULT ) {
296
+ $output = self::fetch($filenames, $data, $expires, $cache_mode);
297
+ echo $output;
298
+ return $output;
299
+ }
300
+
301
+ /**
302
+ * Render string.
303
+ *
304
+ * @param string $string a string with twig variables.
305
+ * @param array $data an array with data in it.
306
+ * @return bool|string
307
+ */
308
+ public static function render_string( $string, $data = array() ) {
309
+ $compiled = self::compile_string($string, $data);
310
+ echo $compiled;
311
+ return $compiled;
312
+ }
313
+
314
+
315
+ /* Sidebar
316
+ ================================ */
317
+
318
+ /**
319
+ * Get sidebar.
320
+ *
321
+ * @param string $sidebar
322
+ * @param array $data
323
+ * @return bool|string
324
+ */
325
+ public static function get_sidebar( $sidebar = '', $data = array() ) {
326
+ if ( $sidebar == '' ) {
327
+ $sidebar = 'sidebar.php';
328
+ }
329
+ if ( strstr(strtolower($sidebar), '.php') ) {
330
+ return self::get_sidebar_from_php($sidebar, $data);
331
+ }
332
+ return self::compile($sidebar, $data);
333
+ }
334
+
335
+ /**
336
+ * Get sidebar from PHP
337
+ *
338
+ * @param string $sidebar
339
+ * @param array $data
340
+ * @return string
341
+ */
342
+ public static function get_sidebar_from_php( $sidebar = '', $data ) {
343
+ $caller = self::get_calling_script_dir();
344
+ $loader = new Loader();
345
+ $uris = $loader->get_locations($caller);
346
+ ob_start();
347
+ $found = false;
348
+ foreach ( $uris as $uri ) {
349
+ if ( file_exists(trailingslashit($uri).$sidebar) ) {
350
+ include trailingslashit($uri).$sidebar;
351
+ $found = true;
352
+ break;
353
+ }
354
+ }
355
+ if ( !$found ) {
356
+ Helper::error_log('error loading your sidebar, check to make sure the file exists');
357
+ }
358
+ $ret = ob_get_contents();
359
+ ob_end_clean();
360
+ return $ret;
361
+ }
362
+
363
+ /* Widgets
364
+ ================================ */
365
+
366
+ /**
367
+ * Get widgets.
368
+ *
369
+ * @param int $widget_id
370
+ * @return TimberFunctionWrapper
371
+ */
372
+ public static function get_widgets( $widget_id ) {
373
+ return trim(Helper::function_wrapper('dynamic_sidebar', array($widget_id), true));
374
+ }
375
+
376
+ /* Pagination
377
+ ================================ */
378
+
379
+ /**
380
+ * Get pagination.
381
+ *
382
+ * @param array $prefs
383
+ * @return array mixed
384
+ */
385
+ public static function get_pagination( $prefs = array() ) {
386
+ global $wp_query;
387
+ global $paged;
388
+ global $wp_rewrite;
389
+ $args = array();
390
+ $args['total'] = ceil($wp_query->found_posts / $wp_query->query_vars['posts_per_page']);
391
+ if ( $wp_rewrite->using_permalinks() ) {
392
+ $url = explode('?', get_pagenum_link(0));
393
+ if ( isset($url[1]) ) {
394
+ parse_str($url[1], $query);
395
+ $args['add_args'] = $query;
396
+ }
397
+ $args['format'] = 'page/%#%';
398
+ $args['base'] = trailingslashit($url[0]).'%_%';
399
+ } else {
400
+ $big = 999999999;
401
+ $args['base'] = str_replace($big, '%#%', esc_url(get_pagenum_link($big)));
402
+ }
403
+ $args['type'] = 'array';
404
+ $args['current'] = max(1, get_query_var('paged'));
405
+ $args['mid_size'] = max(9 - $args['current'], 3);
406
+ if ( is_int($prefs) ) {
407
+ $args['mid_size'] = $prefs - 2;
408
+ } else {
409
+ $args = array_merge($args, $prefs);
410
+ }
411
+ $data = array();
412
+ $data['current'] = $args['current'];
413
+ $data['total'] = $args['total'];
414
+ $data['pages'] = Helper::paginate_links($args);
415
+ $next = get_next_posts_page_link($args['total']);
416
+ if ( $next ) {
417
+ $data['next'] = array('link' => untrailingslashit($next), 'class' => 'page-numbers next');
418
+ }
419
+ $prev = previous_posts(false);
420
+ if ( $prev ) {
421
+ $data['prev'] = array('link' => untrailingslashit($prev), 'class' => 'page-numbers prev');
422
+ }
423
+ if ( $paged < 2 ) {
424
+ $data['prev'] = '';
425
+ }
426
+ if ( $data['total'] === (double) 0 ) {
427
+ $data['next'] = '';
428
+ }
429
+ return $data;
430
+ }
431
+
432
+ /* Utility
433
+ ================================ */
434
+
435
+ /**
436
+ * Get calling script dir.
437
+ *
438
+ * @return string
439
+ */
440
+ public static function get_calling_script_dir( $offset = 0 ) {
441
+ $caller = self::get_calling_script_file($offset);
442
+ if ( !is_null($caller) ) {
443
+ $pathinfo = pathinfo($caller);
444
+ $dir = $pathinfo['dirname'];
445
+ return $dir;
446
+ }
447
+ }
448
+
449
+
450
+ /**
451
+ * Add route.
452
+ *
453
+ * @param string $route
454
+ * @param callable $callback
455
+ * @param array $args
456
+ * @deprecated since 0.20.0 and will be removed in 1.1
457
+ */
458
+ public static function add_route( $route, $callback, $args = array() ) {
459
+ Helper::warn('Timber::add_route (and accompanying methods for load_view, etc. Have been deprecated and will soon be removed. Please update your theme with Route::map. You can read more in the 1.0 Upgrade Guide: https://github.com/timber/timber/wiki/1.0-Upgrade-Guide');
460
+ Routes::map($route, $callback, $args);
461
+ }
462
+
463
+ /**
464
+ * Get calling script file.
465
+ *
466
+ * @param int $offset
467
+ * @return string|null
468
+ * @deprecated since 0.20.0
469
+ */
470
+ public static function get_calling_script_file( $offset = 0 ) {
471
+ $caller = null;
472
+ $backtrace = debug_backtrace();
473
+ $i = 0;
474
+ foreach ( $backtrace as $trace ) {
475
+ if ( array_key_exists('file', $trace) && $trace['file'] != __FILE__ ) {
476
+ $caller = $trace['file'];
477
+ break;
478
+ }
479
+ $i++;
480
+ }
481
+ if ( $offset ) {
482
+ $caller = $backtrace[$i + $offset]['file'];
483
+ }
484
+ return $caller;
485
+ }
486
+ }
lib/Twig.php ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Timber;
4
+
5
+ use Timber\URLHelper;
6
+ use Timber\Helper;
7
+
8
+ use Timber\Post;
9
+ use Timber\Term;
10
+ use Timber\Image;
11
+ use Timber\User;
12
+
13
+ class Twig {
14
+
15
+ public static $dir_name;
16
+
17
+ /**
18
+ * @codeCoverageIgnore
19
+ */
20
+ public static function init() {
21
+ new self();
22
+ }
23
+
24
+ /**
25
+ * @codeCoverageIgnore
26
+ */
27
+ function __construct() {
28
+ add_action('timber/twig/filters', array($this, 'add_timber_filters'));
29
+ }
30
+
31
+ /**
32
+ *
33
+ *
34
+ * @param Twig_Environment $twig
35
+ * @return Twig_Environment
36
+ */
37
+ function add_timber_filters( $twig ) {
38
+ /* image filters */
39
+ $twig->addFilter(new \Twig_SimpleFilter('resize', array('TimberImageHelper', 'resize')));
40
+ $twig->addFilter(new \Twig_SimpleFilter('retina', array('TimberImageHelper', 'retina_resize')));
41
+ $twig->addFilter(new \Twig_SimpleFilter('letterbox', array('TimberImageHelper', 'letterbox')));
42
+ $twig->addFilter(new \Twig_SimpleFilter('tojpg', array('TimberImageHelper', 'img_to_jpg')));
43
+
44
+ /* debugging filters */
45
+ $twig->addFilter(new \Twig_SimpleFilter('get_class', 'get_class'));
46
+ $twig->addFilter(new \Twig_SimpleFilter('get_type', 'get_type'));
47
+ $twig->addFilter(new \Twig_SimpleFilter('print_r', function( $arr ) {
48
+ return print_r($arr, true);
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('excerpt', 'wp_trim_words'));
55
+ $twig->addFilter(new \Twig_SimpleFilter('function', array($this, 'exec_function')));
56
+ $twig->addFilter(new \Twig_SimpleFilter('pretags', array($this, 'twig_pretags')));
57
+ $twig->addFilter(new \Twig_SimpleFilter('sanitize', 'sanitize_title'));
58
+ $twig->addFilter(new \Twig_SimpleFilter('shortcodes', 'do_shortcode'));
59
+ $twig->addFilter(new \Twig_SimpleFilter('time_ago', array($this, 'time_ago')));
60
+ $twig->addFilter(new \Twig_SimpleFilter('wpautop', 'wpautop'));
61
+ $twig->addFilter(new \Twig_SimpleFilter('list', array($this, 'add_list_separators')));
62
+
63
+ $twig->addFilter(new \Twig_SimpleFilter('relative', function( $link ) {
64
+ return URLHelper::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 Helper::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
+ $twig->addFunction(new \Twig_SimpleFunction('shortcode', 'do_shortcode'));
91
+
92
+ /* TimberObjects */
93
+ $twig->addFunction(new \Twig_SimpleFunction('TimberPost', function( $pid, $PostClass = 'Timber\Post' ) {
94
+ if ( is_array($pid) && !Helper::is_array_assoc($pid) ) {
95
+ foreach ( $pid as &$p ) {
96
+ $p = new $PostClass($p);
97
+ }
98
+ return $pid;
99
+ }
100
+ return new $PostClass($pid);
101
+ } ));
102
+ $twig->addFunction(new \Twig_SimpleFunction('TimberImage', function( $pid = false, $ImageClass = 'Timber\Image' ) {
103
+ if ( is_array($pid) && !Helper::is_array_assoc($pid) ) {
104
+ foreach ( $pid as &$p ) {
105
+ $p = new $ImageClass($p);
106
+ }
107
+ return $pid;
108
+ }
109
+ return new $ImageClass($pid);
110
+ } ));
111
+
112
+ $twig->addFunction(new \Twig_SimpleFunction('TimberTerm', function( $pid, $TermClass = 'Timber\Term' ) {
113
+ if ( is_array($pid) && !Helper::is_array_assoc($pid) ) {
114
+ foreach ( $pid as &$p ) {
115
+ $p = new $TermClass($p);
116
+ }
117
+ return $pid;
118
+ }
119
+ return new $TermClass($pid);
120
+ } ));
121
+ $twig->addFunction(new \Twig_SimpleFunction('TimberUser', function( $pid, $UserClass = 'Timber\User' ) {
122
+ if ( is_array($pid) && !Helper::is_array_assoc($pid) ) {
123
+ foreach ( $pid as &$p ) {
124
+ $p = new $UserClass($p);
125
+ }
126
+ return $pid;
127
+ }
128
+ return new $UserClass($pid);
129
+ } ));
130
+
131
+ /* TimberObjects Alias */
132
+ $twig->addFunction(new \Twig_SimpleFunction('Post', function( $pid, $PostClass = 'Timber\Post' ) {
133
+ if ( is_array($pid) && !Helper::is_array_assoc($pid) ) {
134
+ foreach ( $pid as &$p ) {
135
+ $p = new $PostClass($p);
136
+ }
137
+ return $pid;
138
+ }
139
+ return new $PostClass($pid);
140
+ } ));
141
+ $twig->addFunction(new \Twig_SimpleFunction('Image', function( $pid, $ImageClass = 'Timber\Image' ) {
142
+ if ( is_array($pid) && !Helper::is_array_assoc($pid) ) {
143
+ foreach ( $pid as &$p ) {
144
+ $p = new $ImageClass($p);
145
+ }
146
+ return $pid;
147
+ }
148
+ return new $ImageClass($pid);
149
+ } ));
150
+ $twig->addFunction(new \Twig_SimpleFunction('Term', function( $pid, $TermClass = 'Timber\Term' ) {
151
+ if ( is_array($pid) && !Helper::is_array_assoc($pid) ) {
152
+ foreach ( $pid as &$p ) {
153
+ $p = new $TermClass($p);
154
+ }
155
+ return $pid;
156
+ }
157
+ return new $TermClass($pid);
158
+ } ));
159
+ $twig->addFunction(new \Twig_SimpleFunction('User', function( $pid, $UserClass = 'Timber\User' ) {
160
+ if ( is_array($pid) && !Helper::is_array_assoc($pid) ) {
161
+ foreach ( $pid as &$p ) {
162
+ $p = new $UserClass($p);
163
+ }
164
+ return $pid;
165
+ }
166
+ return new $UserClass($pid);
167
+ } ));
168
+
169
+ /* bloginfo and translate */
170
+ $twig->addFunction('bloginfo', new \Twig_SimpleFunction('bloginfo', function( $show = '', $filter = 'raw' ) {
171
+ return get_bloginfo($show, $filter);
172
+ } ));
173
+ $twig->addFunction('__', new \Twig_SimpleFunction('__', function( $text, $domain = 'default' ) {
174
+ return __($text, $domain);
175
+ } ));
176
+ $twig->addFunction('translate', new \Twig_SimpleFunction('translate', function( $text, $domain = 'default' ) {
177
+ return translate($text, $domain);
178
+ } ));
179
+ $twig->addFunction('_e', new \Twig_SimpleFunction('_e', function( $text, $domain = 'default' ) {
180
+ return _e($text, $domain);
181
+ } ));
182
+ $twig->addFunction('_n', new \Twig_SimpleFunction('_n', function( $single, $plural, $number, $domain = 'default' ) {
183
+ return _n($single, $plural, $number, $domain);
184
+ } ));
185
+ $twig->addFunction('_x', new \Twig_SimpleFunction('_x', function( $text, $context, $domain = 'default' ) {
186
+ return _x($text, $context, $domain);
187
+ } ));
188
+ $twig->addFunction('_ex', new \Twig_SimpleFunction('_ex', function( $text, $context, $domain = 'default' ) {
189
+ return _ex($text, $context, $domain);
190
+ } ));
191
+ $twig->addFunction('_nx', new \Twig_SimpleFunction('_nx', function( $single, $plural, $number, $context, $domain = 'default' ) {
192
+ return _nx($single, $plural, $number, $context, $domain);
193
+ } ));
194
+ $twig->addFunction('_n_noop', new \Twig_SimpleFunction('_n_noop', function( $singular, $plural, $domain = 'default' ) {
195
+ return _n_noop($singular, $plural, $domain);
196
+ } ));
197
+ $twig->addFunction('_nx_noop', new \Twig_SimpleFunction('_nx_noop', function( $singular, $plural, $context, $domain = 'default' ) {
198
+ return _nx_noop($singular, $plural, $context, $domain);
199
+ } ));
200
+ $twig->addFunction('translate_nooped_plural', new \Twig_SimpleFunction('translate_nooped_plural', function( $nooped_plural, $count, $domain = 'default' ) {
201
+ return translate_nooped_plural($nooped_plural, $count, $domain);
202
+ } ));
203
+ $twig = apply_filters('timber/twig', $twig);
204
+ /**
205
+ * get_twig is deprecated, use timber/twig
206
+ */
207
+ $twig = apply_filters('get_twig', $twig);
208
+ return $twig;
209
+ }
210
+
211
+ /**
212
+ *
213
+ *
214
+ * @param mixed $arr
215
+ * @return array
216
+ */
217
+ function to_array( $arr ) {
218
+ if ( is_array($arr) ) {
219
+ return $arr;
220
+ }
221
+ $arr = array($arr);
222
+ return $arr;
223
+ }
224
+
225
+ /**
226
+ *
227
+ *
228
+ * @param string $function_name
229
+ * @return mixed
230
+ */
231
+ function exec_function( $function_name ) {
232
+ $args = func_get_args();
233
+ array_shift($args);
234
+ if ( is_string($function_name) ) {
235
+ $function_name = trim($function_name);
236
+ }
237
+ return call_user_func_array($function_name, ($args));
238
+ }
239
+
240
+ /**
241
+ *
242
+ *
243
+ * @param string $content
244
+ * @return string
245
+ */
246
+ function twig_pretags( $content ) {
247
+ return preg_replace_callback('|<pre.*>(.*)</pre|isU', array(&$this, 'convert_pre_entities'), $content);
248
+ }
249
+
250
+ /**
251
+ *
252
+ *
253
+ * @param array $matches
254
+ * @return string
255
+ */
256
+ function convert_pre_entities( $matches ) {
257
+ return str_replace($matches[1], htmlentities($matches[1]), $matches[0]);
258
+ }
259
+
260
+ /**
261
+ *
262
+ *
263
+ * @param string $date
264
+ * @param string $format (optional)
265
+ * @return string
266
+ */
267
+ function intl_date( $date, $format = null ) {
268
+ if ( $format === null ) {
269
+ $format = get_option('date_format');
270
+ }
271
+
272
+ if ( $date instanceof \DateTime ) {
273
+ $timestamp = $date->getTimestamp() + $date->getOffset();
274
+ } else if ( is_numeric($date) && strtotime($date) === false ) {
275
+ $timestamp = intval($date);
276
+ } else {
277
+ $timestamp = strtotime($date);
278
+ }
279
+
280
+ return date_i18n($format, $timestamp);
281
+ }
282
+
283
+ /**
284
+ * @param int|string $from
285
+ * @param int|string $to
286
+ * @param string $format_past
287
+ * @param string $format_future
288
+ * @return string
289
+ */
290
+ function time_ago( $from, $to = null, $format_past = '%s ago', $format_future = '%s from now' ) {
291
+ $to = $to === null ? time() : $to;
292
+ $to = is_int($to) ? $to : strtotime($to);
293
+ $from = is_int($from) ? $from : strtotime($from);
294
+
295
+ if ( $from < $to ) {
296
+ return sprintf($format_past, human_time_diff($from, $to));
297
+ } else {
298
+ return sprintf($format_future, human_time_diff($to, $from));
299
+ }
300
+ }
301
+
302
+ /**
303
+ * @param array $arr
304
+ * @param string $first_delimiter
305
+ * @param string $second_delimiter
306
+ * @return string
307
+ */
308
+ function add_list_separators( $arr, $first_delimiter = ',', $second_delimiter = 'and' ) {
309
+ $length = count($arr);
310
+ $list = '';
311
+ foreach ( $arr as $index => $item ) {
312
+ if ( $index < $length - 2 ) {
313
+ $delimiter = $first_delimiter.' ';
314
+ } elseif ( $index == $length - 2 ) {
315
+ $delimiter = ' '.$second_delimiter.' ';
316
+ } else {
317
+ $delimiter = '';
318
+ }
319
+ $list = $list.$item.$delimiter;
320
+ }
321
+ return $list;
322
+ }
323
+
324
+ }
lib/{timber-url-helper.php → URLHelper.php} RENAMED
@@ -1,6 +1,8 @@
1
  <?php
2
 
3
- class TimberURLHelper {
 
 
4
 
5
  /**
6
  *
@@ -9,13 +11,13 @@ class TimberURLHelper {
9
  */
10
  public static function get_current_url() {
11
  $pageURL = "http://";
12
- if ( isset( $_SERVER['HTTPS'] ) && $_SERVER["HTTPS"] == "on" ) {
13
- $pageURL = "https://";;
14
  }
15
  if ( isset($_SERVER["SERVER_PORT"]) && $_SERVER["SERVER_PORT"] && $_SERVER["SERVER_PORT"] != "80" ) {
16
- $pageURL .= self::get_host() . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
17
  } else {
18
- $pageURL .= self::get_host() . $_SERVER["REQUEST_URI"];
19
  }
20
  return $pageURL;
21
  }
@@ -27,11 +29,11 @@ class TimberURLHelper {
27
  * @return bool
28
  */
29
  public static function is_url( $url ) {
30
- if ( !is_string( $url ) ) {
31
  return false;
32
  }
33
- $url = strtolower( $url );
34
- if ( strstr( $url, '://' ) ) {
35
  return true;
36
  }
37
  return false;
@@ -43,12 +45,12 @@ class TimberURLHelper {
43
  * @return string
44
  */
45
  public static function get_path_base() {
46
- $struc = get_option( 'permalink_structure' );
47
- $struc = explode( '/', $struc );
48
  $p = '/';
49
  foreach ( $struc as $s ) {
50
- if ( !strstr( $s, '%' ) && strlen( $s ) ) {
51
- $p .= $s . '/';
52
  }
53
  }
54
  return $p;
@@ -62,21 +64,21 @@ class TimberURLHelper {
62
  * @return string
63
  */
64
  public static function get_rel_url( $url, $force = false ) {
65
- $url_info = parse_url( $url );
66
- if ( isset( $url_info['host'] ) && $url_info['host'] != self::get_host() && !$force ) {
67
  return $url;
68
  }
69
  $link = '';
70
- if ( isset( $url_info['path'] ) ) {
71
  $link = $url_info['path'];
72
  }
73
- if ( isset( $url_info['query'] ) && strlen( $url_info['query'] ) ) {
74
- $link .= '?' . $url_info['query'];
75
  }
76
- if ( isset( $url_info['fragment'] ) && strlen( $url_info['fragment'] ) ) {
77
- $link .= '#' . $url_info['fragment'];
78
  }
79
- $link = TimberURLHelper::remove_double_slashes( $link );
80
  return $link;
81
  }
82
 
@@ -86,10 +88,10 @@ class TimberURLHelper {
86
  * @return string the HTTP_HOST or SERVER_NAME
87
  */
88
  public static function get_host() {
89
- if (isset($_SERVER['HTTP_HOST'])) {
90
  return $_SERVER['HTTP_HOST'];
91
  }
92
- if (isset($_SERVER['SERVER_NAME'])) {
93
  return $_SERVER['SERVER_NAME'];
94
  }
95
  return '';
@@ -102,7 +104,7 @@ class TimberURLHelper {
102
  * @return bool
103
  */
104
  public static function is_local( $url ) {
105
- if ( strstr( $url, self::get_host() ) ) {
106
  return true;
107
  }
108
  return false;
@@ -116,8 +118,8 @@ class TimberURLHelper {
116
  */
117
  public static function get_full_path( $src ) {
118
  $root = ABSPATH;
119
- $old_root_path = $root . $src;
120
- $old_root_path = str_replace( '//', '/', $old_root_path );
121
  return $old_root_path;
122
  }
123
 
@@ -130,15 +132,15 @@ class TimberURLHelper {
130
  * @return string
131
  */
132
  public static function url_to_file_system( $url ) {
133
- $url_parts = parse_url( $url );
134
- $path = ABSPATH . $url_parts['path'];
135
- $path = str_replace( '//', '/', $path );
136
  return $path;
137
  }
138
 
139
  public static function file_system_to_url( $fs ) {
140
- $relative_path = self::get_rel_path( $fs );
141
- $home = home_url( '/'.$relative_path );
142
  return $home;
143
  }
144
 
@@ -149,12 +151,12 @@ class TimberURLHelper {
149
  * @return string
150
  */
151
  public static function get_rel_path( $src ) {
152
- if ( strstr( $src, ABSPATH ) ) {
153
- return str_replace( ABSPATH, '', $src );
154
  }
155
  //its outside the wordpress directory, alternate setups:
156
- $src = str_replace( WP_CONTENT_DIR, '', $src );
157
- return WP_CONTENT_SUBDIR . $src;
158
  }
159
 
160
  /**
@@ -164,9 +166,9 @@ class TimberURLHelper {
164
  * @return string
165
  */
166
  public static function remove_double_slashes( $url ) {
167
- $url = str_replace( '//', '/', $url );
168
- if ( strstr( $url, 'http:' ) && !strstr( $url, 'http://' ) ) {
169
- $url = str_replace( 'http:/', 'http://', $url );
170
  }
171
  return $url;
172
  }
@@ -179,19 +181,19 @@ class TimberURLHelper {
179
  * @return string
180
  */
181
  public static function prepend_to_url( $url, $path ) {
182
- if ( strstr( strtolower( $url ), 'http' ) ) {
183
- $url_parts = parse_url( $url );
184
- $url = $url_parts['scheme'] . '://' . $url_parts['host'] . $path . $url_parts['path'];
185
- if ( isset( $url_parts['query'] ) ) {
186
  $url .= $url_parts['query'];
187
  }
188
- if ( isset( $url_parts['fragment'] ) ) {
189
  $url .= $url_parts['fragment'];
190
  }
191
  } else {
192
- $url = $url . $path;
193
  }
194
- return self::remove_double_slashes( $url );
195
  }
196
 
197
  /**
@@ -201,8 +203,8 @@ class TimberURLHelper {
201
  * @return string
202
  */
203
  public static function preslashit( $path ) {
204
- if ( strpos( $path, '/' ) != 0 ) {
205
- $path = '/' . $path;
206
  }
207
  return $path;
208
  }
@@ -213,7 +215,7 @@ class TimberURLHelper {
213
  * @return boolean true if $path is an absolute url, false if relative.
214
  */
215
  public static function is_absolute( $path ) {
216
- return (boolean) ( strstr( $path, 'http' ) );
217
  }
218
 
219
  /**
@@ -225,21 +227,21 @@ class TimberURLHelper {
225
  * @return boolean if $url points to an external location returns true
226
  */
227
  public static function is_external_content( $url ) {
228
- $is_external = TimberURLHelper::is_absolute( $url ) && ! TimberURLHelper::is_internal_content( $url );
229
 
230
  return $is_external;
231
  }
232
 
233
- private static function is_internal_content($url) {
234
  // using content_url() instead of site_url or home_url is IMPORTANT
235
  // otherwise you run into errors with sites that:
236
  // 1. use WPML plugin
237
  // 2. or redefine content directory
238
- $is_content_url = strstr( $url, content_url() );
239
 
240
  // this case covers when the upload directory has been redefined
241
  $upload_dir = wp_upload_dir();
242
- $is_upload_url = strstr( $url, $upload_dir['baseurl'] );
243
 
244
  return $is_content_url || $is_upload_url;
245
  }
@@ -252,8 +254,8 @@ class TimberURLHelper {
252
  * true if it's a subdomain (http://cdn.example.org = true)
253
  */
254
  public static function is_external( $url ) {
255
- $has_http = strstr( strtolower( $url ), 'http' );
256
- $on_domain = strstr( $url, self::get_host() );
257
  if ( $has_http && !$on_domain ) {
258
  return true;
259
  }
@@ -268,41 +270,10 @@ class TimberURLHelper {
268
  */
269
  public static function remove_trailing_slash( $link ) {
270
  if ( $link != "/" )
271
- $link = untrailingslashit( $link );
272
  return $link;
273
  }
274
 
275
- /**
276
- * Download an external file via a URL
277
- *
278
- * @param string $url
279
- * @param int $timeout
280
- * @return string|WP_Error the location of the temporay file name or an error
281
- * @deprecated since 0.20.0
282
- */
283
- static function download_url( $url, $timeout = 300 ) {
284
- if ( !$url ) {
285
- return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) );
286
- }
287
-
288
- $tmpfname = wp_tempnam( $url );
289
- if ( !$tmpfname ) {
290
- return new WP_Error( 'http_no_file', __( 'Could not create Temporary file.' ) );
291
- }
292
-
293
- $response = wp_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname ) );
294
-
295
- if ( is_wp_error( $response ) ) {
296
- unlink( $tmpfname );
297
- return $response;
298
- }
299
- if ( 200 != wp_remote_retrieve_response_code( $response ) ) {
300
- unlink( $tmpfname );
301
- return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) );
302
- }
303
- return $tmpfname;
304
- }
305
-
306
  /**
307
  * Returns the url parameters, for example for url http://example.org/blog/post/news/2014/whatever
308
  * this will return array('blog', 'post', 'news', '2014', 'whatever');
@@ -312,10 +283,10 @@ class TimberURLHelper {
312
  * @return array|string
313
  */
314
  public static function get_params( $i = false ) {
315
- $args = explode( '/', trim( strtolower( $_SERVER['REQUEST_URI'] ) ) );
316
  $newargs = array();
317
  foreach ( $args as $arg ) {
318
- if ( strlen( $arg ) ) {
319
  $newargs[] = $arg;
320
  }
321
  }
@@ -324,9 +295,9 @@ class TimberURLHelper {
324
  }
325
  if ( $i < 0 ) {
326
  //count from end
327
- $i = count( $newargs ) + $i;
328
  }
329
- if ( isset( $newargs[$i] ) ) {
330
  return $newargs[$i];
331
  }
332
  }
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ class URLHelper {
6
 
7
  /**
8
  *
11
  */
12
  public static function get_current_url() {
13
  $pageURL = "http://";
14
+ if ( isset($_SERVER['HTTPS']) && $_SERVER["HTTPS"] == "on" ) {
15
+ $pageURL = "https://"; ;
16
  }
17
  if ( isset($_SERVER["SERVER_PORT"]) && $_SERVER["SERVER_PORT"] && $_SERVER["SERVER_PORT"] != "80" ) {
18
+ $pageURL .= self::get_host().":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
19
  } else {
20
+ $pageURL .= self::get_host().$_SERVER["REQUEST_URI"];
21
  }
22
  return $pageURL;
23
  }
29
  * @return bool
30
  */
31
  public static function is_url( $url ) {
32
+ if ( !is_string($url) ) {
33
  return false;
34
  }
35
+ $url = strtolower($url);
36
+ if ( strstr($url, '://') ) {
37
  return true;
38
  }
39
  return false;
45
  * @return string
46
  */
47
  public static function get_path_base() {
48
+ $struc = get_option('permalink_structure');
49
+ $struc = explode('/', $struc);
50
  $p = '/';
51
  foreach ( $struc as $s ) {
52
+ if ( !strstr($s, '%') && strlen($s) ) {
53
+ $p .= $s.'/';
54
  }
55
  }
56
  return $p;
64
  * @return string
65
  */
66
  public static function get_rel_url( $url, $force = false ) {
67
+ $url_info = parse_url($url);
68
+ if ( isset($url_info['host']) && $url_info['host'] != self::get_host() && !$force ) {
69
  return $url;
70
  }
71
  $link = '';
72
+ if ( isset($url_info['path']) ) {
73
  $link = $url_info['path'];
74
  }
75
+ if ( isset($url_info['query']) && strlen($url_info['query']) ) {
76
+ $link .= '?'.$url_info['query'];
77
  }
78
+ if ( isset($url_info['fragment']) && strlen($url_info['fragment']) ) {
79
+ $link .= '#'.$url_info['fragment'];
80
  }
81
+ $link = self::remove_double_slashes($link);
82
  return $link;
83
  }
84
 
88
  * @return string the HTTP_HOST or SERVER_NAME
89
  */
90
  public static function get_host() {
91
+ if ( isset($_SERVER['HTTP_HOST']) ) {
92
  return $_SERVER['HTTP_HOST'];
93
  }
94
+ if ( isset($_SERVER['SERVER_NAME']) ) {
95
  return $_SERVER['SERVER_NAME'];
96
  }
97
  return '';
104
  * @return bool
105
  */
106
  public static function is_local( $url ) {
107
+ if ( strstr($url, self::get_host()) ) {
108
  return true;
109
  }
110
  return false;
118
  */
119
  public static function get_full_path( $src ) {
120
  $root = ABSPATH;
121
+ $old_root_path = $root.$src;
122
+ $old_root_path = str_replace('//', '/', $old_root_path);
123
  return $old_root_path;
124
  }
125
 
132
  * @return string
133
  */
134
  public static function url_to_file_system( $url ) {
135
+ $url_parts = parse_url($url);
136
+ $path = ABSPATH.$url_parts['path'];
137
+ $path = str_replace('//', '/', $path);
138
  return $path;
139
  }
140
 
141
  public static function file_system_to_url( $fs ) {
142
+ $relative_path = self::get_rel_path($fs);
143
+ $home = home_url('/'.$relative_path);
144
  return $home;
145
  }
146
 
151
  * @return string
152
  */
153
  public static function get_rel_path( $src ) {
154
+ if ( strstr($src, ABSPATH) ) {
155
+ return str_replace(ABSPATH, '', $src);
156
  }
157
  //its outside the wordpress directory, alternate setups:
158
+ $src = str_replace(WP_CONTENT_DIR, '', $src);
159
+ return WP_CONTENT_SUBDIR.$src;
160
  }
161
 
162
  /**
166
  * @return string
167
  */
168
  public static function remove_double_slashes( $url ) {
169
+ $url = str_replace('//', '/', $url);
170
+ if ( strstr($url, 'http:') && !strstr($url, 'http://') ) {
171
+ $url = str_replace('http:/', 'http://', $url);
172
  }
173
  return $url;
174
  }
181
  * @return string
182
  */
183
  public static function prepend_to_url( $url, $path ) {
184
+ if ( strstr(strtolower($url), 'http') ) {
185
+ $url_parts = parse_url($url);
186
+ $url = $url_parts['scheme'].'://'.$url_parts['host'].$path.$url_parts['path'];
187
+ if ( isset($url_parts['query']) ) {
188
  $url .= $url_parts['query'];
189
  }
190
+ if ( isset($url_parts['fragment']) ) {
191
  $url .= $url_parts['fragment'];
192
  }
193
  } else {
194
+ $url = $url.$path;
195
  }
196
+ return self::remove_double_slashes($url);
197
  }
198
 
199
  /**
203
  * @return string
204
  */
205
  public static function preslashit( $path ) {
206
+ if ( strpos($path, '/') != 0 ) {
207
+ $path = '/'.$path;
208
  }
209
  return $path;
210
  }
215
  * @return boolean true if $path is an absolute url, false if relative.
216
  */
217
  public static function is_absolute( $path ) {
218
+ return (boolean) (strstr($path, 'http'));
219
  }
220
 
221
  /**
227
  * @return boolean if $url points to an external location returns true
228
  */
229
  public static function is_external_content( $url ) {
230
+ $is_external = self::is_absolute($url) && !self::is_internal_content($url);
231
 
232
  return $is_external;
233
  }
234
 
235
+ private static function is_internal_content( $url ) {
236
  // using content_url() instead of site_url or home_url is IMPORTANT
237
  // otherwise you run into errors with sites that:
238
  // 1. use WPML plugin
239
  // 2. or redefine content directory
240
+ $is_content_url = strstr($url, content_url());
241
 
242
  // this case covers when the upload directory has been redefined
243
  $upload_dir = wp_upload_dir();
244
+ $is_upload_url = strstr($url, $upload_dir['baseurl']);
245
 
246
  return $is_content_url || $is_upload_url;
247
  }
254
  * true if it's a subdomain (http://cdn.example.org = true)
255
  */
256
  public static function is_external( $url ) {
257
+ $has_http = strstr(strtolower($url), 'http');
258
+ $on_domain = strstr($url, self::get_host());
259
  if ( $has_http && !$on_domain ) {
260
  return true;
261
  }
270
  */
271
  public static function remove_trailing_slash( $link ) {
272
  if ( $link != "/" )
273
+ $link = untrailingslashit($link);
274
  return $link;
275
  }
276
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  /**
278
  * Returns the url parameters, for example for url http://example.org/blog/post/news/2014/whatever
279
  * this will return array('blog', 'post', 'news', '2014', 'whatever');
283
  * @return array|string
284
  */
285
  public static function get_params( $i = false ) {
286
+ $args = explode('/', trim(strtolower($_SERVER['REQUEST_URI'])));
287
  $newargs = array();
288
  foreach ( $args as $arg ) {
289
+ if ( strlen($arg) ) {
290
  $newargs[] = $arg;
291
  }
292
  }
295
  }
296
  if ( $i < 0 ) {
297
  //count from end
298
+ $i = count($newargs) + $i;
299
  }
300
+ if ( isset($newargs[$i]) ) {
301
  return $newargs[$i];
302
  }
303
  }
lib/{timber-user.php → User.php} RENAMED
@@ -1,5 +1,12 @@
1
  <?php
2
 
 
 
 
 
 
 
 
3
  /**
4
  * This is used in Timber to represent users retrived from WordPress. You can call `$my_user = new TimberUser(123);` directly, or access it through the `{{ post.author }}` method.
5
  * @example
@@ -17,7 +24,7 @@
17
  * <p class="article-info">This article is called "Consider the Lobster" and it's by David Foster Wallace
18
  * ```
19
  */
20
- class TimberUser extends TimberCore implements TimberCoreInterface {
21
 
22
  public $object_type = 'user';
23
  public static $representation = 'user';
@@ -53,7 +60,7 @@ class TimberUser extends TimberCore implements TimberCoreInterface {
53
  /**
54
  * @param int|bool $uid
55
  */
56
- function __construct($uid = false) {
57
  $this->init($uid);
58
  }
59
 
@@ -84,7 +91,7 @@ class TimberUser extends TimberCore implements TimberCoreInterface {
84
  * @param string $field_name
85
  * @return null
86
  */
87
- function get_meta($field_name) {
88
  return $this->get_meta_field($field_name);
89
  }
90
 
@@ -93,7 +100,7 @@ class TimberUser extends TimberCore implements TimberCoreInterface {
93
  * @param string $field
94
  * @param mixed $value
95
  */
96
- function __set($field, $value) {
97
  if ( $field == 'name' ) {
98
  $this->display_name = $value;
99
  }
@@ -104,7 +111,7 @@ class TimberUser extends TimberCore implements TimberCoreInterface {
104
  * @internal
105
  * @param int|bool $uid The user ID to use
106
  */
107
- protected function init($uid = false) {
108
  if ( $uid === false ) {
109
  $uid = get_current_user_id();
110
  }
@@ -137,7 +144,7 @@ class TimberUser extends TimberCore implements TimberCoreInterface {
137
  * @param string $field_name
138
  * @return mixed
139
  */
140
- function get_meta_field($field_name) {
141
  $value = null;
142
  $value = apply_filters('timber_user_get_meta_field_pre', $value, $this->ID, $field_name, $this);
143
  if ( $value === null ) {
@@ -158,7 +165,7 @@ class TimberUser extends TimberCore implements TimberCoreInterface {
158
  $um = get_user_meta($this->ID);
159
  }
160
  $custom = array();
161
- foreach ($um as $key => $value) {
162
  if ( is_array($value) && count($value) == 1 ) {
163
  $value = $value[0];
164
  }
@@ -193,7 +200,7 @@ class TimberUser extends TimberCore implements TimberCoreInterface {
193
  * @param string $field_name
194
  * @return mixed
195
  */
196
- function meta($field_name) {
197
  return $this->get_meta_field($field_name);
198
  }
199
 
@@ -202,7 +209,7 @@ class TimberUser extends TimberCore implements TimberCoreInterface {
202
  * @return string ex: /author/lincoln
203
  */
204
  public function path() {
205
- return TimberURLHelper::get_rel_url($this->get_link());
206
  }
207
 
208
  /**
@@ -212,37 +219,4 @@ class TimberUser extends TimberCore implements TimberCoreInterface {
212
  public function slug() {
213
  return $this->user_nicename;
214
  }
215
-
216
- /**
217
- * @deprecated 0.21.9
218
- * @return string The link to a user's profile page
219
- */
220
- function get_link() {
221
- return $this->link();
222
- }
223
-
224
- /**
225
- * @deprecated 0.21.8
226
- * @return string ex: /author/lincoln
227
- */
228
- function get_path() {
229
- return $this->path();
230
- }
231
-
232
- /**
233
- * @deprecated 0.21.8
234
- * @return string
235
- */
236
- function get_permalink() {
237
- return $this->get_link();
238
- }
239
-
240
- /**
241
- * @deprecated 0.21.8
242
- * @return string
243
- */
244
- function permalink() {
245
- return $this->get_permalink();
246
- }
247
-
248
- }
1
  <?php
2
 
3
+ namespace Timber;
4
+
5
+ use Timber\Core;
6
+ use Timber\CoreInterface;
7
+
8
+ use Timber\URLHelper;
9
+
10
  /**
11
  * This is used in Timber to represent users retrived from WordPress. You can call `$my_user = new TimberUser(123);` directly, or access it through the `{{ post.author }}` method.
12
  * @example
24
  * <p class="article-info">This article is called "Consider the Lobster" and it's by David Foster Wallace
25
  * ```
26
  */
27
+ class User extends Core implements CoreInterface {
28
 
29
  public $object_type = 'user';
30
  public static $representation = 'user';
60
  /**
61
  * @param int|bool $uid
62
  */
63
+ function __construct( $uid = false ) {
64
  $this->init($uid);
65
  }
66
 
91
  * @param string $field_name
92
  * @return null
93
  */
94
+ function get_meta( $field_name ) {
95
  return $this->get_meta_field($field_name);
96
  }
97
 
100
  * @param string $field
101
  * @param mixed $value
102
  */
103
+ function __set( $field, $value ) {
104
  if ( $field == 'name' ) {
105
  $this->display_name = $value;
106
  }
111
  * @internal
112
  * @param int|bool $uid The user ID to use
113
  */
114
+ protected function init( $uid = false ) {
115
  if ( $uid === false ) {
116
  $uid = get_current_user_id();
117
  }
144
  * @param string $field_name
145
  * @return mixed
146
  */
147
+ function get_meta_field( $field_name ) {
148
  $value = null;
149
  $value = apply_filters('timber_user_get_meta_field_pre', $value, $this->ID, $field_name, $this);
150
  if ( $value === null ) {
165
  $um = get_user_meta($this->ID);
166
  }
167
  $custom = array();
168
+ foreach ( $um as $key => $value ) {
169
  if ( is_array($value) && count($value) == 1 ) {
170
  $value = $value[0];
171
  }
200
  * @param string $field_name
201
  * @return mixed
202
  */
203
+ function meta( $field_name ) {
204
  return $this->get_meta_field($field_name);
205
  }
206
 
209
  * @return string ex: /author/lincoln
210
  */
211
  public function path() {
212
+ return URLHelper::get_rel_url($this->link());
213
  }
214
 
215
  /**
219
  public function slug() {
220
  return $this->user_nicename;
221
  }
222
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/image/timber-image-operation-retina.php DELETED
@@ -1,74 +0,0 @@
1
- <?php
2
- /**
3
- * Contains the class for running image retina-izing operations
4
- */
5
-
6
- /**
7
- * Increases image size by a given factor
8
- * Arguments:
9
- * - factor by which to multiply image dimensions
10
- * @property float $factor the factor (ex: 2, 1.5, 1.75) to multiply dimension by
11
- */
12
- class TimberImageOperationRetina extends TimberImageOperation {
13
-
14
- private $factor;
15
-
16
- /**
17
- * Construct our operation
18
- * @param float $factor to multiply original dimensions by
19
- */
20
- function __construct($factor) {
21
- $this->factor = $factor;
22
- }
23
-
24
- /**
25
- * Generates the final filename based on the source's name and extension
26
- *
27
- * @param string $src_filename the basename of the file (ex: my-awesome-pic)
28
- * @param string $src_extension the extension (ex: .jpg)
29
- * @return string the final filename to be used (ex: my-awesome-pic@2x.jpg)
30
- */
31
- function filename($src_filename, $src_extension) {
32
- $newbase = $src_filename . '@' . $this->factor . 'x'; // add @2x, @3x, @1.5x, etc.
33
- $new_name = $newbase . '.' . $src_extension;
34
- return $new_name;
35
- }
36
-
37
- /**
38
- * Performs the actual image manipulation,
39
- * including saving the target file.
40
- *
41
- * @param string $load_filename filepath (not URL) to source file
42
- * (ex: /src/var/www/wp-content/uploads/my-pic.jpg)
43
- * @param string $save_filename filepath (not URL) where result file should be saved
44
- * (ex: /src/var/www/wp-content/uploads/my-pic@2x.jpg)
45
- * @return bool true if everything went fine, false otherwise
46
- */
47
- function run($load_filename, $save_filename){
48
- $image = wp_get_image_editor( $load_filename );
49
- if ( !is_wp_error( $image ) ) {
50
- $current_size = $image->get_size();
51
- $src_w = $current_size['width'];
52
- $src_h = $current_size['height'];
53
- // Get ratios
54
- $w = $src_w * $this->factor;
55
- $h = $src_h * $this->factor;
56
- $image->crop( 0, 0, $src_w, $src_h, $w, $h );
57
- $result = $image->save( $save_filename );
58
- if ( is_wp_error( $result ) ) {
59
- // @codeCoverageIgnoreStart
60
- TimberHelper::error_log( 'Error resizing image' );
61
- TimberHelper::error_log( $result );
62
- return false;
63
- // @codeCoverageIgnoreEnd
64
- } else {
65
- return true;
66
- }
67
- } else if ( isset( $image->error_data['error_loading_image'] ) ) {
68
- TimberHelper::error_log( 'Error loading ' . $image->error_data['error_loading_image'] );
69
- } else {
70
- TimberHelper::error_log( $image );
71
- }
72
- return false;
73
- }
74
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/integrations/acf-timber.php DELETED
@@ -1,58 +0,0 @@
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
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/integrations/timber-command.php DELETED
@@ -1,36 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * These are methods that can be executed by WPCLI, other CLI mechanism or other external controllers
5
- * @package timber
6
- */
7
- class TimberCommand {
8
-
9
- public static function clear_cache($mode = 'all'){
10
- if (is_array($mode)){
11
- $mode = reset($mode);
12
- }
13
- if ($mode == 'all') {
14
- $twig_cache = self::clear_cache_twig();
15
- $timber_cache = self::clear_cache_timber();
16
- if ($twig_cache && $timber_cache){
17
- return true;
18
- }
19
- } else if ($mode == 'twig') {
20
- return self::clear_cache_twig();
21
- } else if ($mode == 'timber') {
22
- return self::clear_cache_timber();
23
- }
24
- }
25
-
26
- static function clear_cache_timber(){
27
- $loader = new TimberLoader();
28
- return $loader->clear_cache_timber();
29
- }
30
-
31
- static function clear_cache_twig(){
32
- $loader = new TimberLoader();
33
- return $loader->clear_cache_twig();
34
- }
35
-
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/integrations/wpcli-timber.php DELETED
@@ -1,57 +0,0 @@
1
- <?php
2
- if (!class_exists('WP_CLI_Command')) {
3
- return;
4
- }
5
-
6
- class Timber_WP_CLI_Command extends WP_CLI_Command {
7
-
8
- /**
9
- * Clears Timber and Twig's Cache
10
- *
11
- * ## EXAMPLES
12
- *
13
- * wp timber clear_cache
14
- *
15
- */
16
- public function clear_cache($mode = 'all') {
17
- TimberCommand::clear_cache($mode);
18
- }
19
-
20
- /**
21
- * Clears Twig's Cache
22
- *
23
- * ## EXAMPLES
24
- *
25
- * wp timber clear_cache_twig
26
- *
27
- */
28
- function clear_cache_twig(){
29
- $clear = TimberCommand::clear_cache_twig();
30
- if ($clear){
31
- WP_CLI::success('Cleared contents of twig cache');
32
- } else {
33
- WP_CLI::warning('Failed to clear twig cache');
34
- }
35
- }
36
-
37
- /**
38
- * Clears Timber's Cache
39
- *
40
- * ## EXAMPLES
41
- *
42
- * wp timber clear_cache_timber
43
- *
44
- */
45
- function clear_cache_timber() {
46
- $clear = TimberCommand::clear_cache_timber();
47
- $message = 'Failed to clear timber cache';
48
- if ($clear){
49
- $message = "Cleared contents of Timber's Cache";
50
- WP_CLI::success($message);
51
- } else {
52
- WP_CLI::warning($message);
53
- }
54
- return $message;
55
- }
56
-
57
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/timber-admin.php DELETED
@@ -1,26 +0,0 @@
1
- <?php
2
-
3
- class TimberAdmin {
4
-
5
- public static function init() {
6
- return add_filter( 'plugin_row_meta', array( __CLASS__, 'meta_links' ), 10, 2 );
7
- }
8
-
9
- /**
10
- * @param array $links
11
- * @param string $file
12
- * @return array
13
- */
14
- public static function meta_links( $links, $file ) {
15
- if ( strstr( $file, '/timber.php' ) ) {
16
- unset($links[2]);
17
- $links[] = '<a href="/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>';
18
- $links[] = '<a href="http://upstatement.com/timber" target="_blank">Homepage</a>';
19
- $links[] = '<a href="https://github.com/jarednova/timber/wiki" target="_blank">Documentation</a>';
20
- $links[] = '<a href="https://github.com/jarednova/timber/wiki/getting-started" target="_blank">Starter Guide</a>';
21
- return $links;
22
- }
23
- return $links;
24
- }
25
-
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/timber-core.php DELETED
@@ -1,117 +0,0 @@
1
- <?php
2
-
3
- abstract class TimberCore {
4
-
5
- public $id;
6
- public $ID;
7
- public $object_type;
8
-
9
- /**
10
- *
11
- *
12
- * @return boolean
13
- */
14
- function __isset( $field ) {
15
- if ( isset( $this->$field ) ) {
16
- return $this->$field;
17
- }
18
- return false;
19
- }
20
-
21
- /**
22
- * This is helpful for twig to return properties and methods see: https://github.com/fabpot/Twig/issues/2
23
- * @return mixed
24
- */
25
- function __call( $field, $args ) {
26
- return $this->__get( $field );
27
- }
28
-
29
- /**
30
- * This is helpful for twig to return properties and methods see: https://github.com/fabpot/Twig/issues/2
31
- *
32
- * @return mixed
33
- */
34
- function __get( $field ) {
35
- if ( property_exists($this, $field) ) {
36
- return $this->$field;
37
- }
38
- if ( method_exists($this, 'meta') && $meta_value = $this->meta( $field ) ) {
39
- return $this->$field = $meta_value;
40
- }
41
- if ( method_exists($this, $field) ) {
42
- return $this->$field = $this->$field();
43
- }
44
- return $this->$field = false;
45
- }
46
-
47
- /**
48
- * Takes an array or object and adds the properties to the parent object
49
- * @example
50
- * ```php
51
- * $data = array('airplane' => '757-200', 'flight' => '5316');
52
- * $post = new TimberPost()
53
- * $post->import(data);
54
- * echo $post->airplane; //757-200
55
- * ```
56
- * @param array|object $info an object or array you want to grab data from to attach to the Timber object
57
- */
58
- function import( $info, $force = false ) {
59
- if ( is_object( $info ) ) {
60
- $info = get_object_vars( $info );
61
- }
62
- if ( is_array( $info ) ) {
63
- foreach ( $info as $key => $value ) {
64
- if ( !empty( $key ) && $force ) {
65
- $this->$key = $value;
66
- } else if ( !empty( $key ) && !method_exists($this, $key) ){
67
- $this->$key = $value;
68
- }
69
- }
70
- }
71
- }
72
-
73
-
74
- /**
75
- * @ignore
76
- * @param string $key
77
- * @param mixed $value
78
- */
79
- function update( $key, $value ) {
80
- update_metadata( $this->object_type, $this->ID, $key, $value );
81
- }
82
-
83
- /**
84
- * Can you edit this post/term/user? Well good for you. You're no better than me.
85
- * @example
86
- * ```twig
87
- * {% if post.can_edit %}
88
- * <a href="{{ post.edit_link }}">Edit</a>
89
- * {% endif %}
90
- * ```
91
- * ```html
92
- * <a href="http://example.org/wp-admin/edit.php?p=242">Edit</a>
93
- * ```
94
- * @return bool
95
- */
96
- function can_edit() {
97
- if ( !function_exists( 'current_user_can' ) ) {
98
- return false;
99
- }
100
- if ( current_user_can( 'edit_post', $this->ID ) ) {
101
- return true;
102
- }
103
- return false;
104
- }
105
-
106
- /**
107
- *
108
- *
109
- * @return array
110
- */
111
- function get_method_values() {
112
- $ret = array();
113
- $ret['can_edit'] = $this->can_edit();
114
- return $ret;
115
- }
116
-
117
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/timber-function-wrapper.php DELETED
@@ -1,77 +0,0 @@
1
- <?php
2
-
3
- class TimberFunctionWrapper {
4
-
5
- private $_function;
6
- private $_args;
7
- private $_use_ob;
8
-
9
- public function __toString() {
10
- return (string)$this->call();
11
- }
12
-
13
- /**
14
- *
15
- *
16
- * @param callable $function
17
- * @param array $args
18
- * @param bool $return_output_buffer
19
- */
20
- public function __construct( $function, $args = array(), $return_output_buffer = false ) {
21
- $this->_function = $function;
22
- $this->_args = $args;
23
- $this->_use_ob = $return_output_buffer;
24
-
25
- add_filter( 'get_twig', array( &$this, 'add_to_twig' ) );
26
- }
27
-
28
- /**
29
- *
30
- *
31
- * @param Twig_Environment $twig
32
- * @return Twig_Environment
33
- */
34
- public function add_to_twig( $twig ) {
35
- $wrapper = $this;
36
-
37
- $twig->addFunction( new Twig_SimpleFunction( $this->_function, function () use ( $wrapper ) {
38
- return call_user_func_array( array( $wrapper, 'call' ), func_get_args() );
39
- } ) );
40
-
41
- return $twig;
42
- }
43
-
44
- /**
45
- *
46
- *
47
- * @return string
48
- */
49
- public function call() {
50
- $args = $this->_parse_args( func_get_args(), $this->_args );
51
-
52
- if ( $this->_use_ob ) {
53
- return TimberHelper::ob_function( $this->_function, $args );
54
- } else {
55
- return call_user_func_array( $this->_function, $args );
56
- }
57
- }
58
-
59
- /**
60
- *
61
- *
62
- * @param array $args
63
- * @param array $defaults
64
- * @return array
65
- */
66
- private function _parse_args( $args, $defaults ) {
67
- $_arg = reset( $defaults );
68
-
69
- foreach ( $args as $index => $arg ) {
70
- $defaults[$index] = is_null( $arg ) ? $_arg : $arg;
71
- $_arg = next( $defaults );
72
- }
73
-
74
- return $defaults;
75
- }
76
-
77
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/timber-integrations.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * This is for integrating external plugins into timber
5
- * @package timber
6
- */
7
- class TimberIntegrations {
8
- public static function init() {
9
-
10
- add_action( 'init', array( __CLASS__, 'maybe_init_acftimber' ) );
11
-
12
- if ( class_exists( 'WP_CLI_Command' ) ) {
13
- WP_CLI::add_command( 'timber', 'Timber_WP_CLI_Command' );
14
- }
15
- }
16
-
17
- public static function maybe_init_acftimber() {
18
-
19
- if ( class_exists( 'ACF' ) ) {
20
- new ACFTimber();
21
- }
22
-
23
- }
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/timber-page.php DELETED
@@ -1,9 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * @ignore
5
- * @deprecated 0.21.9
6
- */
7
- class TimberPage extends TimberPost {
8
-
9
- }
 
 
 
 
 
 
 
 
 
lib/timber-post-getter.php DELETED
@@ -1,104 +0,0 @@
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
- }
16
-
17
- static function get_posts( $query = false, $PostClass = 'TimberPost', $return_collection = false ) {
18
- $posts = self::query_posts( $query, $PostClass );
19
- return apply_filters('timber_post_getter_get_posts', $posts->get_posts( $return_collection ));
20
- }
21
-
22
- static function query_post( $query = false, $PostClass = 'TimberPost' ) {
23
- $posts = self::query_posts( $query, $PostClass );
24
- if ( method_exists($posts, 'current') && $post = $posts->current() ) {
25
- return $post;
26
- }
27
- }
28
-
29
- /**
30
- * @param mixed $query
31
- * @param string $PostClass
32
- * @return array|bool|null
33
- */
34
- static function query_posts($query = false, $PostClass = 'TimberPost' ) {
35
- if (self::is_post_class_or_class_map($query)) {
36
- $PostClass = $query;
37
- $query = false;
38
- }
39
-
40
- if (is_object($query) && !is_a($query, 'WP_Query') ){
41
- // The only object other than a query is a type of post object
42
- $query = array( $query );
43
- }
44
-
45
- if ( is_array( $query ) && count( $query ) && isset( $query[0] ) && is_object( $query[0] ) ) {
46
- // We have an array of post objects that already have data
47
- return new TimberPostsCollection( $query, $PostClass );
48
- } else {
49
- // We have a query (of sorts) to work with
50
- $tqi = new TimberQueryIterator( $query, $PostClass );
51
- return $tqi;
52
- }
53
- }
54
-
55
- static function get_pids($query){
56
- $posts = self::get_posts($query);
57
- $pids = array();
58
- foreach($posts as $post){
59
- if (isset($post->ID)){
60
- $pids[] = $post->ID;
61
- }
62
- }
63
- return $pids;
64
- }
65
-
66
- static function loop_to_id() {
67
- if (!self::wp_query_has_posts()) { return false; }
68
-
69
- global $wp_query;
70
- $post_num = property_exists($wp_query, 'current_post')
71
- ? $wp_query->current_post + 1
72
- : 0
73
- ;
74
-
75
- if (!isset($wp_query->posts[$post_num])) { return false; }
76
-
77
- return $wp_query->posts[$post_num]->ID;
78
- }
79
-
80
- /**
81
- * @return bool
82
- */
83
- static function wp_query_has_posts() {
84
- global $wp_query;
85
- return ($wp_query && property_exists($wp_query, 'posts') && $wp_query->posts);
86
- }
87
-
88
- /**
89
- * @param string|array $arg
90
- * @return bool
91
- */
92
- static function is_post_class_or_class_map($arg){
93
- if (is_string($arg) && class_exists($arg)) {
94
- return true;
95
- }
96
- if (is_array($arg)) {
97
- foreach ($arg as $item) {
98
- if (is_string($item) && class_exists($item)) {
99
- return true;
100
- }
101
- }
102
- }
103
- }
104
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/timber-posts-collection.php DELETED
@@ -1,97 +0,0 @@
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( $posts = array(), $post_class = 'TimberPost' ) {
10
- $returned_posts = array();
11
- if ( is_null( $posts ) ){
12
- $posts = array();
13
- }
14
- foreach ( $posts as $post_object ) {
15
- $post_class_use = $post_class;
16
-
17
- if ( is_array( $post_class ) ) {
18
- $post_type = get_post_type( $post_object );
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 ' . $post_object->ID . ' 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 $post_object is already of the right type
34
- if ( is_a( $post_object, $post_class_use ) ) {
35
- $post = $post_object;
36
- } else {
37
- $post = new $post_class_use( $post_object );
38
- }
39
-
40
- if ( isset( $post->ID ) ) {
41
- $returned_posts[] = $post;
42
- }
43
- }
44
-
45
- $returned_posts = self::maybe_set_preview($returned_posts);
46
-
47
- parent::__construct( $returned_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
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/timber-query-iterator.php DELETED
@@ -1,156 +0,0 @@
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
- //if we're on a custom posts page?
30
- $the_query = self::handle_maybe_custom_posts_page($the_query);
31
- } elseif ( TimberHelper::is_array_assoc( $query ) || ( is_string( $query ) && strstr( $query, '=' ) ) ) {
32
- // We have a regularly formed WP query string or array to use
33
- $the_query = new WP_Query( $query );
34
-
35
- } elseif ( is_numeric( $query ) || is_string( $query ) ) {
36
- // We have what could be a post name or post ID to pull out
37
- $the_query = self::get_query_from_string( $query );
38
-
39
- } elseif ( is_array( $query ) && count( $query ) && ( is_integer( $query[0] ) || is_string( $query[0] ) ) ) {
40
- // We have a list of pids (post IDs) to extract from
41
- $the_query = self::get_query_from_array_of_ids( $query );
42
- } elseif ( is_array($query) && empty($query)) {
43
- // it's an empty array
44
- $the_query = array();
45
- } else {
46
- TimberHelper::error_log( 'I have failed you! in ' . basename( __FILE__ ) . '::' . __LINE__ );
47
- TimberHelper::error_log( $query );
48
-
49
- // We have failed hard, at least let get something.
50
- $the_query = new WP_Query();
51
- }
52
-
53
- $this->_query = $the_query;
54
-
55
- }
56
-
57
- public function get_posts( $return_collection = false ) {
58
- if (isset($this->_query->posts)){
59
- $posts = new TimberPostsCollection( $this->_query->posts, $this->_posts_class );
60
- return ( $return_collection ) ? $posts : $posts->get_posts();
61
- }
62
- }
63
-
64
- //
65
- // GET POSTS
66
- //
67
- public static function get_query_from_array_of_ids( $query = array() ) {
68
- if ( !is_array( $query ) || !count( $query ) )
69
- return null;
70
-
71
- return new WP_Query( array(
72
- 'post_type'=> 'any',
73
- 'ignore_sticky_posts' => true,
74
- 'post__in' => $query,
75
- 'orderby' => 'post__in',
76
- 'nopaging' => true
77
- ) );
78
- }
79
-
80
- public static function get_query_from_string( $string = '' ) {
81
- $post_type = false;
82
-
83
- if ( is_string( $string ) && strstr( $string, '#' ) ) {
84
- //we have a post_type directive here
85
- list( $post_type, $string ) = explode( '#', $string );
86
- }
87
-
88
- $query = array(
89
- 'post_type' => ( $post_type ) ? $post_type : 'any'
90
- );
91
-
92
- if ( is_numeric( $string ) ) {
93
- $query['p'] = $string;
94
-
95
- } else {
96
- $query['name'] = $string;
97
- }
98
-
99
- return new WP_Query( $query );
100
- }
101
-
102
- //
103
- // Iterator Interface
104
- //
105
-
106
- public function valid() {
107
- return $this->_query->have_posts();
108
- }
109
-
110
- public function current() {
111
- global $post;
112
-
113
- $this->_query->the_post();
114
-
115
- // Sets up the global post, but also return the post, for use in Twig template
116
- $posts_class = $this->_posts_class;
117
- return new $posts_class( $post );
118
- }
119
-
120
- /**
121
- * Don't implement next, because current already advances the loop
122
- */
123
- final public function next() {}
124
-
125
- public function rewind() {
126
- $this->_query->rewind_posts();
127
- }
128
-
129
- public function key() {
130
- $this->_query->current_post;
131
- }
132
-
133
- //get_posts users numberposts
134
- static function fix_number_posts_wp_quirk( $query ) {
135
- if (isset($query->query) && isset($query->query['numberposts'])
136
- && !isset($query->query['posts_per_page'])) {
137
- $query->set( 'posts_per_page', $query->query['numberposts'] );
138
- }
139
- return $query;
140
- }
141
-
142
- /**
143
- * this will test for whether a custom page to display posts is active, and if so, set the query to the default
144
- * @param WP_Query $query the original query recived from WordPress
145
- * @return WP_Query
146
- */
147
- static function handle_maybe_custom_posts_page( $query ) {
148
- if ($custom_posts_page = get_option('page_for_posts')) {
149
- if ( isset($query->query['p']) && $query->query['p'] == $custom_posts_page ) {
150
- return new WP_Query(array('post_type' => 'post'));
151
- }
152
- }
153
- return $query;
154
- }
155
-
156
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/timber-routes.php DELETED
@@ -1,33 +0,0 @@
1
- <?php
2
-
3
- class TimberRoutes {
4
-
5
- /**
6
- * @deprecated since 0.21.1 use Upstatement/routes instead
7
- */
8
- public static function init( $timber ) {
9
- // Install ourselves in Timber
10
- $timber->routes = new TimberRoutes();
11
- }
12
-
13
- /**
14
- * @param string $route
15
- * @param callable $callback
16
- * @deprecated since 0.21.1 use Upstatement/routes instead
17
- */
18
- public static function add_route($route, $callback, $args = array()) {
19
- Routes::map($route, $callback, $args);
20
- }
21
-
22
- /**
23
- * @param array $template
24
- * @param mixed $query
25
- * @param int $status_code
26
- * @param bool $tparams
27
- * @return bool
28
- * @deprecated since 0.21.1 use Upstatement/routes instead
29
- */
30
- public static function load_view($template, $query = false, $status_code = 200, $tparams = false) {
31
- Routes::load($template, $tparams, $query, $status_code);
32
- }
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/timber-twig.php DELETED
@@ -1,347 +0,0 @@
1
- <?php
2
-
3
- class TimberTwig {
4
-
5
- public static $dir_name;
6
-
7
- /**
8
- * @codeCoverageIgnore
9
- */
10
- public static function init() {
11
- new TimberTwig();
12
- }
13
-
14
- /**
15
- * @codeCoverageIgnore
16
- */
17
- function __construct() {
18
- add_action( 'timber/twig/filters', array( $this, 'add_timber_filters_deprecated' ) );
19
- add_action( 'timber/twig/filters', array( $this, 'add_timber_filters' ) );
20
- }
21
-
22
- /**
23
- * These are all deprecated and will be removed in 0.21.0
24
- *
25
- * @param Twig_Environment $twig
26
- * @deprecated since 0.20.7
27
- * @return Twig_Environment
28
- */
29
- function add_timber_filters_deprecated( $twig ) {
30
- $twig->addFilter( new Twig_SimpleFilter( 'get_src_from_attachment_id', 'twig_get_src_from_attachment_id' ) );
31
- $twig->addFilter( new Twig_SimpleFilter( 'wp_body_class', array( $this, 'body_class' ) ) );
32
- $twig->addFilter( new Twig_SimpleFilter( 'twitterify', array( 'TimberHelper', 'twitterify' ) ) );
33
- $twig->addFilter( new Twig_SimpleFilter( 'twitterfy', array( 'TimberHelper', 'twitterify' ) ) );
34
- $twig->addFilter( new Twig_SimpleFilter( 'string', function($arr, $glue = ' '){
35
- return twig_join_filter($arr, $glue);
36
- } ) );
37
- return $twig;
38
- }
39
-
40
- /**
41
- *
42
- *
43
- * @param Twig_Environment $twig
44
- * @return Twig_Environment
45
- */
46
- function add_timber_filters( $twig ) {
47
- /* image filters */
48
- $twig->addFilter( new Twig_SimpleFilter( 'resize', array( 'TimberImageHelper', 'resize' ) ) );
49
- $twig->addFilter( new Twig_SimpleFilter( 'retina', array( 'TimberImageHelper', 'retina_resize' ) ) );
50
- $twig->addFilter( new Twig_SimpleFilter( 'letterbox', array( 'TimberImageHelper', 'letterbox' ) ) );
51
- $twig->addFilter( new Twig_SimpleFilter( 'tojpg', array( 'TimberImageHelper', 'img_to_jpg' ) ) );
52
-
53
- /* debugging filters */
54
- $twig->addFilter( new Twig_SimpleFilter( 'docs', 'twig_object_docs' ) );
55
- $twig->addFilter( new Twig_SimpleFilter( 'get_class', 'get_class' ) );
56
- $twig->addFilter( new Twig_SimpleFilter( 'get_type', 'get_type' ) );
57
- $twig->addFilter( new Twig_SimpleFilter( 'print_r', function( $arr ) {
58
- return print_r( $arr, true );
59
- } ) );
60
- $twig->addFilter( new Twig_SimpleFilter( 'print_a', function( $arr ) {
61
- return '<pre>' . self::object_docs( $arr, true ) . '</pre>';
62
- } ) );
63
-
64
- /* other filters */
65
- $twig->addFilter( new Twig_SimpleFilter( 'stripshortcodes', 'strip_shortcodes' ) );
66
- $twig->addFilter( new Twig_SimpleFilter( 'array', array( $this, 'to_array' ) ) );
67
- $twig->addFilter( new Twig_SimpleFilter( 'excerpt', 'wp_trim_words' ) );
68
- $twig->addFilter( new Twig_SimpleFilter( 'function', array( $this, 'exec_function' ) ) );
69
- $twig->addFilter( new Twig_SimpleFilter( 'pretags', array( $this, 'twig_pretags' ) ) );
70
- $twig->addFilter( new Twig_SimpleFilter( 'sanitize', 'sanitize_title' ) );
71
- $twig->addFilter( new Twig_SimpleFilter( 'shortcodes', 'do_shortcode' ) );
72
- $twig->addFilter( new Twig_SimpleFilter( 'time_ago', array( $this, 'time_ago' ) ) );
73
- $twig->addFilter( new Twig_SimpleFilter( 'wpautop', 'wpautop' ) );
74
- $twig->addFilter( new Twig_SimpleFilter( 'list', array( $this, 'add_list_separators' ) ) );
75
-
76
- $twig->addFilter( new Twig_SimpleFilter( 'relative', function ( $link ) {
77
- return TimberURLHelper::get_rel_url( $link, true );
78
- } ) );
79
-
80
- $twig->addFilter( new Twig_SimpleFilter( 'date', array( $this, 'intl_date' ) ) );
81
-
82
- $twig->addFilter( new Twig_SimpleFilter( 'truncate', function ( $text, $len ) {
83
- return TimberHelper::trim_words( $text, $len );
84
- } ) );
85
-
86
- /* actions and filters */
87
- $twig->addFunction( new Twig_SimpleFunction( 'action', function ( $context ) {
88
- $args = func_get_args();
89
- array_shift( $args );
90
- $args[] = $context;
91
- call_user_func_array( 'do_action', $args );
92
- }, array( 'needs_context' => true ) ) );
93
-
94
- $twig->addFilter( new Twig_SimpleFilter( 'apply_filters', function () {
95
- $args = func_get_args();
96
- $tag = current( array_splice( $args, 1, 1 ) );
97
-
98
- return apply_filters_ref_array( $tag, $args );
99
- } ) );
100
- $twig->addFunction( new Twig_SimpleFunction( 'function', array( &$this, 'exec_function' ) ) );
101
- $twig->addFunction( new Twig_SimpleFunction( 'fn', array( &$this, 'exec_function' ) ) );
102
-
103
- $twig->addFunction( new Twig_SimpleFunction( 'shortcode', 'do_shortcode' ) );
104
-
105
- /* TimberObjects */
106
- $twig->addFunction( new Twig_SimpleFunction( 'TimberPost', function ( $pid, $PostClass = 'TimberPost' ) {
107
- if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
108
- foreach ( $pid as &$p ) {
109
- $p = new $PostClass( $p );
110
- }
111
- return $pid;
112
- }
113
- return new $PostClass( $pid );
114
- } ) );
115
- $twig->addFunction( new Twig_SimpleFunction( 'TimberImage', function ( $pid, $ImageClass = 'TimberImage' ) {
116
- if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
117
- foreach ( $pid as &$p ) {
118
- $p = new $ImageClass( $p );
119
- }
120
- return $pid;
121
- }
122
- return new $ImageClass( $pid );
123
- } ) );
124
- $twig->addFunction( new Twig_SimpleFunction( 'TimberTerm', function ( $pid, $TermClass = 'TimberTerm' ) {
125
- if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
126
- foreach ( $pid as &$p ) {
127
- $p = new $TermClass( $p );
128
- }
129
- return $pid;
130
- }
131
- return new $TermClass( $pid );
132
- } ) );
133
- $twig->addFunction( new Twig_SimpleFunction( 'TimberUser', function ( $pid, $UserClass = 'TimberUser' ) {
134
- if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
135
- foreach ( $pid as &$p ) {
136
- $p = new $UserClass( $p );
137
- }
138
- return $pid;
139
- }
140
- return new $UserClass( $pid );
141
- } ) );
142
-
143
- /* TimberObjects Alias */
144
- $twig->addFunction( new Twig_SimpleFunction( 'Post', function ( $pid, $PostClass = 'TimberPost' ) {
145
- if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
146
- foreach ( $pid as &$p ) {
147
- $p = new $PostClass( $p );
148
- }
149
- return $pid;
150
- }
151
- return new $PostClass( $pid );
152
- } ) );
153
- $twig->addFunction( new Twig_SimpleFunction( 'Image', function ( $pid, $ImageClass = 'TimberImage' ) {
154
- if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
155
- foreach ( $pid as &$p ) {
156
- $p = new $ImageClass( $p );
157
- }
158
- return $pid;
159
- }
160
- return new $ImageClass( $pid );
161
- } ) );
162
- $twig->addFunction( new Twig_SimpleFunction( 'Term', function ( $pid, $TermClass = 'TimberTerm' ) {
163
- if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
164
- foreach ( $pid as &$p ) {
165
- $p = new $TermClass( $p );
166
- }
167
- return $pid;
168
- }
169
- return new $TermClass( $pid );
170
- } ) );
171
- $twig->addFunction( new Twig_SimpleFunction( 'User', function ( $pid, $UserClass = 'TimberUser' ) {
172
- if ( is_array( $pid ) && !TimberHelper::is_array_assoc( $pid ) ) {
173
- foreach ( $pid as &$p ) {
174
- $p = new $UserClass( $p );
175
- }
176
- return $pid;
177
- }
178
- return new $UserClass( $pid );
179
- } ) );
180
-
181
- /* bloginfo and translate */
182
- $twig->addFunction( 'bloginfo', new Twig_SimpleFunction( 'bloginfo', function ( $show = '', $filter = 'raw' ) {
183
- return get_bloginfo( $show, $filter );
184
- } ) );
185
- $twig->addFunction( '__', new Twig_SimpleFunction( '__', function ( $text, $domain = 'default' ) {
186
- return __( $text, $domain );
187
- } ) );
188
- /* get_twig is deprecated, use timber/twig */
189
- $twig = apply_filters( 'get_twig', $twig );
190
- $twig = apply_filters( 'timber/twig', $twig );
191
- return $twig;
192
- }
193
-
194
- /**
195
- *
196
- *
197
- * @param mixed $arr
198
- * @return array
199
- */
200
- function to_array( $arr ) {
201
- if ( is_array( $arr ) ) {
202
- return $arr;
203
- }
204
- $arr = array( $arr );
205
- return $arr;
206
- }
207
-
208
- /**
209
- *
210
- *
211
- * @param string $function_name
212
- * @return mixed
213
- */
214
- function exec_function( $function_name ) {
215
- $args = func_get_args();
216
- array_shift( $args );
217
- if ( is_string($function_name) ) {
218
- $function_name = trim( $function_name );
219
- }
220
- return call_user_func_array( $function_name, ( $args ) );
221
- }
222
-
223
- /**
224
- *
225
- *
226
- * @param string $content
227
- * @return string
228
- */
229
- function twig_pretags( $content ) {
230
- return preg_replace_callback( '|<pre.*>(.*)</pre|isU', array( &$this, 'convert_pre_entities' ), $content );
231
- }
232
-
233
- /**
234
- *
235
- *
236
- * @param array $matches
237
- * @return string
238
- */
239
- function convert_pre_entities( $matches ) {
240
- return str_replace( $matches[1], htmlentities( $matches[1] ), $matches[0] );
241
- }
242
-
243
- /**
244
- * @param mixed $body_classes
245
- * @deprecated 0.20.7
246
- * @return string
247
- */
248
- function body_class( $body_classes ) {
249
- ob_start();
250
- if ( is_array( $body_classes ) ) {
251
- $body_classes = explode( ' ', $body_classes );
252
- }
253
- body_class( $body_classes );
254
- $return = ob_get_contents();
255
- ob_end_clean();
256
- return $return;
257
- }
258
-
259
- /**
260
- *
261
- *
262
- * @param string $date
263
- * @param string $format (optional)
264
- * @return string
265
- */
266
- function intl_date( $date, $format = null ) {
267
- if ( $format === null ) {
268
- $format = get_option( 'date_format' );
269
- }
270
-
271
- if ( $date instanceof DateTime ) {
272
- $timestamp = $date->getTimestamp();
273
- } else if (is_numeric( $date ) && strtotime( $date ) === false ) {
274
- $timestamp = intval( $date );
275
- } else {
276
- $timestamp = strtotime( $date );
277
- }
278
-
279
- return date_i18n( $format, $timestamp );
280
- }
281
-
282
- //debug
283
-
284
- /**
285
- *
286
- *
287
- * @param mixed $obj
288
- * @param bool $methods
289
- * @deprecated since 0.20.7
290
- * @return string
291
- */
292
- function object_docs( $obj, $methods = true ) {
293
- $class = get_class( $obj );
294
- $properties = (array)$obj;
295
- if ( $methods ) {
296
- /** @var array $methods */
297
- $methods = $obj->get_method_values();
298
- }
299
- $rets = array_merge( $properties, $methods );
300
- ksort( $rets );
301
- $str = print_r( $rets, true );
302
- $str = str_replace( 'Array', $class . ' Object', $str );
303
- return $str;
304
- }
305
-
306
- /**
307
- * @param int|string $from
308
- * @param int|string $to
309
- * @param string $format_past
310
- * @param string $format_future
311
- * @return string
312
- */
313
- function time_ago( $from, $to = null, $format_past = '%s ago', $format_future = '%s from now' ) {
314
- $to = $to === null ? time() : $to;
315
- $to = is_int( $to ) ? $to : strtotime( $to );
316
- $from = is_int( $from ) ? $from : strtotime( $from );
317
-
318
- if ( $from < $to ) {
319
- return sprintf( $format_past, human_time_diff( $from, $to ) );
320
- } else {
321
- return sprintf( $format_future, human_time_diff( $to, $from ) );
322
- }
323
- }
324
-
325
- /**
326
- * @param array $arr
327
- * @param string $first_delimiter
328
- * @param string $second_delimiter
329
- * @return string
330
- */
331
- function add_list_separators( $arr, $first_delimiter = ',', $second_delimiter = 'and' ) {
332
- $length = count( $arr );
333
- $list = '';
334
- foreach( $arr as $index => $item ) {
335
- if ( $index < $length - 2 ) {
336
- $delimiter = $first_delimiter.' ';
337
- } elseif ( $index == $length - 2 ) {
338
- $delimiter = ' '.$second_delimiter.' ';
339
- } else {
340
- $delimiter = '';
341
- }
342
- $list = $list.$item.$delimiter;
343
- }
344
- return $list;
345
- }
346
-
347
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: jarednova, lggorman
3
  Tags: template engine, templates, twig
4
  Requires at least: 3.7
5
- Stable tag: 0.22.5
6
- Tested up to: 4.4.1
7
  PHP version: 5.3.0 or greater
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -17,11 +17,11 @@ Once Timber is installed and activated in your plugin directory, it gives any Wo
17
 
18
  ### Looking for docs?
19
  * **[Project Page](http://upstatement.com/timber)**
20
- * [Timber Documentation](https://github.com/jarednova/timber/wiki/)
21
  * [Twig Reference (from SensioLabs)](http://twig.sensiolabs.org/doc/templates.html)
22
  _Twig is the template language powering Timber; if you need a little background on what a template language is, [Twig's homepage has an overview](http://twig.sensiolabs.org/)_
23
- * **[Video Tutorials](https://github.com/jarednova/timber/wiki/Video-Tutorials)**
24
- * [Overview / Getting Started Guide](https://github.com/jarednova/timber/wiki/getting-started)
25
 
26
  #### Related Projects
27
  * [**Timber Debug Bar**](http://wordpress.org/plugins/debug-bar-timber/) Adds a debug bar panel that will show you want template is in-use and the data sent to your twig file.
@@ -33,7 +33,7 @@ Nothing. Timber is meant for you to build a theme on. Like the [Starkers](https:
33
  Timber is great for any WordPress developer who cares about writing good, maintainable code. It helps teams of designers and developers working together. At [Upstatement](http://upstatement.com) we made Timber because not everyone knows the ins-and-outs of the_loop(), WordPress codex and PHP (nor should they). With Timber your best WordPress dev can focus on building the .php files with requests from WordPress and pass the data into .twig files. Once there, designers can easily mark-up data and build out a site's look-and-feel.
34
 
35
  #### Want to read more?
36
- * [Timber on GitHub](http://github.com/jarednova/timber/)
37
  * [Timber Overview on Tidy Repo](http://tidyrepo.com/timber/)
38
  * ["Timber and Twig Reignited My Love for WordPress" on CSS-Tricks](https://css-tricks.com/timber-and-twig-reignited-my-love-for-wordpress/)
39
 
@@ -46,7 +46,7 @@ Timber is great for any WordPress developer who cares about writing good, mainta
46
  * Fixed error in comments_link (thanks @tehlivi)
47
 
48
  = 0.22.4 =
49
- * Fixed [bug](https://github.com/jarednova/timber/issues/785) in get_calling_script file (thanks @gwagroves)
50
  * Added tons of new tests and docs (thanks @lggorman and @jarednova)
51
 
52
  = 0.22.3 =
@@ -61,7 +61,7 @@ Timber is great for any WordPress developer who cares about writing good, mainta
61
  * Added support for animated gifs
62
 
63
  = 0.22.1 =
64
- * Added better support for [post.get_terms](https://github.com/jarednova/timber/pull/737) (thanks @aaemnnosttv)
65
  * Fix for issue with ACF date field (thanks @rpkoller)
66
  * Fix for resizing jpEgs (thanks @eaton)
67
 
@@ -228,7 +228,7 @@ Timber is great for any WordPress developer who cares about writing good, mainta
228
  * Hotfix on timber-admin error
229
 
230
  = 0.17.0 =
231
- * Now you can resize/crop images with positional preferences, thanks @mmikkel. Here are the docs: https://github.com/jarednova/timber/wiki/TimberImage#resize
232
  * Removed the Timber Starter Guide from the admin, a link to the GitHub wiki suffices.
233
 
234
  = 0.16.8 =
@@ -323,7 +323,7 @@ Timber is great for any WordPress developer who cares about writing good, mainta
323
  * New Timber::compile method which _doesn't_ automatically echo. (Same args as Timber::render)
324
  * Added post.get_next / post.get_prev for TimberPosts
325
  * Fixed a thing to make get_preview easier when you want to omit the 'Read More' link
326
- * Read the [Full Release Notes](https://github.com/jarednova/timber/releases/tag/0.15.0)
327
 
328
  = 0.14.1 =
329
  * Added hooks to play nicely with Timber Debug Bar
@@ -441,12 +441,12 @@ That's Timber!
441
 
442
  == Support ==
443
 
444
- Please use the [GitHub repo](https://github.com/jarednova/timber/issues?state=open) to file bugs or questions.
445
 
446
  == Frequently Asked Questions ==
447
 
448
  = Can it be used in an existing theme? =
449
- You bet! Watch these **[video tutorials](https://github.com/jarednova/timber/wiki/Video-Tutorials)** to see how.
450
 
451
  = Is it used in production? =
452
  At Upstatement we've now used it on more than a dozen client sites. Hundreds of other sites use it too. You can check some of them out in the **[showcase](http://upstatement.com/timber/#showcase)**.
@@ -461,4 +461,4 @@ Whatever. It simplifies the silly stuff so that you can focus on building more c
461
  As stated above, we're using it in dozens of sites (and dozens more planned) -- dozens of other developers are using it too. This isn't going anywhere. Twig is the chosen language for other PHP platforms like Symfony, Drupal 8 and Craft. WordPress will eventually adopt Twig too, I promise you that.
462
 
463
  = Support? =
464
- Leave a [GitHub issue](https://github.com/jarednova/timber/issues?state=open) and I'll holler back.
2
  Contributors: jarednova, lggorman
3
  Tags: template engine, templates, twig
4
  Requires at least: 3.7
5
+ Stable tag: 1.0.0
6
+ Tested up to: 4.5.1
7
  PHP version: 5.3.0 or greater
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
17
 
18
  ### Looking for docs?
19
  * **[Project Page](http://upstatement.com/timber)**
20
+ * [Timber Documentation](https://github.com/timber/timber/wiki/)
21
  * [Twig Reference (from SensioLabs)](http://twig.sensiolabs.org/doc/templates.html)
22
  _Twig is the template language powering Timber; if you need a little background on what a template language is, [Twig's homepage has an overview](http://twig.sensiolabs.org/)_
23
+ * **[Video Tutorials](https://github.com/timber/timber/wiki/Video-Tutorials)**
24
+ * [Overview / Getting Started Guide](https://github.com/timber/timber/wiki/getting-started)
25
 
26
  #### Related Projects
27
  * [**Timber Debug Bar**](http://wordpress.org/plugins/debug-bar-timber/) Adds a debug bar panel that will show you want template is in-use and the data sent to your twig file.
33
  Timber is great for any WordPress developer who cares about writing good, maintainable code. It helps teams of designers and developers working together. At [Upstatement](http://upstatement.com) we made Timber because not everyone knows the ins-and-outs of the_loop(), WordPress codex and PHP (nor should they). With Timber your best WordPress dev can focus on building the .php files with requests from WordPress and pass the data into .twig files. Once there, designers can easily mark-up data and build out a site's look-and-feel.
34
 
35
  #### Want to read more?
36
+ * [Timber on GitHub](http://github.com/timber/timber/)
37
  * [Timber Overview on Tidy Repo](http://tidyrepo.com/timber/)
38
  * ["Timber and Twig Reignited My Love for WordPress" on CSS-Tricks](https://css-tricks.com/timber-and-twig-reignited-my-love-for-wordpress/)
39
 
46
  * Fixed error in comments_link (thanks @tehlivi)
47
 
48
  = 0.22.4 =
49
+ * Fixed [bug](https://github.com/timber/timber/issues/785) in get_calling_script file (thanks @gwagroves)
50
  * Added tons of new tests and docs (thanks @lggorman and @jarednova)
51
 
52
  = 0.22.3 =
61
  * Added support for animated gifs
62
 
63
  = 0.22.1 =
64
+ * Added better support for [post.get_terms](https://github.com/timber/timber/pull/737) (thanks @aaemnnosttv)
65
  * Fix for issue with ACF date field (thanks @rpkoller)
66
  * Fix for resizing jpEgs (thanks @eaton)
67
 
228
  * Hotfix on timber-admin error
229
 
230
  = 0.17.0 =
231
+ * Now you can resize/crop images with positional preferences, thanks @mmikkel. Here are the docs: https://github.com/timber/timber/wiki/TimberImage#resize
232
  * Removed the Timber Starter Guide from the admin, a link to the GitHub wiki suffices.
233
 
234
  = 0.16.8 =
323
  * New Timber::compile method which _doesn't_ automatically echo. (Same args as Timber::render)
324
  * Added post.get_next / post.get_prev for TimberPosts
325
  * Fixed a thing to make get_preview easier when you want to omit the 'Read More' link
326
+ * Read the [Full Release Notes](https://github.com/timber/timber/releases/tag/0.15.0)
327
 
328
  = 0.14.1 =
329
  * Added hooks to play nicely with Timber Debug Bar
441
 
442
  == Support ==
443
 
444
+ Please post on [StackOverflow under the "Timber" tag](http://stackoverflow.com/questions/tagged/timber). Please use GitHub issues only for specific bugs, feature requests and other types of issues.
445
 
446
  == Frequently Asked Questions ==
447
 
448
  = Can it be used in an existing theme? =
449
+ You bet! Watch these **[video tutorials](https://github.com/timber/timber/wiki/Video-Tutorials)** to see how.
450
 
451
  = Is it used in production? =
452
  At Upstatement we've now used it on more than a dozen client sites. Hundreds of other sites use it too. You can check some of them out in the **[showcase](http://upstatement.com/timber/#showcase)**.
461
  As stated above, we're using it in dozens of sites (and dozens more planned) -- dozens of other developers are using it too. This isn't going anywhere. Twig is the chosen language for other PHP platforms like Symfony, Drupal 8 and Craft. WordPress will eventually adopt Twig too, I promise you that.
462
 
463
  = Support? =
464
+ Leave a [GitHub issue](https://github.com/timber/timber/issues?state=open) and I'll holler back.
timber-starter-theme/README.md CHANGED
@@ -1,5 +1,35 @@
1
- # timber-starter-theme
2
 
3
- [![Build Status](https://travis-ci.org/Upstatement/timber-starter-theme.svg)](https://travis-ci.org/Upstatement/timber-starter-theme)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
- The "_s" for Timber: a dead-simple theme that you can build from
 
1
 
2
+ # The Timber Starter Theme
3
+
4
+ The "_s" for Timber: a dead-simple theme that you can build from. The primary purpose of this theme is to provide a file structure rather than a framework for markup or styles. Configure your Sass, scripts, and task runners however you would like!
5
+
6
+ [![Build Status](https://travis-ci.org/timber/starter-theme.svg)](https://travis-ci.org/timber/starter-theme)
7
+
8
+ ## Installing the Theme
9
+
10
+ Install this theme as you would any other, and be sure the Timber plugin is activated. But hey, let's break it down into some bullets:
11
+
12
+ 1. Make sure you have installed the plugin for the [Timber Library](https://wordpress.org/plugins/timber-library/) (and Advanced Custom Fields - they [play quite nicely](https://github.com/jarednova/timber/wiki/ACF-Cookbook) together).
13
+ 2. Download the zip for this theme (or clone it) and move it to `wp-content/themes` in your WordPress installation.
14
+ 3. Rename the folder to something that makes sense for your website (generally no spaces and all lowercase). You could keep the name `timber-starter-theme` but the point of a starter theme is to make it your own!
15
+ 4. Activate the theme in Appearance > Themes.
16
+ 5. Do your thing! And read [the docs](https://github.com/jarednova/timber/wiki).
17
+
18
+ ## What's here?
19
+
20
+ `static/` is where you can keep your static front-end scripts, styles, or images. In other words, your Sass files, JS files, fonts, and SVGs would live here.
21
+
22
+ `templates/` contains all of your Twig templates. These pretty much correspond 1 to 1 with the PHP files that respond to the WordPress template hierarchy. At the end of each PHP template, you'll notice a `Timber::render()` function whose first parameter is the Twig file where that data (or `$context`) will be used. Just an FYI.
23
+
24
+ `bin/` and `tests/` ... basically don't worry about (or remove) these unless you know what they are and want to.
25
+
26
+ ## Other Resources
27
+
28
+ The [main Timber Wiki](https://github.com/jarednova/timber/wiki) is super great, so reference those often. Also, check out these articles and projects for more info:
29
+
30
+ * [This branch](https://github.com/laras126/timber-starter-theme/tree/tackle-box) of the starter theme has some more example code with ACF and a slightly different set up.
31
+ * [Twig for Timber Cheatsheet](http://notlaura.com/the-twig-for-timber-cheatsheet/)
32
+ * [Timber and Twig Reignited My Love for WordPress](https://css-tricks.com/timber-and-twig-reignited-my-love-for-wordpress/) on CSS-Tricks
33
+ * [A real live Timber theme](https://github.com/laras126/yuling-theme).
34
+ * [Timber Video Tutorials](https://github.com/jarednova/timber/wiki/Video-Tutorials) and [an incomplete set of screencasts](https://www.youtube.com/playlist?list=PLuIlodXmVQ6pkqWyR6mtQ5gQZ6BrnuFx-) for building a Timber theme from scratch.
35
 
 
timber-starter-theme/archive.php CHANGED
@@ -16,25 +16,25 @@
16
 
17
  $templates = array( 'archive.twig', 'index.twig' );
18
 
19
- $data = Timber::get_context();
20
 
21
- $data['title'] = 'Archive';
22
  if ( is_day() ) {
23
- $data['title'] = 'Archive: '.get_the_date( 'D M Y' );
24
  } else if ( is_month() ) {
25
- $data['title'] = 'Archive: '.get_the_date( 'M Y' );
26
  } else if ( is_year() ) {
27
- $data['title'] = 'Archive: '.get_the_date( 'Y' );
28
  } else if ( is_tag() ) {
29
- $data['title'] = single_tag_title( '', false );
30
  } else if ( is_category() ) {
31
- $data['title'] = single_cat_title( '', false );
32
  array_unshift( $templates, 'archive-' . get_query_var( 'cat' ) . '.twig' );
33
  } else if ( is_post_type_archive() ) {
34
- $data['title'] = post_type_archive_title( '', false );
35
  array_unshift( $templates, 'archive-' . get_post_type() . '.twig' );
36
  }
37
 
38
- $data['posts'] = Timber::get_posts();
39
 
40
- Timber::render( $templates, $data );
16
 
17
  $templates = array( 'archive.twig', 'index.twig' );
18
 
19
+ $context = Timber::get_context();
20
 
21
+ $context['title'] = 'Archive';
22
  if ( is_day() ) {
23
+ $context['title'] = 'Archive: '.get_the_date( 'D M Y' );
24
  } else if ( is_month() ) {
25
+ $context['title'] = 'Archive: '.get_the_date( 'M Y' );
26
  } else if ( is_year() ) {
27
+ $context['title'] = 'Archive: '.get_the_date( 'Y' );
28
  } else if ( is_tag() ) {
29
+ $context['title'] = single_tag_title( '', false );
30
  } else if ( is_category() ) {
31
+ $context['title'] = single_cat_title( '', false );
32
  array_unshift( $templates, 'archive-' . get_query_var( 'cat' ) . '.twig' );
33
  } else if ( is_post_type_archive() ) {
34
+ $context['title'] = post_type_archive_title( '', false );
35
  array_unshift( $templates, 'archive-' . get_post_type() . '.twig' );
36
  }
37
 
38
+ $context['posts'] = Timber::get_posts();
39
 
40
+ Timber::render( $templates, $context );
timber-starter-theme/author.php CHANGED
@@ -10,11 +10,11 @@
10
  */
11
  global $wp_query;
12
 
13
- $data = Timber::get_context();
14
- $data['posts'] = Timber::get_posts();
15
  if ( isset( $wp_query->query_vars['author'] ) ) {
16
  $author = new TimberUser( $wp_query->query_vars['author'] );
17
- $data['author'] = $author;
18
- $data['title'] = 'Author Archives: ' . $author->name();
19
  }
20
- Timber::render( array( 'author.twig', 'archive.twig' ), $data );
10
  */
11
  global $wp_query;
12
 
13
+ $context = Timber::get_context();
14
+ $context['posts'] = Timber::get_posts();
15
  if ( isset( $wp_query->query_vars['author'] ) ) {
16
  $author = new TimberUser( $wp_query->query_vars['author'] );
17
+ $context['author'] = $author;
18
+ $context['title'] = 'Author Archives: ' . $author->name();
19
  }
20
+ Timber::render( array( 'author.twig', 'archive.twig' ), $context );
timber-starter-theme/composer.json CHANGED
@@ -9,6 +9,12 @@
9
  "email": "jared@upstatement.com"
10
  }
11
  ],
 
 
 
 
 
 
12
  "extra": {
13
  "installer-paths": {
14
  "../../plugins/{$name}/": [
@@ -18,12 +24,6 @@
18
  }
19
  },
20
  "require": {
21
- "wpackagist-plugin/timber-library": "0.21.*"
22
- },
23
- "repositories": [
24
- {
25
- "type": "composer",
26
- "url": "http://wpackagist.org"
27
- }
28
- ]
29
  }
9
  "email": "jared@upstatement.com"
10
  }
11
  ],
12
+ "repositories": [
13
+ {
14
+ "type": "composer",
15
+ "url": "https://wpackagist.org"
16
+ }
17
+ ],
18
  "extra": {
19
  "installer-paths": {
20
  "../../plugins/{$name}/": [
24
  }
25
  },
26
  "require": {
27
+ "wpackagist-plugin/timber-library": "0.22.*"
28
+ }
 
 
 
 
 
 
29
  }
timber-starter-theme/composer.lock CHANGED
@@ -4,22 +4,26 @@
4
  "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5
  "This file is @generated automatically"
6
  ],
7
- "hash": "46f646441e91cc10352e6d694cd94e86",
 
8
  "packages": [
9
  {
10
  "name": "composer/installers",
11
- "version": "v1.0.21",
12
  "source": {
13
  "type": "git",
14
  "url": "https://github.com/composer/installers.git",
15
- "reference": "d64e23fce42a4063d63262b19b8e7c0f3b5e4c45"
16
  },
17
  "dist": {
18
  "type": "zip",
19
- "url": "https://api.github.com/repos/composer/installers/zipball/d64e23fce42a4063d63262b19b8e7c0f3b5e4c45",
20
- "reference": "d64e23fce42a4063d63262b19b8e7c0f3b5e4c45",
21
  "shasum": ""
22
  },
 
 
 
23
  "replace": {
24
  "roundcube/plugin-installer": "*",
25
  "shama/baton": "*"
@@ -28,16 +32,16 @@
28
  "composer/composer": "1.0.*@dev",
29
  "phpunit/phpunit": "4.1.*"
30
  },
31
- "type": "composer-installer",
32
  "extra": {
33
- "class": "Composer\\Installers\\Installer",
34
  "branch-alias": {
35
  "dev-master": "1.0-dev"
36
  }
37
  },
38
  "autoload": {
39
- "psr-0": {
40
- "Composer\\Installers\\": "src/"
41
  }
42
  },
43
  "notification-url": "https://packagist.org/downloads/",
@@ -52,12 +56,14 @@
52
  }
53
  ],
54
  "description": "A multi-framework Composer library installer",
55
- "homepage": "http://composer.github.com/installers/",
56
  "keywords": [
57
  "Craft",
58
  "Dolibarr",
59
  "Hurad",
 
60
  "MODX Evo",
 
61
  "OXID",
62
  "SMF",
63
  "Thelia",
@@ -99,19 +105,19 @@
99
  "zend",
100
  "zikula"
101
  ],
102
- "time": "2015-02-18 17:17:01"
103
  },
104
  {
105
  "name": "wpackagist-plugin/timber-library",
106
- "version": "0.21.8",
107
  "source": {
108
  "type": "svn",
109
- "url": "http://plugins.svn.wordpress.org/timber-library/",
110
- "reference": "tags/0.21.8"
111
  },
112
  "dist": {
113
  "type": "zip",
114
- "url": "https://downloads.wordpress.org/plugin/timber-library.0.21.8.zip",
115
  "reference": null,
116
  "shasum": null
117
  },
4
  "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5
  "This file is @generated automatically"
6
  ],
7
+ "hash": "03c15e6c57468a60aa7ddc84134dfb24",
8
+ "content-hash": "ddbdaaec9f3f849d39a5aa1525e75c79",
9
  "packages": [
10
  {
11
  "name": "composer/installers",
12
+ "version": "v1.0.25",
13
  "source": {
14
  "type": "git",
15
  "url": "https://github.com/composer/installers.git",
16
+ "reference": "36e5b5843203d7f1cf6ffb0305a97e014387bd8e"
17
  },
18
  "dist": {
19
  "type": "zip",
20
+ "url": "https://api.github.com/repos/composer/installers/zipball/36e5b5843203d7f1cf6ffb0305a97e014387bd8e",
21
+ "reference": "36e5b5843203d7f1cf6ffb0305a97e014387bd8e",
22
  "shasum": ""
23
  },
24
+ "require": {
25
+ "composer-plugin-api": "^1.0"
26
+ },
27
  "replace": {
28
  "roundcube/plugin-installer": "*",
29
  "shama/baton": "*"
32
  "composer/composer": "1.0.*@dev",
33
  "phpunit/phpunit": "4.1.*"
34
  },
35
+ "type": "composer-plugin",
36
  "extra": {
37
+ "class": "Composer\\Installers\\Plugin",
38
  "branch-alias": {
39
  "dev-master": "1.0-dev"
40
  }
41
  },
42
  "autoload": {
43
+ "psr-4": {
44
+ "Composer\\Installers\\": "src/Composer/Installers"
45
  }
46
  },
47
  "notification-url": "https://packagist.org/downloads/",
56
  }
57
  ],
58
  "description": "A multi-framework Composer library installer",
59
+ "homepage": "https://composer.github.io/installers/",
60
  "keywords": [
61
  "Craft",
62
  "Dolibarr",
63
  "Hurad",
64
+ "ImageCMS",
65
  "MODX Evo",
66
+ "Mautic",
67
  "OXID",
68
  "SMF",
69
  "Thelia",
105
  "zend",
106
  "zikula"
107
  ],
108
+ "time": "2016-04-13 19:46:30"
109
  },
110
  {
111
  "name": "wpackagist-plugin/timber-library",
112
+ "version": "0.22.5",
113
  "source": {
114
  "type": "svn",
115
+ "url": "https://plugins.svn.wordpress.org/timber-library/",
116
+ "reference": "tags/0.22.5"
117
  },
118
  "dist": {
119
  "type": "zip",
120
+ "url": "https://downloads.wordpress.org/plugin/timber-library.0.22.5.zip",
121
  "reference": null,
122
  "shasum": null
123
  },
timber-starter-theme/footer.php CHANGED
@@ -1,8 +1,12 @@
1
  <?php
2
- /*
3
  * Third party plugins that hijack the theme will call wp_footer() to get the footer template.
4
  * We use this to end our output buffer (started in header.php) and render into the view/page-plugin.twig template.
 
 
 
5
  */
 
6
  $timberContext = $GLOBALS['timberContext'];
7
  if ( ! isset( $timberContext ) ) {
8
  throw new \Exception( 'Timber context not set in footer.' );
1
  <?php
2
+ /**
3
  * Third party plugins that hijack the theme will call wp_footer() to get the footer template.
4
  * We use this to end our output buffer (started in header.php) and render into the view/page-plugin.twig template.
5
+ *
6
+ * If you're not using a plugin that requries this behavior (ones that do include Events Calendar Pro and
7
+ * WooCommerce) you can delete this file and header.php
8
  */
9
+
10
  $timberContext = $GLOBALS['timberContext'];
11
  if ( ! isset( $timberContext ) ) {
12
  throw new \Exception( 'Timber context not set in footer.' );
timber-starter-theme/functions.php CHANGED
@@ -39,18 +39,18 @@ class StarterSite extends TimberSite {
39
  return $context;
40
  }
41
 
 
 
 
 
 
42
  function add_to_twig( $twig ) {
43
  /* this is where you can add your own fuctions to twig */
44
  $twig->addExtension( new Twig_Extension_StringLoader() );
45
- $twig->addFilter( 'myfoo', new Twig_Filter_Function( 'myfoo' ) );
46
  return $twig;
47
  }
48
 
49
  }
50
 
51
  new StarterSite();
52
-
53
- function myfoo( $text ) {
54
- $text .= ' bar!';
55
- return $text;
56
- }
39
  return $context;
40
  }
41
 
42
+ function myfoo( $text ) {
43
+ $text .= ' bar!';
44
+ return $text;
45
+ }
46
+
47
  function add_to_twig( $twig ) {
48
  /* this is where you can add your own fuctions to twig */
49
  $twig->addExtension( new Twig_Extension_StringLoader() );
50
+ $twig->addFilter('myfoo', new Twig_SimpleFilter('myfoo', array($this, 'myfoo')));
51
  return $twig;
52
  }
53
 
54
  }
55
 
56
  new StarterSite();
 
 
 
 
 
timber-starter-theme/header.php CHANGED
@@ -1,7 +1,11 @@
1
  <?php
2
- /*
3
  * Third party plugins that hijack the theme will call wp_head() to get the header template.
4
  * We use this to start our output buffer and render into the view/page-plugin.twig template in footer.php
 
 
 
5
  */
 
6
  $GLOBALS['timberContext'] = Timber::get_context();
7
  ob_start();
1
  <?php
2
+ /**
3
  * Third party plugins that hijack the theme will call wp_head() to get the header template.
4
  * We use this to start our output buffer and render into the view/page-plugin.twig template in footer.php
5
+ *
6
+ * If you're not using a plugin that requries this behavior (ones that do include Events Calendar Pro and
7
+ * WooCommerce) you can delete this file and footer.php
8
  */
9
+
10
  $GLOBALS['timberContext'] = Timber::get_context();
11
  ob_start();
timber-starter-theme/templates/base.twig CHANGED
@@ -14,22 +14,7 @@
14
  <a class="hdr-logo-link" href="/" rel="home">{{site.name}}</a>
15
  </h1>
16
  <nav id="nav-main" class="nav-main" role="navigation">
17
- <ul class="nav">
18
- {% for item in menu.get_items %}
19
- <li class="nav-item {{item.classes | join(' ')}}">
20
- <a class="nav-link" href="{{item.get_link}}">{{item.title}}</a>
21
- {% if item.get_children %}
22
- <ul class="nav nav-drop">
23
- {% for child in item.get_children %}
24
- <li class="nav-drop-item">
25
- <a class="nav-link" href="{{child.get_link}}">{{child.title}}</a>
26
- </li>
27
- {% endfor %}
28
- </ul>
29
- {% endif %}
30
- </li>
31
- {% endfor %}
32
- </ul>
33
  </nav><!-- #nav -->
34
  </div>
35
  {% endblock %}
@@ -56,4 +41,4 @@
56
  {{ function('wp_footer') }}
57
  {% endblock %}
58
  </body>
59
- </html>
14
  <a class="hdr-logo-link" href="/" rel="home">{{site.name}}</a>
15
  </h1>
16
  <nav id="nav-main" class="nav-main" role="navigation">
17
+ {% include "menu.twig" with {'menu': menu.get_items} %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  </nav><!-- #nav -->
19
  </div>
20
  {% endblock %}
41
  {{ function('wp_footer') }}
42
  {% endblock %}
43
  </body>
44
+ </html>
timber-starter-theme/templates/single-password.twig CHANGED
@@ -1,7 +1,7 @@
1
  {% extends "base.twig" %}
2
 
3
  {% block content %}
4
- <form class="password-form" action="/wp-login.php?action=postpass" method="post">
5
  <label for="pwbox-{{post.ID}}">Password:</label>
6
  <input class="password-box" name="post_password" id="pwbox-{{post.ID}}" type="password" placeholder="Password" size="20" maxlength="20" />
7
  <input class="password-btn" type="submit" name="Submit" value="Submit" />
1
  {% extends "base.twig" %}
2
 
3
  {% block content %}
4
+ <form class="password-form" action="{{site.link}}/wp-login.php?action=postpass" method="post">
5
  <label for="pwbox-{{post.ID}}">Password:</label>
6
  <input class="password-box" name="post_password" id="pwbox-{{post.ID}}" type="password" placeholder="Password" size="20" maxlength="20" />
7
  <input class="password-btn" type="submit" name="Submit" value="Submit" />
timber-starter-theme/tests/test-timber-starter-theme.php CHANGED
@@ -25,11 +25,11 @@
25
  }
26
 
27
  static function _setupStarterTheme(){
28
- $dest = WP_CONTENT_DIR.'/themes/timber-starter-theme/';
29
- $src = __DIR__.'/../../timber-starter-theme/';
30
  if (is_dir($src)) {
31
  self::_copyDirectory($src, $dest);
32
- switch_theme('timber-starter-theme');
33
  } else {
34
  echo 'no its not';
35
  }
25
  }
26
 
27
  static function _setupStarterTheme(){
28
+ $dest = WP_CONTENT_DIR.'/themes/starter-theme/';
29
+ $src = __DIR__.'/../../starter-theme/';
30
  if (is_dir($src)) {
31
  self::_copyDirectory($src, $dest);
32
+ switch_theme('starter-theme');
33
  } else {
34
  echo 'no its not';
35
  }
timber-starter-theme/views/menu.twig ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <ul>
2
+ {% for item in menu %}
3
+ <li class="{{item.classes | join(' ')}}">
4
+ <a href="{{item.get_link}}">{{item.title}}</a>
5
+ {% include "menu.twig" with {'menu': item.get_children} %}
6
+ </li>
7
+ {% endfor %}
8
+ </ul>
timber.php CHANGED
@@ -4,628 +4,17 @@ Plugin Name: Timber
4
  Description: The WordPress Timber Library allows you to write themes using the power Twig templates.
5
  Plugin URI: http://timber.upstatement.com
6
  Author: Jared Novack + Upstatement
7
- Version: 0.22.5
8
  Author URI: http://upstatement.com/
9
  */
10
-
11
- global $wp_version;
12
- global $timber;
13
-
14
  // we look for Composer files first in the plugins dir.
15
  // then in the wp-content dir (site install).
16
  // and finally in the current themes directories.
17
- if ( file_exists( $composer_autoload = __DIR__ . '/vendor/autoload.php' ) /* check in self */
18
  || file_exists( $composer_autoload = WP_CONTENT_DIR.'/vendor/autoload.php') /* check in wp-content */
19
  || file_exists( $composer_autoload = plugin_dir_path( __FILE__ ).'vendor/autoload.php') /* check in plugin directory */
20
  || file_exists( $composer_autoload = get_stylesheet_directory().'/vendor/autoload.php') /* check in child theme */
21
  || file_exists( $composer_autoload = get_template_directory().'/vendor/autoload.php') /* check in parent theme */
22
- ) {
23
  require_once $composer_autoload;
24
  }
25
-
26
- $timber = new Timber();
27
- Timber::$dirname = 'views';
28
-
29
- /**
30
- * Timber Class.
31
- *
32
- * Main class called Timber for this plugin.
33
- *
34
- * Usage:
35
- * $posts = Timber::get_posts();
36
- * $posts = Timber::get_posts('post_type = article')
37
- * $posts = Timber::get_posts(array('post_type' => 'article', 'category_name' => 'sports')); // uses wp_query format.
38
- * $posts = Timber::get_posts(array(23,24,35,67), 'InkwellArticle');
39
- *
40
- * $context = Timber::get_context(); // returns wp favorites!
41
- * $context['posts'] = $posts;
42
- * Timber::render('index.twig', $context);
43
- */
44
- class Timber {
45
-
46
- public static $locations;
47
- public static $dirname;
48
- public static $twig_cache = false;
49
- public static $cache = false;
50
- public static $auto_meta = true;
51
- public static $autoescape = false;
52
-
53
- /**
54
- * @codeCoverageIgnore
55
- */
56
- public function __construct() {
57
- if ( !defined('ABSPATH') ) {
58
- return;
59
- }
60
- $this->test_compatibility();
61
- $this->init_constants();
62
- $this->init();
63
- }
64
-
65
- /**
66
- * Tests whether we can use Timber
67
- * @codeCoverageIgnore
68
- * @return
69
- */
70
- protected function test_compatibility() {
71
- if ( is_admin() || $_SERVER['PHP_SELF'] == '/wp-login.php' ) {
72
- return;
73
- }
74
- if ( version_compare( phpversion(), '5.3.0', '<' ) && !is_admin() ) {
75
- trigger_error( 'Timber requires PHP 5.3.0 or greater. You have '.phpversion(), E_USER_ERROR );
76
- }
77
- if ( !class_exists( 'Twig_Autoloader' ) ) {
78
- trigger_error( 'You have not run "composer install" to download required dependencies for Timber, you can read more on https://github.com/jarednova/timber#installation', E_USER_ERROR );
79
- }
80
- }
81
-
82
- function init_constants() {
83
- defined( "TIMBER_LOC" ) or define( "TIMBER_LOC", realpath( __DIR__ ) );
84
- }
85
-
86
- /**
87
- * @codeCoverageIgnore
88
- */
89
- protected function init() {
90
- TimberTwig::init();
91
- TimberRoutes::init( $this );
92
- TimberImageHelper::init();
93
- TimberAdmin::init();
94
- TimberIntegrations::init();
95
- }
96
-
97
- /* Post Retrieval Routine
98
- ================================ */
99
-
100
- /**
101
- * Get post.
102
- *
103
- * @param mixed $query
104
- * @param string $PostClass
105
- * @return array|bool|null
106
- */
107
- public static function get_post( $query = false, $PostClass = 'TimberPost' ) {
108
- return TimberPostGetter::get_post( $query, $PostClass );
109
- }
110
-
111
- /**
112
- * Get posts.
113
- *
114
- * @param mixed $query
115
- * @param string $PostClass
116
- * @return array|bool|null
117
- */
118
- public static function get_posts( $query = false, $PostClass = 'TimberPost', $return_collection = false ) {
119
- return TimberPostGetter::get_posts( $query, $PostClass, $return_collection );
120
- }
121
-
122
- /**
123
- * Query post.
124
- *
125
- * @param mixed $query
126
- * @param string $PostClass
127
- * @return array|bool|null
128
- */
129
- public static function query_post( $query = false, $PostClass = 'TimberPost' ) {
130
- return TimberPostGetter::query_post( $query, $PostClass );
131
- }
132
-
133
- /**
134
- * Query posts.
135
- *
136
- * @param mixed $query
137
- * @param string $PostClass
138
- * @return array|bool|null
139
- */
140
- public static function query_posts( $query = false, $PostClass = 'TimberPost' ) {
141
- return TimberPostGetter::query_posts( $query, $PostClass );
142
- }
143
-
144
- /**
145
- * Get pids.
146
- *
147
- * @param array|string $query
148
- * @return array
149
- * @deprecated since 0.20.0
150
- */
151
- static function get_pids( $query = null ) {
152
- return TimberPostGetter::get_pids( $query );
153
- }
154
-
155
- /**
156
- * Get posts from loop.
157
- *
158
- * @param string $PostClass
159
- * @return array
160
- * @deprecated since 0.20.0
161
- */
162
- static function get_posts_from_loop( $PostClass ) {
163
- return TimberPostGetter::get_posts( $PostClass );
164
- }
165
-
166
- /**
167
- * Get posts from slug.
168
- *
169
- * @param string $slug
170
- * @param string $PostClass
171
- * @return array
172
- * @deprecated since 0.20.0
173
- */
174
- static function get_posts_from_slug( $slug, $PostClass = 'TimberPost' ) {
175
- return TimberPostGetter::get_posts( $slug, $PostClass );
176
- }
177
-
178
- /**
179
- * Get posts from WP_Query.
180
- *
181
- * @param array $query
182
- * @param string $PostClass
183
- * @return array
184
- * @deprecated since 0.20.0
185
- */
186
- static function get_posts_from_wp_query( $query = array(), $PostClass = 'TimberPost' ) {
187
- return TimberPostGetter::query_posts( $query, $PostClass );
188
- }
189
-
190
- /**
191
- * Get posts from array of ids.
192
- *
193
- * @param array $query
194
- * @param string $PostClass
195
- * @return array|null
196
- * @deprecated since 0.20.0
197
- */
198
- static function get_posts_from_array_of_ids( $query = array(), $PostClass = 'TimberPost' ) {
199
- return TimberPostGetter::get_posts( $query, $PostClass );
200
- }
201
-
202
- /**
203
- * Get pid.
204
- *
205
- * @param unknown $query
206
- * @return int
207
- * @deprecated since 0.20.0
208
- */
209
- static function get_pid( $query ) {
210
- $pids = TimberPostGetter::get_pids( $query );
211
- if ( is_array( $pids ) && count( $pids ) ) {
212
- return $pids[0];
213
- }
214
- }
215
-
216
- /**
217
- * WP_Query has posts.
218
- *
219
- * @return bool
220
- * @deprecated since 0.20.0
221
- */
222
- static function wp_query_has_posts() {
223
- return TimberPostGetter::wp_query_has_posts();
224
- }
225
-
226
- /* Term Retrieval
227
- ================================ */
228
-
229
- /**
230
- * Get terms.
231
- *
232
- * @param string|array $args
233
- * @param array $maybe_args
234
- * @param string $TermClass
235
- * @return mixed
236
- */
237
- public static function get_terms( $args = null, $maybe_args = array(), $TermClass = 'TimberTerm' ) {
238
- return TimberTermGetter::get_terms( $args, $maybe_args, $TermClass );
239
- }
240
-
241
- /* Site Retrieval
242
- ================================ */
243
-
244
- /**
245
- * Get sites.
246
- *
247
- * @param array|bool $blog_ids
248
- * @return array
249
- */
250
- public static function get_sites( $blog_ids = false ) {
251
- if ( !is_array( $blog_ids ) ) {
252
- global $wpdb;
253
- $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs ORDER BY blog_id ASC" );
254
- }
255
- $return = array();
256
- foreach ( $blog_ids as $blog_id ) {
257
- $return[] = new TimberSite( $blog_id );
258
- }
259
- return $return;
260
- }
261
-
262
-
263
- /* Template Setup and Display
264
- ================================ */
265
-
266
- /**
267
- * Get context.
268
- *
269
- * @return array
270
- */
271
- public static function get_context() {
272
- $data = array();
273
- $data['http_host'] = 'http://' . TimberURLHelper::get_host();
274
- $data['wp_title'] = TimberHelper::get_wp_title();
275
- $data['wp_head'] = TimberHelper::function_wrapper( 'wp_head' );
276
- $data['wp_footer'] = TimberHelper::function_wrapper( 'wp_footer' );
277
- $data['body_class'] = implode( ' ', get_body_class() );
278
-
279
- $data['site'] = new TimberSite();
280
- $data['theme'] = $data['site']->theme;
281
- //deprecated, these should be fetched via TimberSite or TimberTheme
282
- $data['theme_dir'] = WP_CONTENT_SUBDIR.str_replace( WP_CONTENT_DIR, '', get_stylesheet_directory() );
283
- $data['language_attributes'] = TimberHelper::function_wrapper( 'language_attributes' );
284
- $data['stylesheet_uri'] = get_stylesheet_uri();
285
- $data['template_uri'] = get_template_directory_uri();
286
-
287
- $data['posts'] = Timber::query_posts();
288
-
289
- //deprecated, this should be fetched via TimberMenu
290
- if ( function_exists( 'wp_nav_menu' ) ) {
291
- $locations = get_nav_menu_locations();
292
- if ( count( $locations ) ) {
293
- $data['wp_nav_menu'] = wp_nav_menu( array( 'container_class' => 'menu-header', 'echo' => false, 'menu_class' => 'nav-menu' ) );
294
- }
295
- }
296
- $data = apply_filters( 'timber_context', $data );
297
- $data = apply_filters( 'timber/context', $data );
298
- return $data;
299
- }
300
-
301
- /**
302
- * Compile function.
303
- *
304
- * @param array $filenames
305
- * @param array $data
306
- * @param bool $expires
307
- * @param string $cache_mode
308
- * @param bool $via_render
309
- * @return bool|string
310
- */
311
- public static function compile( $filenames, $data = array(), $expires = false, $cache_mode = TimberLoader::CACHE_USE_DEFAULT, $via_render = false ) {
312
- $caller = self::get_calling_script_dir();
313
- $caller_file = self::get_calling_script_file();
314
- $caller_file = apply_filters( 'timber_calling_php_file', $caller_file );
315
- $loader = new TimberLoader( $caller );
316
- $file = $loader->choose_template( $filenames );
317
- $output = '';
318
- if ( is_null( $data ) ) {
319
- $data = array();
320
- }
321
- if ( strlen( $file ) ) {
322
- if ( $via_render ) {
323
- $file = apply_filters( 'timber_render_file', $file );
324
- $data = apply_filters( 'timber_render_data', $data );
325
- } else {
326
- $file = apply_filters( 'timber_compile_file', $file );
327
- $data = apply_filters( 'timber_compile_data', $data );
328
- }
329
- $output = $loader->render( $file, $data, $expires, $cache_mode );
330
- }
331
- do_action( 'timber_compile_done' );
332
- return $output;
333
- }
334
-
335
- /**
336
- * Compile string.
337
- *
338
- * @param string $string a string with twig variables.
339
- * @param array $data an array with data in it.
340
- * @return bool|string
341
- */
342
- public static function compile_string( $string, $data = array() ) {
343
- $dummy_loader = new TimberLoader();
344
- $dummy_loader->get_twig();
345
- $loader = new Twig_Loader_String();
346
- $twig = new Twig_Environment( $loader );
347
- $twig = apply_filters( 'timber/twig/filters', $twig );
348
- $twig = apply_filters( 'twig_apply_filters', $twig );
349
- return $twig->render( $string, $data );
350
- }
351
-
352
- /**
353
- * Fetch function.
354
- *
355
- * @param array $filenames
356
- * @param array $data
357
- * @param bool $expires
358
- * @param string $cache_mode
359
- * @return bool|string
360
- */
361
- public static function fetch( $filenames, $data = array(), $expires = false, $cache_mode = TimberLoader::CACHE_USE_DEFAULT ) {
362
- if ( $expires === true ) {
363
- //if this is reading as true; the user probably is using the old $echo param
364
- //so we should move all vars up by a spot
365
- $expires = $cache_mode;
366
- $cache_mode = TimberLoader::CACHE_USE_DEFAULT;
367
- }
368
- $output = self::compile( $filenames, $data, $expires, $cache_mode, true );
369
- $output = apply_filters( 'timber_compile_result', $output );
370
- return $output;
371
- }
372
-
373
- /**
374
- * Render function.
375
- *
376
- * @param array $filenames
377
- * @param array $data
378
- * @param bool $expires
379
- * @param string $cache_mode
380
- * @return bool|string
381
- */
382
- public static function render( $filenames, $data = array(), $expires = false, $cache_mode = TimberLoader::CACHE_USE_DEFAULT ) {
383
- $output = static::fetch( $filenames, $data, $expires, $cache_mode );
384
- echo $output;
385
- return $output;
386
- }
387
-
388
- /**
389
- * Render string.
390
- *
391
- * @param string $string a string with twig variables.
392
- * @param array $data an array with data in it.
393
- * @return bool|string
394
- */
395
- public static function render_string( $string, $data = array() ) {
396
- $compiled = self::compile_string( $string, $data );
397
- echo $compiled;
398
- return $compiled;
399
- }
400
-
401
-
402
- /* Sidebar
403
- ================================ */
404
-
405
- /**
406
- * Get sidebar.
407
- *
408
- * @param string $sidebar
409
- * @param array $data
410
- * @return bool|string
411
- */
412
- public static function get_sidebar( $sidebar = '', $data = array() ) {
413
- if ( $sidebar == '' ) {
414
- $sidebar = 'sidebar.php';
415
- }
416
- if ( strstr( strtolower( $sidebar ), '.php' ) ) {
417
- return self::get_sidebar_from_php( $sidebar, $data );
418
- }
419
- return self::compile( $sidebar, $data );
420
- }
421
-
422
- /**
423
- * Get sidebar from PHP
424
- *
425
- * @param string $sidebar
426
- * @param array $data
427
- * @return string
428
- */
429
- public static function get_sidebar_from_php( $sidebar = '', $data ) {
430
- $caller = self::get_calling_script_dir();
431
- $loader = new TimberLoader();
432
- $uris = $loader->get_locations( $caller );
433
- ob_start();
434
- $found = false;
435
- foreach ( $uris as $uri ) {
436
- if ( file_exists( trailingslashit( $uri ) . $sidebar ) ) {
437
- include trailingslashit( $uri ) . $sidebar;
438
- $found = true;
439
- break;
440
- }
441
- }
442
- if ( !$found ) {
443
- TimberHelper::error_log( 'error loading your sidebar, check to make sure the file exists' );
444
- }
445
- $ret = ob_get_contents();
446
- ob_end_clean();
447
- return $ret;
448
- }
449
-
450
- /* Widgets
451
- ================================ */
452
-
453
- /**
454
- * Get widgets.
455
- *
456
- * @param int $widget_id
457
- * @return TimberFunctionWrapper
458
- */
459
- public static function get_widgets( $widget_id ) {
460
- return TimberHelper::function_wrapper( 'dynamic_sidebar', array( $widget_id ), true );
461
- }
462
-
463
-
464
- /* Routes
465
- ================================ */
466
-
467
- /**
468
- * Add route.
469
- *
470
- * @param string $route
471
- * @param callable $callback
472
- * @param array $args
473
- * @deprecated since 0.20.0
474
- */
475
- public static function add_route( $route, $callback, $args = array() ) {
476
- Routes::map( $route, $callback, $args );
477
- }
478
-
479
- /**
480
- * @deprecated since 0.22.2
481
- */
482
- public function cancel_query() {
483
- add_action( 'posts_request', array( $this, 'cancel_query_posts_request' ) );
484
- }
485
-
486
- /**
487
- * @deprecated since 0.22.2
488
- */
489
- function cancel_query_posts_request() {
490
- if ( is_main_query() ) {
491
- wp_reset_query();
492
- }
493
- }
494
-
495
- /**
496
- * Load template.
497
- *
498
- * @deprecated since 0.20.0
499
- */
500
- public static function load_template( $template, $query = false, $status_code = 200, $tparams = false ) {
501
- return Routes::load( $template, $tparams, $query, $status_code );
502
- }
503
-
504
- /**
505
- * Load view.
506
- *
507
- * @deprecated since 0.20.2
508
- */
509
- public static function load_view( $template, $query = false, $status_code = 200, $tparams = false ) {
510
- return Routes::load( $template, $tparams, $query, $status_code );
511
- }
512
-
513
-
514
- /* Pagination
515
- ================================ */
516
-
517
- /**
518
- * Get pagination.
519
- *
520
- * @param array $prefs
521
- * @return array mixed
522
- */
523
- public static function get_pagination( $prefs = array() ) {
524
- global $wp_query;
525
- global $paged;
526
- global $wp_rewrite;
527
- $args = array();
528
- $args['total'] = ceil( $wp_query->found_posts / $wp_query->query_vars['posts_per_page'] );
529
- if ( $wp_rewrite->using_permalinks() ) {
530
- $url = explode( '?', get_pagenum_link( 0 ) );
531
- if ( isset( $url[1] ) ) {
532
- parse_str( $url[1], $query );
533
- $args['add_args'] = $query;
534
- }
535
- $args['format'] = 'page/%#%';
536
- $args['base'] = trailingslashit( $url[0] ).'%_%';
537
- } else {
538
- $big = 999999999;
539
- $args['base'] = str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) );
540
- }
541
- $args['type'] = 'array';
542
- $args['current'] = max( 1, get_query_var( 'paged' ) );
543
- $args['mid_size'] = max( 9 - $args['current'], 3 );
544
- if ( is_int( $prefs ) ) {
545
- $args['mid_size'] = $prefs - 2;
546
- } else {
547
- $args = array_merge( $args, $prefs );
548
- }
549
- $data = array();
550
- $data['current'] = $args['current'];
551
- $data['total'] = $args['total'];
552
- $data['pages'] = TimberHelper::paginate_links( $args );
553
- $next = get_next_posts_page_link( $args['total'] );
554
- if ( $next ) {
555
- $data['next'] = array( 'link' => untrailingslashit( $next ), 'class' => 'page-numbers next' );
556
- }
557
- $prev = previous_posts( false );
558
- if ( $prev ) {
559
- $data['prev'] = array( 'link' => untrailingslashit( $prev ), 'class' => 'page-numbers prev' );
560
- }
561
- if ( $paged < 2 ) {
562
- $data['prev'] = '';
563
- }
564
- return $data;
565
- }
566
-
567
- /* Utility
568
- ================================ */
569
-
570
- /**
571
- * Get calling script path.
572
- *
573
- * @param int $offset
574
- * @return string
575
- * @deprecated since 0.20.0
576
- */
577
- public static function get_calling_script_path( $offset = 0 ) {
578
- $dir = self::get_calling_script_dir( $offset );
579
- return str_replace( ABSPATH, '', realpath( $dir ) );
580
- }
581
-
582
- /**
583
- * Get calling script dir.
584
- *
585
- * @return string
586
- */
587
- public static function get_calling_script_dir( $offset = 0 ) {
588
- $caller = self::get_calling_script_file( $offset );
589
- if ( !is_null( $caller ) ) {
590
- $pathinfo = pathinfo( $caller );
591
- $dir = $pathinfo['dirname'];
592
- return $dir;
593
- }
594
- }
595
-
596
- /**
597
- * Get calling script file.
598
- *
599
- * @param int $offset
600
- * @return string|null
601
- * @deprecated since 0.20.0
602
- */
603
- public static function get_calling_script_file( $offset = 0 ) {
604
- $caller = null;
605
- $backtrace = debug_backtrace();
606
- $i = 0;
607
- foreach ( $backtrace as $trace ) {
608
- if ( array_key_exists('file', $trace) && $trace['file'] != __FILE__ ) {
609
- $caller = $trace['file'];
610
- break;
611
- }
612
- $i++;
613
- }
614
- if ( $offset ) {
615
- $caller = $backtrace[$i + $offset]['file'];
616
- }
617
- return $caller;
618
- }
619
-
620
- /**
621
- * Is post class or class map.
622
- *
623
- * @param string|array $args
624
- * @return bool
625
- * @deprecated since 0.20.0
626
- */
627
- public static function is_post_class_or_class_map( $args ) {
628
- return TimberPostGetter::is_post_class_or_class_map( $args );
629
- }
630
-
631
- }
4
  Description: The WordPress Timber Library allows you to write themes using the power Twig templates.
5
  Plugin URI: http://timber.upstatement.com
6
  Author: Jared Novack + Upstatement
7
+ Version: 1.0.0
8
  Author URI: http://upstatement.com/
9
  */
 
 
 
 
10
  // we look for Composer files first in the plugins dir.
11
  // then in the wp-content dir (site install).
12
  // and finally in the current themes directories.
13
+ if ( file_exists( $composer_autoload = __DIR__ . '/vendor/autoload.php' ) /* check in self */
14
  || file_exists( $composer_autoload = WP_CONTENT_DIR.'/vendor/autoload.php') /* check in wp-content */
15
  || file_exists( $composer_autoload = plugin_dir_path( __FILE__ ).'vendor/autoload.php') /* check in plugin directory */
16
  || file_exists( $composer_autoload = get_stylesheet_directory().'/vendor/autoload.php') /* check in child theme */
17
  || file_exists( $composer_autoload = get_template_directory().'/vendor/autoload.php') /* check in parent theme */
18
+ ) {
19
  require_once $composer_autoload;
20
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/asm89/twig-cache-extension/.travis.yml CHANGED
@@ -13,6 +13,6 @@ php:
13
  - 7.0
14
  - hhvm
15
 
16
- before_script: composer install --dev
17
 
18
  script: phpunit
13
  - 7.0
14
  - hhvm
15
 
16
+ before_script: composer install
17
 
18
  script: phpunit
vendor/asm89/twig-cache-extension/composer.json CHANGED
@@ -30,7 +30,7 @@
30
  },
31
  "extra": {
32
  "branch-alias": {
33
- "dev-master": "1.0-dev"
34
  }
35
  }
36
  }
30
  },
31
  "extra": {
32
  "branch-alias": {
33
+ "dev-master": "1.1-dev"
34
  }
35
  }
36
  }
vendor/asm89/twig-cache-extension/lib/Asm89/Twig/CacheExtension/Node/CacheNode.php CHANGED
@@ -27,7 +27,7 @@ class CacheNode extends \Twig_Node
27
  * @param integer $lineno
28
  * @param string $tag
29
  */
30
- public function __construct(\Twig_Node_Expression $annotation, \Twig_Node_Expression $keyInfo, \Twig_NodeInterface $body, $lineno, $tag = null)
31
  {
32
  parent::__construct(array('key_info' => $keyInfo, 'body' => $body, 'annotation' => $annotation), array(), $lineno, $tag);
33
  }
27
  * @param integer $lineno
28
  * @param string $tag
29
  */
30
+ public function __construct(\Twig_Node_Expression $annotation, \Twig_Node_Expression $keyInfo, \Twig_Node $body, $lineno, $tag = null)
31
  {
32
  parent::__construct(array('key_info' => $keyInfo, 'body' => $body, 'annotation' => $annotation), array(), $lineno, $tag);
33
  }
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
- return ComposerAutoloaderInita300217f36ff83f02ad639201f3add11::getLoader();
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit977d5902edc1085fc45cbf70be497654::getLoader();
vendor/composer/ClassLoader.php CHANGED
@@ -13,9 +13,7 @@
13
  namespace Composer\Autoload;
14
 
15
  /**
16
- * ClassLoader implements a PSR-0 class loader
17
- *
18
- * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
19
  *
20
  * $loader = new \Composer\Autoload\ClassLoader();
21
  *
@@ -39,6 +37,8 @@ namespace Composer\Autoload;
39
  *
40
  * @author Fabien Potencier <fabien@symfony.com>
41
  * @author Jordi Boggiano <j.boggiano@seld.be>
 
 
42
  */
43
  class ClassLoader
44
  {
@@ -147,7 +147,7 @@ class ClassLoader
147
  * appending or prepending to the ones previously set for this namespace.
148
  *
149
  * @param string $prefix The prefix/namespace, with trailing '\\'
150
- * @param array|string $paths The PSR-0 base directories
151
  * @param bool $prepend Whether to prepend the directories
152
  *
153
  * @throws \InvalidArgumentException
13
  namespace Composer\Autoload;
14
 
15
  /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
 
 
17
  *
18
  * $loader = new \Composer\Autoload\ClassLoader();
19
  *
37
  *
38
  * @author Fabien Potencier <fabien@symfony.com>
39
  * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
  */
43
  class ClassLoader
44
  {
147
  * appending or prepending to the ones previously set for this namespace.
148
  *
149
  * @param string $prefix The prefix/namespace, with trailing '\\'
150
+ * @param array|string $paths The PSR-4 base directories
151
  * @param bool $prepend Whether to prepend the directories
152
  *
153
  * @throws \InvalidArgumentException
vendor/composer/LICENSE CHANGED
@@ -1,5 +1,5 @@
1
 
2
- Copyright (c) 2015 Nils Adermann, Jordi Boggiano
3
 
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
  of this software and associated documentation files (the "Software"), to deal
1
 
2
+ Copyright (c) 2016 Nils Adermann, Jordi Boggiano
3
 
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
  of this software and associated documentation files (the "Software"), to deal
vendor/composer/autoload_classmap.php CHANGED
@@ -6,43 +6,5 @@ $vendorDir = dirname(dirname(__FILE__));
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
9
- 'ACFTimber' => $baseDir . '/lib/integrations/acf-timber.php',
10
  'AltoRouter' => $vendorDir . '/altorouter/altorouter/AltoRouter.php',
11
- 'TimberAdmin' => $baseDir . '/lib/timber-admin.php',
12
- 'TimberArchives' => $baseDir . '/lib/timber-archives.php',
13
- 'TimberCommand' => $baseDir . '/lib/integrations/timber-command.php',
14
- 'TimberComment' => $baseDir . '/lib/timber-comment.php',
15
- 'TimberCore' => $baseDir . '/lib/timber-core.php',
16
- 'TimberCoreInterface' => $baseDir . '/lib/timber-core-interface.php',
17
- 'TimberFunctionWrapper' => $baseDir . '/lib/timber-function-wrapper.php',
18
- 'TimberHelper' => $baseDir . '/lib/timber-helper.php',
19
- 'TimberImage' => $baseDir . '/lib/timber-image.php',
20
- 'TimberImageHelper' => $baseDir . '/lib/timber-image-helper.php',
21
- 'TimberImageOperation' => $baseDir . '/lib/image/timber-image-operation.php',
22
- 'TimberImageOperationLetterbox' => $baseDir . '/lib/image/timber-image-operation-letterbox.php',
23
- 'TimberImageOperationResize' => $baseDir . '/lib/image/timber-image-operation-resize.php',
24
- 'TimberImageOperationRetina' => $baseDir . '/lib/image/timber-image-operation-retina.php',
25
- 'TimberImageOperationToJpg' => $baseDir . '/lib/image/timber-image-operation-tojpg.php',
26
- 'TimberIntegrations' => $baseDir . '/lib/timber-integrations.php',
27
- 'TimberLoader' => $baseDir . '/lib/timber-loader.php',
28
- 'TimberMenu' => $baseDir . '/lib/timber-menu.php',
29
- 'TimberMenuItem' => $baseDir . '/lib/timber-menu-item.php',
30
- 'TimberPage' => $baseDir . '/lib/timber-page.php',
31
- 'TimberPost' => $baseDir . '/lib/timber-post.php',
32
- 'TimberPostGetter' => $baseDir . '/lib/timber-post-getter.php',
33
- 'TimberPostsCollection' => $baseDir . '/lib/timber-posts-collection.php',
34
- 'TimberPostsIterator' => $baseDir . '/lib/timber-posts-collection.php',
35
- 'TimberQueryIterator' => $baseDir . '/lib/timber-query-iterator.php',
36
- 'TimberRoutes' => $baseDir . '/lib/timber-routes.php',
37
- 'TimberSite' => $baseDir . '/lib/timber-site.php',
38
- 'TimberTerm' => $baseDir . '/lib/timber-term.php',
39
- 'TimberTermGetter' => $baseDir . '/lib/timber-term-getter.php',
40
- 'TimberTheme' => $baseDir . '/lib/timber-theme.php',
41
- 'TimberTwig' => $baseDir . '/lib/timber-twig.php',
42
- 'TimberURLHelper' => $baseDir . '/lib/timber-url-helper.php',
43
- 'TimberUser' => $baseDir . '/lib/timber-user.php',
44
- 'Timber\\Cache\\KeyGenerator' => $baseDir . '/lib/cache/KeyGenerator.php',
45
- 'Timber\\Cache\\TimberKeyGeneratorInterface' => $baseDir . '/lib/cache/TimberKeyGeneratorInterface.php',
46
- 'Timber\\Cache\\WPObjectCacheAdapter' => $baseDir . '/lib/cache/WPObjectCacheAdapter.php',
47
- 'Timber_WP_CLI_Command' => $baseDir . '/lib/integrations/wpcli-timber.php',
48
  );
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
 
9
  'AltoRouter' => $vendorDir . '/altorouter/altorouter/AltoRouter.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  );
vendor/composer/autoload_files.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_files.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'cb78221880aa21d756cc44a8539bb425' => $baseDir . '/init.php',
10
+ );
vendor/composer/autoload_namespaces.php CHANGED
@@ -8,5 +8,4 @@ $baseDir = dirname($vendorDir);
8
  return array(
9
  'Twig_' => array($vendorDir . '/twig/twig/lib'),
10
  'Routes' => array($vendorDir . '/upstatement/routes'),
11
- 'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src'),
12
  );
8
  return array(
9
  'Twig_' => array($vendorDir . '/twig/twig/lib'),
10
  'Routes' => array($vendorDir . '/upstatement/routes'),
 
11
  );
vendor/composer/autoload_psr4.php CHANGED
@@ -6,5 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
 
 
9
  '' => array($vendorDir . '/asm89/twig-cache-extension/lib'),
10
  );
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
9
+ 'Timber\\' => array($baseDir . '/lib'),
10
+ 'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src/Composer/Installers'),
11
  '' => array($vendorDir . '/asm89/twig-cache-extension/lib'),
12
  );
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInita300217f36ff83f02ad639201f3add11
6
  {
7
  private static $loader;
8
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInita300217f36ff83f02ad639201f3add11
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInita300217f36ff83f02ad639201f3add11', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInita300217f36ff83f02ad639201f3add11', 'loadClassLoader'));
25
 
26
  $map = require __DIR__ . '/autoload_namespaces.php';
27
  foreach ($map as $namespace => $path) {
@@ -40,11 +40,20 @@ class ComposerAutoloaderInita300217f36ff83f02ad639201f3add11
40
 
41
  $loader->register(true);
42
 
 
 
 
 
 
43
  return $loader;
44
  }
45
  }
46
 
47
- function composerRequirea300217f36ff83f02ad639201f3add11($file)
48
  {
49
- require $file;
 
 
 
 
50
  }
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit977d5902edc1085fc45cbf70be497654
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInit977d5902edc1085fc45cbf70be497654', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit977d5902edc1085fc45cbf70be497654', 'loadClassLoader'));
25
 
26
  $map = require __DIR__ . '/autoload_namespaces.php';
27
  foreach ($map as $namespace => $path) {
40
 
41
  $loader->register(true);
42
 
43
+ $includeFiles = require __DIR__ . '/autoload_files.php';
44
+ foreach ($includeFiles as $fileIdentifier => $file) {
45
+ composerRequire977d5902edc1085fc45cbf70be497654($fileIdentifier, $file);
46
+ }
47
+
48
  return $loader;
49
  }
50
  }
51
 
52
+ function composerRequire977d5902edc1085fc45cbf70be497654($fileIdentifier, $file)
53
  {
54
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
55
+ require $file;
56
+
57
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
58
+ }
59
  }
vendor/composer/installed.json CHANGED
@@ -1,21 +1,21 @@
1
  [
2
  {
3
  "name": "composer/installers",
4
- "version": "v1.0.22",
5
- "version_normalized": "1.0.22.0",
6
  "source": {
7
  "type": "git",
8
  "url": "https://github.com/composer/installers.git",
9
- "reference": "bd9b14f094c89c8b5804a4e41edeb7853bb85046"
10
  },
11
  "dist": {
12
  "type": "zip",
13
- "url": "https://api.github.com/repos/composer/installers/zipball/bd9b14f094c89c8b5804a4e41edeb7853bb85046",
14
- "reference": "bd9b14f094c89c8b5804a4e41edeb7853bb85046",
15
  "shasum": ""
16
  },
17
  "require": {
18
- "composer-plugin-api": "1.0.0"
19
  },
20
  "replace": {
21
  "roundcube/plugin-installer": "*",
@@ -25,7 +25,7 @@
25
  "composer/composer": "1.0.*@dev",
26
  "phpunit/phpunit": "4.1.*"
27
  },
28
- "time": "2015-10-29 23:28:48",
29
  "type": "composer-plugin",
30
  "extra": {
31
  "class": "Composer\\Installers\\Plugin",
@@ -35,8 +35,8 @@
35
  },
36
  "installation-source": "dist",
37
  "autoload": {
38
- "psr-0": {
39
- "Composer\\Installers\\": "src/"
40
  }
41
  },
42
  "notification-url": "https://packagist.org/downloads/",
@@ -51,12 +51,14 @@
51
  }
52
  ],
53
  "description": "A multi-framework Composer library installer",
54
- "homepage": "http://composer.github.com/installers/",
55
  "keywords": [
56
  "Craft",
57
  "Dolibarr",
58
  "Hurad",
 
59
  "MODX Evo",
 
60
  "OXID",
61
  "SMF",
62
  "Thelia",
@@ -101,17 +103,17 @@
101
  },
102
  {
103
  "name": "twig/twig",
104
- "version": "v1.23.1",
105
- "version_normalized": "1.23.1.0",
106
  "source": {
107
  "type": "git",
108
  "url": "https://github.com/twigphp/Twig.git",
109
- "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6"
110
  },
111
  "dist": {
112
  "type": "zip",
113
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
114
- "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
115
  "shasum": ""
116
  },
117
  "require": {
@@ -121,11 +123,11 @@
121
  "symfony/debug": "~2.7",
122
  "symfony/phpunit-bridge": "~2.7"
123
  },
124
- "time": "2015-11-05 12:49:06",
125
  "type": "library",
126
  "extra": {
127
  "branch-alias": {
128
- "dev-master": "1.23-dev"
129
  }
130
  },
131
  "installation-source": "dist",
@@ -164,17 +166,17 @@
164
  },
165
  {
166
  "name": "asm89/twig-cache-extension",
167
- "version": "1.1.0",
168
- "version_normalized": "1.1.0.0",
169
  "source": {
170
  "type": "git",
171
  "url": "https://github.com/asm89/twig-cache-extension.git",
172
- "reference": "127410728db193a88254c399e3455274fc05fa5d"
173
  },
174
  "dist": {
175
  "type": "zip",
176
- "url": "https://api.github.com/repos/asm89/twig-cache-extension/zipball/127410728db193a88254c399e3455274fc05fa5d",
177
- "reference": "127410728db193a88254c399e3455274fc05fa5d",
178
  "shasum": ""
179
  },
180
  "require": {
@@ -184,11 +186,11 @@
184
  "require-dev": {
185
  "doctrine/cache": "~1.0"
186
  },
187
- "time": "2015-10-17 11:55:02",
188
  "type": "library",
189
  "extra": {
190
  "branch-alias": {
191
- "dev-master": "1.0-dev"
192
  }
193
  },
194
  "installation-source": "dist",
1
  [
2
  {
3
  "name": "composer/installers",
4
+ "version": "v1.0.25",
5
+ "version_normalized": "1.0.25.0",
6
  "source": {
7
  "type": "git",
8
  "url": "https://github.com/composer/installers.git",
9
+ "reference": "36e5b5843203d7f1cf6ffb0305a97e014387bd8e"
10
  },
11
  "dist": {
12
  "type": "zip",
13
+ "url": "https://api.github.com/repos/composer/installers/zipball/36e5b5843203d7f1cf6ffb0305a97e014387bd8e",
14
+ "reference": "36e5b5843203d7f1cf6ffb0305a97e014387bd8e",
15
  "shasum": ""
16
  },
17
  "require": {
18
+ "composer-plugin-api": "^1.0"
19
  },
20
  "replace": {
21
  "roundcube/plugin-installer": "*",
25
  "composer/composer": "1.0.*@dev",
26
  "phpunit/phpunit": "4.1.*"
27
  },
28
+ "time": "2016-04-13 19:46:30",
29
  "type": "composer-plugin",
30
  "extra": {
31
  "class": "Composer\\Installers\\Plugin",
35
  },
36
  "installation-source": "dist",
37
  "autoload": {
38
+ "psr-4": {
39
+ "Composer\\Installers\\": "src/Composer/Installers"
40
  }
41
  },
42
  "notification-url": "https://packagist.org/downloads/",
51
  }
52
  ],
53
  "description": "A multi-framework Composer library installer",
54
+ "homepage": "https://composer.github.io/installers/",
55
  "keywords": [
56
  "Craft",
57
  "Dolibarr",
58
  "Hurad",
59
+ "ImageCMS",
60
  "MODX Evo",
61
+ "Mautic",
62
  "OXID",
63
  "SMF",
64
  "Thelia",
103
  },
104
  {
105
  "name": "twig/twig",
106
+ "version": "v1.24.0",
107
+ "version_normalized": "1.24.0.0",
108
  "source": {
109
  "type": "git",
110
  "url": "https://github.com/twigphp/Twig.git",
111
+ "reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8"
112
  },
113
  "dist": {
114
  "type": "zip",
115
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8",
116
+ "reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8",
117
  "shasum": ""
118
  },
119
  "require": {
123
  "symfony/debug": "~2.7",
124
  "symfony/phpunit-bridge": "~2.7"
125
  },
126
+ "time": "2016-01-25 21:22:18",
127
  "type": "library",
128
  "extra": {
129
  "branch-alias": {
130
+ "dev-master": "1.24-dev"
131
  }
132
  },
133
  "installation-source": "dist",
166
  },
167
  {
168
  "name": "asm89/twig-cache-extension",
169
+ "version": "1.2.0",
170
+ "version_normalized": "1.2.0.0",
171
  "source": {
172
  "type": "git",
173
  "url": "https://github.com/asm89/twig-cache-extension.git",
174
+ "reference": "76a801d6da8e1a1015af974d93df4ade0dda7267"
175
  },
176
  "dist": {
177
  "type": "zip",
178
+ "url": "https://api.github.com/repos/asm89/twig-cache-extension/zipball/76a801d6da8e1a1015af974d93df4ade0dda7267",
179
+ "reference": "76a801d6da8e1a1015af974d93df4ade0dda7267",
180
  "shasum": ""
181
  },
182
  "require": {
186
  "require-dev": {
187
  "doctrine/cache": "~1.0"
188
  },
189
+ "time": "2016-02-14 13:52:38",
190
  "type": "library",
191
  "extra": {
192
  "branch-alias": {
193
+ "dev-master": "1.1-dev"
194
  }
195
  },
196
  "installation-source": "dist",
vendor/composer/installers/.travis.yml CHANGED
@@ -18,4 +18,4 @@ before_script:
18
  - composer install
19
 
20
  script:
21
- - phpunit
18
  - composer install
19
 
20
  script:
21
+ - composer test
vendor/composer/installers/README.md CHANGED
@@ -48,20 +48,22 @@ is not needed to install packages with these frameworks:
48
  | Croogo | `croogo-plugin`<br>`croogo-theme`
49
  | DokuWiki | `dokuwiki-plugin`<br>`dokuwiki-template`
50
  | Dolibarr | `dolibarr-module`
51
- | Drupal | <b>`drupal-module`<br>`drupal-theme`</b><br>`drupal-library`<br>`drupal-profile`<br>`drupal-drush`
52
  | Elgg | `elgg-plugin`
53
  | FuelPHP v1.x | `fuel-module`<br>`fuel-package`<br/>`fuel-theme`
54
  | FuelPHP v2.x | `fuelphp-component`
55
  | Grav | `grav-plugin`<br>`grav-theme`
56
  | Hurad | `hurad-plugin`<br>`hurad-theme`
 
57
  | Joomla | `joomla-component`<br>`joomla-module`<br>`joomla-template`<br>`joomla-plugin`<br>`joomla-library`
58
- | Kirby | **`kirby-plugin`**
59
  | KodiCMS | `kodicms-plugin`<br>`kodicms-media`
60
  | Kohana | **`kohana-module`**
61
  | Laravel | `laravel-library`
62
  | Lithium | **`lithium-library`<br>`lithium-source`**
63
  | Magento | `magento-library`<br>`magento-skin`<br>`magento-theme`
64
  | Mako | `mako-package`
 
65
  | MODX Evo | `modxevo-snippet`<br>`modxevo-plugin`<br>`modxevo-module`<br>`modxevo-template`<br>`modxevo-lib`
66
  | MediaWiki | `mediawiki-extension`
67
  | October | **`october-module`<br>`october-plugin`<br>`october-theme`**
@@ -81,7 +83,7 @@ is not needed to install packages with these frameworks:
81
  | symfony1 | **`symfony1-plugin`**
82
  | Tusk | `tusk-task`<br>`tusk-command`<br>`tusk-asset`
83
  | TYPO3 Flow | `typo3-flow-package`<br>`typo3-flow-framework`<br>`typo3-flow-plugin`<br>`typo3-flow-site`<br>`typo3-flow-boilerplate`<br>`typo3-flow-build`
84
- | TYPO3 CMS | `typo3-cms-extension`
85
  | Wolf CMS | `wolfcms-plugin`
86
  | WordPress | <b>`wordpress-plugin`<br>`wordpress-theme`</b><br>`wordpress-muplugin`
87
  | Zend | `zend-library`<br>`zend-extra`<br>`zend-module`
@@ -137,7 +139,20 @@ A package type can have a custom installation path with a `type:` prefix.
137
  }
138
  ```
139
 
140
- This would use your custom path for each of the listed packages. The available
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  variables to use in your paths are: `{$name}`, `{$vendor}`, `{$type}`.
142
 
143
  ## Custom Install Names
@@ -191,3 +206,5 @@ It has been proposed many times. Even implemented once early on and then
191
  removed. `installers` won't do this because it would allow a single package
192
  author to wipe out entire folders without the user's consent. That user would
193
  then come here to yell at us.
 
 
48
  | Croogo | `croogo-plugin`<br>`croogo-theme`
49
  | DokuWiki | `dokuwiki-plugin`<br>`dokuwiki-template`
50
  | Dolibarr | `dolibarr-module`
51
+ | Drupal | <b>`drupal-core`<br>`drupal-module`<br>`drupal-theme`</b><br>`drupal-library`<br>`drupal-profile`<br>`drupal-drush`
52
  | Elgg | `elgg-plugin`
53
  | FuelPHP v1.x | `fuel-module`<br>`fuel-package`<br/>`fuel-theme`
54
  | FuelPHP v2.x | `fuelphp-component`
55
  | Grav | `grav-plugin`<br>`grav-theme`
56
  | Hurad | `hurad-plugin`<br>`hurad-theme`
57
+ | ImageCMS | `imagecms-template`<br>`imagecms-module`<br>`imagecms-library`
58
  | Joomla | `joomla-component`<br>`joomla-module`<br>`joomla-template`<br>`joomla-plugin`<br>`joomla-library`
59
+ | Kirby | **`kirby-plugin`**<br>`kirby-field`<br>`kirby-tag`
60
  | KodiCMS | `kodicms-plugin`<br>`kodicms-media`
61
  | Kohana | **`kohana-module`**
62
  | Laravel | `laravel-library`
63
  | Lithium | **`lithium-library`<br>`lithium-source`**
64
  | Magento | `magento-library`<br>`magento-skin`<br>`magento-theme`
65
  | Mako | `mako-package`
66
+ | Mautic | `mautic-plugin`<br>`mautic-theme`
67
  | MODX Evo | `modxevo-snippet`<br>`modxevo-plugin`<br>`modxevo-module`<br>`modxevo-template`<br>`modxevo-lib`
68
  | MediaWiki | `mediawiki-extension`
69
  | October | **`october-module`<br>`october-plugin`<br>`october-theme`**
83
  | symfony1 | **`symfony1-plugin`**
84
  | Tusk | `tusk-task`<br>`tusk-command`<br>`tusk-asset`
85
  | TYPO3 Flow | `typo3-flow-package`<br>`typo3-flow-framework`<br>`typo3-flow-plugin`<br>`typo3-flow-site`<br>`typo3-flow-boilerplate`<br>`typo3-flow-build`
86
+ | TYPO3 CMS | `typo3-cms-extension` (Deprecated in this package, use the [TYPO3 CMS Installers](https://packagist.org/packages/typo3/cms-composer-installers) instead)
87
  | Wolf CMS | `wolfcms-plugin`
88
  | WordPress | <b>`wordpress-plugin`<br>`wordpress-theme`</b><br>`wordpress-muplugin`
89
  | Zend | `zend-library`<br>`zend-extra`<br>`zend-module`
139
  }
140
  ```
141
 
142
+ You can also have the same vendor packages with a custom installation path by
143
+ using the `vendor:` prefix.
144
+
145
+ ``` json
146
+ {
147
+ "extra": {
148
+ "installer-paths": {
149
+ "your/custom/path/{$name}/": ["vendor:my_organization"]
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ These would use your custom path for each of the listed packages. The available
156
  variables to use in your paths are: `{$name}`, `{$vendor}`, `{$type}`.
157
 
158
  ## Custom Install Names
206
  removed. `installers` won't do this because it would allow a single package
207
  author to wipe out entire folders without the user's consent. That user would
208
  then come here to yell at us.
209
+
210
+ Anyone still wanting this capability should consider requiring https://github.com/oomphinc/composer-installers-extender.
vendor/composer/installers/composer.json CHANGED
@@ -22,12 +22,14 @@
22
  "FuelPHP",
23
  "Grav",
24
  "Hurad",
 
25
  "Joomla",
26
  "Kohana",
27
  "Laravel",
28
  "Lithium",
29
  "Magento",
30
  "Mako",
 
31
  "MODX Evo",
32
  "MediaWiki",
33
  "OXID",
@@ -49,7 +51,7 @@
49
  "Zend",
50
  "Zikula"
51
  ],
52
- "homepage": "http://composer.github.com/installers/",
53
  "authors": [
54
  {
55
  "name": "Kyle Robinson Young",
@@ -58,7 +60,7 @@
58
  }
59
  ],
60
  "autoload": {
61
- "psr-0": { "Composer\\Installers\\": "src/" }
62
  },
63
  "extra": {
64
  "class": "Composer\\Installers\\Plugin",
@@ -71,10 +73,13 @@
71
  "roundcube/plugin-installer": "*"
72
  },
73
  "require": {
74
- "composer-plugin-api": "1.0.0"
75
  },
76
  "require-dev": {
77
  "composer/composer": "1.0.*@dev",
78
  "phpunit/phpunit": "4.1.*"
 
 
 
79
  }
80
  }
22
  "FuelPHP",
23
  "Grav",
24
  "Hurad",
25
+ "ImageCMS",
26
  "Joomla",
27
  "Kohana",
28
  "Laravel",
29
  "Lithium",
30
  "Magento",
31
  "Mako",
32
+ "Mautic",
33
  "MODX Evo",
34
  "MediaWiki",
35
  "OXID",
51
  "Zend",
52
  "Zikula"
53
  ],
54
+ "homepage": "https://composer.github.io/installers/",
55
  "authors": [
56
  {
57
  "name": "Kyle Robinson Young",
60
  }
61
  ],
62
  "autoload": {
63
+ "psr-4": { "Composer\\Installers\\": "src/Composer/Installers" }
64
  },
65
  "extra": {
66
  "class": "Composer\\Installers\\Plugin",
73
  "roundcube/plugin-installer": "*"
74
  },
75
  "require": {
76
+ "composer-plugin-api": "^1.0"
77
  },
78
  "require-dev": {
79
  "composer/composer": "1.0.*@dev",
80
  "phpunit/phpunit": "4.1.*"
81
+ },
82
+ "scripts": {
83
+ "test": "phpunit"
84
  }
85
  }
vendor/composer/installers/src/Composer/Installers/BaseInstaller.php CHANGED
@@ -55,7 +55,7 @@ abstract class BaseInstaller
55
  if ($this->composer->getPackage()) {
56
  $extra = $this->composer->getPackage()->getExtra();
57
  if (!empty($extra['installer-paths'])) {
58
- $customPath = $this->mapCustomInstallPaths($extra['installer-paths'], $prettyName, $type);
59
  if ($customPath !== false) {
60
  return $this->templatePath($customPath, $availableVars);
61
  }
@@ -120,12 +120,13 @@ abstract class BaseInstaller
120
  * @param array $paths
121
  * @param string $name
122
  * @param string $type
 
123
  * @return string
124
  */
125
- protected function mapCustomInstallPaths(array $paths, $name, $type)
126
  {
127
  foreach ($paths as $path => $names) {
128
- if (in_array($name, $names) || in_array('type:' . $type, $names)) {
129
  return $path;
130
  }
131
  }
55
  if ($this->composer->getPackage()) {
56
  $extra = $this->composer->getPackage()->getExtra();
57
  if (!empty($extra['installer-paths'])) {
58
+ $customPath = $this->mapCustomInstallPaths($extra['installer-paths'], $prettyName, $type, $vendor);
59
  if ($customPath !== false) {
60
  return $this->templatePath($customPath, $availableVars);
61
  }
120
  * @param array $paths
121
  * @param string $name
122
  * @param string $type
123
+ * @param string $vendor = NULL
124
  * @return string
125
  */
126
+ protected function mapCustomInstallPaths(array $paths, $name, $type, $vendor = NULL)
127
  {
128
  foreach ($paths as $path => $names) {
129
+ if (in_array($name, $names) || in_array('type:' . $type, $names) || in_array('vendor:' . $vendor, $names)) {
130
  return $path;
131
  }
132
  }
vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php CHANGED
@@ -1,10 +1,24 @@
1
  <?php
 
2
  namespace Composer\Installers;
3
 
4
  use Composer\Util\Filesystem;
5
 
6
  /**
7
- * Installer for Bitrix Framework
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  *
9
  * @author Nik Samokhvalov <nik@samokhvalov.info>
10
  * @author Denis Kulichkin <onexhovia@gmail.com>
@@ -12,16 +26,36 @@ use Composer\Util\Filesystem;
12
  class BitrixInstaller extends BaseInstaller
13
  {
14
  protected $locations = array(
15
- 'module' => 'bitrix/modules/{$name}/',
16
- 'component' => 'bitrix/components/{$name}/',
17
- 'theme' => 'bitrix/templates/{$name}/',
18
  );
19
 
20
  /**
21
- * @var array Storage for informations about duplicates at all the time of installation packages
22
  */
23
  private static $checkedDuplicates = array();
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  * {@inheritdoc}
27
  */
@@ -34,39 +68,39 @@ class BitrixInstaller extends BaseInstaller
34
  }
35
 
36
  /**
37
- * Duplicates search packages
38
  *
39
- * @param string $templatePath
40
  * @param array $vars
41
  */
42
- protected function checkDuplicates($templatePath, array $vars = array())
43
  {
44
- /**
45
- * Incorrect paths for backward compatibility
46
- */
47
- $oldLocations = array(
48
- 'module' => 'local/modules/{$name}/',
49
- 'component' => 'local/components/{$name}/',
50
- 'theme' => 'local/templates/{$name}/'
51
- );
52
-
53
  $packageType = substr($vars['type'], strlen('bitrix') + 1);
54
- $oldLocation = str_replace('{$name}', $vars['name'], $oldLocations[$packageType]);
 
 
 
 
 
 
 
 
 
55
 
56
- if (in_array($oldLocation, static::$checkedDuplicates)) {
57
  return;
58
  }
59
 
60
- if ($oldLocation !== $templatePath && file_exists($oldLocation) && $this->io && $this->io->isInteractive()) {
61
 
62
  $this->io->writeError(' <error>Duplication of packages:</error>');
63
- $this->io->writeError(' <info>Package ' . $oldLocation . ' will be called instead package ' . $templatePath . '</info>');
64
 
65
  while (true) {
66
- switch ($this->io->ask(' <info>Delete ' . $oldLocation . ' [y,n,?]?</info> ', '?')) {
67
  case 'y':
68
  $fs = new Filesystem();
69
- $fs->removeDirectory($oldLocation);
70
  break 2;
71
 
72
  case 'n':
@@ -75,7 +109,7 @@ class BitrixInstaller extends BaseInstaller
75
  case '?':
76
  default:
77
  $this->io->writeError(array(
78
- ' y - delete package ' . $oldLocation . ' and to continue with the installation',
79
  ' n - don\'t delete and to continue with the installation',
80
  ));
81
  $this->io->writeError(' ? - print help');
@@ -84,6 +118,6 @@ class BitrixInstaller extends BaseInstaller
84
  }
85
  }
86
 
87
- static::$checkedDuplicates[] = $oldLocation;
88
  }
89
  }
1
  <?php
2
+
3
  namespace Composer\Installers;
4
 
5
  use Composer\Util\Filesystem;
6
 
7
  /**
8
+ * Installer for Bitrix Framework. Supported types of extensions:
9
+ * - `bitrix-module` — copy the module to directory `bitrix/modules/` directory.
10
+ * - `bitrix-component` — copy the component to directory `bitrix/components/`.
11
+ * - `bitrix-template` — copy the template to directory `bitrix/templates/`.
12
+ *
13
+ * You can set custom path to directory with Bitrix kernel in `composer.json`:
14
+ *
15
+ * ```json
16
+ * {
17
+ * "extra": {
18
+ * "bitrix-dir": "s1/bitrix"
19
+ * }
20
+ * }
21
+ * ```
22
  *
23
  * @author Nik Samokhvalov <nik@samokhvalov.info>
24
  * @author Denis Kulichkin <onexhovia@gmail.com>
26
  class BitrixInstaller extends BaseInstaller
27
  {
28
  protected $locations = array(
29
+ 'module' => '{$bitrix_dir}/modules/{$name}/',
30
+ 'component' => '{$bitrix_dir}/components/{$name}/',
31
+ 'theme' => '{$bitrix_dir}/templates/{$name}/',
32
  );
33
 
34
  /**
35
+ * @var array Storage for informations about duplicates at all the time of installation packages.
36
  */
37
  private static $checkedDuplicates = array();
38
 
39
+ /**
40
+ * {@inheritdoc}
41
+ */
42
+ public function inflectPackageVars($vars)
43
+ {
44
+ if ($this->composer->getPackage()) {
45
+ $extra = $this->composer->getPackage()->getExtra();
46
+
47
+ if (isset($extra['bitrix-dir'])) {
48
+ $vars['bitrix_dir'] = $extra['bitrix-dir'];
49
+ }
50
+ }
51
+
52
+ if (!isset($vars['bitrix_dir'])) {
53
+ $vars['bitrix_dir'] = 'bitrix';
54
+ }
55
+
56
+ return parent::inflectPackageVars($vars);
57
+ }
58
+
59
  /**
60
  * {@inheritdoc}
61
  */
68
  }
69
 
70
  /**
71
+ * Duplicates search packages.
72
  *
73
+ * @param string $path
74
  * @param array $vars
75
  */
76
+ protected function checkDuplicates($path, array $vars = array())
77
  {
 
 
 
 
 
 
 
 
 
78
  $packageType = substr($vars['type'], strlen('bitrix') + 1);
79
+ $localDir = explode('/', $vars['bitrix_dir']);
80
+ array_pop($localDir);
81
+ $localDir[] = 'local';
82
+ $localDir = implode('/', $localDir);
83
+
84
+ $oldPath = str_replace(
85
+ array('{$bitrix_dir}', '{$name}'),
86
+ array($localDir, $vars['name']),
87
+ $this->locations[$packageType]
88
+ );
89
 
90
+ if (in_array($oldPath, static::$checkedDuplicates)) {
91
  return;
92
  }
93
 
94
+ if ($oldPath !== $path && file_exists($oldPath) && $this->io && $this->io->isInteractive()) {
95
 
96
  $this->io->writeError(' <error>Duplication of packages:</error>');
97
+ $this->io->writeError(' <info>Package ' . $oldPath . ' will be called instead package ' . $path . '</info>');
98
 
99
  while (true) {
100
+ switch ($this->io->ask(' <info>Delete ' . $oldPath . ' [y,n,?]?</info> ', '?')) {
101
  case 'y':
102
  $fs = new Filesystem();
103
+ $fs->removeDirectory($oldPath);
104
  break 2;
105
 
106
  case 'n':
109
  case '?':
110
  default:
111
  $this->io->writeError(array(
112
+ ' y - delete package ' . $oldPath . ' and to continue with the installation',
113
  ' n - don\'t delete and to continue with the installation',
114
  ));
115
  $this->io->writeError(' ? - print help');
118
  }
119
  }
120
 
121
+ static::$checkedDuplicates[] = $oldPath;
122
  }
123
  }
vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php CHANGED
@@ -10,5 +10,7 @@ class DrupalInstaller extends BaseInstaller
10
  'library' => 'libraries/{$name}/',
11
  'profile' => 'profiles/{$name}/',
12
  'drush' => 'drush/{$name}/',
 
 
13
  );
14
  }
10
  'library' => 'libraries/{$name}/',
11
  'profile' => 'profiles/{$name}/',
12
  'drush' => 'drush/{$name}/',
13
+ 'custom-theme' => 'themes/custom/{$name}/',
14
+ 'custom-module' => 'modules/custom/{$name}',
15
  );
16
  }
vendor/composer/installers/src/Composer/Installers/ImageCMSInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class ImageCMSInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'template' => 'templates/{$name}/',
8
+ 'module' => 'application/modules/{$name}/',
9
+ 'library' => 'application/libraries/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/Installer.php CHANGED
@@ -35,6 +35,7 @@ class Installer extends LibraryInstaller
35
  'fuelphp' => 'FuelphpInstaller',
36
  'grav' => 'GravInstaller',
37
  'hurad' => 'HuradInstaller',
 
38
  'joomla' => 'JoomlaInstaller',
39
  'kirby' => 'KirbyInstaller',
40
  'kodicms' => 'KodiCMSInstaller',
@@ -43,6 +44,7 @@ class Installer extends LibraryInstaller
43
  'lithium' => 'LithiumInstaller',
44
  'magento' => 'MagentoInstaller',
45
  'mako' => 'MakoInstaller',
 
46
  'mediawiki' => 'MediaWikiInstaller',
47
  'microweber' => 'MicroweberInstaller',
48
  'modulework' => 'MODULEWorkInstaller',
35
  'fuelphp' => 'FuelphpInstaller',
36
  'grav' => 'GravInstaller',
37
  'hurad' => 'HuradInstaller',
38
+ 'imagecms' => 'ImageCMSInstaller',
39
  'joomla' => 'JoomlaInstaller',
40
  'kirby' => 'KirbyInstaller',
41
  'kodicms' => 'KodiCMSInstaller',
44
  'lithium' => 'LithiumInstaller',
45
  'magento' => 'MagentoInstaller',
46
  'mako' => 'MakoInstaller',
47
+ 'mautic' => 'MauticInstaller',
48
  'mediawiki' => 'MediaWikiInstaller',
49
  'microweber' => 'MicroweberInstaller',
50
  'modulework' => 'MODULEWorkInstaller',
vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php CHANGED
@@ -5,5 +5,7 @@ class KirbyInstaller extends BaseInstaller
5
  {
6
  protected $locations = array(
7
  'plugin' => 'site/plugins/{$name}/',
 
 
8
  );
9
  }
5
  {
6
  protected $locations = array(
7
  'plugin' => 'site/plugins/{$name}/',
8
+ 'field' => 'site/fields/{$name}/',
9
+ 'tag' => 'site/tags/{$name}/'
10
  );
11
  }
vendor/composer/installers/src/Composer/Installers/MauticInstaller.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class MauticInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'plugins/{$name}/',
8
+ 'theme' => 'themes/{$name}/',
9
+ );
10
+
11
+ /**
12
+ * Format package name of mautic-plugins to CamelCase
13
+ */
14
+ public function inflectPackageVars($vars)
15
+ {
16
+ if ($vars['type'] == 'mautic-plugin') {
17
+ $vars['name'] = preg_replace_callback('/(-[a-z])/', function ($matches) {
18
+ return strtoupper($matches[0][1]);
19
+ }, ucfirst($vars['name']));
20
+ }
21
+
22
+ return $vars;
23
+ }
24
+
25
+ }
vendor/composer/installers/src/Composer/Installers/OxidInstaller.php CHANGED
@@ -1,11 +1,59 @@
1
  <?php
2
  namespace Composer\Installers;
3
 
 
 
4
  class OxidInstaller extends BaseInstaller
5
  {
 
 
6
  protected $locations = array(
7
  'module' => 'modules/{$name}/',
8
  'theme' => 'application/views/{$name}/',
9
  'out' => 'out/{$name}/',
10
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  }
1
  <?php
2
  namespace Composer\Installers;
3
 
4
+ use Composer\Package\PackageInterface;
5
+
6
  class OxidInstaller extends BaseInstaller
7
  {
8
+ const VENDOR_PATTERN = '/^modules\/(?P<vendor>.+)\/.+/';
9
+
10
  protected $locations = array(
11
  'module' => 'modules/{$name}/',
12
  'theme' => 'application/views/{$name}/',
13
  'out' => 'out/{$name}/',
14
  );
15
+
16
+ /**
17
+ * getInstallPath
18
+ *
19
+ * @param PackageInterface $package
20
+ * @param string $frameworkType
21
+ * @return void
22
+ */
23
+ public function getInstallPath(PackageInterface $package, $frameworkType = '')
24
+ {
25
+ $installPath = parent::getInstallPath($package, $frameworkType);
26
+ $type = $this->package->getType();
27
+ if ($type === 'oxid-module') {
28
+ $this->prepareVendorDirectory($installPath);
29
+ }
30
+ return $installPath;
31
+ }
32
+
33
+ /**
34
+ * prepareVendorDirectory
35
+ *
36
+ * Makes sure there is a vendormetadata.php file inside
37
+ * the vendor folder if there is a vendor folder.
38
+ *
39
+ * @param string $installPath
40
+ * @return void
41
+ */
42
+ protected function prepareVendorDirectory($installPath)
43
+ {
44
+ $matches = '';
45
+ $hasVendorDirectory = preg_match(self::VENDOR_PATTERN, $installPath, $matches);
46
+ if (!$hasVendorDirectory) {
47
+ return;
48
+ }
49
+
50
+ $vendorDirectory = $matches['vendor'];
51
+ $vendorPath = getcwd() . '/modules/' . $vendorDirectory;
52
+ if (!file_exists($vendorPath)) {
53
+ mkdir($vendorPath, 0755, true);
54
+ }
55
+
56
+ $vendorMetaDataPath = $vendorPath . '/vendormetadata.php';
57
+ touch($vendorMetaDataPath);
58
+ }
59
  }
vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php CHANGED
@@ -4,6 +4,8 @@ namespace Composer\Installers;
4
  /**
5
  * Extension installer for TYPO3 CMS
6
  *
 
 
7
  * @author Sascha Egerer <sascha.egerer@dkd.de>
8
  */
9
  class TYPO3CmsInstaller extends BaseInstaller
4
  /**
5
  * Extension installer for TYPO3 CMS
6
  *
7
+ * @deprecated since 1.0.25, use https://packagist.org/packages/typo3/cms-composer-installers instead
8
+ *
9
  * @author Sascha Egerer <sascha.egerer@dkd.de>
10
  */
11
  class TYPO3CmsInstaller extends BaseInstaller
vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php CHANGED
@@ -121,6 +121,9 @@ class InstallerTest extends TestCase
121
  array('fuelphp-component', true),
122
  array('hurad-plugin', true),
123
  array('hurad-theme', true),
 
 
 
124
  array('joomla-library', true),
125
  array('kirby-plugin', true),
126
  array('kohana-module', true),
@@ -235,6 +238,9 @@ class InstallerTest extends TestCase
235
  array('fuelphp-component', 'components/demo/', 'fuelphp/demo'),
236
  array('hurad-plugin', 'plugins/Akismet/', 'atkrad/akismet'),
237
  array('hurad-theme', 'plugins/Hurad2013/', 'atkrad/Hurad2013'),
 
 
 
238
  array('joomla-plugin', 'plugins/my_plugin/', 'shama/my_plugin'),
239
  array('kirby-plugin', 'site/plugins/my_plugin/', 'shama/my_plugin'),
240
  array('kohana-module', 'modules/my_package/', 'shama/my_package'),
@@ -380,6 +386,27 @@ class InstallerTest extends TestCase
380
  $this->assertEquals('my/custom/path/my_plugin/', $result);
381
  }
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  /**
384
  * testNoVendorName
385
  */
121
  array('fuelphp-component', true),
122
  array('hurad-plugin', true),
123
  array('hurad-theme', true),
124
+ array('imagecms-template', true),
125
+ array('imagecms-module', true),
126
+ array('imagecms-library', true),
127
  array('joomla-library', true),
128
  array('kirby-plugin', true),
129
  array('kohana-module', true),
238
  array('fuelphp-component', 'components/demo/', 'fuelphp/demo'),
239
  array('hurad-plugin', 'plugins/Akismet/', 'atkrad/akismet'),
240
  array('hurad-theme', 'plugins/Hurad2013/', 'atkrad/Hurad2013'),
241
+ array('imagecms-template', 'templates/my_template/', 'shama/my_template'),
242
+ array('imagecms-module', 'application/modules/my_module/', 'shama/my_module'),
243
+ array('imagecms-library', 'application/libraries/my_library/', 'shama/my_library'),
244
  array('joomla-plugin', 'plugins/my_plugin/', 'shama/my_plugin'),
245
  array('kirby-plugin', 'site/plugins/my_plugin/', 'shama/my_plugin'),
246
  array('kohana-module', 'modules/my_package/', 'shama/my_package'),
386
  $this->assertEquals('my/custom/path/my_plugin/', $result);
387
  }
388
 
389
+ /**
390
+ * testVendorPath
391
+ */
392
+ public function testVendorPath()
393
+ {
394
+ $installer = new Installer($this->io, $this->composer);
395
+ $package = new Package('penyaskito/my_module', '1.0.0', '1.0.0');
396
+ $package->setType('drupal-module');
397
+ $consumerPackage = new RootPackage('drupal/drupal', '1.0.0', '1.0.0');
398
+ $this->composer->setPackage($consumerPackage);
399
+ $consumerPackage->setExtra(array(
400
+ 'installer-paths' => array(
401
+ 'modules/custom/{$name}/' => array(
402
+ 'vendor:penyaskito'
403
+ ),
404
+ ),
405
+ ));
406
+ $result = $installer->getInstallPath($package);
407
+ $this->assertEquals('modules/custom/my_module/', $result);
408
+ }
409
+
410
  /**
411
  * testNoVendorName
412
  */
vendor/twig/twig/CHANGELOG CHANGED
@@ -1,4 +1,21 @@
1
- * 1.23.1 (2015-XX-XX)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  * fixed some exception messages which triggered PHP warnings
4
  * fixed BC on Twig_Test_NodeTestCase
1
+ * 1.24.0 (2016-01-25)
2
+
3
+ * adding support for the ?? operator
4
+ * fixed the defined test when used on a constant, a map, or a sequence
5
+ * undeprecated _self (should only be used to get the template name, not the template instance)
6
+ * fixed parsing on PHP7
7
+
8
+ * 1.23.3 (2016-01-11)
9
+
10
+ * fixed typo
11
+
12
+ * 1.23.2 (2015-01-11)
13
+
14
+ * added versions in deprecated messages
15
+ * made file cache tolerant for trailing (back)slashes on directory configuration
16
+ * deprecated unused Twig_Node_Expression_ExtensionReference class
17
+
18
+ * 1.23.1 (2015-11-05)
19
 
20
  * fixed some exception messages which triggered PHP warnings
21
  * fixed BC on Twig_Test_NodeTestCase
vendor/twig/twig/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2014 by the Twig Team.
2
 
3
  Some rights reserved.
4
 
1
+ Copyright (c) 2009-2016 by the Twig Team.
2
 
3
  Some rights reserved.
4
 
vendor/twig/twig/composer.json CHANGED
@@ -40,7 +40,7 @@
40
  },
41
  "extra": {
42
  "branch-alias": {
43
- "dev-master": "1.23-dev"
44
  }
45
  }
46
  }
40
  },
41
  "extra": {
42
  "branch-alias": {
43
+ "dev-master": "1.24-dev"
44
  }
45
  }
46
  }
vendor/twig/twig/doc/advanced.rst CHANGED
@@ -554,7 +554,7 @@ An extension is a class that implements the following interface::
554
  *
555
  * @param Twig_Environment $environment The current Twig_Environment instance
556
  *
557
- * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterace instead
558
  */
559
  function initRuntime(Twig_Environment $environment);
560
 
554
  *
555
  * @param Twig_Environment $environment The current Twig_Environment instance
556
  *
557
+ * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead
558
  */
559
  function initRuntime(Twig_Environment $environment);
560
 
vendor/twig/twig/doc/deprecated.rst CHANGED
@@ -144,9 +144,11 @@ Globals
144
  or the extensions have been initialized is not possible anymore (but
145
  changing the value of an already registered global is possible).
146
 
147
- * As of Twig 1.x, the ``_self`` global variable is deprecated except for usage
148
- in the ``from`` and the ``import`` tags. In Twig 2.0, ``_self`` is not
149
- exposed anymore but still usable in the ``from`` and the ``import`` tags.
 
 
150
 
151
  Miscellaneous
152
  -------------
144
  or the extensions have been initialized is not possible anymore (but
145
  changing the value of an already registered global is possible).
146
 
147
+ * As of Twig 1.x, using the ``_self`` global variable to get access to the
148
+ current ``Twig_Template`` instance is deprecated; most usages only need the
149
+ current template name, which will continue to work in Twig 2.0. In Twig 2.0,
150
+ ``_self`` returns the current template name instead of the current
151
+ ``Twig_Template`` instance.
152
 
153
  Miscellaneous
154
  -------------
vendor/twig/twig/doc/templates.rst CHANGED
@@ -127,7 +127,7 @@ Global Variables
127
 
128
  The following variables are always available in templates:
129
 
130
- * ``_self``: references the current template (deprecated since Twig 1.20);
131
  * ``_context``: references the current context;
132
  * ``_charset``: references the current charset.
133
 
@@ -808,6 +808,13 @@ The following operators don't fit into any of the other categories:
808
  {{ foo ?: 'no' }} is the same as {{ foo ? foo : 'no' }}
809
  {{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }}
810
 
 
 
 
 
 
 
 
811
  String Interpolation
812
  ~~~~~~~~~~~~~~~~~~~~
813
 
127
 
128
  The following variables are always available in templates:
129
 
130
+ * ``_self``: references the current template;
131
  * ``_context``: references the current context;
132
  * ``_charset``: references the current charset.
133
 
808
  {{ foo ?: 'no' }} is the same as {{ foo ? foo : 'no' }}
809
  {{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }}
810
 
811
+ * ``??``: The null-coalescing operator:
812
+
813
+ .. code-block:: jinja
814
+
815
+ {# returns the value of foo if it is defined and not null, 'no' otherwise #}
816
+ {{ foo ?? 'no' }}
817
+
818
  String Interpolation
819
  ~~~~~~~~~~~~~~~~~~~~
820
 
vendor/twig/twig/ext/twig/php_twig.h CHANGED
@@ -15,7 +15,7 @@
15
  #ifndef PHP_TWIG_H
16
  #define PHP_TWIG_H
17
 
18
- #define PHP_TWIG_VERSION "1.23.1"
19
 
20
  #include "php.h"
21
 
15
  #ifndef PHP_TWIG_H
16
  #define PHP_TWIG_H
17
 
18
+ #define PHP_TWIG_VERSION "1.24.0"
19
 
20
  #include "php.h"
21
 
vendor/twig/twig/lib/Twig/Autoloader.php CHANGED
@@ -9,14 +9,14 @@
9
  * file that was distributed with this source code.
10
  */
11
 
12
- @trigger_error('The Twig_Autoloader class is deprecated and will be removed in 2.0. Use Composer instead.', E_USER_DEPRECATED);
13
 
14
  /**
15
  * Autoloads Twig classes.
16
  *
17
  * @author Fabien Potencier <fabien@symfony.com>
18
  *
19
- * @deprecated Use Composer instead. Will be removed in Twig 2.0.
20
  */
21
  class Twig_Autoloader
22
  {
@@ -27,7 +27,7 @@ class Twig_Autoloader
27
  */
28
  public static function register($prepend = false)
29
  {
30
- @trigger_error('Using Twig_Autoloader is deprecated. Use Composer instead.', E_USER_DEPRECATED);
31
 
32
  if (PHP_VERSION_ID < 50300) {
33
  spl_autoload_register(array(__CLASS__, 'autoload'));
9
  * file that was distributed with this source code.
10
  */
11
 
12
+ @trigger_error('The Twig_Autoloader class is deprecated since version 1.21 and will be removed in 2.0. Use Composer instead.', E_USER_DEPRECATED);
13
 
14
  /**
15
  * Autoloads Twig classes.
16
  *
17
  * @author Fabien Potencier <fabien@symfony.com>
18
  *
19
+ * @deprecated since 1.21 and will be removed in 2.0. Use Composer instead. 2.0.
20
  */
21
  class Twig_Autoloader
22
  {
27
  */
28
  public static function register($prepend = false)
29
  {
30
+ @trigger_error('Using Twig_Autoloader is deprecated since version 1.21. Use Composer instead.', E_USER_DEPRECATED);
31
 
32
  if (PHP_VERSION_ID < 50300) {
33
  spl_autoload_register(array(__CLASS__, 'autoload'));
vendor/twig/twig/lib/Twig/Cache/Filesystem.php CHANGED
@@ -27,7 +27,7 @@ class Twig_Cache_Filesystem implements Twig_CacheInterface
27
  */
28
  public function __construct($directory, $options = 0)
29
  {
30
- $this->directory = $directory;
31
  $this->options = $options;
32
  }
33
 
@@ -38,7 +38,7 @@ class Twig_Cache_Filesystem implements Twig_CacheInterface
38
  {
39
  $hash = hash('sha256', $className);
40
 
41
- return $this->directory.'/'.$hash[0].$hash[1].'/'.$hash.'.php';
42
  }
43
 
44
  /**
27
  */
28
  public function __construct($directory, $options = 0)
29
  {
30
+ $this->directory = rtrim($directory, '\/').'/';
31
  $this->options = $options;
32
  }
33
 
38
  {
39
  $hash = hash('sha256', $className);
40
 
41
+ return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
42
  }
43
 
44
  /**
vendor/twig/twig/lib/Twig/Environment.php CHANGED
@@ -16,7 +16,7 @@
16
  */
17
  class Twig_Environment
18
  {
19
- const VERSION = '1.23.1';
20
 
21
  protected $charset;
22
  protected $loader;
@@ -93,7 +93,7 @@ class Twig_Environment
93
  if (null !== $loader) {
94
  $this->setLoader($loader);
95
  } else {
96
- @trigger_error('Not passing a Twig_LoaderInterface as the first constructor argument of Twig_Environment is deprecated.', E_USER_DEPRECATED);
97
  }
98
 
99
  $options = array_merge(array(
@@ -123,14 +123,14 @@ class Twig_Environment
123
  if (is_string($this->originalCache)) {
124
  $r = new ReflectionMethod($this, 'writeCacheFile');
125
  if ($r->getDeclaringClass()->getName() !== __CLASS__) {
126
- @trigger_error('The Twig_Environment::writeCacheFile method is deprecated and will be removed in Twig 2.0.', E_USER_DEPRECATED);
127
 
128
  $this->bcWriteCacheFile = true;
129
  }
130
 
131
  $r = new ReflectionMethod($this, 'getCacheFilename');
132
  if ($r->getDeclaringClass()->getName() !== __CLASS__) {
133
- @trigger_error('The Twig_Environment::getCacheFilename method is deprecated and will be removed in Twig 2.0.', E_USER_DEPRECATED);
134
 
135
  $this->bcGetCacheFilename = true;
136
  }
@@ -265,7 +265,7 @@ class Twig_Environment
265
  $this->originalCache = $cache;
266
  $this->cache = new Twig_Cache_Null();
267
  } elseif (null === $cache) {
268
- @trigger_error('Using "null" as the cache strategy is deprecated and will be removed in Twig 2.0.', E_USER_DEPRECATED);
269
  $this->originalCache = false;
270
  $this->cache = new Twig_Cache_Null();
271
  } elseif ($cache instanceof Twig_CacheInterface) {
@@ -286,7 +286,7 @@ class Twig_Environment
286
  */
287
  public function getCacheFilename($name)
288
  {
289
- @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
290
 
291
  $key = $this->cache->generateKey($name, $this->getTemplateClass($name));
292
 
@@ -325,7 +325,7 @@ class Twig_Environment
325
  */
326
  public function getTemplateClassPrefix()
327
  {
328
- @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
329
 
330
  return $this->templateClassPrefix;
331
  }
@@ -515,7 +515,7 @@ class Twig_Environment
515
  */
516
  public function clearTemplateCache()
517
  {
518
- @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
519
 
520
  $this->loadedTemplates = array();
521
  }
@@ -527,7 +527,7 @@ class Twig_Environment
527
  */
528
  public function clearCacheFiles()
529
  {
530
- @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
531
 
532
  if (is_string($this->originalCache)) {
533
  foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->originalCache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
@@ -737,7 +737,7 @@ class Twig_Environment
737
  $m = new ReflectionMethod($extension, 'initRuntime');
738
 
739
  if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
740
- @trigger_error(sprintf('Defining the initRuntime() method in the "%s" extension is deprecated. Use the `needs_environment` option to get the Twig_Environment instance in filters, functions, or tests; or explicitly implement Twig_Extension_InitRuntimeInterface if needed (not recommended).', $name), E_USER_DEPRECATED);
741
  }
742
  }
743
 
@@ -787,7 +787,7 @@ class Twig_Environment
787
  }
788
 
789
  if (isset($this->extensions[$name])) {
790
- @trigger_error(sprintf('The possibility to register the same extension twice ("%s") is deprecated and will be removed in Twig 2.0. Use proper PHP inheritance instead.', $name), E_USER_DEPRECATED);
791
  }
792
 
793
  $this->lastModifiedExtension = 0;
@@ -806,7 +806,7 @@ class Twig_Environment
806
  */
807
  public function removeExtension($name)
808
  {
809
- @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
810
 
811
  if ($this->extensionInitialized) {
812
  throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
@@ -928,7 +928,7 @@ class Twig_Environment
928
  $filter = $name;
929
  $name = $filter->getName();
930
  } else {
931
- @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleFilter" instead when defining filter "%s".', __METHOD__, $name), E_USER_DEPRECATED);
932
  }
933
 
934
  if ($this->extensionInitialized) {
@@ -988,7 +988,7 @@ class Twig_Environment
988
  /**
989
  * Gets the registered Filters.
990
  *
991
- * Be warned that this method cannot return filters defined with registerUndefinedFunctionCallback.
992
  *
993
  * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances
994
  *
@@ -1019,7 +1019,7 @@ class Twig_Environment
1019
  $test = $name;
1020
  $name = $test->getName();
1021
  } else {
1022
- @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleTest" instead when defining test "%s".', __METHOD__, $name), E_USER_DEPRECATED);
1023
  }
1024
 
1025
  if ($this->extensionInitialized) {
@@ -1079,7 +1079,7 @@ class Twig_Environment
1079
  $function = $name;
1080
  $name = $function->getName();
1081
  } else {
1082
- @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleFunction" instead when defining function "%s".', __METHOD__, $name), E_USER_DEPRECATED);
1083
  }
1084
 
1085
  if ($this->extensionInitialized) {
@@ -1172,7 +1172,7 @@ class Twig_Environment
1172
 
1173
  if (!array_key_exists($name, $this->globals)) {
1174
  // The deprecation notice must be turned into the following exception in Twig 2.0
1175
- @trigger_error(sprintf('Registering global variable "%s" at runtime or when the extensions have already been initialized is deprecated.', $name), E_USER_DEPRECATED);
1176
  //throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
1177
  }
1178
  }
@@ -1256,7 +1256,7 @@ class Twig_Environment
1256
  */
1257
  public function computeAlternatives($name, $items)
1258
  {
1259
- @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
1260
 
1261
  return Twig_Error_Syntax::computeAlternatives($name, $items);
1262
  }
@@ -1269,7 +1269,7 @@ class Twig_Environment
1269
  $m = new ReflectionMethod($extension, 'getGlobals');
1270
 
1271
  if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
1272
- @trigger_error(sprintf('Defining the getGlobals() method in the "%s" extension is deprecated without explicitly implementing Twig_Extension_GlobalsInterface.', $name), E_USER_DEPRECATED);
1273
  }
1274
  }
1275
 
@@ -1314,7 +1314,7 @@ class Twig_Environment
1314
  if ($filter instanceof Twig_SimpleFilter) {
1315
  $name = $filter->getName();
1316
  } else {
1317
- @trigger_error(sprintf('Using an instance of "%s" for filter "%s" is deprecated. Use Twig_SimpleFilter instead.', get_class($filter), $name), E_USER_DEPRECATED);
1318
  }
1319
 
1320
  $this->filters[$name] = $filter;
@@ -1325,7 +1325,7 @@ class Twig_Environment
1325
  if ($function instanceof Twig_SimpleFunction) {
1326
  $name = $function->getName();
1327
  } else {
1328
- @trigger_error(sprintf('Using an instance of "%s" for function "%s" is deprecated. Use Twig_SimpleFunction instead.', get_class($function), $name), E_USER_DEPRECATED);
1329
  }
1330
 
1331
  $this->functions[$name] = $function;
@@ -1336,7 +1336,7 @@ class Twig_Environment
1336
  if ($test instanceof Twig_SimpleTest) {
1337
  $name = $test->getName();
1338
  } else {
1339
- @trigger_error(sprintf('Using an instance of "%s" for test "%s" is deprecated. Use Twig_SimpleTest instead.', get_class($test), $name), E_USER_DEPRECATED);
1340
  }
1341
 
1342
  $this->tests[$name] = $test;
@@ -1347,7 +1347,7 @@ class Twig_Environment
1347
  if ($parser instanceof Twig_TokenParserInterface) {
1348
  $this->parsers->addTokenParser($parser);
1349
  } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
1350
- @trigger_error('Registering a Twig_TokenParserBrokerInterface instance is deprecated.', E_USER_DEPRECATED);
1351
 
1352
  $this->parsers->addTokenParserBroker($parser);
1353
  } else {
16
  */
17
  class Twig_Environment
18
  {
19
+ const VERSION = '1.24.0';
20
 
21
  protected $charset;
22
  protected $loader;
93
  if (null !== $loader) {
94
  $this->setLoader($loader);
95
  } else {
96
+ @trigger_error('Not passing a Twig_LoaderInterface as the first constructor argument of Twig_Environment is deprecated since version 1.21.', E_USER_DEPRECATED);
97
  }
98
 
99
  $options = array_merge(array(
123
  if (is_string($this->originalCache)) {
124
  $r = new ReflectionMethod($this, 'writeCacheFile');
125
  if ($r->getDeclaringClass()->getName() !== __CLASS__) {
126
+ @trigger_error('The Twig_Environment::writeCacheFile method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
127
 
128
  $this->bcWriteCacheFile = true;
129
  }
130
 
131
  $r = new ReflectionMethod($this, 'getCacheFilename');
132
  if ($r->getDeclaringClass()->getName() !== __CLASS__) {
133
+ @trigger_error('The Twig_Environment::getCacheFilename method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
134
 
135
  $this->bcGetCacheFilename = true;
136
  }
265
  $this->originalCache = $cache;
266
  $this->cache = new Twig_Cache_Null();
267
  } elseif (null === $cache) {
268
+ @trigger_error('Using "null" as the cache strategy is deprecated since version 1.23 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
269
  $this->originalCache = false;
270
  $this->cache = new Twig_Cache_Null();
271
  } elseif ($cache instanceof Twig_CacheInterface) {
286
  */
287
  public function getCacheFilename($name)
288
  {
289
+ @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
290
 
291
  $key = $this->cache->generateKey($name, $this->getTemplateClass($name));
292
 
325
  */
326
  public function getTemplateClassPrefix()
327
  {
328
+ @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
329
 
330
  return $this->templateClassPrefix;
331
  }
515
  */
516
  public function clearTemplateCache()
517
  {
518
+ @trigger_error(sprintf('The %s method is deprecated since version 1.18.3 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
519
 
520
  $this->loadedTemplates = array();
521
  }
527
  */
528
  public function clearCacheFiles()
529
  {
530
+ @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
531
 
532
  if (is_string($this->originalCache)) {
533
  foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->originalCache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
737
  $m = new ReflectionMethod($extension, 'initRuntime');
738
 
739
  if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
740
+ @trigger_error(sprintf('Defining the initRuntime() method in the "%s" extension is deprecated since version 1.23. Use the `needs_environment` option to get the Twig_Environment instance in filters, functions, or tests; or explicitly implement Twig_Extension_InitRuntimeInterface if needed (not recommended).', $name), E_USER_DEPRECATED);
741
  }
742
  }
743
 
787
  }
788
 
789
  if (isset($this->extensions[$name])) {
790
+ @trigger_error(sprintf('The possibility to register the same extension twice ("%s") is deprecated since version 1.23 and will be removed in Twig 2.0. Use proper PHP inheritance instead.', $name), E_USER_DEPRECATED);
791
  }
792
 
793
  $this->lastModifiedExtension = 0;
806
  */
807
  public function removeExtension($name)
808
  {
809
+ @trigger_error(sprintf('The %s method is deprecated since version 1.12 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
810
 
811
  if ($this->extensionInitialized) {
812
  throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
928
  $filter = $name;
929
  $name = $filter->getName();
930
  } else {
931
+ @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleFilter" instead when defining filter "%s".', __METHOD__, $name), E_USER_DEPRECATED);
932
  }
933
 
934
  if ($this->extensionInitialized) {
988
  /**
989
  * Gets the registered Filters.
990
  *
991
+ * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback.
992
  *
993
  * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances
994
  *
1019
  $test = $name;
1020
  $name = $test->getName();
1021
  } else {
1022
+ @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleTest" instead when defining test "%s".', __METHOD__, $name), E_USER_DEPRECATED);
1023
  }
1024
 
1025
  if ($this->extensionInitialized) {
1079
  $function = $name;
1080
  $name = $function->getName();
1081
  } else {
1082
+ @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleFunction" instead when defining function "%s".', __METHOD__, $name), E_USER_DEPRECATED);
1083
  }
1084
 
1085
  if ($this->extensionInitialized) {
1172
 
1173
  if (!array_key_exists($name, $this->globals)) {
1174
  // The deprecation notice must be turned into the following exception in Twig 2.0
1175
+ @trigger_error(sprintf('Registering global variable "%s" at runtime or when the extensions have already been initialized is deprecated since version 1.21.', $name), E_USER_DEPRECATED);
1176
  //throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
1177
  }
1178
  }
1256
  */
1257
  public function computeAlternatives($name, $items)
1258
  {
1259
+ @trigger_error(sprintf('The %s method is deprecated since version 1.23 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
1260
 
1261
  return Twig_Error_Syntax::computeAlternatives($name, $items);
1262
  }
1269
  $m = new ReflectionMethod($extension, 'getGlobals');
1270
 
1271
  if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
1272
+ @trigger_error(sprintf('Defining the getGlobals() method in the "%s" extension without explicitly implementing Twig_Extension_GlobalsInterface is deprecated since version 1.23.', $name), E_USER_DEPRECATED);
1273
  }
1274
  }
1275
 
1314
  if ($filter instanceof Twig_SimpleFilter) {
1315
  $name = $filter->getName();
1316
  } else {
1317
+ @trigger_error(sprintf('Using an instance of "%s" for filter "%s" is deprecated since version 1.21. Use Twig_SimpleFilter instead.', get_class($filter), $name), E_USER_DEPRECATED);
1318
  }
1319
 
1320
  $this->filters[$name] = $filter;
1325
  if ($function instanceof Twig_SimpleFunction) {
1326
  $name = $function->getName();
1327
  } else {
1328
+ @trigger_error(sprintf('Using an instance of "%s" for function "%s" is deprecated since version 1.21. Use Twig_SimpleFunction instead.', get_class($function), $name), E_USER_DEPRECATED);
1329
  }
1330
 
1331
  $this->functions[$name] = $function;
1336
  if ($test instanceof Twig_SimpleTest) {
1337
  $name = $test->getName();
1338
  } else {
1339
+ @trigger_error(sprintf('Using an instance of "%s" for test "%s" is deprecated since version 1.21. Use Twig_SimpleTest instead.', get_class($test), $name), E_USER_DEPRECATED);
1340
  }
1341
 
1342
  $this->tests[$name] = $test;
1347
  if ($parser instanceof Twig_TokenParserInterface) {
1348
  $this->parsers->addTokenParser($parser);
1349
  } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
1350
+ @trigger_error('Registering a Twig_TokenParserBrokerInterface instance is deprecated since version 1.21.', E_USER_DEPRECATED);
1351
 
1352
  $this->parsers->addTokenParserBroker($parser);
1353
  } else {
vendor/twig/twig/lib/Twig/ExpressionParser.php CHANGED
@@ -373,7 +373,7 @@ class Twig_ExpressionParser
373
  $arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
374
 
375
  if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
376
- $type = Twig_TemplateInterface::METHOD_CALL;
377
  foreach ($this->parseArguments() as $n) {
378
  $arguments->addElement($n);
379
  }
@@ -578,6 +578,9 @@ class Twig_ExpressionParser
578
 
579
  if ($function instanceof Twig_SimpleFunction && $function->isDeprecated()) {
580
  $message = sprintf('Twig Function "%s" is deprecated', $function->getName());
 
 
 
581
  if ($function->getAlternative()) {
582
  $message .= sprintf('. Use "%s" instead', $function->getAlternative());
583
  }
@@ -606,6 +609,9 @@ class Twig_ExpressionParser
606
 
607
  if ($filter instanceof Twig_SimpleFilter && $filter->isDeprecated()) {
608
  $message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());
 
 
 
609
  if ($filter->getAlternative()) {
610
  $message .= sprintf('. Use "%s" instead', $filter->getAlternative());
611
  }
373
  $arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
374
 
375
  if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
376
+ $type = Twig_Template::METHOD_CALL;
377
  foreach ($this->parseArguments() as $n) {
378
  $arguments->addElement($n);
379
  }
578
 
579
  if ($function instanceof Twig_SimpleFunction && $function->isDeprecated()) {
580
  $message = sprintf('Twig Function "%s" is deprecated', $function->getName());
581
+ if (!is_bool($function->getDeprecatedVersion())) {
582
+ $message .= sprintf(' since version %s', $function->getDeprecatedVersion());
583
+ }
584
  if ($function->getAlternative()) {
585
  $message .= sprintf('. Use "%s" instead', $function->getAlternative());
586
  }
609
 
610
  if ($filter instanceof Twig_SimpleFilter && $filter->isDeprecated()) {
611
  $message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());
612
+ if (!is_bool($filter->getDeprecatedVersion())) {
613
+ $message .= sprintf(' since version %s', $filter->getDeprecatedVersion());
614
+ }
615
  if ($filter->getAlternative()) {
616
  $message .= sprintf('. Use "%s" instead', $filter->getAlternative());
617
  }
vendor/twig/twig/lib/Twig/Extension/Core.php CHANGED
@@ -213,11 +213,11 @@ class Twig_Extension_Core extends Twig_Extension
213
  new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')),
214
  new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')),
215
  new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')),
216
- new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas', 'deprecated' => true, 'alternative' => 'same as')),
217
  new Twig_SimpleTest('same as', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
218
  new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
219
  new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
220
- new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby', 'deprecated' => true, 'alternative' => 'divisible by')),
221
  new Twig_SimpleTest('divisible by', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
222
  new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')),
223
  new Twig_SimpleTest('empty', 'twig_test_empty'),
@@ -261,6 +261,7 @@ class Twig_Extension_Core extends Twig_Extension
261
  'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
262
  'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
263
  '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
 
264
  ),
265
  );
266
  }
@@ -277,6 +278,9 @@ class Twig_Extension_Core extends Twig_Extension
277
 
278
  if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) {
279
  $message = sprintf('Twig Test "%s" is deprecated', $name);
 
 
 
280
  if ($test->getAlternative()) {
281
  $message .= sprintf('. Use "%s" instead', $test->getAlternative());
282
  }
@@ -383,7 +387,7 @@ function twig_random(Twig_Environment $env, $values = null)
383
  return '';
384
  }
385
  if (null !== $charset = $env->getCharset()) {
386
- if ('UTF-8' != $charset) {
387
  $values = twig_convert_encoding($values, 'UTF-8', $charset);
388
  }
389
 
@@ -391,7 +395,7 @@ function twig_random(Twig_Environment $env, $values = null)
391
  // split at all positions, but not after the start and not before the end
392
  $values = preg_split('/(?<!^)(?!$)/u', $values);
393
 
394
- if ('UTF-8' != $charset) {
395
  foreach ($values as $i => $value) {
396
  $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8');
397
  }
@@ -536,7 +540,7 @@ function twig_replace_filter($str, $from, $to = null)
536
  if ($from instanceof Traversable) {
537
  $from = iterator_to_array($from);
538
  } elseif (is_string($from) && is_string($to)) {
539
- @trigger_error('Using "replace" with character by character replacement is deprecated and will be removed in Twig 2.0', E_USER_DEPRECATED);
540
 
541
  return strtr($str, $from, $to);
542
  } elseif (!is_array($from)) {
@@ -916,7 +920,7 @@ function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false
916
  if (null !== $charset = $env->getCharset()) {
917
  $string = (string) $item;
918
 
919
- if ('UTF-8' != $charset) {
920
  $item = twig_convert_encoding($string, 'UTF-8', $charset);
921
  }
922
 
@@ -924,7 +928,7 @@ function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false
924
 
925
  $string = implode('', array_reverse($matches[0]));
926
 
927
- if ('UTF-8' != $charset) {
928
  $string = twig_convert_encoding($string, $charset, 'UTF-8');
929
  }
930
 
@@ -1050,7 +1054,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
1050
  case 'js':
1051
  // escape all non-alphanumeric characters
1052
  // into their \xHH or \uHHHH representations
1053
- if ('UTF-8' != $charset) {
1054
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1055
  }
1056
 
@@ -1060,14 +1064,14 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
1060
 
1061
  $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string);
1062
 
1063
- if ('UTF-8' != $charset) {
1064
  $string = twig_convert_encoding($string, $charset, 'UTF-8');
1065
  }
1066
 
1067
  return $string;
1068
 
1069
  case 'css':
1070
- if ('UTF-8' != $charset) {
1071
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1072
  }
1073
 
@@ -1077,14 +1081,14 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
1077
 
1078
  $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string);
1079
 
1080
- if ('UTF-8' != $charset) {
1081
  $string = twig_convert_encoding($string, $charset, 'UTF-8');
1082
  }
1083
 
1084
  return $string;
1085
 
1086
  case 'html_attr':
1087
- if ('UTF-8' != $charset) {
1088
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1089
  }
1090
 
@@ -1094,7 +1098,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
1094
 
1095
  $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string);
1096
 
1097
- if ('UTF-8' != $charset) {
1098
  $string = twig_convert_encoding($string, $charset, 'UTF-8');
1099
  }
1100
 
@@ -1272,7 +1276,7 @@ if (function_exists('mb_get_info')) {
1272
  */
1273
  function twig_upper_filter(Twig_Environment $env, $string)
1274
  {
1275
- if (null !== ($charset = $env->getCharset())) {
1276
  return mb_strtoupper($string, $charset);
1277
  }
1278
 
@@ -1289,7 +1293,7 @@ if (function_exists('mb_get_info')) {
1289
  */
1290
  function twig_lower_filter(Twig_Environment $env, $string)
1291
  {
1292
- if (null !== ($charset = $env->getCharset())) {
1293
  return mb_strtolower($string, $charset);
1294
  }
1295
 
@@ -1306,7 +1310,7 @@ if (function_exists('mb_get_info')) {
1306
  */
1307
  function twig_title_string_filter(Twig_Environment $env, $string)
1308
  {
1309
- if (null !== ($charset = $env->getCharset())) {
1310
  return mb_convert_case($string, MB_CASE_TITLE, $charset);
1311
  }
1312
 
213
  new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')),
214
  new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')),
215
  new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')),
216
+ new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas', 'deprecated' => '1.21', 'alternative' => 'same as')),
217
  new Twig_SimpleTest('same as', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
218
  new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
219
  new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
220
+ new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby', 'deprecated' => '1.21', 'alternative' => 'divisible by')),
221
  new Twig_SimpleTest('divisible by', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
222
  new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')),
223
  new Twig_SimpleTest('empty', 'twig_test_empty'),
261
  'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
262
  'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
263
  '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
264
+ '??' => array('precedence' => 300, 'class' => 'Twig_Node_Expression_NullCoalesce', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
265
  ),
266
  );
267
  }
278
 
279
  if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) {
280
  $message = sprintf('Twig Test "%s" is deprecated', $name);
281
+ if (!is_bool($test->getDeprecatedVersion())) {
282
+ $message .= sprintf(' since version %s', $test->getDeprecatedVersion());
283
+ }
284
  if ($test->getAlternative()) {
285
  $message .= sprintf('. Use "%s" instead', $test->getAlternative());
286
  }
387
  return '';
388
  }
389
  if (null !== $charset = $env->getCharset()) {
390
+ if ('UTF-8' !== $charset) {
391
  $values = twig_convert_encoding($values, 'UTF-8', $charset);
392
  }
393
 
395
  // split at all positions, but not after the start and not before the end
396
  $values = preg_split('/(?<!^)(?!$)/u', $values);
397
 
398
+ if ('UTF-8' !== $charset) {
399
  foreach ($values as $i => $value) {
400
  $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8');
401
  }
540
  if ($from instanceof Traversable) {
541
  $from = iterator_to_array($from);
542
  } elseif (is_string($from) && is_string($to)) {
543
+ @trigger_error('Using "replace" with character by character replacement is deprecated since version 1.22 and will be removed in Twig 2.0', E_USER_DEPRECATED);
544
 
545
  return strtr($str, $from, $to);
546
  } elseif (!is_array($from)) {
920
  if (null !== $charset = $env->getCharset()) {
921
  $string = (string) $item;
922
 
923
+ if ('UTF-8' !== $charset) {
924
  $item = twig_convert_encoding($string, 'UTF-8', $charset);
925
  }
926
 
928
 
929
  $string = implode('', array_reverse($matches[0]));
930
 
931
+ if ('UTF-8' !== $charset) {
932
  $string = twig_convert_encoding($string, $charset, 'UTF-8');
933
  }
934
 
1054
  case 'js':
1055
  // escape all non-alphanumeric characters
1056
  // into their \xHH or \uHHHH representations
1057
+ if ('UTF-8' !== $charset) {
1058
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1059
  }
1060
 
1064
 
1065
  $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string);
1066
 
1067
+ if ('UTF-8' !== $charset) {
1068
  $string = twig_convert_encoding($string, $charset, 'UTF-8');
1069
  }
1070
 
1071
  return $string;
1072
 
1073
  case 'css':
1074
+ if ('UTF-8' !== $charset) {
1075
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1076
  }
1077
 
1081
 
1082
  $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string);
1083
 
1084
+ if ('UTF-8' !== $charset) {
1085
  $string = twig_convert_encoding($string, $charset, 'UTF-8');
1086
  }
1087
 
1088
  return $string;
1089
 
1090
  case 'html_attr':
1091
+ if ('UTF-8' !== $charset) {
1092
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1093
  }
1094
 
1098
 
1099
  $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string);
1100
 
1101
+ if ('UTF-8' !== $charset) {
1102
  $string = twig_convert_encoding($string, $charset, 'UTF-8');
1103
  }
1104
 
1276
  */
1277
  function twig_upper_filter(Twig_Environment $env, $string)
1278
  {
1279
+ if (null !== $charset = $env->getCharset()) {
1280
  return mb_strtoupper($string, $charset);
1281
  }
1282
 
1293
  */
1294
  function twig_lower_filter(Twig_Environment $env, $string)
1295
  {
1296
+ if (null !== $charset = $env->getCharset()) {
1297
  return mb_strtolower($string, $charset);
1298
  }
1299
 
1310
  */
1311
  function twig_title_string_filter(Twig_Environment $env, $string)
1312
  {
1313
+ if (null !== $charset = $env->getCharset()) {
1314
  return mb_convert_case($string, MB_CASE_TITLE, $charset);
1315
  }
1316
 
vendor/twig/twig/lib/Twig/Extension/Escaper.php CHANGED
@@ -53,7 +53,7 @@ class Twig_Extension_Escaper extends Twig_Extension
53
  {
54
  // for BC
55
  if (true === $defaultStrategy) {
56
- @trigger_error('Using "true" as the default strategy is deprecated. Use "html" instead.', E_USER_DEPRECATED);
57
 
58
  $defaultStrategy = 'html';
59
  }
53
  {
54
  // for BC
55
  if (true === $defaultStrategy) {
56
+ @trigger_error('Using "true" as the default strategy is deprecated since version 1.21. Use "html" instead.', E_USER_DEPRECATED);
57
 
58
  $defaultStrategy = 'html';
59
  }
vendor/twig/twig/lib/Twig/ExtensionInterface.php CHANGED
@@ -23,7 +23,7 @@ interface Twig_ExtensionInterface
23
  *
24
  * @param Twig_Environment $environment The current Twig_Environment instance
25
  *
26
- * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterace instead
27
  */
28
  public function initRuntime(Twig_Environment $environment);
29
 
23
  *
24
  * @param Twig_Environment $environment The current Twig_Environment instance
25
  *
26
+ * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead
27
  */
28
  public function initRuntime(Twig_Environment $environment);
29
 
vendor/twig/twig/lib/Twig/Lexer.php CHANGED
@@ -288,7 +288,7 @@ class Twig_Lexer implements Twig_LexerInterface
288
  protected function lexRawData($tag)
289
  {
290
  if ('raw' === $tag) {
291
- @trigger_error(sprintf('Twig Tag "raw" is deprecated. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED);
292
  }
293
 
294
  if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
288
  protected function lexRawData($tag)
289
  {
290
  if ('raw' === $tag) {
291
+ @trigger_error(sprintf('Twig Tag "raw" is deprecated since version 1.21. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED);
292
  }
293
 
294
  if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
vendor/twig/twig/lib/Twig/Node.php CHANGED
@@ -74,7 +74,7 @@ class Twig_Node implements Twig_NodeInterface
74
  */
75
  public function toXml($asDom = false)
76
  {
77
- @trigger_error(sprintf('%s is deprecated.', __METHOD__), E_USER_DEPRECATED);
78
 
79
  $dom = new DOMDocument('1.0', 'UTF-8');
80
  $dom->formatOutput = true;
74
  */
75
  public function toXml($asDom = false)
76
  {
77
+ @trigger_error(sprintf('%s is deprecated since version 1.16.1 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED);
78
 
79
  $dom = new DOMDocument('1.0', 'UTF-8');
80
  $dom->formatOutput = true;
vendor/twig/twig/lib/Twig/Node/Expression/Call.php CHANGED
@@ -122,53 +122,14 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
122
  }
123
 
124
  // manage named arguments
125
- if (is_array($callable)) {
126
- $r = new ReflectionMethod($callable[0], $callable[1]);
127
- } elseif (is_object($callable) && !$callable instanceof Closure) {
128
- $r = new ReflectionObject($callable);
129
- $r = $r->getMethod('__invoke');
130
- } elseif (is_string($callable) && false !== strpos($callable, '::')) {
131
- $r = new ReflectionMethod($callable);
132
- } else {
133
- $r = new ReflectionFunction($callable);
134
- }
135
-
136
- $definition = $r->getParameters();
137
- if ($this->hasNode('node')) {
138
- array_shift($definition);
139
- }
140
- if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
141
- array_shift($definition);
142
- }
143
- if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
144
- array_shift($definition);
145
- }
146
- if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
147
- foreach ($this->getAttribute('arguments') as $argument) {
148
- array_shift($definition);
149
- }
150
- }
151
- if ($isVariadic) {
152
- $argument = end($definition);
153
- if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && array() === $argument->getDefaultValue()) {
154
- array_pop($definition);
155
- } else {
156
- $callableName = $r->name;
157
- if ($r->getDeclaringClass()) {
158
- $callableName = $r->getDeclaringClass()->name.'::'.$callableName;
159
- }
160
-
161
- throw new LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = array()".', $callableName, $callType, $callName));
162
- }
163
- }
164
-
165
  $arguments = array();
166
  $names = array();
167
  $missingArguments = array();
168
  $optionalArguments = array();
169
  $pos = 0;
170
- foreach ($definition as $param) {
171
- $names[] = $name = $this->normalizeName($param->name);
172
 
173
  if (array_key_exists($name, $parameters)) {
174
  if (array_key_exists($pos, $parameters)) {
@@ -192,9 +153,9 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
192
  unset($parameters[$pos]);
193
  $optionalArguments = array();
194
  ++$pos;
195
- } elseif ($param->isDefaultValueAvailable()) {
196
- $optionalArguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1);
197
- } elseif ($param->isOptional()) {
198
  if (empty($parameters)) {
199
  break;
200
  } else {
@@ -244,4 +205,49 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
244
  {
245
  return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
246
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
122
  }
123
 
124
  // manage named arguments
125
+ $callableParameters = $this->getCallableParameters($callable, $isVariadic);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  $arguments = array();
127
  $names = array();
128
  $missingArguments = array();
129
  $optionalArguments = array();
130
  $pos = 0;
131
+ foreach ($callableParameters as $callableParameter) {
132
+ $names[] = $name = $this->normalizeName($callableParameter->name);
133
 
134
  if (array_key_exists($name, $parameters)) {
135
  if (array_key_exists($pos, $parameters)) {
153
  unset($parameters[$pos]);
154
  $optionalArguments = array();
155
  ++$pos;
156
+ } elseif ($callableParameter->isDefaultValueAvailable()) {
157
+ $optionalArguments[] = new Twig_Node_Expression_Constant($callableParameter->getDefaultValue(), -1);
158
+ } elseif ($callableParameter->isOptional()) {
159
  if (empty($parameters)) {
160
  break;
161
  } else {
205
  {
206
  return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
207
  }
208
+
209
+ private function getCallableParameters($callable, $isVariadic)
210
+ {
211
+ if (is_array($callable)) {
212
+ $r = new ReflectionMethod($callable[0], $callable[1]);
213
+ } elseif (is_object($callable) && !$callable instanceof Closure) {
214
+ $r = new ReflectionObject($callable);
215
+ $r = $r->getMethod('__invoke');
216
+ } elseif (is_string($callable) && false !== strpos($callable, '::')) {
217
+ $r = new ReflectionMethod($callable);
218
+ } else {
219
+ $r = new ReflectionFunction($callable);
220
+ }
221
+
222
+ $parameters = $r->getParameters();
223
+ if ($this->hasNode('node')) {
224
+ array_shift($parameters);
225
+ }
226
+ if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
227
+ array_shift($parameters);
228
+ }
229
+ if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
230
+ array_shift($parameters);
231
+ }
232
+ if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
233
+ foreach ($this->getAttribute('arguments') as $argument) {
234
+ array_shift($parameters);
235
+ }
236
+ }
237
+ if ($isVariadic) {
238
+ $argument = end($parameters);
239
+ if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && array() === $argument->getDefaultValue()) {
240
+ array_pop($parameters);
241
+ } else {
242
+ $callableName = $r->name;
243
+ if ($r->getDeclaringClass()) {
244
+ $callableName = $r->getDeclaringClass()->name.'::'.$callableName;
245
+ }
246
+
247
+ throw new LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = array()".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
248
+ }
249
+ }
250
+
251
+ return $parameters;
252
+ }
253
  }
vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php CHANGED
@@ -9,10 +9,14 @@
9
  * file that was distributed with this source code.
10
  */
11
 
 
 
12
  /**
13
  * Represents an extension call node.
14
  *
15
  * @author Fabien Potencier <fabien@symfony.com>
 
 
16
  */
17
  class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression
18
  {
9
  * file that was distributed with this source code.
10
  */
11
 
12
+ @trigger_error('The Twig_Node_Expression_ExtensionReference class is deprecated since version 1.23 and will be removed in 2.0.', E_USER_DEPRECATED);
13
+
14
  /**
15
  * Represents an extension call node.
16
  *
17
  * @author Fabien Potencier <fabien@symfony.com>
18
+ *
19
+ * @deprecated since 1.23 and will be removed in 2.0.
20
  */
21
  class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression
22
  {
vendor/twig/twig/lib/Twig/Node/Expression/Name.php CHANGED
@@ -30,19 +30,11 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression
30
 
31
  if ($this->getAttribute('is_defined_test')) {
32
  if ($this->isSpecial()) {
33
- if ('_self' === $name) {
34
- @trigger_error(sprintf('Global variable "_self" is deprecated in %s at line %d', '?', $this->getLine()), E_USER_DEPRECATED);
35
- }
36
-
37
  $compiler->repr(true);
38
  } else {
39
  $compiler->raw('array_key_exists(')->repr($name)->raw(', $context)');
40
  }
41
  } elseif ($this->isSpecial()) {
42
- if ('_self' === $name) {
43
- @trigger_error(sprintf('Global variable "_self" is deprecated in %s at line %d', '?', $this->getLine()), E_USER_DEPRECATED);
44
- }
45
-
46
  $compiler->raw($this->specialVars[$name]);
47
  } elseif ($this->getAttribute('always_defined')) {
48
  $compiler
30
 
31
  if ($this->getAttribute('is_defined_test')) {
32
  if ($this->isSpecial()) {
 
 
 
 
33
  $compiler->repr(true);
34
  } else {
35
  $compiler->raw('array_key_exists(')->repr($name)->raw(', $context)');
36
  }
37
  } elseif ($this->isSpecial()) {
 
 
 
 
38
  $compiler->raw($this->specialVars[$name]);
39
  } elseif ($this->getAttribute('always_defined')) {
40
  $compiler
vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_NullCoalesce extends Twig_Node_Expression_Conditional
12
+ {
13
+ public function __construct(Twig_NodeInterface $left, Twig_NodeInterface $right, $lineno)
14
+ {
15
+ $test = new Twig_Node_Expression_Binary_And(
16
+ new Twig_Node_Expression_Test_Defined(clone $left, 'defined', new Twig_Node(), $left->getLine()),
17
+ new Twig_Node_Expression_Unary_Not(new Twig_Node_Expression_Test_Null($left, 'null', new Twig_Node(), $left->getLine()), $left->getLine()),
18
+ $left->getLine()
19
+ );
20
+
21
+ parent::__construct($test, $left, $right, $lineno);
22
+ }
23
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php CHANGED
@@ -25,17 +25,19 @@ class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test
25
  {
26
  public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
27
  {
28
- parent::__construct($node, $name, $arguments, $lineno);
29
-
30
  if ($node instanceof Twig_Node_Expression_Name) {
31
  $node->setAttribute('is_defined_test', true);
32
  } elseif ($node instanceof Twig_Node_Expression_GetAttr) {
33
  $node->setAttribute('is_defined_test', true);
34
 
35
  $this->changeIgnoreStrictCheck($node);
 
 
36
  } else {
37
  throw new Twig_Error_Syntax('The "defined" test only works with simple variables.', $this->getLine());
38
  }
 
 
39
  }
40
 
41
  protected function changeIgnoreStrictCheck(Twig_Node_Expression_GetAttr $node)
25
  {
26
  public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
27
  {
 
 
28
  if ($node instanceof Twig_Node_Expression_Name) {
29
  $node->setAttribute('is_defined_test', true);
30
  } elseif ($node instanceof Twig_Node_Expression_GetAttr) {
31
  $node->setAttribute('is_defined_test', true);
32
 
33
  $this->changeIgnoreStrictCheck($node);
34
+ } elseif ($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array) {
35
+ $node = new Twig_Node_Expression_Constant(true, $node->getLine());
36
  } else {
37
  throw new Twig_Error_Syntax('The "defined" test only works with simple variables.', $this->getLine());
38
  }
39
+
40
+ parent::__construct($node, $name, $arguments, $lineno);
41
  }
42
 
43
  protected function changeIgnoreStrictCheck(Twig_Node_Expression_GetAttr $node)
vendor/twig/twig/lib/Twig/Parser.php CHANGED
@@ -63,7 +63,12 @@ class Twig_Parser implements Twig_ParserInterface
63
  public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
64
  {
65
  // push all variables into the stack to keep the current state of the parser
66
- $vars = get_object_vars($this);
 
 
 
 
 
67
  unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']);
68
  $this->stack[] = $vars;
69
 
63
  public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
64
  {
65
  // push all variables into the stack to keep the current state of the parser
66
+ // using get_object_vars() instead of foreach would lead to https://bugs.php.net/71336
67
+ $vars = array();
68
+ foreach ($this as $k => $v) {
69
+ $vars[$k] = $v;
70
+ }
71
+
72
  unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']);
73
  $this->stack[] = $vars;
74
 
vendor/twig/twig/lib/Twig/SimpleFilter.php CHANGED
@@ -101,6 +101,11 @@ class Twig_SimpleFilter
101
  }
102
 
103
  public function isDeprecated()
 
 
 
 
 
104
  {
105
  return $this->options['deprecated'];
106
  }
101
  }
102
 
103
  public function isDeprecated()
104
+ {
105
+ return (bool) $this->options['deprecated'];
106
+ }
107
+
108
+ public function getDeprecatedVersion()
109
  {
110
  return $this->options['deprecated'];
111
  }
vendor/twig/twig/lib/Twig/SimpleFunction.php CHANGED
@@ -91,6 +91,11 @@ class Twig_SimpleFunction
91
  }
92
 
93
  public function isDeprecated()
 
 
 
 
 
94
  {
95
  return $this->options['deprecated'];
96
  }
91
  }
92
 
93
  public function isDeprecated()
94
+ {
95
+ return (bool) $this->options['deprecated'];
96
+ }
97
+
98
+ public function getDeprecatedVersion()
99
  {
100
  return $this->options['deprecated'];
101
  }
vendor/twig/twig/lib/Twig/SimpleTest.php CHANGED
@@ -53,6 +53,11 @@ class Twig_SimpleTest
53
  }
54
 
55
  public function isDeprecated()
 
 
 
 
 
56
  {
57
  return $this->options['deprecated'];
58
  }
53
  }
54
 
55
  public function isDeprecated()
56
+ {
57
+ return (bool) $this->options['deprecated'];
58
+ }
59
+
60
+ public function getDeprecatedVersion()
61
  {
62
  return $this->options['deprecated'];
63
  }
vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php CHANGED
@@ -50,7 +50,7 @@ class Twig_TokenParser_AutoEscape extends Twig_TokenParser
50
  }
51
 
52
  if ($compat && $stream->test(Twig_Token::NAME_TYPE)) {
53
- @trigger_error('Using the autoescape tag with "true" or "false" before the strategy name is deprecated.', E_USER_DEPRECATED);
54
 
55
  if (false === $value) {
56
  throw new Twig_Error_Syntax('Unexpected escaping strategy as you set autoescaping to false.', $stream->getCurrent()->getLine(), $stream->getFilename());
50
  }
51
 
52
  if ($compat && $stream->test(Twig_Token::NAME_TYPE)) {
53
+ @trigger_error('Using the autoescape tag with "true" or "false" before the strategy name is deprecated since version 1.21.', E_USER_DEPRECATED);
54
 
55
  if (false === $value) {
56
  throw new Twig_Error_Syntax('Unexpected escaping strategy as you set autoescaping to false.', $stream->getCurrent()->getLine(), $stream->getFilename());
vendor/twig/twig/test/Twig/Tests/Cache/FilesystemTest.php CHANGED
@@ -9,35 +9,26 @@
9
  * file that was distributed with this source code.
10
  */
11
 
 
 
12
  class Twig_Tests_Cache_FilesystemTest extends PHPUnit_Framework_TestCase
13
  {
14
- private $nonce;
15
  private $classname;
16
  private $directory;
17
  private $cache;
18
 
19
  protected function setUp()
20
  {
21
- $this->nonce = hash('sha256', uniqid(mt_rand(), true));
22
- $this->classname = '__Twig_Tests_Cache_FilesystemTest_Template_'.$this->nonce;
23
- $this->directory = sys_get_temp_dir().'/twig-test-'.$this->nonce;
24
  $this->cache = new Twig_Cache_Filesystem($this->directory);
25
  }
26
 
27
  protected function tearDown()
28
  {
29
  if (file_exists($this->directory)) {
30
- $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->directory), RecursiveIteratorIterator::CHILD_FIRST);
31
- foreach ($iterator as $filename => $fileInfo) {
32
- if (!$iterator->isDot()) {
33
- if ($fileInfo->isDir()) {
34
- rmdir($filename);
35
- } else {
36
- unlink($filename);
37
- }
38
- }
39
- }
40
- rmdir($this->directory);
41
  }
42
  }
43
 
@@ -86,10 +77,14 @@ class Twig_Tests_Cache_FilesystemTest extends PHPUnit_Framework_TestCase
86
 
87
  /**
88
  * @expectedException RuntimeException
89
- * @expectedExceptionMessageRegExp #^Unable to create the cache directory #
90
  */
91
  public function testWriteFailMkdir()
92
  {
 
 
 
 
93
  $key = $this->directory.'/cache/cachefile.php';
94
  $content = $this->generateSource();
95
 
@@ -104,10 +99,14 @@ class Twig_Tests_Cache_FilesystemTest extends PHPUnit_Framework_TestCase
104
 
105
  /**
106
  * @expectedException RuntimeException
107
- * @expectedExceptionMessageRegExp #^Unable to write in the cache directory #
108
  */
109
  public function testWriteFailDirWritable()
110
  {
 
 
 
 
111
  $key = $this->directory.'/cache/cachefile.php';
112
  $content = $this->generateSource();
113
 
@@ -124,7 +123,7 @@ class Twig_Tests_Cache_FilesystemTest extends PHPUnit_Framework_TestCase
124
 
125
  /**
126
  * @expectedException RuntimeException
127
- * @expectedExceptionMessageRegExp #^Failed to write cache file #
128
  */
129
  public function testWriteFailWriteFile()
130
  {
@@ -160,6 +159,31 @@ class Twig_Tests_Cache_FilesystemTest extends PHPUnit_Framework_TestCase
160
  $this->assertSame(0, $this->cache->getTimestamp($key));
161
  }
162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  private function generateSource()
164
  {
165
  return strtr('<?php class {{classname}} {}', array(
9
  * file that was distributed with this source code.
10
  */
11
 
12
+ require_once dirname(dirname(__FILE__)).'/FilesystemHelper.php';
13
+
14
  class Twig_Tests_Cache_FilesystemTest extends PHPUnit_Framework_TestCase
15
  {
 
16
  private $classname;
17
  private $directory;
18
  private $cache;
19
 
20
  protected function setUp()
21
  {
22
+ $nonce = hash('sha256', uniqid(mt_rand(), true));
23
+ $this->classname = '__Twig_Tests_Cache_FilesystemTest_Template_'.$nonce;
24
+ $this->directory = sys_get_temp_dir().'/twig-test';
25
  $this->cache = new Twig_Cache_Filesystem($this->directory);
26
  }
27
 
28
  protected function tearDown()
29
  {
30
  if (file_exists($this->directory)) {
31
+ Twig_Tests_FilesystemHelper::removeDir($this->directory);
 
 
 
 
 
 
 
 
 
 
32
  }
33
  }
34
 
77
 
78
  /**
79
  * @expectedException RuntimeException
80
+ * @expectedExceptionMessage Unable to create the cache directory
81
  */
82
  public function testWriteFailMkdir()
83
  {
84
+ if (defined('PHP_WINDOWS_VERSION_BUILD')) {
85
+ $this->markTestSkipped('Read-only directories not possible on Windows.');
86
+ }
87
+
88
  $key = $this->directory.'/cache/cachefile.php';
89
  $content = $this->generateSource();
90
 
99
 
100
  /**
101
  * @expectedException RuntimeException
102
+ * @expectedExceptionMessage Unable to write in the cache directory
103
  */
104
  public function testWriteFailDirWritable()
105
  {
106
+ if (defined('PHP_WINDOWS_VERSION_BUILD')) {
107
+ $this->markTestSkipped('Read-only directories not possible on Windows.');
108
+ }
109
+
110
  $key = $this->directory.'/cache/cachefile.php';
111
  $content = $this->generateSource();
112
 
123
 
124
  /**
125
  * @expectedException RuntimeException
126
+ * @expectedExceptionMessage Failed to write cache file
127
  */
128
  public function testWriteFailWriteFile()
129
  {
159
  $this->assertSame(0, $this->cache->getTimestamp($key));
160
  }
161
 
162
+ /**
163
+ * Test file cache is tolerant towards trailing (back)slashes on the configured cache directory.
164
+ *
165
+ * @dataProvider provideDirectories
166
+ */
167
+ public function testGenerateKey($expected, $input)
168
+ {
169
+ $cache = new Twig_Cache_Filesystem($input);
170
+ $this->assertRegExp($expected, $cache->generateKey('_test_', get_class($this)));
171
+ }
172
+
173
+ public function provideDirectories()
174
+ {
175
+ $pattern = '#a/b/[a-zA-Z0-9]+/[a-zA-Z0-9]+.php$#';
176
+
177
+ return array(
178
+ array($pattern, 'a/b'),
179
+ array($pattern, 'a/b/'),
180
+ array($pattern, 'a/b\\'),
181
+ array($pattern, 'a/b\\/'),
182
+ array($pattern, 'a/b\\//'),
183
+ array('#/'.substr($pattern, 1), '/a/b'),
184
+ );
185
+ }
186
+
187
  private function generateSource()
188
  {
189
  return strtr('<?php class {{classname}} {}', array(
vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php CHANGED
@@ -9,6 +9,8 @@
9
  * file that was distributed with this source code.
10
  */
11
 
 
 
12
  class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
13
  {
14
  private $deprecations = array();
@@ -154,8 +156,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
154
 
155
  public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate()
156
  {
157
- $uid = function_exists('posix_getuid') ? posix_getuid() : '';
158
- $cache = new Twig_Cache_Filesystem($dir = sys_get_temp_dir().'/twig'.$uid);
159
  $options = array('cache' => $cache, 'auto_reload' => false, 'debug' => false);
160
 
161
  // force compilation
@@ -178,7 +179,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
178
  $output = $twig->render('index', array('foo' => 'bar'));
179
  $this->assertEquals('bar', $output);
180
 
181
- unlink($key);
182
  }
183
 
184
  public function testAutoReloadCacheMiss()
@@ -290,7 +291,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
290
  $this->assertArrayHasKey('foo_global', $twig->getGlobals());
291
 
292
  $this->assertCount(1, $this->deprecations);
293
- $this->assertContains('Defining the getGlobals() method in the "environment_test" extension is deprecated', $this->deprecations[0]);
294
 
295
  restore_error_handler();
296
  }
@@ -352,7 +353,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
352
  $twig->initRuntime();
353
 
354
  $this->assertCount(1, $this->deprecations);
355
- $this->assertContains('Defining the initRuntime() method in the "with_deprecation" extension is deprecated.', $this->deprecations[0]);
356
 
357
  restore_error_handler();
358
  }
9
  * file that was distributed with this source code.
10
  */
11
 
12
+ require_once dirname(__FILE__).'/FilesystemHelper.php';
13
+
14
  class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
15
  {
16
  private $deprecations = array();
156
 
157
  public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate()
158
  {
159
+ $cache = new Twig_Cache_Filesystem($dir = sys_get_temp_dir().'/twig');
 
160
  $options = array('cache' => $cache, 'auto_reload' => false, 'debug' => false);
161
 
162
  // force compilation
179
  $output = $twig->render('index', array('foo' => 'bar'));
180
  $this->assertEquals('bar', $output);
181
 
182
+ Twig_Tests_FilesystemHelper::removeDir($dir);
183
  }
184
 
185
  public function testAutoReloadCacheMiss()
291
  $this->assertArrayHasKey('foo_global', $twig->getGlobals());
292
 
293
  $this->assertCount(1, $this->deprecations);
294
+ $this->assertContains('Defining the getGlobals() method in the "environment_test" extension ', $this->deprecations[0]);
295
 
296
  restore_error_handler();
297
  }
353
  $twig->initRuntime();
354
 
355
  $this->assertCount(1, $this->deprecations);
356
+ $this->assertContains('Defining the initRuntime() method in the "with_deprecation" extension is deprecated since version 1.23.', $this->deprecations[0]);
357
 
358
  restore_error_handler();
359
  }
vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php CHANGED
@@ -13,7 +13,7 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
13
  {
14
  protected static $params, $templates;
15
 
16
- public function setUp()
17
  {
18
  self::$params = array(
19
  'name' => 'Fabien',
13
  {
14
  protected static $params, $templates;
15
 
16
+ protected function setUp()
17
  {
18
  self::$params = array(
19
  'name' => 'Fabien',
vendor/twig/twig/test/Twig/Tests/FileCachingTest.php CHANGED
@@ -9,13 +9,14 @@
9
  * file that was distributed with this source code.
10
  */
11
 
 
 
12
  class Twig_Tests_FileCachingTest extends PHPUnit_Framework_TestCase
13
  {
14
- protected $fileName;
15
- protected $env;
16
- protected $tmpDir;
17
 
18
- public function setUp()
19
  {
20
  $this->tmpDir = sys_get_temp_dir().'/TwigTests';
21
  if (!file_exists($this->tmpDir)) {
@@ -29,13 +30,9 @@ class Twig_Tests_FileCachingTest extends PHPUnit_Framework_TestCase
29
  $this->env = new Twig_Environment(new Twig_Loader_Array(array('index' => 'index', 'index2' => 'index2')), array('cache' => $this->tmpDir));
30
  }
31
 
32
- public function tearDown()
33
  {
34
- if ($this->fileName) {
35
- unlink($this->fileName);
36
- }
37
-
38
- $this->removeDir($this->tmpDir);
39
  }
40
 
41
  /**
@@ -48,7 +45,6 @@ class Twig_Tests_FileCachingTest extends PHPUnit_Framework_TestCase
48
  $cacheFileName = $this->env->getCacheFilename($name);
49
 
50
  $this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.');
51
- $this->fileName = $cacheFileName;
52
  }
53
 
54
  /**
@@ -64,22 +60,4 @@ class Twig_Tests_FileCachingTest extends PHPUnit_Framework_TestCase
64
  $this->env->clearCacheFiles();
65
  $this->assertFalse(file_exists($cacheFileName), 'Cache file was not cleared.');
66
  }
67
-
68
- private function removeDir($target)
69
- {
70
- $fp = opendir($target);
71
- while (false !== $file = readdir($fp)) {
72
- if (in_array($file, array('.', '..'))) {
73
- continue;
74
- }
75
-
76
- if (is_dir($target.'/'.$file)) {
77
- self::removeDir($target.'/'.$file);
78
- } else {
79
- unlink($target.'/'.$file);
80
- }
81
- }
82
- closedir($fp);
83
- rmdir($target);
84
- }
85
  }
9
  * file that was distributed with this source code.
10
  */
11
 
12
+ require_once dirname(__FILE__).'/FilesystemHelper.php';
13
+
14
  class Twig_Tests_FileCachingTest extends PHPUnit_Framework_TestCase
15
  {
16
+ private $env;
17
+ private $tmpDir;
 
18
 
19
+ protected function setUp()
20
  {
21
  $this->tmpDir = sys_get_temp_dir().'/TwigTests';
22
  if (!file_exists($this->tmpDir)) {
30
  $this->env = new Twig_Environment(new Twig_Loader_Array(array('index' => 'index', 'index2' => 'index2')), array('cache' => $this->tmpDir));
31
  }
32
 
33
+ protected function tearDown()
34
  {
35
+ Twig_Tests_FilesystemHelper::removeDir($this->tmpDir);
 
 
 
 
36
  }
37
 
38
  /**
45
  $cacheFileName = $this->env->getCacheFilename($name);
46
 
47
  $this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.');
 
48
  }
49
 
50
  /**
60
  $this->env->clearCacheFiles();
61
  $this->assertFalse(file_exists($cacheFileName), 'Cache file was not cleared.');
62
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
vendor/twig/twig/test/Twig/Tests/FilesystemHelper.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ class Twig_Tests_FilesystemHelper
13
+ {
14
+ public static function removeDir($dir)
15
+ {
16
+ $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, PHP_VERSION_ID < 50300 ? 0 : FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
17
+ foreach ($iterator as $filename => $fileInfo) {
18
+ if ($iterator->isDot()) {
19
+ continue;
20
+ }
21
+
22
+ if ($fileInfo->isDir()) {
23
+ rmdir($filename);
24
+ } else {
25
+ unlink($filename);
26
+ }
27
+ }
28
+ rmdir($dir);
29
+ }
30
+ }
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test CHANGED
@@ -17,6 +17,7 @@
17
  block1extended
18
  {% endblock %}
19
  {% endembed %}
 
20
  {% endblock %}
21
  --TEMPLATE(base.twig)--
22
  A
@@ -54,4 +55,6 @@ A
54
  block1extended
55
  B
56
  block2
57
- CB
 
 
17
  block1extended
18
  {% endblock %}
19
  {% endembed %}
20
+ {{ parent() }}
21
  {% endblock %}
22
  --TEMPLATE(base.twig)--
23
  A
55
  block1extended
56
  B
57
  block2
58
+ C blockc2base
59
+
60
+ B
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test CHANGED
@@ -26,6 +26,13 @@
26
  {{ object.self.foo is defined ? 'ok' : 'ko' }}
27
  {{ object.self.undefinedMethod is defined ? 'ko' : 'ok' }}
28
  {{ object.undefinedMethod.self is defined ? 'ko' : 'ok' }}
 
 
 
 
 
 
 
29
  --DATA--
30
  return array(
31
  'definedVar' => 'defined',
@@ -65,6 +72,13 @@ ok
65
  ok
66
  ok
67
  ok
 
 
 
 
 
 
 
68
  --DATA--
69
  return array(
70
  'definedVar' => 'defined',
@@ -106,3 +120,10 @@ ok
106
  ok
107
  ok
108
  ok
 
 
 
 
 
 
 
26
  {{ object.self.foo is defined ? 'ok' : 'ko' }}
27
  {{ object.self.undefinedMethod is defined ? 'ko' : 'ok' }}
28
  {{ object.undefinedMethod.self is defined ? 'ko' : 'ok' }}
29
+ {{ 0 is defined ? 'ok' : 'ko' }}
30
+ {{ "foo" is defined ? 'ok' : 'ko' }}
31
+ {{ true is defined ? 'ok' : 'ko' }}
32
+ {{ false is defined ? 'ok' : 'ko' }}
33
+ {{ null is defined ? 'ok' : 'ko' }}
34
+ {{ [1, 2] is defined ? 'ok' : 'ko' }}
35
+ {{ { foo: "bar" } is defined ? 'ok' : 'ko' }}
36
  --DATA--
37
  return array(
38
  'definedVar' => 'defined',
72
  ok
73
  ok
74
  ok
75
+ ok
76
+ ok
77
+ ok
78
+ ok
79
+ ok
80
+ ok
81
+ ok
82
  --DATA--
83
  return array(
84
  'definedVar' => 'defined',
120
  ok
121
  ok
122
  ok
123
+ ok
124
+ ok
125
+ ok
126
+ ok
127
+ ok
128
+ ok
129
+ ok
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/null_coalesce.test ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ Twig supports the ?? operator
3
+ --TEMPLATE--
4
+ {{ 'OK' ?? 'KO' }}
5
+ {{ null ?? 'OK' }}
6
+ {{ bar ?? 'KO' }}
7
+ {{ baz ?? 'OK' }}
8
+ {{ foo.bar ?? 'KO' }}
9
+ {{ foo.missing ?? 'OK' }}
10
+ {{ foo.bar.baz.missing ?? 'OK' }}
11
+ {{ foo['bar'] ?? 'KO' }}
12
+ {{ foo['missing'] ?? 'OK' }}
13
+ {{ nope ?? nada ?? 'OK' }}
14
+ {{ 1 + nope ?? nada ?? 2 }}
15
+ {{ 1 + nope ?? 3 + nada ?? 2 }}
16
+ --DATA--
17
+ return array('bar' => 'OK', 'foo' => array('bar' => 'OK'))
18
+ --EXPECT--
19
+ OK
20
+ OK
21
+ OK
22
+ OK
23
+ OK
24
+ OK
25
+ OK
26
+ OK
27
+ OK
28
+ OK
29
+ 3
30
+ 6
vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php CHANGED
@@ -13,7 +13,7 @@ class Twig_Tests_TokenStreamTest extends PHPUnit_Framework_TestCase
13
  {
14
  protected static $tokens;
15
 
16
- public function setUp()
17
  {
18
  self::$tokens = array(
19
  new Twig_Token(Twig_Token::TEXT_TYPE, 1, 1),
13
  {
14
  protected static $tokens;
15
 
16
+ protected function setUp()
17
  {
18
  self::$tokens = array(
19
  new Twig_Token(Twig_Token::TEXT_TYPE, 1, 1),
vendor/twig/twig/test/Twig/Tests/escapingTest.php CHANGED
@@ -144,7 +144,7 @@ class Twig_Test_EscapingTest extends PHPUnit_Framework_TestCase
144
 
145
  protected $env;
146
 
147
- public function setUp()
148
  {
149
  $this->env = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
150
  }
144
 
145
  protected $env;
146
 
147
+ protected function setUp()
148
  {
149
  $this->env = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
150
  }