Toolset Types – Custom Post Types, Custom Fields and Taxonomies - Version 2.2.4

Version Description

  • Fix an issue with registering custom taxonomies in WordPress 4.7.
  • Implement an alternative escaping mechanism for custom format setting of the date field.
  • types_render_shortcode() function and [types] shortcode now allows to use "id" attribute for $parent-post selection
  • Exclude Media from post relationships since the current GUI isn't able to support it properly
  • Fix exporting taxonomies with legacy "object_type" setting that was causing syntax errors in the output XML.
  • Support all CPTs in Toolset Dashboard.
  • Change the way we store the context of a Types field for string translation (use field group name instead of ID)
  • Fix a WordPress 4.7 compatibility issue with direct access to $wp_filter.
  • Make the manipulation with repetitive user field values more similar to post fields. Fix a front-end notice when there is only one value in a repeating user Skype field.
  • Add missing mandatory URL validation to file fields.
  • Add PHP template example files.
  • Fix an issue with Types export and non-latin characters in a field group slug.
Download this release

Release Info

Developer jadpm
Plugin Icon 128x128 Toolset Types – Custom Post Types, Custom Fields and Taxonomies
Version 2.2.4
Comparing to
See all releases

Code changes from version 2.2.3 to 2.2.4

Files changed (215) hide show
  1. application/controllers/field/utils.php +24 -0
  2. application/controllers/utils.php +9 -9
  3. application/functions.php +1 -1
  4. application/models/wpml/field/group/string.php +193 -0
  5. application/models/wpml/field/group/string/description.php +15 -0
  6. application/models/wpml/field/group/string/name.php +16 -0
  7. application/models/wpml/field_group.php +90 -0
  8. application/models/wpml/interface.php +6 -0
  9. examples/author.php +87 -0
  10. examples/content-book.php +101 -0
  11. examples/content-consultant.php +83 -0
  12. examples/content-house.php +131 -0
  13. examples/content-writer.php +88 -0
  14. examples/single-consultant.php +129 -0
  15. library/otgs/installer/changelog.txt +150 -142
  16. library/otgs/installer/includes/class-installer-dependencies.php +278 -278
  17. library/otgs/installer/includes/class-installer-theme.php +978 -978
  18. library/otgs/installer/includes/installer-api.php +115 -115
  19. library/otgs/installer/includes/installer-upgrader-skins.php +36 -36
  20. library/otgs/installer/includes/installer.class.php +2672 -2579
  21. library/otgs/installer/includes/translation-service-info.class.php +39 -39
  22. library/otgs/installer/installer.php +21 -21
  23. library/otgs/installer/loader.php +150 -150
  24. library/otgs/installer/locale/orig/installer.po +230 -230
  25. library/otgs/installer/repositories.xml +13 -13
  26. library/otgs/installer/res/css/admin.css +200 -192
  27. library/otgs/installer/res/js/admin.js +416 -402
  28. library/otgs/installer/res/js/iframeResizer.min.js +10 -10
  29. library/otgs/installer/res/js/installer_theme_install.js +96 -96
  30. library/otgs/installer/templates/downloads-list-compact.php +80 -80
  31. library/otgs/installer/templates/downloads-list.php +85 -85
  32. library/otgs/installer/templates/products-compact.php +147 -129
  33. library/otgs/installer/templates/repository-listing.php +196 -179
  34. library/toolset/types/embedded/frontend.php +41 -36
  35. library/toolset/types/embedded/includes/api.php +4 -3
  36. library/toolset/types/embedded/includes/custom-taxonomies.php +111 -83
  37. library/toolset/types/embedded/includes/fields-post.php +12 -10
  38. library/toolset/types/embedded/includes/fields.php +58 -0
  39. library/toolset/types/embedded/includes/fields/wysiwyg.php +23 -4
  40. library/toolset/types/embedded/includes/module-manager.php +42 -26
  41. library/toolset/types/embedded/includes/post-relationship.php +119 -44
  42. library/toolset/types/embedded/includes/usermeta-post.php +12 -7
  43. library/toolset/types/embedded/includes/wpml.php +4 -9
  44. library/toolset/types/embedded/resources/css/basic.css +5 -0
  45. library/toolset/types/embedded/resources/js/post-relationship.js +103 -84
  46. library/toolset/types/embedded/usermeta-init.php +8 -146
  47. library/toolset/types/includes/fields.php +18 -4
  48. library/toolset/types/includes/post-relationship.php +67 -53
  49. library/twig/twig/.travis.yml +7 -2
  50. library/twig/twig/CHANGELOG +51 -0
  51. library/twig/twig/doc/advanced.rst +118 -33
  52. library/twig/twig/doc/advanced_legacy.rst +5 -7
  53. library/twig/twig/doc/api.rst +46 -8
  54. library/twig/twig/doc/deprecated.rst +51 -3
  55. library/twig/twig/doc/filters/date.rst +6 -0
  56. library/twig/twig/doc/filters/escape.rst +3 -0
  57. library/twig/twig/doc/filters/number_format.rst +3 -0
  58. library/twig/twig/doc/functions/block.rst +26 -0
  59. library/twig/twig/doc/functions/constant.rst +11 -0
  60. library/twig/twig/doc/functions/date.rst +3 -0
  61. library/twig/twig/doc/functions/include.rst +7 -3
  62. library/twig/twig/doc/internals.rst +5 -1
  63. library/twig/twig/doc/recipes.rst +53 -3
  64. library/twig/twig/doc/tags/autoescape.rst +0 -2
  65. library/twig/twig/doc/tags/embed.rst +4 -4
  66. library/twig/twig/doc/tags/extends.rst +6 -2
  67. library/twig/twig/doc/tags/include.rst +7 -3
  68. library/twig/twig/doc/tags/index.rst +1 -0
  69. library/twig/twig/doc/tags/with.rst +44 -0
  70. library/twig/twig/doc/templates.rst +2 -4
  71. library/twig/twig/doc/tests/empty.rst +0 -1
  72. library/twig/twig/ext/twig/php_twig.h +1 -1
  73. library/twig/twig/ext/twig/twig.c +22 -22
  74. library/twig/twig/lib/Twig/BaseNodeVisitor.php +0 -6
  75. library/twig/twig/lib/Twig/Compiler.php +22 -28
  76. library/twig/twig/lib/Twig/CompilerInterface.php +1 -3
  77. library/twig/twig/lib/Twig/Environment.php +204 -93
  78. library/twig/twig/lib/Twig/Error.php +49 -18
  79. library/twig/twig/lib/Twig/ExpressionParser.php +120 -32
  80. library/twig/twig/lib/Twig/Extension.php +10 -0
  81. library/twig/twig/lib/Twig/Extension/Core.php +59 -104
  82. library/twig/twig/lib/Twig/Extension/Escaper.php +10 -6
  83. library/twig/twig/lib/Twig/Extension/Profiler.php +1 -1
  84. library/twig/twig/lib/Twig/Extension/StringLoader.php +1 -1
  85. library/twig/twig/lib/Twig/ExtensionInterface.php +3 -3
  86. library/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php +7 -7
  87. library/twig/twig/lib/Twig/Filter/Method.php +1 -1
  88. library/twig/twig/lib/Twig/Function/Method.php +1 -1
  89. library/twig/twig/lib/Twig/Lexer.php +13 -5
  90. library/twig/twig/lib/Twig/LexerInterface.php +4 -4
  91. library/twig/twig/lib/Twig/Loader/Array.php +17 -4
  92. library/twig/twig/lib/Twig/Loader/Chain.php +34 -10
  93. library/twig/twig/lib/Twig/Loader/Filesystem.php +57 -25
  94. library/twig/twig/lib/Twig/Loader/String.php +11 -1
  95. library/twig/twig/lib/Twig/LoaderInterface.php +2 -0
  96. library/twig/twig/lib/Twig/Node.php +40 -40
  97. library/twig/twig/lib/Twig/Node/CheckSecurity.php +3 -3
  98. library/twig/twig/lib/Twig/Node/Embed.php +7 -5
  99. library/twig/twig/lib/Twig/Node/Expression/Array.php +2 -2
  100. library/twig/twig/lib/Twig/Node/Expression/Binary/Power.php +4 -0
  101. library/twig/twig/lib/Twig/Node/Expression/BlockReference.php +59 -14
  102. library/twig/twig/lib/Twig/Node/Expression/Call.php +54 -18
  103. library/twig/twig/lib/Twig/Node/Expression/Filter/Default.php +4 -4
  104. library/twig/twig/lib/Twig/Node/Expression/Function.php +7 -2
  105. library/twig/twig/lib/Twig/Node/Expression/Name.php +14 -4
  106. library/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php +26 -3
  107. library/twig/twig/lib/Twig/Node/Expression/Test/Defined.php +6 -3
  108. library/twig/twig/lib/Twig/Node/Import.php +2 -2
  109. library/twig/twig/lib/Twig/Node/Include.php +2 -2
  110. library/twig/twig/lib/Twig/Node/Macro.php +3 -3
  111. library/twig/twig/lib/Twig/Node/Module.php +51 -17
  112. library/twig/twig/lib/Twig/Node/Sandbox.php +1 -1
  113. library/twig/twig/lib/Twig/Node/SandboxedPrint.php +2 -4
  114. library/twig/twig/lib/Twig/Node/Set.php +1 -1
  115. library/twig/twig/lib/Twig/Node/With.php +62 -0
  116. library/twig/twig/lib/Twig/NodeInterface.php +3 -2
  117. library/twig/twig/lib/Twig/NodeTraverser.php +2 -11
  118. library/twig/twig/lib/Twig/NodeVisitor/Escaper.php +3 -3
  119. library/twig/twig/lib/Twig/NodeVisitor/Optimizer.php +9 -22
  120. library/twig/twig/lib/Twig/NodeVisitor/Sandbox.php +1 -1
  121. library/twig/twig/lib/Twig/NodeVisitorInterface.php +0 -6
  122. library/twig/twig/lib/Twig/Parser.php +37 -27
  123. library/twig/twig/lib/Twig/ParserInterface.php +1 -3
  124. library/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php +1 -1
  125. library/twig/twig/lib/Twig/RuntimeLoaderInterface.php +27 -0
  126. library/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php +38 -0
  127. library/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php +38 -0
  128. library/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php +4 -2
  129. library/twig/twig/lib/Twig/Source.php +49 -0
  130. library/twig/twig/lib/Twig/SourceContextLoaderInterface.php +31 -0
  131. library/twig/twig/lib/Twig/Template.php +158 -73
  132. library/twig/twig/lib/Twig/TemplateInterface.php +1 -1
  133. library/twig/twig/lib/Twig/TemplateWrapper.php +131 -0
  134. library/twig/twig/lib/Twig/Test/IntegrationTestCase.php +11 -5
  135. library/twig/twig/lib/Twig/Test/Method.php +1 -1
  136. library/twig/twig/lib/Twig/Test/NodeTestCase.php +4 -0
  137. library/twig/twig/lib/Twig/Token.php +7 -20
  138. library/twig/twig/lib/Twig/TokenParser.php +0 -2
  139. library/twig/twig/lib/Twig/TokenParser/AutoEscape.php +2 -2
  140. library/twig/twig/lib/Twig/TokenParser/Block.php +2 -2
  141. library/twig/twig/lib/Twig/TokenParser/Embed.php +1 -1
  142. library/twig/twig/lib/Twig/TokenParser/Extends.php +5 -3
  143. library/twig/twig/lib/Twig/TokenParser/Filter.php +1 -1
  144. library/twig/twig/lib/Twig/TokenParser/For.php +5 -5
  145. library/twig/twig/lib/Twig/TokenParser/From.php +1 -1
  146. library/twig/twig/lib/Twig/TokenParser/If.php +1 -1
  147. library/twig/twig/lib/Twig/TokenParser/Macro.php +1 -1
  148. library/twig/twig/lib/Twig/TokenParser/Sandbox.php +4 -3
  149. library/twig/twig/lib/Twig/TokenParser/Set.php +2 -2
  150. library/twig/twig/lib/Twig/TokenParser/Use.php +1 -1
  151. library/twig/twig/lib/Twig/TokenParser/With.php +48 -0
  152. library/twig/twig/lib/Twig/TokenParserBroker.php +0 -22
  153. library/twig/twig/lib/Twig/TokenParserBrokerInterface.php +1 -3
  154. library/twig/twig/lib/Twig/TokenParserInterface.php +1 -5
  155. library/twig/twig/lib/Twig/TokenStream.php +43 -20
  156. library/twig/twig/lib/Twig/Util/DeprecationCollector.php +2 -2
  157. library/twig/twig/phpunit.xml.dist +8 -0
  158. library/twig/twig/test/Twig/Tests/Cache/FilesystemTest.php +1 -1
  159. library/twig/twig/test/Twig/Tests/EnvironmentTest.php +166 -38
  160. library/twig/twig/test/Twig/Tests/ErrorTest.php +8 -8
  161. library/twig/twig/test/Twig/Tests/ExpressionParserTest.php +17 -17
  162. library/twig/twig/test/Twig/Tests/Extension/CoreTest.php +1 -1
  163. library/twig/twig/test/Twig/Tests/Extension/SandboxTest.php +65 -0
  164. library/twig/twig/test/Twig/Tests/Fixtures/autoescape/block.test +1 -1
  165. library/twig/twig/test/Twig/Tests/Fixtures/autoescape/name.test +18 -0
  166. library/twig/twig/test/Twig/Tests/Fixtures/exceptions/child_contents_outside_blocks.test +15 -0
  167. library/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test +1 -1
  168. library/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test +1 -1
  169. library/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test +1 -1
  170. library/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test +1 -1
  171. library/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test +11 -11
  172. library/twig/twig/test/Twig/Tests/Fixtures/expressions/power.test +20 -0
  173. library/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test +1 -1
  174. library/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test +1 -1
  175. library/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test +1 -1
  176. library/twig/twig/test/Twig/Tests/Fixtures/filters/static_calls.test +10 -0
  177. library/twig/twig/test/Twig/Tests/Fixtures/functions/block_with_template.test +22 -0
  178. library/twig/twig/test/Twig/Tests/Fixtures/functions/block_without_name.test +12 -0
  179. library/twig/twig/test/Twig/Tests/Fixtures/functions/date.test +1 -1
  180. library/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call.test +8 -0
  181. library/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call53.test +12 -0
  182. library/twig/twig/test/Twig/Tests/Fixtures/functions/static_calls.test +10 -0
  183. library/twig/twig/test/Twig/Tests/Fixtures/macros/varargs_argument.test +0 -1
  184. library/twig/twig/test/Twig/Tests/Fixtures/regression/combined_debug_info.test +1 -1
  185. library/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test +1 -1
  186. library/twig/twig/test/Twig/Tests/Fixtures/tags/with/basic.test +13 -0
  187. library/twig/twig/test/Twig/Tests/Fixtures/tags/with/expression.test +10 -0
  188. library/twig/twig/test/Twig/Tests/Fixtures/tags/with/nested.test +15 -0
  189. library/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_no_hash.test +10 -0
  190. library/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_only.test +10 -0
  191. library/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_attribute.test +35 -0
  192. library/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks.test +38 -0
  193. library/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks_with_template.test +17 -0
  194. library/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_constants.test +14 -0
  195. library/twig/twig/test/Twig/Tests/IntegrationTest.php +27 -2
  196. library/twig/twig/test/Twig/Tests/LegacyFixtures/autoescape/filename.legacy.test +18 -0
  197. library/twig/twig/test/Twig/Tests/LexerTest.php +33 -22
  198. library/twig/twig/test/Twig/Tests/Loader/ArrayTest.php +16 -2
  199. library/twig/twig/test/Twig/Tests/Loader/ChainTest.php +46 -5
  200. library/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php +55 -11
  201. library/twig/twig/test/Twig/Tests/Loader/Fixtures/phar/phar-sample.phar +0 -0
  202. library/twig/twig/test/Twig/Tests/NativeExtensionTest.php +3 -0
  203. library/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php +9 -1
  204. library/twig/twig/test/Twig/Tests/Node/Expression/NullCoalesceTest.php +31 -0
  205. library/twig/twig/test/Twig/Tests/Node/ModuleTest.php +35 -10
  206. library/twig/twig/test/Twig/Tests/Node/SandboxTest.php +1 -1
  207. library/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php +1 -1
  208. library/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php +4 -4
  209. library/twig/twig/test/Twig/Tests/ParserTest.php +4 -3
  210. library/twig/twig/test/Twig/Tests/TemplateTest.php +111 -26
  211. library/twig/twig/test/Twig/Tests/TemplateWrapperTest.php +64 -0
  212. library/twig/twig/test/Twig/Tests/TokenStreamTest.php +12 -0
  213. library/twig/twig/test/Twig/Tests/escapingTest.php +1 -1
  214. readme.txt +31 -7
  215. wpcf.php +4 -3
application/controllers/field/utils.php CHANGED
@@ -74,6 +74,28 @@ final class Types_Field_Utils {
74
  }
75
 
76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  private static $domain_legacy_value_map = array(
78
  self::DOMAIN_POSTS => 'postmeta',
79
  self::DOMAIN_USERS => 'usermeta',
@@ -128,7 +150,9 @@ final class Types_Field_Utils {
128
  * Obtain toolset-forms "field configuration", which is an array of settings for specific field instance.
129
  *
130
  * @param WPCF_Field_Instance $field
 
131
  * @since 1.9
 
132
  */
133
  public static function get_toolset_forms_field_config( $field ) {
134
  return wptoolset_form_filter_types_field(
74
  }
75
 
76
 
77
+ /**
78
+ * Get the correct field group factory for provided underlying post type of the field group.
79
+ *
80
+ * This should not be needed from outside the legacy code.
81
+ *
82
+ * @param string $group_post_type
83
+ * @return Types_Field_Group_Factory
84
+ * @throws InvalidArgumentException when the post type doesn't belong to any field group.
85
+ * @since 2.2.4
86
+ */
87
+ public static function get_group_factory_by_post_type( $group_post_type ) {
88
+ $domains = self::get_domains();
89
+ foreach( $domains as $domain ) {
90
+ $factory = Types_Field_Group_Factory::get_factory_by_domain( $domain );
91
+ if( $factory->get_post_type() == $group_post_type ) {
92
+ return $factory;
93
+ }
94
+ }
95
+ throw new InvalidArgumentException( 'Invalid field group post type.' );
96
+ }
97
+
98
+
99
  private static $domain_legacy_value_map = array(
100
  self::DOMAIN_POSTS => 'postmeta',
101
  self::DOMAIN_USERS => 'usermeta',
150
  * Obtain toolset-forms "field configuration", which is an array of settings for specific field instance.
151
  *
152
  * @param WPCF_Field_Instance $field
153
+ *
154
  * @since 1.9
155
+ * @return array
156
  */
157
  public static function get_toolset_forms_field_config( $field ) {
158
  return wptoolset_form_filter_types_field(
application/controllers/utils.php CHANGED
@@ -34,18 +34,18 @@ final class Types_Utils {
34
  // todo add simple caching
35
  $taxonomies = array();
36
 
37
- // Read custom taxonomies first.
38
- $custom_taxonomies = get_option( WPCF_OPTION_NAME_CUSTOM_TAXONOMIES, array() );
39
- if ( is_array( $custom_taxonomies ) ) {
40
- foreach ( $custom_taxonomies as $slug => $data ) {
41
  $taxonomies[ $slug ] = $data;
42
  }
43
  }
44
 
45
- // Get built-in taxonomies and add them to the set, but avoid overwriting custom taxonomies
46
- $builtin_taxonomies = self::object_to_array_deep( self::get_builtin_taxonomies() );
47
- foreach ( $builtin_taxonomies as $slug => $data ) {
48
- // check if built-in taxonomies are already saved as custom taxonomies
49
  if ( isset( $taxonomies[ $slug ] ) ) {
50
  continue;
51
  }
@@ -218,4 +218,4 @@ final class Types_Utils {
218
  }
219
 
220
 
221
- }
34
  // todo add simple caching
35
  $taxonomies = array();
36
 
37
+ // Read Types taxonomies first.
38
+ $types_taxonomies = get_option( WPCF_OPTION_NAME_CUSTOM_TAXONOMIES, array() );
39
+ if ( is_array( $types_taxonomies ) ) {
40
+ foreach ( $types_taxonomies as $slug => $data ) {
41
  $taxonomies[ $slug ] = $data;
42
  }
43
  }
44
 
45
+ // Get all taxonomies and add them to the set, but avoid overwriting Types taxonomies
46
+ $all_taxonomies = self::object_to_array_deep( get_taxonomies( array( 'public' => true ) , 'objects' ) );
47
+ foreach ( $all_taxonomies as $slug => $data ) {
48
+ // check if taxonomies are already saved as custom taxonomies
49
  if ( isset( $taxonomies[ $slug ] ) ) {
50
  continue;
51
  }
218
  }
219
 
220
 
221
+ }
application/functions.php CHANGED
@@ -68,7 +68,7 @@ if( !function_exists( 'wpcf_getarr' ) ) {
68
  * @since 1.9
69
  */
70
  function wpcf_getarr( &$source, $key, $default = '', $valid = null ) {
71
- if ( isset( $source[ $key ] ) ) {
72
  $val = $source[ $key ];
73
  if ( is_array( $valid ) && ! in_array( $val, $valid ) ) {
74
  return $default;
68
  * @since 1.9
69
  */
70
  function wpcf_getarr( &$source, $key, $default = '', $valid = null ) {
71
+ if ( is_array( $source ) && array_key_exists( $key, $source ) ) {
72
  $val = $source[ $key ];
73
  if ( is_array( $valid ) && ! in_array( $val, $valid ) ) {
74
  return $default;
application/models/wpml/field/group/string.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Types_Wpml_Field_Group_String implements Types_Wpml_Interface {
4
+
5
+ const CONTEXT = 'plugin Types';
6
+ const TRANSLATE_FILTER = 'wpml_translate_single_string';
7
+
8
+ /**
9
+ * @var Types_Field_Group
10
+ */
11
+ protected $group;
12
+
13
+ /**
14
+ * String to translate
15
+ * @var string
16
+ */
17
+ protected $string_to_translate;
18
+
19
+ /**
20
+ * Types_Wpml_Field_Group_String constructor.
21
+ *
22
+ * @param Types_Field_Group $group
23
+ */
24
+ public function __construct( Types_Field_Group $group ) {
25
+ $this->group = $group;
26
+ }
27
+
28
+ /**
29
+ * The pattern of the string name in icl_strings
30
+ * - [name]: group %s name
31
+ * - [description]: group %s description
32
+ *
33
+ * @return string
34
+ */
35
+ abstract protected function get_db_pattern();
36
+
37
+
38
+ /**
39
+ * Returns the string which should be translated
40
+ *
41
+ * @return string
42
+ */
43
+ protected function get_string_to_translate() {
44
+ return $this->string_to_translate;
45
+ }
46
+
47
+
48
+ /**
49
+ * Get the db identifier (uses slug of group)
50
+ *
51
+ * @return string
52
+ */
53
+ protected function get_db_identifier() {
54
+ return sprintf( $this->get_db_pattern(), $this->group->get_slug() );
55
+ }
56
+
57
+ /**
58
+ * Get the db identifier (uses id of group)
59
+ *
60
+ * @return string
61
+ */
62
+ protected function get_db_identifier_legacy() {
63
+ return sprintf( $this->get_db_pattern(), $this->group->get_id() );
64
+ }
65
+
66
+
67
+ /**
68
+ * Translate name of the group
69
+ *
70
+ * @return string
71
+ */
72
+ public function translate() {
73
+ if ( empty( $this->string_to_translate ) || ! is_string( $this->string_to_translate ) ) {
74
+ return $this->string_to_translate;
75
+ }
76
+
77
+ // trying the new pattern, which uses the GROUP NAME
78
+ $translated_string = $this->get_translation(
79
+ $this->string_to_translate,
80
+ $this->get_db_identifier()
81
+ );
82
+
83
+ if( $translated_string && $translated_string != $this->string_to_translate )
84
+ return $translated_string;
85
+
86
+ // nothing found yet, try the old pattern for group storage
87
+ return $this->translate_legacy();
88
+ }
89
+
90
+ /**
91
+ * Returning the legacy pattern which is using the GROUP ID
92
+ *
93
+ * @return mixed|void
94
+ */
95
+ private function translate_legacy() {
96
+ $translated_string = $this->get_translation(
97
+ $this->string_to_translate,
98
+ $this->get_db_identifier_legacy()
99
+ );
100
+
101
+ // no translation found
102
+ if( ! $translated_string )
103
+ return $this->string_to_translate;
104
+
105
+ // update pattern of name field in "icl_strings" table
106
+ // to use name of group instead of id
107
+ $this->update_db_identifier();
108
+
109
+ // return translated string
110
+ return $translated_string;
111
+ }
112
+
113
+ /**
114
+ * Get translation of string
115
+ *
116
+ * @param $string
117
+ * @param $field_id
118
+ *
119
+ * @return string|false
120
+ */
121
+ private function get_translation( $string, $field_id ) {
122
+
123
+ // check if translation exists
124
+ $is_registered = apply_filters(
125
+ 'wpml_string_id',
126
+ null,
127
+ array(
128
+ 'context' => self::CONTEXT,
129
+ 'name' => $field_id
130
+ )
131
+ );
132
+
133
+ if( $is_registered === null )
134
+ return false;
135
+
136
+ // string is registered, return translation
137
+ return apply_filters(
138
+ self::TRANSLATE_FILTER,
139
+ $string,
140
+ self::CONTEXT,
141
+ $field_id
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Update the identifier on db table "{prefix}_icl_strings" to use the "name" of a group instead of the "id"
147
+ */
148
+ private function update_db_identifier() {
149
+ global $wpdb;
150
+
151
+ $wpdb->query(
152
+ $wpdb->prepare(
153
+ "UPDATE {$wpdb->prefix}icl_strings
154
+ SET name = %s,
155
+ domain_name_context_md5 = md5( CONCAT( context, name, gettext_context ) )
156
+ WHERE name = %s",
157
+ $this->get_db_identifier(),
158
+ $this->get_db_identifier_legacy()
159
+ )
160
+ );
161
+ }
162
+
163
+ /**
164
+ * Say Hello to WPML
165
+ */
166
+ public function register( $slug_update = false ) {
167
+ // abort if needed function not exists
168
+ if( ! function_exists( 'icl_register_string' ) )
169
+ return;
170
+
171
+ // update string identifier
172
+ if( $slug_update ) {
173
+ global $wpdb;
174
+
175
+ $wpdb->query(
176
+ $wpdb->prepare(
177
+ "UPDATE {$wpdb->prefix}icl_strings
178
+ SET name = %s
179
+ WHERE name = %s",
180
+ $this->get_db_identifier(),
181
+ sprintf( $this->get_db_pattern(), $slug_update )
182
+ )
183
+ );
184
+ }
185
+
186
+ // register/update string
187
+ icl_register_string(
188
+ self::CONTEXT,
189
+ $this->get_db_identifier(),
190
+ $this->get_string_to_translate()
191
+ );
192
+ }
193
+ }
application/models/wpml/field/group/string/description.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Types_Wpml_Field_Group_String_Description extends Types_Wpml_Field_Group_String {
5
+ const DB_NAME_PATTERN = 'group %s description';
6
+
7
+ public function __construct( Types_Field_Group $group ) {
8
+ parent::__construct( $group );
9
+ $this->string_to_translate = stripslashes( $this->group->get_description() );
10
+ }
11
+
12
+ protected function get_db_pattern() {
13
+ return self::DB_NAME_PATTERN;
14
+ }
15
+ }
application/models/wpml/field/group/string/name.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Types_Wpml_Field_Group_String_Name extends Types_Wpml_Field_Group_String {
5
+
6
+ const DB_NAME_PATTERN = 'group %s name';
7
+
8
+ public function __construct( Types_Field_Group $group ) {
9
+ parent::__construct( $group );
10
+ $this->string_to_translate = stripslashes( $this->group->get_name() );
11
+ }
12
+
13
+ protected function get_db_pattern() {
14
+ return self::DB_NAME_PATTERN;
15
+ }
16
+ }
application/models/wpml/field_group.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Types_Wpml_Field_Group
5
+ *
6
+ * @fixme this is missing documentation
7
+ *
8
+ * @since 2.3
9
+ */
10
+ class Types_Wpml_Field_Group implements Types_Wpml_Interface {
11
+
12
+ const STRING_NAME = 'name';
13
+ const STRING_DESCRIPTION = 'description';
14
+
15
+ /**
16
+ * @var Types_Field_Group
17
+ */
18
+ private $group;
19
+
20
+ /**
21
+ * @var Types_Wpml_Interface
22
+ */
23
+ private $name;
24
+
25
+ /**
26
+ * @var Types_Wpml_Interface
27
+ */
28
+ private $description;
29
+
30
+ /**
31
+ * Types_Wpml_Field_Group constructor.
32
+ *
33
+ * @param Types_Field_Group $group
34
+ */
35
+ public function __construct( Types_Field_Group $group ) {
36
+ $this->group = $group;
37
+
38
+ // todo get rid of these hard dependencies
39
+ $this->name = new Types_Wpml_Field_Group_String_Name( $this->group );
40
+ $this->description = new Types_Wpml_Field_Group_String_Description( $this->group );
41
+ }
42
+
43
+
44
+ /**
45
+ * Translate name or description of group
46
+ *
47
+ * @param string $part
48
+ *
49
+ * @return string
50
+ */
51
+ public function translate( $part = self::STRING_NAME ) {
52
+ switch( $part ) {
53
+ case self::STRING_NAME:
54
+ return $this->translate_name();
55
+ break;
56
+ case self::STRING_DESCRIPTION:
57
+ return $this->translate_description();
58
+ break;
59
+ default:
60
+ return '';
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Translate name of the group
66
+ *
67
+ * @return string
68
+ */
69
+ public function translate_name() {
70
+ return $this->name->translate();
71
+ }
72
+
73
+ /**
74
+ * Translate description of group
75
+ *
76
+ * @return string
77
+ */
78
+ public function translate_description() {
79
+ return $this->description->translate();
80
+ }
81
+
82
+ /**
83
+ * Registration of name and description strings
84
+ * @param bool|string $slug_update
85
+ */
86
+ public function register( $slug_update = false ) {
87
+ $this->name->register( $slug_update );
88
+ $this->description->register( $slug_update );
89
+ }
90
+ }
application/models/wpml/interface.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ interface Types_Wpml_Interface {
4
+ public function translate();
5
+ public function register( $slug_update = false );
6
+ }
examples/author.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This example file is a part of the Types plugin online documentation found at: https://wp-types.com/documentation/customizing-sites-using-php/
4
+ * It is based on the original Twenty Sixteen theme's archive.php file.
5
+ * It features additional code to render custom user fields created with the Types plugin.
6
+ *
7
+ * Please note that the names of the custom fields are for example purposes only and will not work in your site as-is. You need to edit this example according to the documentation mentioned above.
8
+ *
9
+ * The template for displaying custom author archive pages
10
+ *
11
+ *
12
+ * @package WordPress
13
+ * @subpackage Twenty_Sixteen
14
+ * @since Twenty Sixteen 1.0
15
+ */
16
+
17
+ get_header(); ?>
18
+
19
+ <div id="primary" class="content-area">
20
+ <main id="main" class="site-main" role="main">
21
+
22
+ <?php if ( have_posts() ) : ?>
23
+
24
+ <header class="page-header">
25
+ <!-- TYPES TIP: using get_the_author_meta function to output author name as the archive's title -->
26
+ <?php
27
+ echo '<h1 class="page-title">Author: ' . get_the_author_meta( 'first_name' ) . ' ' . get_the_author_meta( 'last_name' ) . '</h1>';
28
+ ?>
29
+
30
+ <!-- TYPES TIP: using get_avatar function to output author's avatar image -->
31
+ <div class="user-avatar"><?php echo get_avatar( get_the_author_meta( 'user_email' ), '150' ); ?></div>
32
+
33
+ <!-- TYPES TIP: using Types function to custom user fields -->
34
+ <p><strong>Specialty topics:</strong> <?php echo types_render_usermeta( "specialty-topics" ); ?><br />
35
+ <strong>Staff role:</strong> <?php echo types_render_usermeta( "staff-role" ); ?></p>
36
+
37
+ <!-- TYPES TIP: using the the_author_meta function to output the description of the user -->
38
+ <p><b>About:</b> <?php the_author_meta( 'description' ); ?></p>
39
+
40
+ </header><!-- .page-header -->
41
+
42
+ <hr />
43
+
44
+
45
+ <!-- TYPES TIP: Creating a custom query for our author/user to display posts belonging to the "Post" and "Book" post types -->
46
+ <?php
47
+ // Add Custom Post Type to author archive
48
+ $args = array(
49
+ 'post_type' => array('post', 'book') ,
50
+ 'author' => get_queried_object_id(), // this will be the author ID on the author page
51
+ 'showposts' => 10
52
+ );
53
+ $custom_posts = new WP_Query( $args );
54
+
55
+ // Start the Loop.
56
+ while ( $custom_posts->have_posts() ) : $custom_posts->the_post();
57
+
58
+ /*
59
+ * Include the Post-Format-specific template for the content.
60
+ * If you want to override this in a child theme, then include a file
61
+ * called content-___.php (where ___ is the Post Format name) and that will be used instead.
62
+ */
63
+
64
+ get_template_part( 'template-parts/content', get_post_type() );
65
+
66
+ // End the loop.
67
+ endwhile;
68
+
69
+ // Previous/next page navigation.
70
+ the_posts_pagination( array(
71
+ 'prev_text' => __( 'Previous page', 'twentysixteen' ),
72
+ 'next_text' => __( 'Next page', 'twentysixteen' ),
73
+ 'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'twentysixteen' ) . ' </span>',
74
+ ) );
75
+
76
+ // If no content, include the "No posts found" template.
77
+ else :
78
+
79
+
80
+ endif;
81
+ ?>
82
+
83
+ </main><!-- .site-main -->
84
+ </div><!-- .content-area -->
85
+
86
+ <?php get_sidebar(); ?>
87
+ <?php get_footer(); ?>
examples/content-book.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This example file is a part of the Types plugin online documentation found at: https://wp-types.com/documentation/customizing-sites-using-php/
4
+ * It is based on the original Twenty Sixteen theme's template part file for displaying single items.
5
+ * It features additional code to render custom fields created with the Types plugin and some parent post contents.
6
+ *
7
+ * Please note that the names of the custom fields are for example purposes only and will not work in your site as-is. You need to edit this example according to the documentation mentioned above.
8
+ *
9
+ * The template part for displaying Book post content
10
+ *
11
+ * @package WordPress
12
+ * @subpackage Twenty_Sixteen
13
+ * @since Twenty Sixteen 1.0
14
+ */
15
+ ?>
16
+
17
+ <!-- Standard Twenty Sixteen article header output -->
18
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
19
+ <header class="entry-header">
20
+ <?php if ( is_sticky() && is_home() && ! is_paged() ) : ?>
21
+ <span class="sticky-post"><?php _e( 'Featured', 'twentysixteen' ); ?></span>
22
+ <?php endif; ?>
23
+
24
+ <?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
25
+ </header><!-- .entry-header -->
26
+
27
+ <?php twentysixteen_excerpt(); ?>
28
+
29
+
30
+ <!-- TYPES TIP: Custom call to Types function to render a custom image field "Book Cover" -->
31
+ <?php echo types_render_field( "book-cover", array( "size" => "thumbnail" ) ); ?>
32
+
33
+ <div class="entry-content">
34
+ <?php
35
+ /* translators: %s: Name of current post */
36
+ the_content( sprintf(
37
+ __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
38
+ get_the_title()
39
+ ) );
40
+
41
+ wp_link_pages( array(
42
+ 'before' => '<div class="page-links"><span class="page-links-title">' . __( 'Pages:', 'twentysixteen' ) . '</span>',
43
+ 'after' => '</div>',
44
+ 'link_before' => '<span>',
45
+ 'link_after' => '</span>',
46
+ 'pagelink' => '<span class="screen-reader-text">' . __( 'Page', 'twentysixteen' ) . ' </span>%',
47
+ 'separator' => '<span class="screen-reader-text">, </span>',
48
+ ) );
49
+ ?>
50
+
51
+
52
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Number of pages" -->
53
+ <p><strong>Number of pages:</strong> <?php echo types_render_field( "number-of-pages"); ?></p>
54
+
55
+
56
+ <!-- TYPES TIP: Custom code to load and display contents of the parent ("Writer") post -->
57
+ <?php
58
+ // Get the ID of the parent post, which belongs to the "Writer" post type
59
+ $writer_id = wpcf_pr_post_get_belongs( get_the_ID(), 'writer' );
60
+
61
+ // Get all the parent (writer) post data
62
+ $writer_post = get_post( $writer_id );
63
+
64
+ // Get the title of the parent (writer) post
65
+ $writer_name = $writer_post->post_title;
66
+
67
+ // Get the contents of the parent (writer) post
68
+ $writer_content = $writer_post->post_content;
69
+ ?>
70
+ <!-- After loading the data of the parent post we display it using our custom HTML structure -->
71
+ <div class="writer">
72
+ <h5>Author: <?php echo $writer_name; ?></h5>
73
+ <div class="writer-description"><?php echo $writer_content; ?></div>
74
+
75
+ <!-- We can use the "post_id" argument with the types_render_field function to get a custom field of any post -->
76
+ <div class="writer-photo"><?php echo types_render_field( 'author-image', array( 'post_id' => $writer_id, 'size' => 'thumbnail' ) );
77
+ ?></div>
78
+
79
+ <br class="clear">
80
+ </div>
81
+
82
+
83
+ </div><!-- .entry-content -->
84
+
85
+ <!-- Standard Twenty Sixteen content footer output -->
86
+ <footer class="entry-footer">
87
+ <?php twentysixteen_entry_meta(); ?>
88
+ <?php
89
+ edit_post_link(
90
+ sprintf(
91
+ /* translators: %s: Name of current post */
92
+ __( 'Edit<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
93
+ get_the_title()
94
+ ),
95
+ '<span class="edit-link">',
96
+ '</span>'
97
+ );
98
+ ?>
99
+ </footer><!-- .entry-footer -->
100
+ </article><!-- #post-## -->
101
+
examples/content-consultant.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This example file is a part of the Types plugin online documentation found at: https://wp-types.com/documentation/customizing-sites-using-php/
4
+ * It is based on the original Twenty Sixteen theme's template part file for displaying single items.
5
+ * It features additional code to render custom fields created with the Types plugin.
6
+ *
7
+ * Please note that the names of the custom fields are for example purposes only and will not work in your site as-is. You need to edit this example according to the documentation mentioned above.
8
+ *
9
+ * The template part for displaying Consultant post content
10
+ *
11
+ * @package WordPress
12
+ * @subpackage Twenty_Sixteen
13
+ * @since Twenty Sixteen 1.0
14
+ */
15
+ ?>
16
+
17
+ <!-- Standard Twenty Sixteen article header output -->
18
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
19
+ <header class="entry-header">
20
+ <?php if ( is_sticky() && is_home() && ! is_paged() ) : ?>
21
+ <span class="sticky-post"><?php _e( 'Featured', 'twentysixteen' ); ?></span>
22
+ <?php endif; ?>
23
+
24
+ <?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
25
+ </header><!-- .entry-header -->
26
+
27
+ <?php twentysixteen_excerpt(); ?>
28
+
29
+
30
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Consultant Photo" -->
31
+ <?php echo types_render_field( "consultant-photo", array( "size" => "thumbnail" )); ?>
32
+
33
+
34
+ <div class="entry-content">
35
+
36
+ <!-- TYPES TIP: Custom call to get_the_term_list function to display a list of taxonomy terms that the current "Consultant" post belongs to -->
37
+ <?php echo get_the_term_list( $post->ID, 'spoken-language', '<p><strong>Spoken languages: </strong>', ', ', '</p>'); ?>
38
+
39
+
40
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Consultant Roles" -->
41
+ <p><strong>Role: <?php echo types_render_field( "consultant-roles" ); // Call to Types function for rendering a custom field "Consultant Roles" ?></strong></p>
42
+
43
+
44
+ <!-- Standard Twenty Sixteen content output -->
45
+ <?php
46
+ /* translators: %s: Name of current post */
47
+ the_content( sprintf(
48
+ __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
49
+ get_the_title()
50
+ ) );
51
+
52
+ wp_link_pages( array(
53
+ 'before' => '<div class="page-links"><span class="page-links-title">' . __( 'Pages:', 'twentysixteen' ) . '</span>',
54
+ 'after' => '</div>',
55
+ 'link_before' => '<span>',
56
+ 'link_after' => '</span>',
57
+ 'pagelink' => '<span class="screen-reader-text">' . __( 'Page', 'twentysixteen' ) . ' </span>%',
58
+ 'separator' => '<span class="screen-reader-text">, </span>',
59
+ ) );
60
+ ?>
61
+
62
+
63
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Consultant Contact Phone" -->
64
+ <p><strong>Contact Phone: <?php echo types_render_field( "consultant-phone-number"); ?></strong></p>
65
+ </div><!-- .entry-content -->
66
+
67
+
68
+ <!-- Standard Twenty Sixteen content footer output -->
69
+ <footer class="entry-footer">
70
+ <?php twentysixteen_entry_meta(); ?>
71
+ <?php
72
+ edit_post_link(
73
+ sprintf(
74
+ /* translators: %s: Name of current post */
75
+ __( 'Edit<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
76
+ get_the_title()
77
+ ),
78
+ '<span class="edit-link">',
79
+ '</span>'
80
+ );
81
+ ?>
82
+ </footer><!-- .entry-footer -->
83
+ </article><!-- #post-## -->
examples/content-house.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This example file is a part of the Types plugin online documentation found at: https://wp-types.com/documentation/customizing-sites-using-php/
4
+ * It is based on the original Twenty Sixteen theme's template part file for displaying single items.
5
+ * It features additional code to render custom fields created with the Types plugin, including an image gallery created with images from the repeating image field.
6
+ *
7
+ * Please note that the names of the custom fields are for example purposes only and will not work in your site as-is. You need to edit this example according to the documentation mentioned above.
8
+ *
9
+ * The template part for displaying single House posts
10
+ *
11
+ * @package WordPress
12
+ * @subpackage Twenty_Sixteen
13
+ * @since Twenty Sixteen 1.0
14
+ */
15
+ ?>
16
+
17
+ <!-- Standard Twenty Sixteen article header output -->
18
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
19
+ <header class="entry-header">
20
+ <?php if ( is_sticky() && is_home() && ! is_paged() ) : ?>
21
+ <span class="sticky-post"><?php _e( 'Featured', 'twentysixteen' ); ?></span>
22
+ <?php endif; ?>
23
+
24
+ <?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
25
+ </header><!-- .entry-header -->
26
+
27
+ <?php twentysixteen_excerpt(); ?>
28
+
29
+
30
+ <!-- TYPES TIP: Custom call to Types function to render a custom image field "House Photos". We used the "index" attribute to display only the first, representative photo of a house. -->
31
+ <?php echo types_render_field( "house-photos", array( "size"=>"thumbnail", "index" => "0" ) ); ?>
32
+
33
+
34
+ <div class="entry-content">
35
+
36
+ <!-- Standard Twenty Sixteen content output -->
37
+ <?php
38
+ /* translators: %s: Name of current post */
39
+ the_content( sprintf(
40
+ __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
41
+ get_the_title()
42
+ ) );
43
+
44
+ wp_link_pages( array(
45
+ 'before' => '<div class="page-links"><span class="page-links-title">' . __( 'Pages:', 'twentysixteen' ) . '</span>',
46
+ 'after' => '</div>',
47
+ 'link_before' => '<span>',
48
+ 'link_after' => '</span>',
49
+ 'pagelink' => '<span class="screen-reader-text">' . __( 'Page', 'twentysixteen' ) . ' </span>%',
50
+ 'separator' => '<span class="screen-reader-text">, </span>',
51
+ ) );
52
+ ?>
53
+
54
+
55
+
56
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Size" belonging to a "Houses" post type -->
57
+ <p><strong>Size:</strong> <?php echo types_render_field( "size" ); ?></p>
58
+
59
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Area" belonging to a "Houses" post type -->
60
+ <p><strong>Area:</strong> <?php echo types_render_field( "area" ); ?></p>
61
+
62
+ <!-- TYPES TIP: Custom code to load Types function and render a REPEATING custom field "House Photos" but with each output thumbnail image also linking to the full-size image file (for lightbox effect, for example). -->
63
+ <p><strong>Property Photos</strong></p>
64
+
65
+ <?php
66
+ // Do nothing if we don't have Types.
67
+ if( apply_filters( 'types_is_active', false ) ) {
68
+
69
+ $output = '';
70
+
71
+ // ID of the current post
72
+ $post_id = get_the_ID();
73
+
74
+ // Slug of a Types repeating image field, without the "wpcf-" prefix.
75
+ $field_slug = 'house-photos'; // TODO set the field slug you want to display
76
+
77
+ // Parameters that define the field
78
+ $field_definition = wpcf_fields_get_field_by_slug( $field_slug );
79
+ if( ! empty( $field_definition ) ) {
80
+
81
+ // Get the raw field data.
82
+ $images = get_post_meta( $post_id, "wpcf-{$field_slug}" );
83
+
84
+ foreach( $images as $image_index => $image ) {
85
+
86
+ // Parameters for the Types field rendering mechanism.
87
+ $image_parameters = array(
88
+ 'proportional' => 'true',
89
+ 'url' => 'true',
90
+ 'field_value' => $image
91
+ );
92
+
93
+ // Get an image of specific (maximum) proportions.
94
+ // NOTE: Update image size to your needs
95
+ $thumbnail_parameters = array_merge( $image_parameters, array( 'width' => '200', 'height' => '200' ) );
96
+ $thumbnail_url = types_render_field_single( $field_definition, $thumbnail_parameters, null, '', $image_index );
97
+
98
+ // Get the image in full size.
99
+ $fullsize_parameters = array_merge( $image_parameters, array( 'size' => 'full' ) );
100
+ $fullsize_url = types_render_field_single( $field_definition, $fullsize_parameters, null, '', $image_index );
101
+
102
+ // Append the markup (a thumbnail linking to the full image) to existing content.
103
+ // NOTE: Customize the output to your needs
104
+ $output .= sprintf(
105
+ '<div class="img"><a href=" '. $fullsize_url .' "><img src=" '. $thumbnail_url .' "></a></div>'
106
+ );
107
+ }
108
+ }
109
+
110
+ echo $output;
111
+ }
112
+ ?>
113
+
114
+ </div><!-- .entry-content -->
115
+
116
+ <!-- Continue the standard Twenty Sixteen content footer output -->
117
+ <footer class="entry-footer">
118
+ <?php twentysixteen_entry_meta(); ?>
119
+ <?php
120
+ edit_post_link(
121
+ sprintf(
122
+ /* translators: %s: Name of current post */
123
+ __( 'Edit<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
124
+ get_the_title()
125
+ ),
126
+ '<span class="edit-link">',
127
+ '</span>'
128
+ );
129
+ ?>
130
+ </footer><!-- .entry-footer -->
131
+ </article><!-- #post-## -->
examples/content-writer.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This example file is a part of the Types plugin online documentation found at: https://wp-types.com/documentation/customizing-sites-using-php/
4
+ * It is based on the original Twenty Sixteen theme's template part file for displaying single items.
5
+ * It features additional code to render custom fields created with the Types plugin and display contents of child posts.
6
+ *
7
+ * The template part for displaying Author (Writer) post content
8
+ *
9
+ * @package WordPress
10
+ * @subpackage Twenty_Sixteen
11
+ * @since Twenty Sixteen 1.0
12
+ */
13
+ ?>
14
+
15
+ <!-- Standard Twenty Sixteen article header output -->
16
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
17
+ <header class="entry-header">
18
+ <?php if ( is_sticky() && is_home() && ! is_paged() ) : ?>
19
+ <span class="sticky-post"><?php _e( 'Featured', 'twentysixteen' ); ?></span>
20
+ <?php endif; ?>
21
+
22
+ <?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
23
+ </header><!-- .entry-header -->
24
+
25
+ <?php twentysixteen_excerpt(); ?>
26
+
27
+
28
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Author Image" -->
29
+ <?php echo types_render_field( "author-image", array( "size" => "thumbnail" )); ?>
30
+
31
+
32
+ <div class="entry-content">
33
+
34
+ <!-- Standard Twenty Sixteen content output -->
35
+ <?php
36
+ /* translators: %s: Name of current post */
37
+ the_content( sprintf(
38
+ __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
39
+ get_the_title()
40
+ ) );
41
+ ?>
42
+
43
+
44
+ <!-- TYPES TIP: Custom section to display a list of child posts ("Books"), belonging to the currently displayed parent post ("Author") -->
45
+ <h4>Books from this author</h4>
46
+
47
+ <?php
48
+ // Using the Types function to load the contents of child posts and then starting a custom loop to output them
49
+ $child_posts = types_child_posts("book"); // Load the contents of related posts in the array
50
+ foreach ($child_posts as $child_post) { // Loop through each of the child post in the array
51
+ ?>
52
+
53
+ <div class="book-listing">
54
+ <h5><?php echo $child_post->post_title; // Display the post title of a current child post ?></h5>
55
+ <?php echo types_render_field( "book-cover", array( "id"=> "$child_post->ID", "size" => "thumbnail" )); // Display the image coming from a custom field of the current child post ?>
56
+ </div>
57
+
58
+
59
+ <!-- Continuation of the standard Twenty Sixteen content output -->
60
+ <?php }
61
+ wp_link_pages( array(
62
+ 'before' => '<div class="page-links"><span class="page-links-title">' . __( 'Pages:', 'twentysixteen' ) . '</span>',
63
+ 'after' => '</div>',
64
+ 'link_before' => '<span>',
65
+ 'link_after' => '</span>',
66
+ 'pagelink' => '<span class="screen-reader-text">' . __( 'Page', 'twentysixteen' ) . ' </span>%',
67
+ 'separator' => '<span class="screen-reader-text">, </span>',
68
+ ) );
69
+ ?>
70
+
71
+ </div><!-- .entry-content -->
72
+
73
+ <footer class="entry-footer">
74
+ <?php twentysixteen_entry_meta(); ?>
75
+ <?php
76
+ edit_post_link(
77
+ sprintf(
78
+ /* translators: %s: Name of current post */
79
+ __( 'Edit<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
80
+ get_the_title()
81
+ ),
82
+ '<span class="edit-link">',
83
+ '</span>'
84
+ );
85
+ ?>
86
+ </footer><!-- .entry-footer -->
87
+ </article><!-- #post-## -->
88
+
examples/single-consultant.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This example file is a part of the Types plugin online documentation found at: https://wp-types.com/documentation/customizing-sites-using-php/
4
+ * It is based on the original Twenty Sixteen theme's single.php file.
5
+ * It does not use template parts, but loads the post contents directly.
6
+ * It also features additional code to render custom fields created with the Types plugin.
7
+ *
8
+ * Please note that the names of the custom fields are for example purposes only and will not work in your site as-is. You need to edit this example according to the documentation mentioned above.
9
+ *
10
+ * The template for displaying single "Consultant" posts
11
+ *
12
+ * @package WordPress
13
+ * @subpackage Twenty_Sixteen
14
+ * @since Twenty Sixteen 1.0
15
+ */
16
+
17
+ get_header(); ?>
18
+
19
+ <div id="primary" class="content-area">
20
+ <main id="main" class="site-main" role="main">
21
+ <?php
22
+ // Start the loop.
23
+ while ( have_posts() ) : the_post();
24
+ ?>
25
+
26
+ <!-- Standard Twenty Sixteen article header output -->
27
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
28
+ <header class="entry-header">
29
+ <?php if ( is_sticky() && is_home() && ! is_paged() ) : ?>
30
+ <span class="sticky-post"><?php _e( 'Featured', 'twentysixteen' ); ?></span>
31
+ <?php endif; ?>
32
+
33
+ <?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
34
+ </header><!-- .entry-header -->
35
+
36
+ <?php twentysixteen_excerpt(); ?>
37
+
38
+
39
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Consultant Photo" -->
40
+ <?php echo types_render_field( "consultant-photo", array( "size" => "thumbnail" )); ?>
41
+
42
+
43
+ <div class="entry-content">
44
+
45
+
46
+ <!-- TYPES TIP: Custom call to get_the_term_list function to display a list of taxonomy terms that the current "Consultant" post belongs to -->
47
+ <?php echo get_the_term_list( $post->ID, 'spoken-language', '<p><strong>Spoken languages: </strong>', ', ', '</p>'); ?>
48
+
49
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Consultant Roles" -->
50
+ <p><strong>Role: <?php echo types_render_field( "consultant-roles" ); // Call to Types function for rendering a custom field "Consultant Roles" ?></strong></p>
51
+
52
+
53
+ <!-- Standard Twenty Sixteen content output -->
54
+ <?php
55
+ /* translators: %s: Name of current post */
56
+ the_content( sprintf(
57
+ __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
58
+ get_the_title()
59
+ ) );
60
+
61
+ wp_link_pages( array(
62
+ 'before' => '<div class="page-links"><span class="page-links-title">' . __( 'Pages:', 'twentysixteen' ) . '</span>',
63
+ 'after' => '</div>',
64
+ 'link_before' => '<span>',
65
+ 'link_after' => '</span>',
66
+ 'pagelink' => '<span class="screen-reader-text">' . __( 'Page', 'twentysixteen' ) . ' </span>%',
67
+ 'separator' => '<span class="screen-reader-text">, </span>',
68
+ ) );
69
+ ?>
70
+
71
+ <!-- TYPES TIP: Custom call to Types function to render a custom field "Consultant Contact Phone" -->
72
+ <p><strong>Contact Phone: <?php echo types_render_field( "consultant-phone-number"); ?></strong></p>
73
+ </div><!-- .entry-content -->
74
+
75
+ <!-- Standard Twenty Sixteen content footer output -->
76
+ <footer class="entry-footer">
77
+ <?php twentysixteen_entry_meta(); ?>
78
+ <?php
79
+ edit_post_link(
80
+ sprintf(
81
+ /* translators: %s: Name of current post */
82
+ __( 'Edit<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
83
+ get_the_title()
84
+ ),
85
+ '<span class="edit-link">',
86
+ '</span>'
87
+ );
88
+ ?>
89
+ </footer><!-- .entry-footer -->
90
+ </article><!-- #post-## -->
91
+
92
+
93
+ <?php
94
+ // TYPES TIP: all of the lines below this point are the standard part of a single.php of the Twenty Sixteen theme.
95
+
96
+ // If comments are open or we have at least one comment, load up the comment template.
97
+ if ( comments_open() || get_comments_number() ) {
98
+ comments_template();
99
+ }
100
+
101
+ if ( is_singular( 'attachment' ) ) {
102
+ // Parent post navigation.
103
+ the_post_navigation( array(
104
+ 'prev_text' => _x( '<span class="meta-nav">Published in</span><span class="post-title">%title</span>', 'Parent post link', 'twentysixteen' ),
105
+ ) );
106
+ } elseif ( is_singular( 'post' ) ) {
107
+ // Previous/next post navigation.
108
+ the_post_navigation( array(
109
+ 'next_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Next', 'twentysixteen' ) . '</span> ' .
110
+ '<span class="screen-reader-text">' . __( 'Next post:', 'twentysixteen' ) . '</span> ' .
111
+ '<span class="post-title">%title</span>',
112
+ 'prev_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Previous', 'twentysixteen' ) . '</span> ' .
113
+ '<span class="screen-reader-text">' . __( 'Previous post:', 'twentysixteen' ) . '</span> ' .
114
+ '<span class="post-title">%title</span>',
115
+ ) );
116
+ }
117
+
118
+ // End of the loop.
119
+ endwhile;
120
+ ?>
121
+
122
+ </main><!-- .site-main -->
123
+
124
+ <?php get_sidebar( 'content-bottom' ); ?>
125
+
126
+ </div><!-- .content-area -->
127
+
128
+ <?php get_sidebar(); ?>
129
+ <?php get_footer(); ?>
library/otgs/installer/changelog.txt CHANGED
@@ -1,142 +1,150 @@
1
- = 1.7.11 =
2
- * Fixed styling for 'must-register' warnings on the plugins page
3
-
4
- = 1.7.10 =
5
- * Fixed a notice that was appearing when the site key registration failed
6
-
7
- = 1.7.9 =
8
- * Save the client_id value from the site_key_validation API call response and make it available via WP_Installer_API::get_ts_client_id API call
9
-
10
- = 1.7.8 =
11
- * Small fix for hiding the WPML registration notice
12
-
13
- = 1.7.7 =
14
- * Fixed js error showing up during registration
15
- * Styles update for unified WPML messages
16
-
17
- = 1.7.6 =
18
- * Updated error messages when validating site keys and stopped removing site keys in case of communication errors
19
- * Added a note for users who renewed or purchased new subscriptions and who need to revalidate their subscription from their websites
20
- * Fixed a problem with the registrations for multi-site setups when WordPress was installed in a separate folder
21
-
22
- = 1.7.5 =
23
- * Fixed a bug causing registration to not be recognized for the entire network in the multi-site mode
24
-
25
- = 1.7.4 =
26
- * Use https for wp-types API
27
-
28
- = 1.7.3 =
29
- * Added a dependencies class and check for the windows paths length exception
30
- * Bug fix: all downloads showed up twice on the plugins list whe upgrading from WPML 3.3
31
-
32
- = 1.7.2 =
33
- * Added an exception to handle the case when Types embedded is installer from Toolset Installer and its included Installer version overrides the one running the Toolset setup wizard
34
-
35
- = 1.7.1 =
36
- * Added an exception for the case of Types 1.8.9 (Installer 1.7.0) together with older WPML (older Installer)
37
-
38
- = 1.7.0 =
39
- * New format for the products data file.
40
- * Other fixes
41
-
42
- = 1.6.8 =
43
- * Sanitized an input that was a potential security issue
44
-
45
- = 1.6.7 =
46
- * Fixed a bug causing repeated calls to the Toolset api to validate the user subscription
47
- * Use https for API urls
48
-
49
- = 1.6.6 =
50
- * Fixed the 'Call to undefined function get_plugins()' issue
51
-
52
- = 1.6.5 =
53
- * Added configuration file for composer
54
- * Updated how free plugins are shown on the plugins list (commercial tab)
55
- * API calls for manipulating translation service preferences
56
- * Support for hosting custom Installer packages on wpml.org
57
- * Fixed a warning that was showing when using the OTGS_DISABLE_AUTO_UPDATES constant before any product data was downloaded
58
- * Changed the frequency with which product updates are checked automatically (24 hours)
59
- * Improved reporting for version numbers
60
-
61
- = 1.6.4 =
62
- * Enabled the OTGS_DISABLE_AUTO_UPDATES constant for theme update checks
63
- * Fixed a bug that was causing Register links to show for all installed plugins
64
-
65
- = 1.6.3 =
66
- * Fixed performance issue related to themes upgrade logic
67
-
68
- = 1.6 =
69
- * Improved the way plugins are matched: not just by the folder name (slug) but also by name
70
- * Added support for installing and upgrading themes from repositories (currently: Toolset themes)
71
- * Added support for 'alias' plugins on the toolset and wpml repositories (currently: Types)
72
- * Enhanced the progress animation during plugins downloading
73
-
74
- = 1.5.6 =
75
- * Updated the translations
76
- * Fix for WPML 3.2 conditional upgrade logic
77
-
78
- = 1.5.5 =
79
- * Fixed the logic for the high_priority parameter
80
- * Fixed js bug causing a conflict with NextGen
81
- * Fixed bug preventing users to install and upgrade Types when they didn't have a Toolset subscription
82
- * Fixed bug preventing users to upgrade from the embedded Types to the full version
83
-
84
- = 1.5.4 =
85
- * Option to disable auto-updates
86
- * Escaped urls generated with add_query_arg
87
-
88
- = 1.5.3 =
89
- * Fixed bug in WP_Installer::custom_plugins_api_call (filter for plugins_api) causing conflicts with other filters for plugins_api
90
-
91
- = 1.5.2 =
92
- * More meaningful errors when plugin downloads fail
93
- * WordPress 4.2 compatibility
94
- * Performance improvements (will not load in places where it's not needed and not make unnecessary requests to the CDN)
95
- * Support putting deps.xml config file in the theme folder (root)
96
- * Included code for importing data for toolset plugins
97
- * Use CloudFront urls for products list files
98
-
99
- = 1.5.1 =
100
- * Fix for allowing embedded plugins to be updated
101
- * Logic for the migration from embedded plugins to full plugins
102
-
103
- = 1.5 =
104
- * Support for embedded plugins
105
- * Bug fix: When user registers site key with trailing slash, downloads might not work
106
- * Tweak: Set a higher timeout limit for the http requests to CDN and API
107
- * API function: link to specific repository
108
- * API function: get product price
109
- * New method for defining affiliate info (with backwards compatibility)
110
-
111
- = 1.4 =
112
- * Show explicit error in case of connectivity issues while validating a key.
113
- * Bug fix: Downloading plugins in bulk was broken by plugin that had a redirect after activation
114
- * Display friendly error message when WordPress does not have permissions to write to the plugins folder
115
- * Added support for configuration files to auto-download required plugins and theme keys
116
- * Changed the "Update this info" button to "Check for updates" (it refreshes the subscription info and checks for updates)
117
- * Support for high_priority parameter that allows setting priority for an Installer instance when more with the same version number exist.
118
- * Config files from different instances are combined (define repositories in different instances)
119
- * Updated support for conditional updates display for ICL users
120
- * More friendly error reporting and handling when using an invalid site key or the plugins archives are not valid.
121
-
122
-
123
- = 1.3.1 =
124
- * Support for conditional release notification (ICanLocalize)
125
-
126
- = 1.3 =
127
- * Added a new repository: Toolset
128
- * The product packages can be displayed hierarchically and ordered
129
- * The link to automatically create site keys will follow through login on the account site (e.g. wpml.org, wp-types.com)
130
- * Fixed animation issues (not showing in most browsers) when downloading plugins.
131
- * Created an admin screen on the repository end (icl-mpp) to sho registration stats (site keys, site keys usage, components usage etc..).
132
- * Bug fix: Renew and Upgrade buttons were not entirely clickable
133
- * Bug fix: Action buttons (buy, renew, upgrade) were not displayed correctly when WPML was not active (Installer embedded in theme)
134
- * Support for site-wide registration. Products can be registered on the network instead of on each site separately.
135
- * Users are able to add either http or https version for any site urls. There will be one site key that will work with both http and https versions.
136
-
137
- = 1.2 =
138
- * Added pagination for site keys list of Account -> My Sites
139
- * Reversed the order in which the site keys are displayed.
140
- * Fixed problem with WPML registration information (site key) not being saved when the option_value field in the wp_options table used a different charset than the default WordPress charset defined in wp-config.php
141
- * Allow registering new sites by clicking a link in the WordPress admin instead of copying and pasting the site url in the Account -> My Sites section
142
- * Display more detailed debug information related to connectivity issues with the WPML repository
 
 
 
 
 
 
 
 
1
+ = 1.7.13 =
2
+ * Added sanitization for some inputs
3
+ * Fixed PHP notice being logged when installing a plugin from the WP plugins directory
4
+
5
+ = 1.7.12 =
6
+ * Allow to set the site keys in PHP (as constants)
7
+ * Fixed an issue with unregistered sites gets wrong info (or error) when clicking "View version x.y.x details" link
8
+
9
+ = 1.7.11 =
10
+ * Fixed styling for 'must-register' warnings on the plugins page
11
+
12
+ = 1.7.10 =
13
+ * Fixed a notice that was appearing when the site key registration failed
14
+
15
+ = 1.7.9 =
16
+ * Save the client_id value from the site_key_validation API call response and make it available via WP_Installer_API::get_ts_client_id API call
17
+
18
+ = 1.7.8 =
19
+ * Small fix for hiding the WPML registration notice
20
+
21
+ = 1.7.7 =
22
+ * Fixed js error showing up during registration
23
+ * Styles update for unified WPML messages
24
+
25
+ = 1.7.6 =
26
+ * Updated error messages when validating site keys and stopped removing site keys in case of communication errors
27
+ * Added a note for users who renewed or purchased new subscriptions and who need to revalidate their subscription from their websites
28
+ * Fixed a problem with the registrations for multi-site setups when WordPress was installed in a separate folder
29
+
30
+ = 1.7.5 =
31
+ * Fixed a bug causing registration to not be recognized for the entire network in the multi-site mode
32
+
33
+ = 1.7.4 =
34
+ * Use https for wp-types API
35
+
36
+ = 1.7.3 =
37
+ * Added a dependencies class and check for the windows paths length exception
38
+ * Bug fix: all downloads showed up twice on the plugins list whe upgrading from WPML 3.3
39
+
40
+ = 1.7.2 =
41
+ * Added an exception to handle the case when Types embedded is installer from Toolset Installer and its included Installer version overrides the one running the Toolset setup wizard
42
+
43
+ = 1.7.1 =
44
+ * Added an exception for the case of Types 1.8.9 (Installer 1.7.0) together with older WPML (older Installer)
45
+
46
+ = 1.7.0 =
47
+ * New format for the products data file.
48
+ * Other fixes
49
+
50
+ = 1.6.8 =
51
+ * Sanitized an input that was a potential security issue
52
+
53
+ = 1.6.7 =
54
+ * Fixed a bug causing repeated calls to the Toolset api to validate the user subscription
55
+ * Use https for API urls
56
+
57
+ = 1.6.6 =
58
+ * Fixed the 'Call to undefined function get_plugins()' issue
59
+
60
+ = 1.6.5 =
61
+ * Added configuration file for composer
62
+ * Updated how free plugins are shown on the plugins list (commercial tab)
63
+ * API calls for manipulating translation service preferences
64
+ * Support for hosting custom Installer packages on wpml.org
65
+ * Fixed a warning that was showing when using the OTGS_DISABLE_AUTO_UPDATES constant before any product data was downloaded
66
+ * Changed the frequency with which product updates are checked automatically (24 hours)
67
+ * Improved reporting for version numbers
68
+
69
+ = 1.6.4 =
70
+ * Enabled the OTGS_DISABLE_AUTO_UPDATES constant for theme update checks
71
+ * Fixed a bug that was causing Register links to show for all installed plugins
72
+
73
+ = 1.6.3 =
74
+ * Fixed performance issue related to themes upgrade logic
75
+
76
+ = 1.6 =
77
+ * Improved the way plugins are matched: not just by the folder name (slug) but also by name
78
+ * Added support for installing and upgrading themes from repositories (currently: Toolset themes)
79
+ * Added support for 'alias' plugins on the toolset and wpml repositories (currently: Types)
80
+ * Enhanced the progress animation during plugins downloading
81
+
82
+ = 1.5.6 =
83
+ * Updated the translations
84
+ * Fix for WPML 3.2 conditional upgrade logic
85
+
86
+ = 1.5.5 =
87
+ * Fixed the logic for the high_priority parameter
88
+ * Fixed js bug causing a conflict with NextGen
89
+ * Fixed bug preventing users to install and upgrade Types when they didn't have a Toolset subscription
90
+ * Fixed bug preventing users to upgrade from the embedded Types to the full version
91
+
92
+ = 1.5.4 =
93
+ * Option to disable auto-updates
94
+ * Escaped urls generated with add_query_arg
95
+
96
+ = 1.5.3 =
97
+ * Fixed bug in WP_Installer::custom_plugins_api_call (filter for plugins_api) causing conflicts with other filters for plugins_api
98
+
99
+ = 1.5.2 =
100
+ * More meaningful errors when plugin downloads fail
101
+ * WordPress 4.2 compatibility
102
+ * Performance improvements (will not load in places where it's not needed and not make unnecessary requests to the CDN)
103
+ * Support putting deps.xml config file in the theme folder (root)
104
+ * Included code for importing data for toolset plugins
105
+ * Use CloudFront urls for products list files
106
+
107
+ = 1.5.1 =
108
+ * Fix for allowing embedded plugins to be updated
109
+ * Logic for the migration from embedded plugins to full plugins
110
+
111
+ = 1.5 =
112
+ * Support for embedded plugins
113
+ * Bug fix: When user registers site key with trailing slash, downloads might not work
114
+ * Tweak: Set a higher timeout limit for the http requests to CDN and API
115
+ * API function: link to specific repository
116
+ * API function: get product price
117
+ * New method for defining affiliate info (with backwards compatibility)
118
+
119
+ = 1.4 =
120
+ * Show explicit error in case of connectivity issues while validating a key.
121
+ * Bug fix: Downloading plugins in bulk was broken by plugin that had a redirect after activation
122
+ * Display friendly error message when WordPress does not have permissions to write to the plugins folder
123
+ * Added support for configuration files to auto-download required plugins and theme keys
124
+ * Changed the "Update this info" button to "Check for updates" (it refreshes the subscription info and checks for updates)
125
+ * Support for high_priority parameter that allows setting priority for an Installer instance when more with the same version number exist.
126
+ * Config files from different instances are combined (define repositories in different instances)
127
+ * Updated support for conditional updates display for ICL users
128
+ * More friendly error reporting and handling when using an invalid site key or the plugins archives are not valid.
129
+
130
+
131
+ = 1.3.1 =
132
+ * Support for conditional release notification (ICanLocalize)
133
+
134
+ = 1.3 =
135
+ * Added a new repository: Toolset
136
+ * The product packages can be displayed hierarchically and ordered
137
+ * The link to automatically create site keys will follow through login on the account site (e.g. wpml.org, wp-types.com)
138
+ * Fixed animation issues (not showing in most browsers) when downloading plugins.
139
+ * Created an admin screen on the repository end (icl-mpp) to sho registration stats (site keys, site keys usage, components usage etc..).
140
+ * Bug fix: Renew and Upgrade buttons were not entirely clickable
141
+ * Bug fix: Action buttons (buy, renew, upgrade) were not displayed correctly when WPML was not active (Installer embedded in theme)
142
+ * Support for site-wide registration. Products can be registered on the network instead of on each site separately.
143
+ * Users are able to add either http or https version for any site urls. There will be one site key that will work with both http and https versions.
144
+
145
+ = 1.2 =
146
+ * Added pagination for site keys list of Account -> My Sites
147
+ * Reversed the order in which the site keys are displayed.
148
+ * Fixed problem with WPML registration information (site key) not being saved when the option_value field in the wp_options table used a different charset than the default WordPress charset defined in wp-config.php
149
+ * Allow registering new sites by clicking a link in the WordPress admin instead of copying and pasting the site url in the Account -> My Sites section
150
+ * Display more detailed debug information related to connectivity issues with the WPML repository
library/otgs/installer/includes/class-installer-dependencies.php CHANGED
@@ -1,278 +1,278 @@
1
- <?php
2
-
3
- class Installer_Dependencies{
4
-
5
- private $uploading_allowed = null;
6
- private $is_win_paths_exception = array();
7
-
8
-
9
- function __construct(){
10
-
11
- add_action( 'admin_init', array( $this, 'prevent_plugins_update_on_plugins_page' ), 100);
12
-
13
-
14
-
15
- global $pagenow;
16
- if($pagenow == 'update.php'){
17
- if(isset($_GET['action']) && $_GET['action'] == 'update-selected'){
18
- add_action('admin_head', array($this, 'prevent_plugins_update_on_updates_screen')); //iframe/bulk
19
- }else{
20
- add_action('all_admin_notices', array($this, 'prevent_plugins_update_on_updates_screen')); //regular/singular
21
- }
22
- }
23
- add_action('wp_ajax_update-plugin', array($this, 'prevent_plugins_update_on_updates_screen'), 0); // high priority, before WP
24
-
25
- }
26
-
27
- public function is_win_paths_exception($repository_id){
28
-
29
- if(!isset($this->is_win_paths_exception[$repository_id])) {
30
-
31
- $this->is_win_paths_exception[$repository_id] = false;
32
-
33
- if ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' ) {
34
-
35
- $windows_max_path_length = 256;
36
- $longest_path['wpml'] = 109;
37
- $longest_path['toolset'] = 99;
38
-
39
- $margin = 15;
40
-
41
- $upgrade_path_length = strlen( WP_CONTENT_DIR . '/upgrade' );
42
-
43
- $installer_settings = WP_Installer()->settings;
44
-
45
- if ( is_array( $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'] ) ) {
46
- $a_plugin = current( $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'] );
47
- $url = WP_Installer()->append_site_key_to_download_url( $a_plugin['url'], 'xxxxxx', $repository_id );
48
- $tmpfname = wp_tempnam( $url );
49
-
50
- $tmpname_length = strlen( basename( $tmpfname ) ) - 4; // -.tmp
51
-
52
- if ( $upgrade_path_length + $tmpname_length + $longest_path[$repository_id] + $margin > $windows_max_path_length ) {
53
-
54
- $this->is_win_paths_exception[$repository_id] = true;
55
-
56
- }
57
-
58
- }
59
-
60
-
61
- }
62
-
63
- }
64
-
65
- return $this->is_win_paths_exception[$repository_id];
66
-
67
- }
68
-
69
- public function is_uploading_allowed(){
70
-
71
- if(!isset($this->uploading_allowed)){
72
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
73
- require_once WP_Installer()->plugin_path() . '/includes/installer-upgrader-skins.php';
74
-
75
- $upgrader_skins = new Installer_Upgrader_Skins(); //use our custom (mute) Skin
76
- $upgrader = new Plugin_Upgrader($upgrader_skins);
77
-
78
- ob_start();
79
- $res = $upgrader->fs_connect( array(WP_CONTENT_DIR, WP_PLUGIN_DIR) );
80
- ob_end_clean();
81
-
82
- if ( ! $res || is_wp_error( $res ) ) {
83
- $this->uploading_allowed = false;
84
- }else{
85
- $this->uploading_allowed = true;
86
- }
87
- }
88
-
89
- return $this->uploading_allowed;
90
-
91
- }
92
-
93
- public function cant_download($repository_id){
94
-
95
- return !$this->is_uploading_allowed() || $this->is_win_paths_exception($repository_id);
96
-
97
- }
98
-
99
- public function win_paths_exception_message(){
100
- return __('Downloading is not possible. WordPress cannot create required folders because of the
101
- 256 characters limitation of the current Windows environment.', 'installer');
102
- }
103
-
104
- public function prevent_plugins_update_on_plugins_page(){
105
-
106
- $plugins = get_site_transient( 'update_plugins' );
107
- if ( isset($plugins->response) && is_array($plugins->response) ) {
108
- $plugins_with_updates = array_keys( $plugins->response );
109
- }
110
-
111
- if( !empty($plugins_with_updates) ) {
112
-
113
- $plugins = get_plugins();
114
-
115
- $installer_settings = WP_Installer()->settings;
116
- foreach ($installer_settings['repositories'] as $repository_id => $repository) {
117
-
118
- if ($this->is_win_paths_exception($repository_id)) {
119
-
120
- $repositories_plugins = array();
121
- foreach ($repository['data']['packages'] as $package) {
122
- foreach ($package['products'] as $product) {
123
- foreach ($product['plugins'] as $plugin_slug) {
124
- $download = $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
125
- if ( empty($download['free-on-wporg']) ) {
126
- $repositories_plugins[$download['slug']] = $download['name'];
127
- }
128
- }
129
- }
130
- }
131
-
132
- foreach ($plugins as $plugin_id => $plugin) {
133
-
134
- if( in_array( $plugin_id, $plugins_with_updates ) ) {
135
-
136
- $wp_plugin_slug = dirname($plugin_id);
137
- if (empty($wp_plugin_slug)) {
138
- $wp_plugin_slug = basename($plugin_id, '.php');
139
- }
140
-
141
- foreach ($repositories_plugins as $slug => $name) {
142
- if ($wp_plugin_slug == $slug || $name == $plugin['Name'] || $name == $plugin['Title']) { //match order: slug, name, title
143
-
144
- remove_action("after_plugin_row_$plugin_id", 'wp_plugin_update_row', 10, 2);
145
- add_action("after_plugin_row_$plugin_id", array($this, 'wp_plugin_update_row_win_exception'), 10, 2);
146
-
147
- }
148
- }
149
-
150
- }
151
-
152
- }
153
-
154
- }
155
-
156
-
157
- }
158
-
159
- }
160
-
161
- }
162
-
163
- public function wp_plugin_update_row_win_exception(){
164
- $wp_list_table = _get_list_table('WP_Plugins_List_Table');
165
- echo '<tr class="plugin-update-tr">';
166
- echo '<td class="plugin-update colspanchange" colspan="' . esc_attr( $wp_list_table->get_column_count() ) .
167
- '"><div class="update-message">' . $this->win_paths_exception_message() . '</div></td>';
168
- echo '</tr>';
169
- }
170
-
171
- public function prevent_plugins_update_on_updates_screen(){
172
-
173
- if ( isset($_REQUEST['action']) ) {
174
-
175
- $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
176
-
177
- $installer_settings = WP_Installer()->settings;
178
-
179
- //bulk mode
180
- if('update-selected' == $action) {
181
-
182
- global $plugins;
183
-
184
- if(isset($plugins) && is_array($plugins)) {
185
-
186
- foreach ($plugins as $k => $plugin) {
187
-
188
- $wp_plugin_slug = dirname($plugin);
189
-
190
- foreach ($installer_settings['repositories'] as $repository_id => $repository) {
191
-
192
- if( $this->is_win_paths_exception($repository_id) ){
193
-
194
- foreach ($repository['data']['packages'] as $package) {
195
-
196
- foreach ($package['products'] as $product) {
197
-
198
- foreach ($product['plugins'] as $plugin_slug) {
199
-
200
- $download = $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
201
-
202
- if ($download['slug'] == $wp_plugin_slug && empty($download['free-on-wporg']) ) {
203
-
204
- echo '<div class="updated error"><p>' . $this->win_paths_exception_message() .
205
- ' <strong>(' . $download['name'] . ')</strong>' . '</p></div>';
206
- unset($plugins[$k]);
207
-
208
- break(3);
209
-
210
- }
211
-
212
- }
213
-
214
- }
215
-
216
- }
217
-
218
-
219
- }
220
-
221
- }
222
-
223
- }
224
-
225
- }
226
-
227
- }
228
-
229
-
230
- if( 'upgrade-plugin' == $action || 'update-plugin' == $action ) {
231
-
232
- $plugin = isset($_REQUEST['plugin']) ? trim($_REQUEST['plugin']) : '';
233
-
234
- $wp_plugin_slug = dirname($plugin);
235
-
236
- foreach($installer_settings['repositories'] as $repository_id => $repository){
237
-
238
- if( $this->is_win_paths_exception( $repository_id ) ) {
239
- foreach ($repository['data']['packages'] as $package) {
240
-
241
- foreach($package['products'] as $product) {
242
-
243
- foreach($product['plugins'] as $plugin_slug) {
244
- $download = $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
245
-
246
- //match by folder, will change to match by name and folder
247
- if ( $download['slug'] == $wp_plugin_slug && empty ($download['free-on-wporg'] ) ) {
248
-
249
- echo '<div class="updated error"><p>' . $this->win_paths_exception_message() . '</p></div>';
250
-
251
- echo '<div class="wrap">';
252
- echo '<h2>' . __('Update Plugin') . '</h2>';
253
- echo '<a href="' . admin_url('update-core.php') . '">' . __('Return to the updates page', 'installer') . '</a>';
254
- echo '</div>';
255
- require_once(ABSPATH . 'wp-admin/admin-footer.php');
256
- exit;
257
-
258
- }
259
-
260
- }
261
-
262
- }
263
-
264
- }
265
- }
266
-
267
- }
268
-
269
- }
270
- }
271
-
272
- }
273
-
274
-
275
- }
276
-
277
-
278
-
1
+ <?php
2
+
3
+ class Installer_Dependencies{
4
+
5
+ private $uploading_allowed = null;
6
+ private $is_win_paths_exception = array();
7
+
8
+
9
+ function __construct(){
10
+
11
+ add_action( 'admin_init', array( $this, 'prevent_plugins_update_on_plugins_page' ), 100);
12
+
13
+
14
+
15
+ global $pagenow;
16
+ if($pagenow == 'update.php'){
17
+ if(isset($_GET['action']) && $_GET['action'] == 'update-selected'){
18
+ add_action('admin_head', array($this, 'prevent_plugins_update_on_updates_screen')); //iframe/bulk
19
+ }else{
20
+ add_action('all_admin_notices', array($this, 'prevent_plugins_update_on_updates_screen')); //regular/singular
21
+ }
22
+ }
23
+ add_action('wp_ajax_update-plugin', array($this, 'prevent_plugins_update_on_updates_screen'), 0); // high priority, before WP
24
+
25
+ }
26
+
27
+ public function is_win_paths_exception($repository_id){
28
+
29
+ if(!isset($this->is_win_paths_exception[$repository_id])) {
30
+
31
+ $this->is_win_paths_exception[$repository_id] = false;
32
+
33
+ if ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' ) {
34
+
35
+ $windows_max_path_length = 256;
36
+ $longest_path['wpml'] = 109;
37
+ $longest_path['toolset'] = 99;
38
+
39
+ $margin = 15;
40
+
41
+ $upgrade_path_length = strlen( WP_CONTENT_DIR . '/upgrade' );
42
+
43
+ $installer_settings = WP_Installer()->settings;
44
+
45
+ if ( is_array( $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'] ) ) {
46
+ $a_plugin = current( $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'] );
47
+ $url = WP_Installer()->append_site_key_to_download_url( $a_plugin['url'], 'xxxxxx', $repository_id );
48
+ $tmpfname = wp_tempnam( $url );
49
+
50
+ $tmpname_length = strlen( basename( $tmpfname ) ) - 4; // -.tmp
51
+
52
+ if ( $upgrade_path_length + $tmpname_length + $longest_path[$repository_id] + $margin > $windows_max_path_length ) {
53
+
54
+ $this->is_win_paths_exception[$repository_id] = true;
55
+
56
+ }
57
+
58
+ }
59
+
60
+
61
+ }
62
+
63
+ }
64
+
65
+ return $this->is_win_paths_exception[$repository_id];
66
+
67
+ }
68
+
69
+ public function is_uploading_allowed(){
70
+
71
+ if(!isset($this->uploading_allowed)){
72
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
73
+ require_once WP_Installer()->plugin_path() . '/includes/installer-upgrader-skins.php';
74
+
75
+ $upgrader_skins = new Installer_Upgrader_Skins(); //use our custom (mute) Skin
76
+ $upgrader = new Plugin_Upgrader($upgrader_skins);
77
+
78
+ ob_start();
79
+ $res = $upgrader->fs_connect( array(WP_CONTENT_DIR, WP_PLUGIN_DIR) );
80
+ ob_end_clean();
81
+
82
+ if ( ! $res || is_wp_error( $res ) ) {
83
+ $this->uploading_allowed = false;
84
+ }else{
85
+ $this->uploading_allowed = true;
86
+ }
87
+ }
88
+
89
+ return $this->uploading_allowed;
90
+
91
+ }
92
+
93
+ public function cant_download($repository_id){
94
+
95
+ return !$this->is_uploading_allowed() || $this->is_win_paths_exception($repository_id);
96
+
97
+ }
98
+
99
+ public function win_paths_exception_message(){
100
+ return __('Downloading is not possible. WordPress cannot create required folders because of the
101
+ 256 characters limitation of the current Windows environment.', 'installer');
102
+ }
103
+
104
+ public function prevent_plugins_update_on_plugins_page(){
105
+
106
+ $plugins = get_site_transient( 'update_plugins' );
107
+ if ( isset($plugins->response) && is_array($plugins->response) ) {
108
+ $plugins_with_updates = array_keys( $plugins->response );
109
+ }
110
+
111
+ if( !empty($plugins_with_updates) ) {
112
+
113
+ $plugins = get_plugins();
114
+
115
+ $installer_settings = WP_Installer()->settings;
116
+ foreach ($installer_settings['repositories'] as $repository_id => $repository) {
117
+
118
+ if ($this->is_win_paths_exception($repository_id)) {
119
+
120
+ $repositories_plugins = array();
121
+ foreach ($repository['data']['packages'] as $package) {
122
+ foreach ($package['products'] as $product) {
123
+ foreach ($product['plugins'] as $plugin_slug) {
124
+ $download = $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
125
+ if ( empty($download['free-on-wporg']) ) {
126
+ $repositories_plugins[$download['slug']] = $download['name'];
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ foreach ($plugins as $plugin_id => $plugin) {
133
+
134
+ if( in_array( $plugin_id, $plugins_with_updates ) ) {
135
+
136
+ $wp_plugin_slug = dirname($plugin_id);
137
+ if (empty($wp_plugin_slug)) {
138
+ $wp_plugin_slug = basename($plugin_id, '.php');
139
+ }
140
+
141
+ foreach ($repositories_plugins as $slug => $name) {
142
+ if ($wp_plugin_slug == $slug || $name == $plugin['Name'] || $name == $plugin['Title']) { //match order: slug, name, title
143
+
144
+ remove_action("after_plugin_row_$plugin_id", 'wp_plugin_update_row', 10, 2);
145
+ add_action("after_plugin_row_$plugin_id", array($this, 'wp_plugin_update_row_win_exception'), 10, 2);
146
+
147
+ }
148
+ }
149
+
150
+ }
151
+
152
+ }
153
+
154
+ }
155
+
156
+
157
+ }
158
+
159
+ }
160
+
161
+ }
162
+
163
+ public function wp_plugin_update_row_win_exception(){
164
+ $wp_list_table = _get_list_table('WP_Plugins_List_Table');
165
+ echo '<tr class="plugin-update-tr">';
166
+ echo '<td class="plugin-update colspanchange" colspan="' . esc_attr( $wp_list_table->get_column_count() ) .
167
+ '"><div class="update-message">' . $this->win_paths_exception_message() . '</div></td>';
168
+ echo '</tr>';
169
+ }
170
+
171
+ public function prevent_plugins_update_on_updates_screen(){
172
+
173
+ if ( isset($_REQUEST['action']) ) {
174
+
175
+ $action = isset($_REQUEST['action']) ? sanitize_text_field ( $_REQUEST['action'] ) : '';
176
+
177
+ $installer_settings = WP_Installer()->settings;
178
+
179
+ //bulk mode
180
+ if('update-selected' == $action) {
181
+
182
+ global $plugins;
183
+
184
+ if(isset($plugins) && is_array($plugins)) {
185
+
186
+ foreach ($plugins as $k => $plugin) {
187
+
188
+ $wp_plugin_slug = dirname($plugin);
189
+
190
+ foreach ($installer_settings['repositories'] as $repository_id => $repository) {
191
+
192
+ if( $this->is_win_paths_exception($repository_id) ){
193
+
194
+ foreach ($repository['data']['packages'] as $package) {
195
+
196
+ foreach ($package['products'] as $product) {
197
+
198
+ foreach ($product['plugins'] as $plugin_slug) {
199
+
200
+ $download = $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
201
+
202
+ if ($download['slug'] == $wp_plugin_slug && empty($download['free-on-wporg']) ) {
203
+
204
+ echo '<div class="updated error"><p>' . $this->win_paths_exception_message() .
205
+ ' <strong>(' . $download['name'] . ')</strong>' . '</p></div>';
206
+ unset($plugins[$k]);
207
+
208
+ break(3);
209
+
210
+ }
211
+
212
+ }
213
+
214
+ }
215
+
216
+ }
217
+
218
+
219
+ }
220
+
221
+ }
222
+
223
+ }
224
+
225
+ }
226
+
227
+ }
228
+
229
+
230
+ if( 'upgrade-plugin' == $action || 'update-plugin' == $action ) {
231
+
232
+ $plugin = isset($_REQUEST['plugin']) ? trim( sanitize_text_field ( $_REQUEST['plugin'] ) ) : '';
233
+
234
+ $wp_plugin_slug = dirname($plugin);
235
+
236
+ foreach($installer_settings['repositories'] as $repository_id => $repository){
237
+
238
+ if( $this->is_win_paths_exception( $repository_id ) ) {
239
+ foreach ($repository['data']['packages'] as $package) {
240
+
241
+ foreach($package['products'] as $product) {
242
+
243
+ foreach($product['plugins'] as $plugin_slug) {
244
+ $download = $installer_settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
245
+
246
+ //match by folder, will change to match by name and folder
247
+ if ( $download['slug'] == $wp_plugin_slug && empty ($download['free-on-wporg'] ) ) {
248
+
249
+ echo '<div class="updated error"><p>' . $this->win_paths_exception_message() . '</p></div>';
250
+
251
+ echo '<div class="wrap">';
252
+ echo '<h2>' . __('Update Plugin') . '</h2>';
253
+ echo '<a href="' . admin_url('update-core.php') . '">' . __('Return to the updates page', 'installer') . '</a>';
254
+ echo '</div>';
255
+ require_once(ABSPATH . 'wp-admin/admin-footer.php');
256
+ exit;
257
+
258
+ }
259
+
260
+ }
261
+
262
+ }
263
+
264
+ }
265
+ }
266
+
267
+ }
268
+
269
+ }
270
+ }
271
+
272
+ }
273
+
274
+
275
+ }
276
+
277
+
278
+
library/otgs/installer/includes/class-installer-theme.php CHANGED
@@ -1,979 +1,979 @@
1
- <?php
2
- /**
3
- * Installer Class for Theme Support
4
- *
5
- * Supports automatic updates and installation of Toolset/WPML Themes
6
- *
7
- * @class Installer_Theme_Class
8
- * @version 1.6
9
- * @category Class
10
- * @author OnTheGoSystems
11
- */
12
-
13
- if ( !defined( 'ABSPATH' ) ) {
14
- exit;
15
- }
16
-
17
- /**
18
- * Installer_Theme_Class
19
- */
20
- class Installer_Theme_Class {
21
-
22
- /** Theme Repository */
23
- private $theme_repo;
24
-
25
- /** Repository API */
26
- private $repository_api;
27
-
28
- /** Repository Theme Products */
29
- private $repository_theme_products;
30
-
31
- /** Site URL */
32
- private $installer_site_url;
33
-
34
- /** Site Key */
35
- private $installer_site_key;
36
-
37
- /** The Themes Option */
38
- protected $installer_themes_option;
39
-
40
- /** Update settings */
41
- protected $installer_themes_available_updates;
42
-
43
- /** The Themes */
44
- protected $installer_themes = array();
45
-
46
- /** Repository with themes */
47
- protected $installer_repo_with_themes;
48
-
49
- /** Active tab */
50
- protected $installer_theme_active_tab;
51
-
52
- /** Theme user registration */
53
- protected $theme_user_registration;
54
-
55
- /** Client active subscription */
56
- protected $installer_theme_subscription_type;
57
-
58
- public function __construct() {
59
-
60
- /** Properties */
61
-
62
- //Get installer repositories
63
- $installer_repositories = WP_Installer()->get_repositories();
64
-
65
- //Get repos with themes
66
- $repos_with_themes = $this->installer_theme_reposities_that_has_themes( $installer_repositories );
67
-
68
- if ( is_array( $repos_with_themes ) ) {
69
- //Assign to property
70
- $this->installer_repo_with_themes = $repos_with_themes;
71
-
72
- //Let's looped through repos with themes
73
- foreach ( $repos_with_themes as $k => $repo ) {
74
-
75
- //$repo could be 'toolset' or 'wpml'
76
- //Assign each repo with theme to property
77
- $this->theme_repo[] = $repo;
78
-
79
- if ( (isset($installer_repositories[$repo]['api-url'])) && (isset($installer_repositories[$repo]['products'])) ) {
80
-
81
- //Define the rest of the properties based on the given repo
82
- $this->repository_api[$repo] = $installer_repositories[$repo]['api-url'];
83
- $this->repository_theme_products[$repo] = $installer_repositories[$repo]['products'];
84
- $this->installer_site_url[$repo] = WP_Installer()->get_installer_site_url( $repo );
85
- $this->installer_site_key[$repo] = WP_Installer()->get_site_key( $repo );
86
- $this->theme_user_registration[$repo] = false;
87
-
88
- if ( WP_Installer()->repository_has_valid_subscription( $repo ) ) {
89
-
90
- $this->installer_theme_subscription_type = WP_Installer()->get_subscription_type_for_repository( $repo );
91
- $this->installer_themes_option[$repo] = 'wp_installer_' . $repo . '_themes';
92
- $this->installer_themes_available_updates[$repo] = 'wp_installer_' . $repo . '_updated_themes';
93
- $this->installer_theme_active_tab = '';
94
-
95
- //We only set themes available to this validated subscription
96
- $this->installer_theme_available( $repo, $this->installer_theme_subscription_type );
97
-
98
- add_action( 'installer_themes_support_set_up', array($this, 'installer_theme_sets_active_tab_on_init'), 10 );
99
- $this->theme_user_registration[$repo] = true;
100
- }
101
-
102
- /** We are ready.. let's initialize .... */
103
- $this->init();
104
- }
105
- }
106
- add_action( 'installer_themes_support_set_up', array($this, 'installer_theme_loaded_hooks') );
107
- }
108
- }
109
-
110
- /** Init */
111
- public function init() {
112
- add_action( 'admin_enqueue_scripts', array($this, 'installer_theme_enqueue_scripts') );
113
- add_filter( 'themes_api', array($this, 'installer_theme_api_override'), 10, 3 );
114
- add_filter( 'themes_api_result', array($this, 'installer_theme_api_override_response'), 10, 3 );
115
- add_filter( 'site_transient_update_themes', array($this, 'installer_theme_upgrade_check'), 10, 1 );
116
- add_action( 'http_api_debug', array($this, 'installer_theme_sync_native_wp_api'), 10, 5 );
117
- add_filter( 'installer_theme_hook_response_theme', array($this, 'installer_theme_add_num_ratings'), 10, 1 );
118
- add_filter( 'themes_update_check_locales', array($this, 'installer_theme_sync_call_wp_theme_api'), 10, 1 );
119
- add_filter( 'admin_url', array($this, 'installer_theme_add_query_arg_tab'), 10, 3 );
120
- add_filter( 'network_admin_url', array($this, 'installer_theme_add_query_arg_tab'), 10, 2 );
121
- add_action( 'wp_ajax_installer_theme_frontend_selected_tab', array($this, 'installer_theme_frontend_selected_tab'), 0 );
122
- add_action( 'wp_loaded', array($this, 'installer_themes_support_set_up_func') );
123
- }
124
-
125
- /** Enqueue scripts */
126
- public function installer_theme_enqueue_scripts() {
127
- $current_screen = $this->installer_theme_current_screen();
128
- $commercial_plugin_screen = $this->installer_theme_is_commercial_plugin_screen( $current_screen );
129
- if ( ('theme-install' == $current_screen) || ($commercial_plugin_screen) || ('theme-install-network' == $current_screen) ) {
130
- $repo_with_themes = $this->installer_repo_with_themes;
131
- $js_array = array();
132
- if ( is_array( $repo_with_themes ) ) {
133
- foreach ( $repo_with_themes as $k => $v ) {
134
-
135
- //Hyperlink text
136
- $theme_repo_name = $this->installer_theme_get_repo_product_name( $v );
137
- $the_hyperlink_text = esc_js( $theme_repo_name );
138
-
139
- if ( is_multisite() ) {
140
- $admin_url_passed = network_admin_url();
141
- } else {
142
- $admin_url_passed = admin_url();
143
- }
144
-
145
- //Define
146
- $js_array[$v] = array(
147
- 'the_hyperlink_text' => $the_hyperlink_text,
148
- 'registration_status' => $this->theme_user_registration[$v],
149
- 'is_commercial_plugin_tab' => $commercial_plugin_screen,
150
- 'registration_url' => $admin_url_passed . 'plugin-install.php?tab=commercial#installer_repo_' . $v
151
- );
152
-
153
- }
154
- }
155
-
156
- if ( !(empty($js_array)) ) {
157
- wp_enqueue_script( 'installer-theme-install', WP_Installer()->res_url() . '/res/js/installer_theme_install.js', array('jquery', 'installer-admin'), WP_Installer()->version() );
158
- $installer_ajax_url = admin_url( 'admin-ajax.php' );
159
-
160
- if ( is_ssl() ) {
161
- $installer_ajax_url = str_replace( 'http://', 'https://', $installer_ajax_url );
162
- } else {
163
- $installer_ajax_url = str_replace( 'https://', 'http://', $installer_ajax_url );
164
- }
165
-
166
- //Case where user is subscribed to a subscription that does not have themes
167
- $subscription_js_check = $this->installer_theme_subscription_does_not_have_theme( $js_array );
168
-
169
- wp_localize_script( 'installer-theme-install', 'installer_theme_install_localize',
170
- array(
171
- 'js_array_installer' => $js_array,
172
- 'ajaxurl' => $installer_ajax_url,
173
- 'no_associated_themes' => $subscription_js_check,
174
- 'installer_theme_frontend_selected_tab_nonce' => wp_create_nonce( 'installer_theme_frontend_selected_tab' )
175
- )
176
- );
177
- }
178
- }
179
- }
180
-
181
- /** Case where user is subscribed to a subscription that does not have themes */
182
- protected function installer_theme_subscription_does_not_have_theme( $js_array ) {
183
-
184
- $any_subscription_has_theme = array();
185
- $number_of_registrations = array();
186
-
187
- //Step1, we looped through JS array
188
- foreach ( $js_array as $repo_slug => $js_details ) {
189
-
190
- //Step2, checked if user is registered
191
- if ( isset($this->theme_user_registration[$repo_slug]) ) {
192
- $registration_status = $this->theme_user_registration[$repo_slug];
193
- if ( $registration_status ) {
194
-
195
- //Registered
196
- $number_of_registrations[] = $repo_slug;
197
-
198
- //Step3, we checked if the $repo_slug has available theme
199
- $themes_available = false;
200
- if ( isset($this->installer_themes[$repo_slug]) ) {
201
- $themes_available = $this->installer_themes[$repo_slug];
202
- if ( !(empty($themes_available)) ) {
203
- //This subscription has theme
204
- $themes_available = true;
205
- }
206
- }
207
-
208
- if ( $themes_available ) {
209
- $any_subscription_has_theme[] = $repo_slug;
210
- }
211
- }
212
- }
213
-
214
- }
215
-
216
- //Step4, we are done looping, check if there are any repos that have themes
217
- if ( empty($registration_status) ) {
218
-
219
- //No registration on any repos
220
- return FALSE;
221
-
222
- } elseif ( !(empty($registration_status)) ) {
223
-
224
- //Has some registration on some repos
225
- //We then checked if this user has any active subscriptions
226
- if ( empty($any_subscription_has_theme) ) {
227
- //No subscription
228
- return TRUE;
229
- } else {
230
- //Has subscription found
231
- return FALSE;
232
- }
233
- }
234
- }
235
-
236
- /** Check if its the commercial plugin screen */
237
- private function installer_theme_is_commercial_plugin_screen( $current_screen ) {
238
- $commercial = false;
239
- if ( ('plugin-install' == $current_screen) || ('plugin-install-network' == $current_screen) ) {
240
- if ( isset($_GET['tab']) ) {
241
- $tab = $_GET['tab'];
242
- if ( 'commercial' == $tab ) {
243
- $commercial = true;
244
- }
245
- }
246
- }
247
- return $commercial;
248
- }
249
-
250
- /** Current screen */
251
- private function installer_theme_current_screen() {
252
-
253
- $current_screen_loaded = false;
254
-
255
- if ( function_exists( 'get_current_screen' ) ) {
256
-
257
- $screen_output = get_current_screen();
258
- $current_screen_loaded = $screen_output->id;
259
-
260
- }
261
-
262
- return $current_screen_loaded;
263
-
264
- }
265
-
266
- /** Override WordPress Themes API */
267
- public function installer_theme_api_override( $api_boolean, $action, $args ) {
268
-
269
- //Let's checked if user is browsing our themes
270
- if ( isset($args->browse) ) {
271
- $browse = $args->browse;
272
- if ( in_array( $browse, $this->theme_repo ) ) {
273
- //Uniquely validated for our Themes
274
- if ( 'query_themes' == $action ) {
275
- //User is querying or asking information about our themes, let's override
276
- $api_boolean = true;
277
- }
278
- }
279
- } elseif ( isset($args->slug) ) {
280
- //We are installing our themes
281
- $theme_to_install = $args->slug;
282
-
283
- //Lets uniquely validate if this belongs to us
284
- //Check if this is OTGS theme
285
- $validate_check = $this->installer_themes_belong_to_us( $theme_to_install );
286
- if ( $validate_check ) {
287
- //Belongs to us
288
- if ( !(empty($theme_to_install)) ) {
289
- $api_boolean = true;
290
- }
291
- }
292
- }
293
-
294
- return $api_boolean;
295
- }
296
-
297
- /** Override WordPress Themes API response with our own themes API*/
298
- public function installer_theme_api_override_response( $res, $action, $args ) {
299
-
300
- if ( true === $res ) {
301
- if ( isset($args->browse) ) {
302
- $browse = $args->browse;
303
- if ( in_array( $browse, $this->theme_repo ) ) {
304
- //Uniquely validated for our themes
305
- if ( 'query_themes' == $action ) {
306
- //Client querying OTGS themes
307
- //Check for registration status
308
- if ( isset($this->theme_user_registration[$browse]) ) {
309
- //Set
310
- if ( !($this->theme_user_registration[$browse]) ) {
311
- //Not registered yet
312
- $res = new stdClass();
313
- $res->info = array();
314
- $res->themes = array();
315
- return $res;
316
- } else {
317
- //Registered
318
- $themes = $this->installer_theme_get_themes( '', $browse );
319
- $res = $this->installer_theme_format_response( $themes, $action );
320
- }
321
- }
322
- }
323
- }
324
- } elseif ( isset($args->slug) ) {
325
- //We are installing theme
326
- //Lets uniquely validate if this belongs to our theme
327
- $theme_to_install = $args->slug;
328
-
329
- //Lets uniquely validate if this belongs to us
330
- //Check if this is OTGS theme
331
- $validate_check = $this->installer_themes_belong_to_us( $theme_to_install );
332
- if ( $validate_check ) {
333
- //Belongs to us
334
- if ( ($res) && ('theme_information' == $action) ) {
335
- $themes = $this->installer_theme_get_themes( '', $this->installer_theme_active_tab );
336
- $res = $this->installer_theme_format_response( $themes, $action, $args->slug );
337
- }
338
- }
339
- }
340
- return $res;
341
- } else {
342
- //Default WP Themes here
343
- $client_side_active_tab = get_option( 'wp_installer_clientside_active_tab' );
344
- if ( $client_side_active_tab ) {
345
- if ( !(in_array( $client_side_active_tab, $this->theme_repo )) ) {
346
- //Not OTGS tab
347
- return $res;
348
- }
349
- }
350
-
351
- }
352
- }
353
-
354
- /** Get Themes */
355
- private function installer_theme_get_themes( $product_url = '', $repo_source = '' ) {
356
-
357
- //Query API
358
- if ( empty($product_url) ) {
359
- //Not set
360
- if ( isset($this->repository_theme_products[$this->installer_theme_active_tab]) ) {
361
- $query_remote_url = $this->repository_theme_products[$this->installer_theme_active_tab];
362
- }
363
-
364
- } else {
365
- $query_remote_url = $product_url;
366
- }
367
-
368
- //Let's retrieved current installer settings so we won't be querying all the time
369
- $current_installer_settings = WP_Installer()->get_settings();
370
-
371
- //Set $themes to FALSE by default
372
- $themes = false;
373
-
374
- if ( (is_array( $current_installer_settings )) && (!(empty($current_installer_settings))) ) {
375
-
376
- //Set and already defined, retrieved $products
377
- if ( isset($current_installer_settings['repositories'][$repo_source]['data']) ) {
378
- $products = $current_installer_settings['repositories'][$repo_source]['data'];
379
- if ( isset($products['downloads']['themes']) ) {
380
- $themes = $products['downloads']['themes'];
381
- }
382
- }
383
-
384
- } else {
385
-
386
- //Call API
387
- $response = wp_remote_get( $query_remote_url );
388
-
389
- if ( is_wp_error( $response ) ) {
390
- //Error detected: http fallback
391
- $query_remote_url = preg_replace( "@^https://@", 'http://', $query_remote_url );
392
- $response = wp_remote_get( $query_remote_url );
393
- }
394
-
395
- if ( !(is_wp_error( $response )) ) {
396
- //Not WP error
397
- //Evaluate response
398
- if ( $response && isset($response['response']['code']) && $response['response']['code'] == 200 ) {
399
- //In this case, response is set and defined, proceed...
400
- $body = wp_remote_retrieve_body( $response );
401
- if ( $body ) {
402
- $products = json_decode( $body, true );
403
- if ( isset($products['downloads']['themes']) ) {
404
- $themes = $products['downloads']['themes'];
405
- }
406
- }
407
-
408
- }
409
- }
410
- }
411
-
412
- //Return themes, can be filtered by user subscription type
413
- return apply_filters( 'installer_theme_get_themes', $themes, $this->installer_theme_active_tab );
414
- }
415
-
416
- /** Format response in compatibility with WordPress Theme API response */
417
- private function installer_theme_format_response( $themes, $action, $slug = '' ) {
418
-
419
- //Let's append download link only when retrieving theme information for installation
420
- if ( ('theme_information' == $action) && (!(empty($slug))) ) {
421
-
422
- //Only return one result -> the theme to be installed
423
- foreach ( $themes as $k => $theme ) {
424
- if ( $slug == $theme['basename'] ) {
425
- $theme['download_link'] = WP_Installer()->append_site_key_to_download_url( $theme['url'], $this->installer_site_key[$this->installer_theme_active_tab], $this->installer_theme_active_tab );
426
- $theme = json_decode( json_encode( $theme ), FALSE );
427
- return $theme;
428
- }
429
- }
430
-
431
- } else {
432
-
433
- $res = new stdClass();
434
- $res->info = array();
435
- $res->themes = array();
436
-
437
- //Define info
438
- $res->info['page'] = 1;
439
- $res->info['pages'] = 10;
440
-
441
- //Let's count available themes ;
442
- $res->info['results'] = count( $themes );
443
-
444
- //Let's saved themes for easy access later on
445
- $this->installer_theme_savethemes_by_slug( $themes );
446
-
447
- //Let's defined available themes
448
- if ( isset($this->installer_theme_subscription_type) ) {
449
- //Has subscription type defined, let's saved what is associated with this subscription
450
- $this->installer_theme_available( $this->installer_theme_active_tab, $this->installer_theme_subscription_type );
451
- } else {
452
- $this->installer_theme_available( $this->installer_theme_active_tab );
453
- }
454
-
455
- //Let's add themes to the overriden WordPress API Theme response
456
- /** Installer 1.7.6: Update to compatible data format response from WP Theme API */
457
- $theme_compatible_array=array();
458
- if ((is_array($themes))) {
459
- foreach ($themes as $k=>$v) {
460
- $theme_compatible_array[]=(object)($v);
461
- }
462
- }
463
- $res->themes = $theme_compatible_array;
464
- $res->themes = apply_filters( 'installer_theme_hook_response_theme', $res->themes );
465
- return $res;
466
- }
467
- }
468
-
469
- /** Let's save all available themes by its slug after any latest API query */
470
- private function installer_theme_savethemes_by_slug( $themes, $doing_query = false ) {
471
-
472
- if ( !($doing_query) ) {
473
- $this->installer_themes[$this->installer_theme_active_tab] = array();
474
- }
475
-
476
- if ( !(empty($themes)) ) {
477
- $themes_for_saving = array();
478
- foreach ( $themes as $k => $theme ) {
479
- if ( !($doing_query) ) {
480
- if ( isset($theme['slug']) ) {
481
- $theme_slug = $theme['slug'];
482
- if ( !(empty($theme_slug)) ) {
483
- $themes_for_saving[] = $theme_slug;
484
- }
485
- }
486
- } else {
487
-
488
- if ( ((isset($theme['slug'])) && (isset($theme['version'])) &&
489
- (isset($theme['theme_page_url']))) && (isset($theme['url']))
490
- ) {
491
- $theme_slug = $theme['slug'];
492
- $theme_version = $theme['version'];
493
- $theme_page_url = $theme['theme_page_url'];
494
- $theme_url = $theme['url'];
495
- if ( (!(empty($theme_slug))) && (!(empty($theme_version))) &&
496
- (!(empty($theme_page_url))) && (!(empty($theme_url)))
497
- ) {
498
- //$theme_slug is unique for every theme
499
- $themes_for_saving[$theme_slug] = array(
500
- 'version' => $theme_version,
501
- 'theme_page_url' => $theme_page_url,
502
- 'url' => $theme_url
503
- );
504
-
505
- }
506
- }
507
- }
508
-
509
- }
510
-
511
- if ( !(empty($themes_for_saving)) ) {
512
- //Has themes for saving
513
- if ( !($doing_query) ) {
514
- //Not doing query
515
- $existing_themes = get_option( $this->installer_themes_option[$this->installer_theme_active_tab] );
516
- if ( !($existing_themes) ) {
517
- //Does not yet exists
518
- delete_option( $this->installer_themes_option[$this->installer_theme_active_tab] );
519
- update_option( $this->installer_themes_option[$this->installer_theme_active_tab], $themes_for_saving );
520
- } else {
521
- //exists, check if we need to update
522
- if ( $existing_themes == $themes_for_saving ) {
523
- //Equal, no need to update here
524
- } else {
525
- //Update
526
- delete_option( $this->installer_themes_option[$this->installer_theme_active_tab] );
527
- update_option( $this->installer_themes_option[$this->installer_theme_active_tab], $themes_for_saving );
528
- }
529
- }
530
- } else {
531
- //Used for query purposes only, don't save anything
532
- return $themes_for_saving;
533
- }
534
- }
535
- }
536
- }
537
-
538
- /** Available themes */
539
- private function installer_theme_available( $repo, $subscription_type = '' ) {
540
-
541
- $subscription_type = intval( $subscription_type );
542
- if ( $subscription_type > 0 ) {
543
-
544
- //Here we have a case of validated subscription
545
- //We need to set themes that is available to this subscription
546
- $themes_associated_with_subscription = $this->installer_themes[$repo] = $this->installer_theme_get_themes_by_subscription( $subscription_type, $repo );
547
- if ( !(empty($themes_associated_with_subscription)) ) {
548
- //Has themes
549
- $this->installer_themes[$repo] = $themes_associated_with_subscription;
550
- }
551
- } else {
552
-
553
- //Get themes
554
- $this->installer_themes[$repo] = get_option( $this->installer_themes_option[$repo] );
555
- }
556
- }
557
-
558
- /** Theme upgrade check */
559
- public function installer_theme_upgrade_check( $the_value ) {
560
-
561
- //Step1: Let's looped through repos with themes and check if we have updates available for them.
562
- if ( (is_array( $this->installer_repo_with_themes )) && (!(empty($this->installer_repo_with_themes))) ) {
563
- foreach ( $this->installer_repo_with_themes as $k => $repo_slug ) {
564
- //Step2: Let's checked if we have update for this theme
565
- $update_available = get_option( $this->installer_themes_available_updates[$repo_slug] );
566
- if ( $update_available ) {
567
- if ( (is_array( $update_available )) && (!(empty($update_available))) ) {
568
- //Has updates available coming from this specific theme repo
569
- //Let's loop through the themes that needs update
570
- foreach ( $update_available as $theme_slug => $v ) {
571
- //Add to response API
572
- $the_value->response [$theme_slug] = array(
573
- 'theme' => $theme_slug,
574
- 'new_version' => $v['new_version'],
575
- 'url' => $v['url'],
576
- 'package' => $v['package']
577
- );
578
- }
579
- }
580
- }
581
- }
582
- }
583
- //Return
584
- return $the_value;
585
- }
586
-
587
- /** Return repositories that has themes */
588
- private function installer_theme_reposities_that_has_themes( $repositories, $ret_value = true, $doing_api_query = false ) {
589
-
590
- $repositories_with_themes = array();
591
-
592
- if ( (is_array( $repositories )) && (!(empty($repositories))) ) {
593
-
594
- //Let's checked if we have something before
595
- $themes = get_option( 'installer_repositories_with_theme' );
596
-
597
- if ( (!($themes)) || ($doing_api_query) ) {
598
- //Not yet defined
599
- //Loop through each repositories and check whether they have themes
600
- foreach ( $repositories as $k => $v ) {
601
- if ( isset($v['products']) ) {
602
- $products_url = $v['products'];
603
- $themes = $this->installer_theme_get_themes( $products_url, $k );
604
- if ( (is_array( $themes )) && (!(empty($themes))) ) {
605
- //Repo has themes
606
- $repositories_with_themes[] = $k;
607
- }
608
- }
609
- }
610
- } else {
611
- //Already set
612
- $repositories_with_themes = $themes;
613
- }
614
-
615
- if ( (((is_array( $repositories_with_themes )) && (!(empty($repositories_with_themes)))) && (!($themes))) || ($doing_api_query) ) {
616
- //Save to db
617
- update_option( 'installer_repositories_with_theme', $repositories_with_themes );
618
- }
619
- }
620
-
621
- if ( $ret_value ) {
622
- return $repositories_with_themes;
623
- }
624
-
625
- }
626
-
627
- /** When WordPress queries its own Themes API, we sync with our own */
628
- public function installer_theme_sync_native_wp_api( $response, $responsetext, $class, $args, $url ) {
629
-
630
- $api_native_string = 'api.wordpress.org/themes/';
631
- if ( (strpos( $url, $api_native_string ) !== false) ) {
632
- //WordPress is querying its own themes API
633
- $installer_repositories = WP_Installer()->get_repositories();
634
-
635
- //Query our own API and update repository values too
636
- $this->installer_theme_reposities_that_has_themes( $installer_repositories, false, true );
637
- }
638
- }
639
-
640
- /** Returns product name by theme repo slug */
641
- private function installer_theme_get_repo_product_name( $theme_repo ) {
642
-
643
- $theme_repo_name = false;
644
-
645
- if ( isset(WP_Installer()->settings['repositories'][$theme_repo]['data']['product-name']) ) {
646
- //Set
647
- $prod_name = WP_Installer()->settings['repositories'][$theme_repo]['data']['product-name'];
648
- if ( !(empty($prod_name)) ) {
649
- $theme_repo_name = $prod_name;
650
- }
651
- } else {
652
- //Not yet
653
- if ( $theme_repo == $this->theme_repo ) {
654
- $result = $this->installer_theme_general_api_query();
655
- if ( isset($result['product-name']) ) {
656
- $product_name = $result['product-name'];
657
- if ( !(empty($product_name)) ) {
658
- $theme_repo_name = $product_name;
659
- }
660
- }
661
- }
662
- }
663
-
664
- return $theme_repo_name;
665
- }
666
-
667
- /** General query API method, returns $products */
668
- private function installer_theme_general_api_query() {
669
- $products = false;
670
- $response = wp_remote_get( $this->repository_theme_products );
671
- if ( !(is_wp_error( $response )) ) {
672
- //Not WP error
673
- //Evaluate response
674
- if ( $response && isset($response['response']['code']) && $response['response']['code'] == 200 ) {
675
- //In this case, response is set and defined, proceed...
676
- $body = wp_remote_retrieve_body( $response );
677
- if ( $body ) {
678
- $result = json_decode( $body, true );
679
- if ( (is_array( $result )) && (!(empty($result))) ) {
680
- $products = $result;
681
- }
682
- }
683
-
684
- }
685
- }
686
-
687
- return $products;
688
- }
689
-
690
- /** General method to check if themes are OTGS themes based on its slug*/
691
- private function installer_themes_belong_to_us( $theme_slug ) {
692
-
693
- $found = false;
694
- $theme_slug = trim( $theme_slug );
695
-
696
- foreach ( $this->installer_themes as $repo_with_theme => $themes ) {
697
- foreach ( $themes as $k => $otgs_theme_slug ) {
698
- if ( $theme_slug == $otgs_theme_slug ) {
699
- //match found! Theme belongs to otgs
700
- return true;
701
- }
702
- }
703
- }
704
- return $found;
705
-
706
- }
707
-
708
- /** Sets active tab on init */
709
- public function installer_theme_sets_active_tab_on_init() {
710
-
711
- if ( isset ($_SERVER ['REQUEST_URI']) ) {
712
- $request_uri = $_SERVER ['REQUEST_URI'];
713
- if ( isset ($_GET ['browse']) ) {
714
- $active_tab = trim( $_GET ['browse'] );
715
- $this->installer_theme_active_tab = $active_tab;
716
- } elseif ( isset ($_POST ['request'] ['browse']) ) {
717
- $active_tab = trim( $_POST ['request'] ['browse'] );
718
- $this->installer_theme_active_tab = $active_tab;
719
- } elseif ( (isset ($_GET ['theme_repo'])) && (isset ($_GET ['action'])) ) {
720
- $theme_repo = trim( $_GET ['theme_repo'] );
721
- $the_action = trim( $_GET ['action'] );
722
- if ( ('install-theme' == $the_action) && (!(empty($theme_repo))) ) {
723
- $this->installer_theme_active_tab = $theme_repo;
724
- }
725
- } elseif ( wp_get_referer() ) {
726
- $referer = wp_get_referer();
727
- $parts = parse_url( $referer );
728
- if ( isset($parts['query']) ) {
729
- parse_str( $parts['query'], $query );
730
- if ( isset($query['browse']) ) {
731
- $this->installer_theme_active_tab = $query['browse'];
732
- }
733
- }
734
- }
735
- }
736
- }
737
-
738
- /** WP Theme API compatibility- added num ratings */
739
- /** Installer 1.7.6+ Added updated 'rating' field */
740
- public function installer_theme_add_num_ratings( $themes ) {
741
-
742
- if ( (is_array( $themes )) && (!(empty($themes))) ) {
743
- foreach ( $themes as $k => $v ) {
744
- if ( !(isset($v->num_ratings)) ) {
745
- $themes[$k]->num_ratings = 100;
746
- }
747
- if ( !(isset($v->rating)) ) {
748
- $themes[$k]->rating = 100;
749
- }
750
- }
751
- }
752
-
753
- return $themes;
754
- }
755
-
756
- /** When WordPress.org makes a call to its repository, let's run our own upgrade checks too */
757
- public function installer_theme_sync_call_wp_theme_api( $locales ) {
758
-
759
- $this->installer_theme_upgrade_theme_check();
760
-
761
- return $locales;
762
- }
763
-
764
- /** Upgrade theme check */
765
- private function installer_theme_upgrade_theme_check() {
766
-
767
- // Step1-> we get all installed themes in clients local themes directory
768
- $installed_themes = wp_get_themes();
769
-
770
- // Step2: We need to loop through each repository with themes
771
- foreach ( $this->installer_repo_with_themes as $k => $repo_slug ) {
772
-
773
- // We then need to retrieved the products URL for each of this repo
774
- $products_url = $this->repository_theme_products [$repo_slug];
775
-
776
- // Step3-> we get all available themes in our repository via API based on this URL
777
- $available_themes = $this->installer_theme_get_themes( $products_url, $repo_slug );
778
-
779
- if ( !($available_themes) ) {
780
-
781
- // API is not available as of the moment, return..
782
- return;
783
- } else {
784
-
785
- // We have available themes here...
786
- // Step4->let's simplify available themes data by slugs
787
- $simplified_available_themes = $this->installer_theme_savethemes_by_slug( $available_themes, true );
788
-
789
- // Step5->Let's loop through installed themes
790
- if ( (is_array( $installed_themes )) && (!(empty ($installed_themes))) ) {
791
- $otgs_theme_updates_available = array();
792
- foreach ( $installed_themes as $theme_slug => $theme_object ) {
793
- if ( array_key_exists( $theme_slug, $simplified_available_themes ) ) {
794
-
795
- // This is our theme
796
- // Step6->Let's get version of the local theme installed
797
- $local_version = $theme_object->get( 'Version' );
798
-
799
- // Step7->Let's get the latest version of this theme, page URL and download URL from our repository
800
- $repository_version = $simplified_available_themes [$theme_slug] ['version'];
801
- $theme_page_url = $simplified_available_themes [$theme_slug] ['theme_page_url'];
802
- $theme_download_url = $simplified_available_themes [$theme_slug] ['url'];
803
-
804
- // Step8->Let's compare the version
805
- if ( version_compare( $repository_version, $local_version, '>' ) ) {
806
-
807
- // Update available for this theme
808
- // Step9-> Define download URL with site key
809
- $package_url = WP_Installer()->append_site_key_to_download_url( $theme_download_url, $this->installer_site_key [$repo_slug], $repo_slug );
810
-
811
- //Step10-> Assign to updates array for later accessing.
812
- $otgs_theme_updates_available[$theme_slug] = array(
813
- 'theme' => $theme_slug,
814
- 'new_version' => $repository_version,
815
- 'url' => $theme_page_url,
816
- 'package' => $package_url
817
- );
818
- }
819
- }
820
- }
821
- //Exited the upgrade loop for this specific theme repository
822
- if ( !(empty($otgs_theme_updates_available)) ) {
823
- //Has updates
824
- update_option( $this->installer_themes_available_updates[$repo_slug], $otgs_theme_updates_available );
825
- } else {
826
- //No updates
827
- delete_option( $this->installer_themes_available_updates[$repo_slug] );
828
- }
829
-
830
- }
831
- }
832
- }
833
- }
834
-
835
- /** When the user is on Themes install page OTG themes repository, let's the currently selected tab */
836
- public function installer_theme_add_query_arg_tab( $url, $path, $blog_id = null ) {
837
-
838
- $wp_install_string = 'update.php?action=install-theme';
839
- if ( $path == $wp_install_string ) {
840
- if ( isset($this->installer_theme_active_tab) ) {
841
- if ( !(empty($this->installer_theme_active_tab)) ) {
842
- $url = add_query_arg( array(
843
- 'theme_repo' => $this->installer_theme_active_tab
844
- ), $url );
845
- }
846
- }
847
- }
848
- return $url;
849
- }
850
-
851
- /** Save frontend theme tab selected */
852
- public function installer_theme_frontend_selected_tab() {
853
- if ( isset($_POST["frontend_tab_selected"]) ) {
854
- check_ajax_referer( 'installer_theme_frontend_selected_tab', 'installer_theme_frontend_selected_tab_nonce' );
855
-
856
- //Client_side_active_tab
857
- $frontend_tab_selected = filter_input( INPUT_POST, 'frontend_tab_selected', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
858
- if ( !(empty($frontend_tab_selected)) ) {
859
- //Front end tab selected
860
- update_option( 'wp_installer_clientside_active_tab', $frontend_tab_selected, false );
861
-
862
- //Check for registration status
863
- if ( isset($this->theme_user_registration[$frontend_tab_selected]) ) {
864
- //Set
865
- if ( !($this->theme_user_registration[$frontend_tab_selected]) ) {
866
- //Not registered yet
867
-
868
- if ( is_multisite() ) {
869
- $admin_url_passed = network_admin_url();
870
- } else {
871
- $admin_url_passed = admin_url();
872
- }
873
-
874
- $registration_url = $admin_url_passed . 'plugin-install.php?tab=commercial#installer_repo_' . $frontend_tab_selected;
875
-
876
- //Message and link
877
- $theme_repo_name = $this->installer_theme_get_repo_product_name( $frontend_tab_selected );;
878
- $response['unregistered_messages'] = sprintf( __( 'To install and update %s, please %sregister%s %s for this site.', 'installer' ),
879
- $theme_repo_name, '<a href="' . $registration_url . '">', '</a>', $theme_repo_name );
880
-
881
- }
882
- }
883
-
884
- $response['output'] = $frontend_tab_selected;
885
- echo json_encode( $response );
886
- }
887
- die();
888
- }
889
- die();
890
- }
891
-
892
- /** Installer loaded aux hooks */
893
- public function installer_theme_loaded_hooks() {
894
-
895
- if ( isset($this->installer_theme_subscription_type) ) {
896
- $subscription_type = intval( $this->installer_theme_subscription_type );
897
- if ( $subscription_type > 0 ) {
898
- //Client is subscribed
899
- add_filter( 'installer_theme_get_themes', array($this, 'installer_theme_filter_themes_by_subscription'), 10, 2 );
900
- }
901
- }
902
-
903
- }
904
-
905
- /** Get themes by subscription type */
906
- protected function installer_theme_get_themes_by_subscription( $subscription_type, $repo ) {
907
-
908
- $themes_associated_with_subscription = array();
909
- if ( isset(WP_Installer()->settings['repositories'][$repo]['data']['packages']) ) {
910
- //Set
911
- $packages = WP_Installer()->settings['repositories'][$repo]['data']['packages'];
912
- $available_themes_subscription = array();
913
- foreach ( $packages as $package_id => $package_details ) {
914
- if ( isset($package_details['products']) ) {
915
- $the_products = $package_details['products'];
916
- foreach ( $the_products as $product_slug => $product_details ) {
917
- if ( isset($product_details['subscription_type']) ) {
918
- $subscription_type_from_settings = intval( $product_details['subscription_type'] );
919
- if ( $subscription_type_from_settings == $subscription_type ) {
920
- //We found the subscription
921
- if ( isset($product_details['themes']) ) {
922
- $themes_associated_with_subscription = $product_details['themes'];
923
- return $themes_associated_with_subscription;
924
- }
925
- }
926
- }
927
-
928
- }
929
- }
930
- }
931
- }
932
- return $themes_associated_with_subscription;
933
- }
934
-
935
- /** Filter API theme response according to user subscription */
936
- public function installer_theme_filter_themes_by_subscription( $themes, $active_tab ) {
937
-
938
- //Step1, we only filter OTGS themes
939
- $orig = count( $themes );
940
- if ( in_array( $active_tab, $this->theme_repo ) ) {
941
- //OTGS Theme
942
- //Step2, we retrieved the available themes based on client subscription
943
- if ( isset($this->installer_themes[$active_tab]) ) {
944
- $available_themes = $this->installer_themes[$active_tab];
945
- //Step3, we filter $themes based on this info
946
- if ( (is_array( $themes )) && (!(empty($themes))) ) {
947
- foreach ( $themes as $k => $theme ) {
948
- //Step4, get theme slug
949
- if ( isset($theme['slug']) ) {
950
- $theme_slug = $theme['slug'];
951
- if ( !(empty($theme_slug)) ) {
952
- if ( !(in_array( $theme_slug, $available_themes )) ) {
953
- //This theme is not in available themes
954
- unset($themes[$k]);
955
- }
956
- }
957
- }
958
- }
959
- }
960
- }
961
- }
962
- $new = count( $themes );
963
- if ( $orig != $new ) {
964
- //It is filtered
965
- $themes = array_values( $themes );
966
- }
967
-
968
- return $themes;
969
- }
970
-
971
- /** Hook to wp_loaded, fires when all Installer theme class is ready */
972
- public function installer_themes_support_set_up_func() {
973
- do_action( 'installer_themes_support_set_up' );
974
- }
975
-
976
- }
977
-
978
- /** Instantiate Installer Theme Class */
979
  new Installer_Theme_Class;
1
+ <?php
2
+ /**
3
+ * Installer Class for Theme Support
4
+ *
5
+ * Supports automatic updates and installation of Toolset/WPML Themes
6
+ *
7
+ * @class Installer_Theme_Class
8
+ * @version 1.6
9
+ * @category Class
10
+ * @author OnTheGoSystems
11
+ */
12
+
13
+ if ( !defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * Installer_Theme_Class
19
+ */
20
+ class Installer_Theme_Class {
21
+
22
+ /** Theme Repository */
23
+ private $theme_repo;
24
+
25
+ /** Repository API */
26
+ private $repository_api;
27
+
28
+ /** Repository Theme Products */
29
+ private $repository_theme_products;
30
+
31
+ /** Site URL */
32
+ private $installer_site_url;
33
+
34
+ /** Site Key */
35
+ private $installer_site_key;
36
+
37
+ /** The Themes Option */
38
+ protected $installer_themes_option;
39
+
40
+ /** Update settings */
41
+ protected $installer_themes_available_updates;
42
+
43
+ /** The Themes */
44
+ protected $installer_themes = array();
45
+
46
+ /** Repository with themes */
47
+ protected $installer_repo_with_themes;
48
+
49
+ /** Active tab */
50
+ protected $installer_theme_active_tab;
51
+
52
+ /** Theme user registration */
53
+ protected $theme_user_registration;
54
+
55
+ /** Client active subscription */
56
+ protected $installer_theme_subscription_type;
57
+
58
+ public function __construct() {
59
+
60
+ /** Properties */
61
+
62
+ //Get installer repositories
63
+ $installer_repositories = WP_Installer()->get_repositories();
64
+
65
+ //Get repos with themes
66
+ $repos_with_themes = $this->installer_theme_reposities_that_has_themes( $installer_repositories );
67
+
68
+ if ( is_array( $repos_with_themes ) ) {
69
+ //Assign to property
70
+ $this->installer_repo_with_themes = $repos_with_themes;
71
+
72
+ //Let's looped through repos with themes
73
+ foreach ( $repos_with_themes as $k => $repo ) {
74
+
75
+ //$repo could be 'toolset' or 'wpml'
76
+ //Assign each repo with theme to property
77
+ $this->theme_repo[] = $repo;
78
+
79
+ if ( (isset($installer_repositories[$repo]['api-url'])) && (isset($installer_repositories[$repo]['products'])) ) {
80
+
81
+ //Define the rest of the properties based on the given repo
82
+ $this->repository_api[$repo] = $installer_repositories[$repo]['api-url'];
83
+ $this->repository_theme_products[$repo] = $installer_repositories[$repo]['products'];
84
+ $this->installer_site_url[$repo] = WP_Installer()->get_installer_site_url( $repo );
85
+ $this->installer_site_key[$repo] = WP_Installer()->get_site_key( $repo );
86
+ $this->theme_user_registration[$repo] = false;
87
+
88
+ if ( WP_Installer()->repository_has_valid_subscription( $repo ) ) {
89
+
90
+ $this->installer_theme_subscription_type = WP_Installer()->get_subscription_type_for_repository( $repo );
91
+ $this->installer_themes_option[$repo] = 'wp_installer_' . $repo . '_themes';
92
+ $this->installer_themes_available_updates[$repo] = 'wp_installer_' . $repo . '_updated_themes';
93
+ $this->installer_theme_active_tab = '';
94
+
95
+ //We only set themes available to this validated subscription
96
+ $this->installer_theme_available( $repo, $this->installer_theme_subscription_type );
97
+
98
+ add_action( 'installer_themes_support_set_up', array($this, 'installer_theme_sets_active_tab_on_init'), 10 );
99
+ $this->theme_user_registration[$repo] = true;
100
+ }
101
+
102
+ /** We are ready.. let's initialize .... */
103
+ $this->init();
104
+ }
105
+ }
106
+ add_action( 'installer_themes_support_set_up', array($this, 'installer_theme_loaded_hooks') );
107
+ }
108
+ }
109
+
110
+ /** Init */
111
+ public function init() {
112
+ add_action( 'admin_enqueue_scripts', array($this, 'installer_theme_enqueue_scripts') );
113
+ add_filter( 'themes_api', array($this, 'installer_theme_api_override'), 10, 3 );
114
+ add_filter( 'themes_api_result', array($this, 'installer_theme_api_override_response'), 10, 3 );
115
+ add_filter( 'site_transient_update_themes', array($this, 'installer_theme_upgrade_check'), 10, 1 );
116
+ add_action( 'http_api_debug', array($this, 'installer_theme_sync_native_wp_api'), 10, 5 );
117
+ add_filter( 'installer_theme_hook_response_theme', array($this, 'installer_theme_add_num_ratings'), 10, 1 );
118
+ add_filter( 'themes_update_check_locales', array($this, 'installer_theme_sync_call_wp_theme_api'), 10, 1 );
119
+ add_filter( 'admin_url', array($this, 'installer_theme_add_query_arg_tab'), 10, 3 );
120
+ add_filter( 'network_admin_url', array($this, 'installer_theme_add_query_arg_tab'), 10, 2 );
121
+ add_action( 'wp_ajax_installer_theme_frontend_selected_tab', array($this, 'installer_theme_frontend_selected_tab'), 0 );
122
+ add_action( 'wp_loaded', array($this, 'installer_themes_support_set_up_func') );
123
+ }
124
+
125
+ /** Enqueue scripts */
126
+ public function installer_theme_enqueue_scripts() {
127
+ $current_screen = $this->installer_theme_current_screen();
128
+ $commercial_plugin_screen = $this->installer_theme_is_commercial_plugin_screen( $current_screen );
129
+ if ( ('theme-install' == $current_screen) || ($commercial_plugin_screen) || ('theme-install-network' == $current_screen) ) {
130
+ $repo_with_themes = $this->installer_repo_with_themes;
131
+ $js_array = array();
132
+ if ( is_array( $repo_with_themes ) ) {
133
+ foreach ( $repo_with_themes as $k => $v ) {
134
+
135
+ //Hyperlink text
136
+ $theme_repo_name = $this->installer_theme_get_repo_product_name( $v );
137
+ $the_hyperlink_text = esc_js( $theme_repo_name );
138
+
139
+ if ( is_multisite() ) {
140
+ $admin_url_passed = network_admin_url();
141
+ } else {
142
+ $admin_url_passed = admin_url();
143
+ }
144
+
145
+ //Define
146
+ $js_array[$v] = array(
147
+ 'the_hyperlink_text' => $the_hyperlink_text,
148
+ 'registration_status' => $this->theme_user_registration[$v],
149
+ 'is_commercial_plugin_tab' => $commercial_plugin_screen,
150
+ 'registration_url' => $admin_url_passed . 'plugin-install.php?tab=commercial#installer_repo_' . $v
151
+ );
152
+
153
+ }
154
+ }
155
+
156
+ if ( !(empty($js_array)) ) {
157
+ wp_enqueue_script( 'installer-theme-install', WP_Installer()->res_url() . '/res/js/installer_theme_install.js', array('jquery', 'installer-admin'), WP_Installer()->version() );
158
+ $installer_ajax_url = admin_url( 'admin-ajax.php' );
159
+
160
+ if ( is_ssl() ) {
161
+ $installer_ajax_url = str_replace( 'http://', 'https://', $installer_ajax_url );
162
+ } else {
163
+ $installer_ajax_url = str_replace( 'https://', 'http://', $installer_ajax_url );
164
+ }
165
+
166
+ //Case where user is subscribed to a subscription that does not have themes
167
+ $subscription_js_check = $this->installer_theme_subscription_does_not_have_theme( $js_array );
168
+
169
+ wp_localize_script( 'installer-theme-install', 'installer_theme_install_localize',
170
+ array(
171
+ 'js_array_installer' => $js_array,
172
+ 'ajaxurl' => $installer_ajax_url,
173
+ 'no_associated_themes' => $subscription_js_check,
174
+ 'installer_theme_frontend_selected_tab_nonce' => wp_create_nonce( 'installer_theme_frontend_selected_tab' )
175
+ )
176
+ );
177
+ }
178
+ }
179
+ }
180
+
181
+ /** Case where user is subscribed to a subscription that does not have themes */
182
+ protected function installer_theme_subscription_does_not_have_theme( $js_array ) {
183
+
184
+ $any_subscription_has_theme = array();
185
+ $number_of_registrations = array();
186
+
187
+ //Step1, we looped through JS array
188
+ foreach ( $js_array as $repo_slug => $js_details ) {
189
+
190
+ //Step2, checked if user is registered
191
+ if ( isset($this->theme_user_registration[$repo_slug]) ) {
192
+ $registration_status = $this->theme_user_registration[$repo_slug];
193
+ if ( $registration_status ) {
194
+
195
+ //Registered
196
+ $number_of_registrations[] = $repo_slug;
197
+
198
+ //Step3, we checked if the $repo_slug has available theme
199
+ $themes_available = false;
200
+ if ( isset($this->installer_themes[$repo_slug]) ) {
201
+ $themes_available = $this->installer_themes[$repo_slug];
202
+ if ( !(empty($themes_available)) ) {
203
+ //This subscription has theme
204
+ $themes_available = true;
205
+ }
206
+ }
207
+
208
+ if ( $themes_available ) {
209
+ $any_subscription_has_theme[] = $repo_slug;
210
+ }
211
+ }
212
+ }
213
+
214
+ }
215
+
216
+ //Step4, we are done looping, check if there are any repos that have themes
217
+ if ( empty($registration_status) ) {
218
+
219
+ //No registration on any repos
220
+ return FALSE;
221
+
222
+ } elseif ( !(empty($registration_status)) ) {
223
+
224
+ //Has some registration on some repos
225
+ //We then checked if this user has any active subscriptions
226
+ if ( empty($any_subscription_has_theme) ) {
227
+ //No subscription
228
+ return TRUE;
229
+ } else {
230
+ //Has subscription found
231
+ return FALSE;
232
+ }
233
+ }
234
+ }
235
+
236
+ /** Check if its the commercial plugin screen */
237
+ private function installer_theme_is_commercial_plugin_screen( $current_screen ) {
238
+ $commercial = false;
239
+ if ( ('plugin-install' == $current_screen) || ('plugin-install-network' == $current_screen) ) {
240
+ if ( isset($_GET['tab']) ) {
241
+ $tab = sanitize_text_field( $_GET['tab'] );
242
+ if ( 'commercial' == $tab ) {
243
+ $commercial = true;
244
+ }
245
+ }
246
+ }
247
+ return $commercial;
248
+ }
249
+
250
+ /** Current screen */
251
+ private function installer_theme_current_screen() {
252
+
253
+ $current_screen_loaded = false;
254
+
255
+ if ( function_exists( 'get_current_screen' ) ) {
256
+
257
+ $screen_output = get_current_screen();
258
+ $current_screen_loaded = $screen_output->id;
259
+
260
+ }
261
+
262
+ return $current_screen_loaded;
263
+
264
+ }
265
+
266
+ /** Override WordPress Themes API */
267
+ public function installer_theme_api_override( $api_boolean, $action, $args ) {
268
+
269
+ //Let's checked if user is browsing our themes
270
+ if ( isset($args->browse) ) {
271
+ $browse = $args->browse;
272
+ if ( in_array( $browse, $this->theme_repo ) ) {
273
+ //Uniquely validated for our Themes
274
+ if ( 'query_themes' == $action ) {
275
+ //User is querying or asking information about our themes, let's override
276
+ $api_boolean = true;
277
+ }
278
+ }
279
+ } elseif ( isset($args->slug) ) {
280
+ //We are installing our themes
281
+ $theme_to_install = $args->slug;
282
+
283
+ //Lets uniquely validate if this belongs to us
284
+ //Check if this is OTGS theme
285
+ $validate_check = $this->installer_themes_belong_to_us( $theme_to_install );
286
+ if ( $validate_check ) {
287
+ //Belongs to us
288
+ if ( !(empty($theme_to_install)) ) {
289
+ $api_boolean = true;
290
+ }
291
+ }
292
+ }
293
+
294
+ return $api_boolean;
295
+ }
296
+
297
+ /** Override WordPress Themes API response with our own themes API*/
298
+ public function installer_theme_api_override_response( $res, $action, $args ) {
299
+
300
+ if ( true === $res ) {
301
+ if ( isset($args->browse) ) {
302
+ $browse = $args->browse;
303
+ if ( in_array( $browse, $this->theme_repo ) ) {
304
+ //Uniquely validated for our themes
305
+ if ( 'query_themes' == $action ) {
306
+ //Client querying OTGS themes
307
+ //Check for registration status
308
+ if ( isset($this->theme_user_registration[$browse]) ) {
309
+ //Set
310
+ if ( !($this->theme_user_registration[$browse]) ) {
311
+ //Not registered yet
312
+ $res = new stdClass();
313
+ $res->info = array();
314
+ $res->themes = array();
315
+ return $res;
316
+ } else {
317
+ //Registered
318
+ $themes = $this->installer_theme_get_themes( '', $browse );
319
+ $res = $this->installer_theme_format_response( $themes, $action );
320
+ }
321
+ }
322
+ }
323
+ }
324
+ } elseif ( isset($args->slug) ) {
325
+ //We are installing theme
326
+ //Lets uniquely validate if this belongs to our theme
327
+ $theme_to_install = $args->slug;
328
+
329
+ //Lets uniquely validate if this belongs to us
330
+ //Check if this is OTGS theme
331
+ $validate_check = $this->installer_themes_belong_to_us( $theme_to_install );
332
+ if ( $validate_check ) {
333
+ //Belongs to us
334
+ if ( ($res) && ('theme_information' == $action) ) {
335
+ $themes = $this->installer_theme_get_themes( '', $this->installer_theme_active_tab );
336
+ $res = $this->installer_theme_format_response( $themes, $action, $args->slug );
337
+ }
338
+ }
339
+ }
340
+ return $res;
341
+ } else {
342
+ //Default WP Themes here
343
+ $client_side_active_tab = get_option( 'wp_installer_clientside_active_tab' );
344
+ if ( $client_side_active_tab ) {
345
+ if ( !(in_array( $client_side_active_tab, $this->theme_repo )) ) {
346
+ //Not OTGS tab
347
+ return $res;
348
+ }
349
+ }
350
+
351
+ }
352
+ }
353
+
354
+ /** Get Themes */
355
+ private function installer_theme_get_themes( $product_url = '', $repo_source = '' ) {
356
+
357
+ //Query API
358
+ if ( empty($product_url) ) {
359
+ //Not set
360
+ if ( isset($this->repository_theme_products[$this->installer_theme_active_tab]) ) {
361
+ $query_remote_url = $this->repository_theme_products[$this->installer_theme_active_tab];
362
+ }
363
+
364
+ } else {
365
+ $query_remote_url = $product_url;
366
+ }
367
+
368
+ //Let's retrieved current installer settings so we won't be querying all the time
369
+ $current_installer_settings = WP_Installer()->get_settings();
370
+
371
+ //Set $themes to FALSE by default
372
+ $themes = false;
373
+
374
+ if ( (is_array( $current_installer_settings )) && (!(empty($current_installer_settings))) ) {
375
+
376
+ //Set and already defined, retrieved $products
377
+ if ( isset($current_installer_settings['repositories'][$repo_source]['data']) ) {
378
+ $products = $current_installer_settings['repositories'][$repo_source]['data'];
379
+ if ( isset($products['downloads']['themes']) ) {
380
+ $themes = $products['downloads']['themes'];
381
+ }
382
+ }
383
+
384
+ } else {
385
+
386
+ //Call API
387
+ $response = wp_remote_get( $query_remote_url );
388
+
389
+ if ( is_wp_error( $response ) ) {
390
+ //Error detected: http fallback
391
+ $query_remote_url = preg_replace( "@^https://@", 'http://', $query_remote_url );
392
+ $response = wp_remote_get( $query_remote_url );
393
+ }
394
+
395
+ if ( !(is_wp_error( $response )) ) {
396
+ //Not WP error
397
+ //Evaluate response
398
+ if ( $response && isset($response['response']['code']) && $response['response']['code'] == 200 ) {
399
+ //In this case, response is set and defined, proceed...
400
+ $body = wp_remote_retrieve_body( $response );
401
+ if ( $body ) {
402
+ $products = json_decode( $body, true );
403
+ if ( isset($products['downloads']['themes']) ) {
404
+ $themes = $products['downloads']['themes'];
405
+ }
406
+ }
407
+
408
+ }
409
+ }
410
+ }
411
+
412
+ //Return themes, can be filtered by user subscription type
413
+ return apply_filters( 'installer_theme_get_themes', $themes, $this->installer_theme_active_tab );
414
+ }
415
+
416
+ /** Format response in compatibility with WordPress Theme API response */
417
+ private function installer_theme_format_response( $themes, $action, $slug = '' ) {
418
+
419
+ //Let's append download link only when retrieving theme information for installation
420
+ if ( ('theme_information' == $action) && (!(empty($slug))) ) {
421
+
422
+ //Only return one result -> the theme to be installed
423
+ foreach ( $themes as $k => $theme ) {
424
+ if ( $slug == $theme['basename'] ) {
425
+ $theme['download_link'] = WP_Installer()->append_site_key_to_download_url( $theme['url'], $this->installer_site_key[$this->installer_theme_active_tab], $this->installer_theme_active_tab );
426
+ $theme = json_decode( json_encode( $theme ), FALSE );
427
+ return $theme;
428
+ }
429
+ }
430
+
431
+ } else {
432
+
433
+ $res = new stdClass();
434
+ $res->info = array();
435
+ $res->themes = array();
436
+
437
+ //Define info
438
+ $res->info['page'] = 1;
439
+ $res->info['pages'] = 10;
440
+
441
+ //Let's count available themes ;
442
+ $res->info['results'] = count( $themes );
443
+
444
+ //Let's saved themes for easy access later on
445
+ $this->installer_theme_savethemes_by_slug( $themes );
446
+
447
+ //Let's defined available themes
448
+ if ( isset($this->installer_theme_subscription_type) ) {
449
+ //Has subscription type defined, let's saved what is associated with this subscription
450
+ $this->installer_theme_available( $this->installer_theme_active_tab, $this->installer_theme_subscription_type );
451
+ } else {
452
+ $this->installer_theme_available( $this->installer_theme_active_tab );
453
+ }
454
+
455
+ //Let's add themes to the overriden WordPress API Theme response
456
+ /** Installer 1.7.6: Update to compatible data format response from WP Theme API */
457
+ $theme_compatible_array=array();
458
+ if ((is_array($themes))) {
459
+ foreach ($themes as $k=>$v) {
460
+ $theme_compatible_array[]=(object)($v);
461
+ }
462
+ }
463
+ $res->themes = $theme_compatible_array;
464
+ $res->themes = apply_filters( 'installer_theme_hook_response_theme', $res->themes );
465
+ return $res;
466
+ }
467
+ }
468
+
469
+ /** Let's save all available themes by its slug after any latest API query */
470
+ private function installer_theme_savethemes_by_slug( $themes, $doing_query = false ) {
471
+
472
+ if ( !($doing_query) ) {
473
+ $this->installer_themes[$this->installer_theme_active_tab] = array();
474
+ }
475
+
476
+ if ( !(empty($themes)) ) {
477
+ $themes_for_saving = array();
478
+ foreach ( $themes as $k => $theme ) {
479
+ if ( !($doing_query) ) {
480
+ if ( isset($theme['slug']) ) {
481
+ $theme_slug = $theme['slug'];
482
+ if ( !(empty($theme_slug)) ) {
483
+ $themes_for_saving[] = $theme_slug;
484
+ }
485
+ }
486
+ } else {
487
+
488
+ if ( ((isset($theme['slug'])) && (isset($theme['version'])) &&
489
+ (isset($theme['theme_page_url']))) && (isset($theme['url']))
490
+ ) {
491
+ $theme_slug = $theme['slug'];
492
+ $theme_version = $theme['version'];
493
+ $theme_page_url = $theme['theme_page_url'];
494
+ $theme_url = $theme['url'];
495
+ if ( (!(empty($theme_slug))) && (!(empty($theme_version))) &&
496
+ (!(empty($theme_page_url))) && (!(empty($theme_url)))
497
+ ) {
498
+ //$theme_slug is unique for every theme
499
+ $themes_for_saving[$theme_slug] = array(
500
+ 'version' => $theme_version,
501
+ 'theme_page_url' => $theme_page_url,
502
+ 'url' => $theme_url
503
+ );
504
+
505
+ }
506
+ }
507
+ }
508
+
509
+ }
510
+
511
+ if ( !(empty($themes_for_saving)) ) {
512
+ //Has themes for saving
513
+ if ( !($doing_query) ) {
514
+ //Not doing query
515
+ $existing_themes = get_option( $this->installer_themes_option[$this->installer_theme_active_tab] );
516
+ if ( !($existing_themes) ) {
517
+ //Does not yet exists
518
+ delete_option( $this->installer_themes_option[$this->installer_theme_active_tab] );
519
+ update_option( $this->installer_themes_option[$this->installer_theme_active_tab], $themes_for_saving );
520
+ } else {
521
+ //exists, check if we need to update
522
+ if ( $existing_themes == $themes_for_saving ) {
523
+ //Equal, no need to update here
524
+ } else {
525
+ //Update
526
+ delete_option( $this->installer_themes_option[$this->installer_theme_active_tab] );
527
+ update_option( $this->installer_themes_option[$this->installer_theme_active_tab], $themes_for_saving );
528
+ }
529
+ }
530
+ } else {
531
+ //Used for query purposes only, don't save anything
532
+ return $themes_for_saving;
533
+ }
534
+ }
535
+ }
536
+ }
537
+
538
+ /** Available themes */
539
+ private function installer_theme_available( $repo, $subscription_type = '' ) {
540
+
541
+ $subscription_type = intval( $subscription_type );
542
+ if ( $subscription_type > 0 ) {
543
+
544
+ //Here we have a case of validated subscription
545
+ //We need to set themes that is available to this subscription
546
+ $themes_associated_with_subscription = $this->installer_themes[$repo] = $this->installer_theme_get_themes_by_subscription( $subscription_type, $repo );
547
+ if ( !(empty($themes_associated_with_subscription)) ) {
548
+ //Has themes
549
+ $this->installer_themes[$repo] = $themes_associated_with_subscription;
550
+ }
551
+ } else {
552
+
553
+ //Get themes
554
+ $this->installer_themes[$repo] = get_option( $this->installer_themes_option[$repo] );
555
+ }
556
+ }
557
+
558
+ /** Theme upgrade check */
559
+ public function installer_theme_upgrade_check( $the_value ) {
560
+
561
+ //Step1: Let's looped through repos with themes and check if we have updates available for them.
562
+ if ( (is_array( $this->installer_repo_with_themes )) && (!(empty($this->installer_repo_with_themes))) ) {
563
+ foreach ( $this->installer_repo_with_themes as $k => $repo_slug ) {
564
+ //Step2: Let's checked if we have update for this theme
565
+ $update_available = get_option( $this->installer_themes_available_updates[$repo_slug] );
566
+ if ( $update_available ) {
567
+ if ( (is_array( $update_available )) && (!(empty($update_available))) ) {
568
+ //Has updates available coming from this specific theme repo
569
+ //Let's loop through the themes that needs update
570
+ foreach ( $update_available as $theme_slug => $v ) {
571
+ //Add to response API
572
+ $the_value->response [$theme_slug] = array(
573
+ 'theme' => $theme_slug,
574
+ 'new_version' => $v['new_version'],
575
+ 'url' => $v['url'],
576
+ 'package' => $v['package']
577
+ );
578
+ }
579
+ }
580
+ }
581
+ }
582
+ }
583
+ //Return
584
+ return $the_value;
585
+ }
586
+
587
+ /** Return repositories that has themes */
588
+ private function installer_theme_reposities_that_has_themes( $repositories, $ret_value = true, $doing_api_query = false ) {
589
+
590
+ $repositories_with_themes = array();
591
+
592
+ if ( (is_array( $repositories )) && (!(empty($repositories))) ) {
593
+
594
+ //Let's checked if we have something before
595
+ $themes = get_option( 'installer_repositories_with_theme' );
596
+
597
+ if ( (!($themes)) || ($doing_api_query) ) {
598
+ //Not yet defined
599
+ //Loop through each repositories and check whether they have themes
600
+ foreach ( $repositories as $k => $v ) {
601
+ if ( isset($v['products']) ) {
602
+ $products_url = $v['products'];
603
+ $themes = $this->installer_theme_get_themes( $products_url, $k );
604
+ if ( (is_array( $themes )) && (!(empty($themes))) ) {
605
+ //Repo has themes
606
+ $repositories_with_themes[] = $k;
607
+ }
608
+ }
609
+ }
610
+ } else {
611
+ //Already set
612
+ $repositories_with_themes = $themes;
613
+ }
614
+
615
+ if ( (((is_array( $repositories_with_themes )) && (!(empty($repositories_with_themes)))) && (!($themes))) || ($doing_api_query) ) {
616
+ //Save to db
617
+ update_option( 'installer_repositories_with_theme', $repositories_with_themes );
618
+ }
619
+ }
620
+
621
+ if ( $ret_value ) {
622
+ return $repositories_with_themes;
623
+ }
624
+
625
+ }
626
+
627
+ /** When WordPress queries its own Themes API, we sync with our own */
628
+ public function installer_theme_sync_native_wp_api( $response, $responsetext, $class, $args, $url ) {
629
+
630
+ $api_native_string = 'api.wordpress.org/themes/';
631
+ if ( (strpos( $url, $api_native_string ) !== false) ) {
632
+ //WordPress is querying its own themes API
633
+ $installer_repositories = WP_Installer()->get_repositories();
634
+
635
+ //Query our own API and update repository values too
636
+ $this->installer_theme_reposities_that_has_themes( $installer_repositories, false, true );
637
+ }
638
+ }
639
+
640
+ /** Returns product name by theme repo slug */
641
+ private function installer_theme_get_repo_product_name( $theme_repo ) {
642
+
643
+ $theme_repo_name = false;
644
+
645
+ if ( isset(WP_Installer()->settings['repositories'][$theme_repo]['data']['product-name']) ) {
646
+ //Set
647
+ $prod_name = WP_Installer()->settings['repositories'][$theme_repo]['data']['product-name'];
648
+ if ( !(empty($prod_name)) ) {
649
+ $theme_repo_name = $prod_name;
650
+ }
651
+ } else {
652
+ //Not yet
653
+ if ( $theme_repo == $this->theme_repo ) {
654
+ $result = $this->installer_theme_general_api_query();
655
+ if ( isset($result['product-name']) ) {
656
+ $product_name = $result['product-name'];
657
+ if ( !(empty($product_name)) ) {
658
+ $theme_repo_name = $product_name;
659
+ }
660
+ }
661
+ }
662
+ }
663
+
664
+ return $theme_repo_name;
665
+ }
666
+
667
+ /** General query API method, returns $products */
668
+ private function installer_theme_general_api_query() {
669
+ $products = false;
670
+ $response = wp_remote_get( $this->repository_theme_products );
671
+ if ( !(is_wp_error( $response )) ) {
672
+ //Not WP error
673
+ //Evaluate response
674
+ if ( $response && isset($response['response']['code']) && $response['response']['code'] == 200 ) {
675
+ //In this case, response is set and defined, proceed...
676
+ $body = wp_remote_retrieve_body( $response );
677
+ if ( $body ) {
678
+ $result = json_decode( $body, true );
679
+ if ( (is_array( $result )) && (!(empty($result))) ) {
680
+ $products = $result;
681
+ }
682
+ }
683
+
684
+ }
685
+ }
686
+
687
+ return $products;
688
+ }
689
+
690
+ /** General method to check if themes are OTGS themes based on its slug*/
691
+ private function installer_themes_belong_to_us( $theme_slug ) {
692
+
693
+ $found = false;
694
+ $theme_slug = trim( $theme_slug );
695
+
696
+ foreach ( $this->installer_themes as $repo_with_theme => $themes ) {
697
+ foreach ( $themes as $k => $otgs_theme_slug ) {
698
+ if ( $theme_slug == $otgs_theme_slug ) {
699
+ //match found! Theme belongs to otgs
700
+ return true;
701
+ }
702
+ }
703
+ }
704
+ return $found;
705
+
706
+ }
707
+
708
+ /** Sets active tab on init */
709
+ public function installer_theme_sets_active_tab_on_init() {
710
+
711
+ if ( isset ($_SERVER ['REQUEST_URI']) ) {
712
+ $request_uri = $_SERVER ['REQUEST_URI'];
713
+ if ( isset ($_GET ['browse']) ) {
714
+ $active_tab = sanitize_text_field( $_GET['browse'] );
715
+ $this->installer_theme_active_tab = $active_tab;
716
+ } elseif ( isset ($_POST ['request'] ['browse']) ) {
717
+ $active_tab = sanitize_text_field ( $_POST['request']['browse'] );
718
+ $this->installer_theme_active_tab = $active_tab;
719
+ } elseif ( (isset ($_GET ['theme_repo'])) && (isset ($_GET ['action'])) ) {
720
+ $theme_repo = sanitize_text_field( $_GET['theme_repo'] );
721
+ $the_action = sanitize_text_field( $_GET['action'] );
722
+ if ( ('install-theme' == $the_action) && (!(empty($theme_repo))) ) {
723
+ $this->installer_theme_active_tab = $theme_repo;
724
+ }
725
+ } elseif ( wp_get_referer() ) {
726
+ $referer = wp_get_referer();
727
+ $parts = parse_url( $referer );
728
+ if ( isset($parts['query']) ) {
729
+ parse_str( $parts['query'], $query );
730
+ if ( isset($query['browse']) ) {
731
+ $this->installer_theme_active_tab = $query['browse'];
732
+ }
733
+ }
734
+ }
735
+ }
736
+ }
737
+
738
+ /** WP Theme API compatibility- added num ratings */
739
+ /** Installer 1.7.6+ Added updated 'rating' field */
740
+ public function installer_theme_add_num_ratings( $themes ) {
741
+
742
+ if ( (is_array( $themes )) && (!(empty($themes))) ) {
743
+ foreach ( $themes as $k => $v ) {
744
+ if ( !(isset($v->num_ratings)) ) {
745
+ $themes[$k]->num_ratings = 100;
746
+ }
747
+ if ( !(isset($v->rating)) ) {
748
+ $themes[$k]->rating = 100;
749
+ }
750
+ }
751
+ }
752
+
753
+ return $themes;
754
+ }
755
+
756
+ /** When WordPress.org makes a call to its repository, let's run our own upgrade checks too */
757
+ public function installer_theme_sync_call_wp_theme_api( $locales ) {
758
+
759
+ $this->installer_theme_upgrade_theme_check();
760
+
761
+ return $locales;
762
+ }
763
+
764
+ /** Upgrade theme check */
765
+ private function installer_theme_upgrade_theme_check() {
766
+
767
+ // Step1-> we get all installed themes in clients local themes directory
768
+ $installed_themes = wp_get_themes();
769
+
770
+ // Step2: We need to loop through each repository with themes
771
+ foreach ( $this->installer_repo_with_themes as $k => $repo_slug ) {
772
+
773
+ // We then need to retrieved the products URL for each of this repo
774
+ $products_url = $this->repository_theme_products [$repo_slug];
775
+
776
+ // Step3-> we get all available themes in our repository via API based on this URL
777
+ $available_themes = $this->installer_theme_get_themes( $products_url, $repo_slug );
778
+
779
+ if ( !($available_themes) ) {
780
+
781
+ // API is not available as of the moment, return..
782
+ return;
783
+ } else {
784
+
785
+ // We have available themes here...
786
+ // Step4->let's simplify available themes data by slugs
787
+ $simplified_available_themes = $this->installer_theme_savethemes_by_slug( $available_themes, true );
788
+
789
+ // Step5->Let's loop through installed themes
790
+ if ( (is_array( $installed_themes )) && (!(empty ($installed_themes))) ) {
791
+ $otgs_theme_updates_available = array();
792
+ foreach ( $installed_themes as $theme_slug => $theme_object ) {
793
+ if ( array_key_exists( $theme_slug, $simplified_available_themes ) ) {
794
+
795
+ // This is our theme
796
+ // Step6->Let's get version of the local theme installed
797
+ $local_version = $theme_object->get( 'Version' );
798
+
799
+ // Step7->Let's get the latest version of this theme, page URL and download URL from our repository
800
+ $repository_version = $simplified_available_themes [$theme_slug] ['version'];
801
+ $theme_page_url = $simplified_available_themes [$theme_slug] ['theme_page_url'];
802
+ $theme_download_url = $simplified_available_themes [$theme_slug] ['url'];
803
+
804
+ // Step8->Let's compare the version
805
+ if ( version_compare( $repository_version, $local_version, '>' ) ) {
806
+
807
+ // Update available for this theme
808
+ // Step9-> Define download URL with site key
809
+ $package_url = WP_Installer()->append_site_key_to_download_url( $theme_download_url, $this->installer_site_key [$repo_slug], $repo_slug );
810
+
811
+ //Step10-> Assign to updates array for later accessing.
812
+ $otgs_theme_updates_available[$theme_slug] = array(
813
+ 'theme' => $theme_slug,
814
+ 'new_version' => $repository_version,
815
+ 'url' => $theme_page_url,
816
+ 'package' => $package_url
817
+ );
818
+ }
819
+ }
820
+ }
821
+ //Exited the upgrade loop for this specific theme repository
822
+ if ( !(empty($otgs_theme_updates_available)) ) {
823
+ //Has updates
824
+ update_option( $this->installer_themes_available_updates[$repo_slug], $otgs_theme_updates_available );
825
+ } else {
826
+ //No updates
827
+ delete_option( $this->installer_themes_available_updates[$repo_slug] );
828
+ }
829
+
830
+ }
831
+ }
832
+ }
833
+ }
834
+
835
+ /** When the user is on Themes install page OTG themes repository, let's the currently selected tab */
836
+ public function installer_theme_add_query_arg_tab( $url, $path, $blog_id = null ) {
837
+
838
+ $wp_install_string = 'update.php?action=install-theme';
839
+ if ( $path == $wp_install_string ) {
840
+ if ( isset($this->installer_theme_active_tab) ) {
841
+ if ( !(empty($this->installer_theme_active_tab)) ) {
842
+ $url = add_query_arg( array(
843
+ 'theme_repo' => $this->installer_theme_active_tab
844
+ ), $url );
845
+ }
846
+ }
847
+ }
848
+ return $url;
849
+ }
850
+
851
+ /** Save frontend theme tab selected */
852
+ public function installer_theme_frontend_selected_tab() {
853
+ if ( isset($_POST["frontend_tab_selected"]) ) {
854
+ check_ajax_referer( 'installer_theme_frontend_selected_tab', 'installer_theme_frontend_selected_tab_nonce' );
855
+
856
+ //Client_side_active_tab
857
+ $frontend_tab_selected = sanitize_text_field( $_POST['frontend_tab_selected'] );
858
+ if ( !(empty($frontend_tab_selected)) ) {
859
+ //Front end tab selected
860
+ update_option( 'wp_installer_clientside_active_tab', $frontend_tab_selected, false );
861
+
862
+ //Check for registration status
863
+ if ( isset($this->theme_user_registration[$frontend_tab_selected]) ) {
864
+ //Set
865
+ if ( !($this->theme_user_registration[$frontend_tab_selected]) ) {
866
+ //Not registered yet
867
+
868
+ if ( is_multisite() ) {
869
+ $admin_url_passed = network_admin_url();
870
+ } else {
871
+ $admin_url_passed = admin_url();
872
+ }
873
+
874
+ $registration_url = $admin_url_passed . 'plugin-install.php?tab=commercial#installer_repo_' . $frontend_tab_selected;
875
+
876
+ //Message and link
877
+ $theme_repo_name = $this->installer_theme_get_repo_product_name( $frontend_tab_selected );;
878
+ $response['unregistered_messages'] = sprintf( __( 'To install and update %s, please %sregister%s %s for this site.', 'installer' ),
879
+ $theme_repo_name, '<a href="' . $registration_url . '">', '</a>', $theme_repo_name );
880
+
881
+ }
882
+ }
883
+
884
+ $response['output'] = $frontend_tab_selected;
885
+ echo json_encode( $response );
886
+ }
887
+ die();
888
+ }
889
+ die();
890
+ }
891
+
892
+ /** Installer loaded aux hooks */
893
+ public function installer_theme_loaded_hooks() {
894
+
895
+ if ( isset($this->installer_theme_subscription_type) ) {
896
+ $subscription_type = intval( $this->installer_theme_subscription_type );
897
+ if ( $subscription_type > 0 ) {
898
+ //Client is subscribed
899
+ add_filter( 'installer_theme_get_themes', array($this, 'installer_theme_filter_themes_by_subscription'), 10, 2 );
900
+ }
901
+ }
902
+
903
+ }
904
+
905
+ /** Get themes by subscription type */
906
+ protected function installer_theme_get_themes_by_subscription( $subscription_type, $repo ) {
907
+
908
+ $themes_associated_with_subscription = array();
909
+ if ( isset(WP_Installer()->settings['repositories'][$repo]['data']['packages']) ) {
910
+ //Set
911
+ $packages = WP_Installer()->settings['repositories'][$repo]['data']['packages'];
912
+ $available_themes_subscription = array();
913
+ foreach ( $packages as $package_id => $package_details ) {
914
+ if ( isset($package_details['products']) ) {
915
+ $the_products = $package_details['products'];
916
+ foreach ( $the_products as $product_slug => $product_details ) {
917
+ if ( isset($product_details['subscription_type']) ) {
918
+ $subscription_type_from_settings = intval( $product_details['subscription_type'] );
919
+ if ( $subscription_type_from_settings == $subscription_type ) {
920
+ //We found the subscription
921
+ if ( isset($product_details['themes']) ) {
922
+ $themes_associated_with_subscription = $product_details['themes'];
923
+ return $themes_associated_with_subscription;
924
+ }
925
+ }
926
+ }
927
+
928
+ }
929
+ }
930
+ }
931
+ }
932
+ return $themes_associated_with_subscription;
933
+ }
934
+
935
+ /** Filter API theme response according to user subscription */
936
+ public function installer_theme_filter_themes_by_subscription( $themes, $active_tab ) {
937
+
938
+ //Step1, we only filter OTGS themes
939
+ $orig = count( $themes );
940
+ if ( in_array( $active_tab, $this->theme_repo ) ) {
941
+ //OTGS Theme
942
+ //Step2, we retrieved the available themes based on client subscription
943
+ if ( isset($this->installer_themes[$active_tab]) ) {
944
+ $available_themes = $this->installer_themes[$active_tab];
945
+ //Step3, we filter $themes based on this info
946
+ if ( (is_array( $themes )) && (!(empty($themes))) ) {
947
+ foreach ( $themes as $k => $theme ) {
948
+ //Step4, get theme slug
949
+ if ( isset($theme['slug']) ) {
950
+ $theme_slug = $theme['slug'];
951
+ if ( !(empty($theme_slug)) ) {
952
+ if ( !(in_array( $theme_slug, $available_themes )) ) {
953
+ //This theme is not in available themes
954
+ unset($themes[$k]);
955
+ }
956
+ }
957
+ }
958
+ }
959
+ }
960
+ }
961
+ }
962
+ $new = count( $themes );
963
+ if ( $orig != $new ) {
964
+ //It is filtered
965
+ $themes = array_values( $themes );
966
+ }
967
+
968
+ return $themes;
969
+ }
970
+
971
+ /** Hook to wp_loaded, fires when all Installer theme class is ready */
972
+ public function installer_themes_support_set_up_func() {
973
+ do_action( 'installer_themes_support_set_up' );
974
+ }
975
+
976
+ }
977
+
978
+ /** Instantiate Installer Theme Class */
979
  new Installer_Theme_Class;
library/otgs/installer/includes/installer-api.php CHANGED
@@ -1,116 +1,116 @@
1
- <?php
2
-
3
- class WP_Installer_API{
4
-
5
- public static function get_product_installer_link($repository_id, $package_id = false){
6
-
7
- $menu_url = WP_Installer()->menu_url();
8
-
9
- $url = $menu_url . '#' . $repository_id;
10
- if($package_id){
11
- $url .= '/' . $package_id;
12
- }
13
-
14
- return $url;
15
-
16
- }
17
-
18
- public static function get_product_price($repository_id, $package_id, $product_id, $incl_discount = false){
19
-
20
- $price = WP_Installer()->get_product_price($repository_id, $package_id, $product_id, $incl_discount);
21
-
22
- return $price;
23
- }
24
-
25
- /**
26
- * Retrieve the preferred translation service.
27
- *
28
- * @since 1.6.5
29
- *
30
- * @param string The repository id (e.g. wpml)
31
- * @return string The translation service id
32
- */
33
- public static function get_preferred_ts($repository_id = 'wpml'){
34
-
35
- if(isset(WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'])){
36
- return WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'];
37
- }
38
-
39
- return false;
40
-
41
- }
42
-
43
- /**
44
- * Set the preferred translation service.
45
- *
46
- * @since 1.6.5
47
- *
48
- * @param string The translation service id
49
- * @param string The repository id (e.g. wpml)
50
- */
51
- public static function set_preferred_ts( $value, $repository_id = 'wpml' ){
52
-
53
- if( isset( WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'] ) ){
54
-
55
- WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'] = $value;
56
-
57
- WP_Installer()->save_settings();
58
-
59
- }
60
-
61
- }
62
-
63
- /**
64
- * Retrieve the referring translation service (if any)
65
- *
66
- * @since 1.6.5
67
- *
68
- * @param string The repository id (e.g. wpml)
69
- * @return string The translation service id or false
70
- */
71
- public static function get_ts_referal($repository_id = 'wpml'){
72
-
73
- if(isset(WP_Installer()->settings['repositories'][$repository_id]['ts_info']['referal'])){
74
- return WP_Installer()->settings['repositories'][$repository_id]['ts_info']['referal'];
75
- }
76
-
77
- return false;
78
-
79
- }
80
-
81
- /**
82
- * Retrieve the translation services client id for a specific repository (if any)
83
- *
84
- * @since 1.7.9
85
- *
86
- * @param string The repository id (e.g. wpml)
87
- * @return string The client id or false
88
- */
89
- public static function get_ts_client_id( $repository_id = 'wpml' ){
90
-
91
- if(isset(WP_Installer()->settings['repositories'][$repository_id]['ts_info']['client_id'])){
92
- return WP_Installer()->settings['repositories'][$repository_id]['ts_info']['client_id'];
93
- }
94
-
95
- return false;
96
-
97
- }
98
-
99
- /**
100
- * Retrieve the site key corresponding to a repository.
101
- * This is a wrapper of WP_Installer::get_site_key()
102
- * @see WP_Installer::get_site_key()
103
- *
104
- * @since 1.7.9
105
- *
106
- * @param string The repository id (e.g. wpml)
107
- * @return string The site key (or false)
108
- */
109
- public static function get_site_key( $repository_id = 'wpml' ){
110
-
111
- return WP_Installer()->get_site_key( $repository_id );
112
-
113
- }
114
-
115
-
116
  }
1
+ <?php
2
+
3
+ class WP_Installer_API{
4
+
5
+ public static function get_product_installer_link($repository_id, $package_id = false){
6
+
7
+ $menu_url = WP_Installer()->menu_url();
8
+
9
+ $url = $menu_url . '#' . $repository_id;
10
+ if($package_id){
11
+ $url .= '/' . $package_id;
12
+ }
13
+
14
+ return $url;
15
+
16
+ }
17
+
18
+ public static function get_product_price($repository_id, $package_id, $product_id, $incl_discount = false){
19
+
20
+ $price = WP_Installer()->get_product_price($repository_id, $package_id, $product_id, $incl_discount);
21
+
22
+ return $price;
23
+ }
24
+
25
+ /**
26
+ * Retrieve the preferred translation service.
27
+ *
28
+ * @since 1.6.5
29
+ *
30
+ * @param string The repository id (e.g. wpml)
31
+ * @return string The translation service id
32
+ */
33
+ public static function get_preferred_ts($repository_id = 'wpml'){
34
+
35
+ if(isset(WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'])){
36
+ return WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'];
37
+ }
38
+
39
+ return false;
40
+
41
+ }
42
+
43
+ /**
44
+ * Set the preferred translation service.
45
+ *
46
+ * @since 1.6.5
47
+ *
48
+ * @param string The translation service id
49
+ * @param string The repository id (e.g. wpml)
50
+ */
51
+ public static function set_preferred_ts( $value, $repository_id = 'wpml' ){
52
+
53
+ if( isset( WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'] ) ){
54
+
55
+ WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'] = $value;
56
+
57
+ WP_Installer()->save_settings();
58
+
59
+ }
60
+
61
+ }
62
+
63
+ /**
64
+ * Retrieve the referring translation service (if any)
65
+ *
66
+ * @since 1.6.5
67
+ *
68
+ * @param string The repository id (e.g. wpml)
69
+ * @return string The translation service id or false
70
+ */
71
+ public static function get_ts_referal($repository_id = 'wpml'){
72
+
73
+ if(isset(WP_Installer()->settings['repositories'][$repository_id]['ts_info']['referal'])){
74
+ return WP_Installer()->settings['repositories'][$repository_id]['ts_info']['referal'];
75
+ }
76
+
77
+ return false;
78
+
79
+ }
80
+
81
+ /**
82
+ * Retrieve the translation services client id for a specific repository (if any)
83
+ *
84
+ * @since 1.7.9
85
+ *
86
+ * @param string The repository id (e.g. wpml)
87
+ * @return string The client id or false
88
+ */
89
+ public static function get_ts_client_id( $repository_id = 'wpml' ){
90
+
91
+ if(isset(WP_Installer()->settings['repositories'][$repository_id]['ts_info']['client_id'])){
92
+ return WP_Installer()->settings['repositories'][$repository_id]['ts_info']['client_id'];
93
+ }
94
+
95
+ return false;
96
+
97
+ }
98
+
99
+ /**
100
+ * Retrieve the site key corresponding to a repository.
101
+ * This is a wrapper of WP_Installer::get_site_key()
102
+ * @see WP_Installer::get_site_key()
103
+ *
104
+ * @since 1.7.9
105
+ *
106
+ * @param string The repository id (e.g. wpml)
107
+ * @return string The site key (or false)
108
+ */
109
+ public static function get_site_key( $repository_id = 'wpml' ){
110
+
111
+ return WP_Installer()->get_site_key( $repository_id );
112
+
113
+ }
114
+
115
+
116
  }
library/otgs/installer/includes/installer-upgrader-skins.php CHANGED
@@ -1,37 +1,37 @@
1
- <?php
2
- class Installer_Upgrader_Skins extends WP_Upgrader_Skin{
3
-
4
- function __construct($args = array()){
5
- $defaults = array( 'url' => '', 'nonce' => '', 'title' => '', 'context' => false );
6
- $this->options = wp_parse_args($args, $defaults);
7
- }
8
-
9
- function header(){
10
-
11
- }
12
-
13
- function footer(){
14
-
15
- }
16
-
17
- function error($error){
18
- $this->installer_error = $error;
19
- }
20
-
21
- function add_strings(){
22
-
23
- }
24
-
25
- function feedback($string){
26
-
27
- }
28
-
29
- function before(){
30
-
31
- }
32
-
33
- function after(){
34
-
35
- }
36
-
37
  }
1
+ <?php
2
+ class Installer_Upgrader_Skins extends WP_Upgrader_Skin{
3
+
4
+ function __construct($args = array()){
5
+ $defaults = array( 'url' => '', 'nonce' => '', 'title' => '', 'context' => false );
6
+ $this->options = wp_parse_args($args, $defaults);
7
+ }
8
+
9
+ function header(){
10
+
11
+ }
12
+
13
+ function footer(){
14
+
15
+ }
16
+
17
+ function error($error){
18
+ $this->installer_error = $error;
19
+ }
20
+
21
+ function add_strings(){
22
+
23
+ }
24
+
25
+ function feedback($string){
26
+
27
+ }
28
+
29
+ function before(){
30
+
31
+ }
32
+
33
+ function after(){
34
+
35
+ }
36
+
37
  }
library/otgs/installer/includes/installer.class.php CHANGED
@@ -1,2579 +1,2672 @@
1
- <?php
2
-
3
- final class WP_Installer{
4
- protected static $_instance = null;
5
-
6
- public $settings = array();
7
-
8
- private $repositories = array();
9
-
10
- protected $api_debug = '';
11
-
12
- private $config = array();
13
-
14
- protected $_plugins_renew_warnings = array();
15
-
16
- protected $_gz_on = false;
17
-
18
- private $admin_messages = array();
19
-
20
- private $_using_icl = false;
21
- private $_wpml_version = false;
22
-
23
- private $package_source = array();
24
-
25
- const SITE_KEY_VALIDATION_SOURCE_OTHER = 0;
26
- const SITE_KEY_VALIDATION_SOURCE_DOWNLOAD_SPECIFIC = 1;
27
- const SITE_KEY_VALIDATION_SOURCE_DOWNLOAD_REPORT = 2;
28
- const SITE_KEY_VALIDATION_SOURCE_REGISTRATION = 3;
29
- const SITE_KEY_VALIDATION_SOURCE_REVALIDATION = 4;
30
- const SITE_KEY_VALIDATION_SOURCE_UPDATES_CHECK = 5;
31
-
32
- public $dependencies;
33
-
34
- public static function instance() {
35
-
36
- if ( is_null( self::$_instance ) ) {
37
- self::$_instance = new self();
38
- }
39
-
40
- return self::$_instance;
41
- }
42
-
43
- public function __construct(){
44
-
45
- if(!is_admin() || !is_user_logged_in()) return; //Only for admin
46
-
47
- $this->_gz_on = function_exists('gzuncompress') && function_exists('gzcompress');
48
- $this->settings = $this->get_settings();
49
-
50
- add_action('admin_notices', array($this, 'show_site_key_nags'));
51
-
52
- add_action('admin_notices', array($this, 'show_admin_messages'));
53
-
54
- add_action('admin_init', array($this, 'load_embedded_plugins'), 0);
55
-
56
- add_action('admin_menu', array($this, 'menu_setup'));
57
- add_action('network_admin_menu', array($this, 'menu_setup'));
58
-
59
- if(defined('DOING_AJAX') && isset($_POST['action']) && $_POST['action'] == 'installer_download_plugin'){
60
- add_filter( 'site_transient_update_plugins', array( $this, 'plugins_upgrade_check') );
61
- }
62
- add_filter('plugins_api', array( $this, 'custom_plugins_api_call'), 10, 3);
63
- add_filter('pre_set_site_transient_update_plugins', array( $this, 'plugins_upgrade_check'));
64
-
65
- // register repositories
66
- $this->load_repositories_list();
67
-
68
- if( empty($this->settings['last_repositories_update']) || time() - $this->settings['last_repositories_update'] > 86400
69
- || ( isset($_GET['force-check']) && $_GET['force-check'] == 1 ) ){
70
- $this->refresh_repositories_data();
71
- }
72
-
73
- // default config
74
- $this->config['plugins_install_tab'] = false;
75
-
76
- add_action('init', array($this, 'init'));
77
-
78
- //add_filter('wp_installer_buy_url', array($this, 'append_parameters_to_buy_url'));
79
-
80
- add_action('init', array($this,'load_locale'));
81
-
82
- }
83
-
84
- public function get_repositories() {
85
-
86
- return $this->repositories;
87
-
88
- }
89
-
90
- public function set_config($key, $value){
91
-
92
- $this->config[$key] = $value;
93
-
94
- }
95
-
96
- public function init(){
97
- global $pagenow;
98
-
99
- $this->dependencies = new Installer_Dependencies;
100
-
101
- if(empty($this->settings['_pre_1_0_clean_up'])) {
102
- $this->_pre_1_0_clean_up();
103
- }
104
-
105
- $this->settings = $this->_old_products_format_backwards_compatibility($this->settings);
106
-
107
- if ( !function_exists( 'get_plugins' ) ) {
108
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
109
- }
110
-
111
- $this->_using_icl = function_exists('wpml_site_uses_icl') && wpml_site_uses_icl();
112
- $this->_wpml_version = defined('ICL_SITEPRESS_VERSION') ? ICL_SITEPRESS_VERSION : '';
113
-
114
- wp_enqueue_script('installer-admin', $this->res_url() . '/res/js/admin.js', array('jquery'), $this->version());
115
- wp_enqueue_style('installer-admin', $this->res_url() . '/res/css/admin.css', array(), $this->version());
116
-
117
- $translation_array = array(
118
- 'installing' => __( 'Installing %s', 'installer' ),
119
- 'updating' => __( 'Updating %s', 'installer' ),
120
- 'activating' => __( 'Activating %s', 'installer' )
121
- );
122
-
123
- wp_localize_script( 'installer-admin', 'installer_strings', $translation_array );
124
-
125
- if($pagenow == 'plugins.php'){
126
- add_action('admin_notices', array($this, 'setup_plugins_page_notices'));
127
- add_action('admin_notices', array($this, 'setup_plugins_renew_warnings'), 10);
128
- add_action('admin_notices', array($this, 'queue_plugins_renew_warnings'), 20);
129
-
130
- add_action('admin_init', array($this, 'setup_plugins_action_links'));
131
-
132
- }
133
-
134
- if($this->is_repositories_page()){
135
- add_action('admin_init', array($this, 'validate_repository_subscription'));
136
- }
137
-
138
- if(defined('DOING_AJAX')){
139
- add_action('wp_ajax_save_site_key', array($this, 'save_site_key'));
140
- add_action('wp_ajax_remove_site_key', array($this, 'remove_site_key'));
141
- add_action('wp_ajax_update_site_key', array($this, 'update_site_key'));
142
-
143
- add_action('wp_ajax_installer_download_plugin', array($this, 'download_plugin_ajax_handler'));
144
- add_action('wp_ajax_installer_activate_plugin', array($this, 'activate_plugin'));
145
-
146
- add_action('wp_ajax_installer_dismiss_nag', array($this, 'dismiss_nag'));
147
- }
148
-
149
- if($pagenow == 'update.php'){
150
- if(isset($_GET['action']) && $_GET['action'] == 'update-selected'){
151
- add_action('admin_head', array($this, 'plugin_upgrade_custom_errors')); //iframe/bulk
152
- }else{
153
- add_action('all_admin_notices', array($this, 'plugin_upgrade_custom_errors')); //regular/singular
154
- }
155
- }
156
-
157
- // WP 4.2
158
- if(defined('DOING_AJAX')){
159
- add_action('wp_ajax_update-plugin', array($this, 'plugin_upgrade_custom_errors'), 0); // high priority, before WP
160
- }
161
-
162
- //Include theme support
163
- include_once $this->plugin_path() . '/includes/class-installer-theme.php';
164
-
165
- // Extra information about the source of Installer
166
- $package_source_file = $this->plugin_path() . '/installer-source.json';
167
- if( file_exists( $package_source_file ) ){
168
- $this->package_source = json_decode( file_get_contents( $package_source_file ) );
169
- }
170
- }
171
-
172
- protected function log($message){
173
- if( defined('WPML_INSTALLER_LOGGING') && WPML_INSTALLER_LOGGING ){
174
- if($fh = @fopen( $this->plugin_path() . '/installer.log', 'a' )){
175
- fwrite($fh, current_time( 'mysql' ) . "\t" . $message . "\n");
176
- }
177
- }
178
- }
179
-
180
- public function register_admin_message($text, $type = 'updated'){
181
- $this->admin_messages[] = array('text' => $text, 'type' => $type);
182
- }
183
-
184
- public function show_admin_messages(){
185
- if(!empty($this->admin_messages)){
186
- $types = array( 'error', 'updated', 'notice' );
187
- foreach($this->admin_messages as $message){
188
- $class = in_array( $message['type'], $types ) ? $message['type'] : 'updated';
189
- ?>
190
- <div class="<?php echo $class ?>">
191
- <p>
192
- <?php echo $message['text'] ?>
193
- </p>
194
- </div>
195
- <?php
196
- }
197
- }
198
- }
199
-
200
- public function load_locale(){
201
- $locale = get_locale();
202
- $locale = apply_filters( 'plugin_locale', $locale, 'installer' );
203
- $mo_file = $this->plugin_path() . '/locale/installer-' . $locale . '.mo';
204
- if(file_exists($mo_file)){
205
- load_textdomain( 'installer', $mo_file );
206
- }
207
- }
208
-
209
- public function load_embedded_plugins(){
210
- if(file_exists($this->plugin_path() . '/embedded-plugins' )) {
211
- include_once $this->plugin_path() . '/embedded-plugins/embedded-plugins.class.php';
212
- $this->installer_embedded_plugins = new Installer_Embedded_Plugins();
213
- }
214
- }
215
-
216
- public function menu_setup(){
217
- global $pagenow;
218
-
219
- if(is_multisite() && !is_network_admin()){
220
- $this->menu_multisite_redirect();
221
- add_options_page(__('Installer', 'installer'), __('Installer', 'installer'), 'manage_options', 'installer', array($this, 'show_products')) ;
222
- }else{
223
- if($this->config['plugins_install_tab'] && is_admin() && $pagenow == 'plugin-install.php'){
224
- // Default GUI, under Plugins -> Install
225
- add_filter('install_plugins_tabs', array($this, 'add_install_plugins_tab'));
226
- add_action('install_plugins_commercial', array($this, 'show_products'));
227
- }
228
- }
229
-
230
- }
231
-
232
- public function menu_url(){
233
- if(is_multisite()){
234
- if(is_network_admin()){
235
- $url = network_admin_url('plugin-install.php?tab=commercial');
236
- }else{
237
- $url = admin_url('options-general.php?page=installer');
238
- }
239
- }else{
240
- $url = admin_url('plugin-install.php?tab=commercial');
241
- }
242
- return $url;
243
- }
244
-
245
- private function menu_multisite_redirect(){
246
- global $pagenow;
247
-
248
- if($pagenow == 'plugin-install.php' && isset($_GET['tab']) && $_GET['tab'] == 'commercial'){
249
- wp_redirect($this->menu_url());
250
- exit;
251
- }
252
-
253
- }
254
-
255
- private function _pre_1_0_clean_up(){
256
- global $wpdb;
257
-
258
- if(!defined('WPRC_VERSION')){
259
- $old_tables = array(
260
- $wpdb->prefix . 'wprc_cached_requests',
261
- $wpdb->prefix . 'wprc_extension_types',
262
- $wpdb->prefix . 'wprc_extensions',
263
- $wpdb->prefix . 'wprc_repositories',
264
- $wpdb->prefix . 'wprc_repositories_relationships',
265
- );
266
-
267
- foreach($old_tables as $table){
268
- $wpdb->query(sprintf("DROP TABLE IF EXISTS %s", $table));
269
- }
270
-
271
- }
272
-
273
- $this->settings['_pre_1_0_clean_up'] = true;
274
- $this->save_settings();
275
- }
276
-
277
- public function setup_plugins_action_links(){
278
-
279
- $plugins = get_plugins();
280
-
281
- $repositories_plugins = array();
282
-
283
- if( !empty($this->settings['repositories']) ) {
284
-
285
- foreach ( $this->settings['repositories'] as $repository_id => $repository ) {
286
-
287
- foreach ( $repository['data']['packages'] as $package ) {
288
-
289
- foreach ( $package['products'] as $product ) {
290
-
291
- foreach ( $product['plugins'] as $plugin_slug ) {
292
-
293
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
294
-
295
- if ( !isset($repositories_plugins[$repository_id][$download['slug']]) ) {
296
- $repositories_plugins[$repository_id][$download['slug']] = array(
297
- 'name' => $download['name'],
298
- 'registered' => $this->plugin_is_registered( $repository_id, $download['slug'] ) ? 1 : 0
299
- );
300
- }
301
-
302
- }
303
-
304
- }
305
-
306
- }
307
-
308
- foreach ( $plugins as $plugin_id => $plugin ) {
309
-
310
- $wp_plugin_slug = dirname( $plugin_id );
311
- if ( empty($wp_plugin_slug) ) {
312
- $wp_plugin_slug = basename( $plugin_id, '.php' );
313
- }
314
-
315
- foreach ( $repositories_plugins as $repository_id => $r_plugins ) {
316
-
317
- foreach ( $r_plugins as $slug => $r_plugin ) {
318
-
319
- if ( $wp_plugin_slug == $slug || $r_plugin['name'] == $plugin['Name'] || $r_plugin['name'] == $plugin['Title'] ) { //match order: slug, name, title
320
-
321
- if ( $r_plugin['registered'] ) {
322
- add_filter( 'plugin_action_links_' . $plugin_id, array($this, 'plugins_action_links_registered') );
323
- } else {
324
- add_filter( 'plugin_action_links_' . $plugin_id, array($this, 'plugins_action_links_not_registered') );
325
- }
326
-
327
- }
328
-
329
- }
330
-
331
- }
332
-
333
-
334
- }
335
-
336
- }
337
- }
338
-
339
- }
340
-
341
- public function plugins_action_links_registered($links){
342
- $links[] = '<a href="' . $this->menu_url() . '">' . __('Registered', 'installer') . '</a>';
343
- return $links;
344
- }
345
-
346
- public function plugins_action_links_not_registered($links){
347
- $links[] = '<a href="' . $this->menu_url() . '">' . __('Register', 'installer') . '</a>';
348
- return $links;
349
- }
350
-
351
- public function plugin_is_registered($repository_id, $slug){
352
-
353
- $registered = false;
354
-
355
- if( $this->repository_has_valid_subscription($repository_id) ){
356
-
357
- $subscription_type = $this->get_subscription_type_for_repository($repository_id);
358
- $r_plugins = array();
359
-
360
- foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package){
361
-
362
- foreach($package['products'] as $product){
363
-
364
- if( $product['subscription_type'] == $subscription_type || $this->have_superior_subscription($subscription_type, $product) ) {
365
-
366
- foreach ($product['plugins'] as $plugin_slug) {
367
-
368
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
369
-
370
- if (!isset($rep_plugins[$download['slug']])) {
371
- $r_plugins[$download['slug']] = $download['slug'];
372
- }
373
-
374
- }
375
-
376
- }
377
-
378
- }
379
-
380
- }
381
-
382
- $registered = isset($r_plugins[$slug]);
383
-
384
- }
385
-
386
-
387
- return $registered;
388
-
389
- }
390
-
391
- public function version(){
392
- return WP_INSTALLER_VERSION;
393
- }
394
-
395
- public function plugin_path() {
396
- return untrailingslashit( plugin_dir_path( dirname(__FILE__) ) );
397
- }
398
-
399
- public function plugin_url() {
400
- if(isset($this->config['in_theme_folder']) && !empty($this->config['in_theme_folder'])){
401
- $url = untrailingslashit(get_template_directory_uri() . '/' . $this->config['in_theme_folder']);
402
- }else{
403
- $url = untrailingslashit( plugins_url( '/', dirname(__FILE__) ) );
404
- }
405
-
406
- return $url;
407
- }
408
-
409
- public function is_repositories_page(){
410
- global $pagenow;
411
-
412
- return $pagenow == 'plugin-install.php' && isset($_GET['tab']) && $_GET['tab'] == 'commercial';
413
- }
414
-
415
- public function res_url(){
416
- if(isset($this->config['in_theme_folder']) && !empty($this->config['in_theme_folder'])){
417
- $url = untrailingslashit(get_template_directory_uri() . '/' . $this->config['in_theme_folder']);
418
- }else{
419
- $url = $this->plugin_url();
420
- }
421
- return $url;
422
- }
423
-
424
- public function save_settings(){
425
-
426
- $_settings = serialize($this->settings);
427
- if($this->_gz_on){
428
- $_settings = gzcompress($_settings);
429
- }
430
- $_settings = base64_encode($_settings);
431
-
432
- update_option( 'wp_installer_settings', $_settings );
433
-
434
- if( is_multisite() && is_main_site() && isset($this->settings['repositories']) ){
435
- $network_settings = array();
436
-
437
- foreach( $this->settings['repositories'] as $rep_id => $repository ){
438
- if( isset($repository['subscription']) )
439
- $network_settings[$rep_id] = $repository['subscription'];
440
- }
441
-
442
- update_site_option( 'wp_installer_network', $network_settings );
443
-
444
-
445
- }
446
-
447
- }
448
-
449
- public function get_settings($refresh = false){
450
-
451
- if($refresh || empty($this->settings)){
452
-
453
- $_settings = get_option('wp_installer_settings');
454
-
455
-
456
- if (is_array($_settings) || empty($_settings)) { //backward compatibility 1.1
457
- $this->settings = $_settings;
458
-
459
- } else {
460
- $_settings = base64_decode($_settings);
461
- if ($this->_gz_on) {
462
- $_settings = gzuncompress($_settings);
463
- }
464
- $this->settings = unserialize($_settings);
465
- }
466
-
467
- if (is_multisite() && isset($this->settings['repositories'])) {
468
- $network_settings = maybe_unserialize(get_site_option('wp_installer_network'));
469
- if ($network_settings) {
470
- foreach ($this->settings['repositories'] as $rep_id => $repository) {
471
- if (isset($network_settings[$rep_id])) {
472
- $this->settings['repositories'][$rep_id]['subscription'] = $network_settings[$rep_id];
473
- }
474
- }
475
- }
476
- }
477
-
478
-
479
- $this->settings = $this->_pre_1_6_backwards_compatibility($this->settings);
480
-
481
- $this->settings = $this->_old_products_format_backwards_compatibility($this->settings);
482
-
483
- }
484
-
485
-
486
- return $this->settings;
487
- }
488
-
489
- //backward compatibility, will remove 'basename' in version 1.8
490
- private function _pre_1_6_backwards_compatibility($settings){
491
-
492
- if( version_compare($this->version(), '1.8', '<') && !empty($settings['repositories']) ){
493
-
494
- foreach ($settings['repositories'] as $repository_id => $repository) {
495
-
496
- foreach ($repository['data']['downloads']['plugins'] as $slug => $download) {
497
-
498
- $settings['repositories'][$repository_id]['data']['downloads']['plugins'][$slug]['slug'] = $download['basename'];
499
-
500
- }
501
- }
502
-
503
- }
504
-
505
- return $settings;
506
-
507
- }
508
-
509
- //backward compatibility - support old products list format (downloads under products instead of global downloads list)
510
- private function _old_products_format_backwards_compatibility($settings){
511
-
512
- if( version_compare($this->version(), '1.8', '<') && !empty($settings['repositories']) && empty($this->_old_products_format_backwards_compatibility) ) {
513
-
514
- foreach ($settings['repositories'] as $repository_id => $repository) {
515
-
516
- $populate_downloads = false;
517
-
518
- foreach ($repository['data']['packages'] as $package_id => $package) {
519
-
520
- foreach ($package['products'] as $product_id => $product) {
521
-
522
- if (!isset($product['plugins'])) {
523
-
524
- $populate_downloads = true;
525
-
526
- foreach ($product['downloads'] as $download_id => $download) {
527
-
528
- $settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['plugins'][] = $download['slug'];
529
-
530
- }
531
-
532
- }
533
-
534
- }
535
-
536
- }
537
-
538
- if ($populate_downloads) {
539
-
540
- // Add downloads branch
541
- foreach ($repository['data']['packages'] as $package_id => $package) {
542
-
543
- foreach ($package['products'] as $product_id => $product) {
544
-
545
- foreach ($product['downloads'] as $download_id => $download) {
546
-
547
- if (!isset($settings['repositories'][$repository_id]['data']['downloads']['plugins'][$download['slug']])) {
548
- $settings['repositories'][$repository_id]['data']['downloads']['plugins'][$download['slug']] = $download;
549
- }
550
-
551
- $settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['plugins'][] = $download['slug'];
552
- }
553
-
554
- unset($settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['downloads']);
555
-
556
- }
557
-
558
- }
559
-
560
- }
561
-
562
- }
563
-
564
- $this->_old_products_format_backwards_compatibility = true;
565
-
566
- }
567
-
568
- return $settings;
569
-
570
- }
571
-
572
- public function get_installer_site_url( $repository_id = false ){
573
- global $current_site;
574
-
575
- $site_url = get_site_url();
576
-
577
- if( $repository_id && is_multisite() && isset( $this->settings['repositories'] ) ){
578
- $network_settings = maybe_unserialize( get_site_option('wp_installer_network') );
579
-
580
- if ( isset( $network_settings[$repository_id] ) ) {
581
- $site_url = get_site_url( $current_site->blog_id );
582
- }
583
-
584
- }
585
-
586
- return $site_url;
587
- }
588
-
589
- public function show_site_key_nags(){
590
- $screen = get_current_screen();
591
-
592
- if($screen->base == 'settings_page_installer' || ($screen->base == 'plugin-install' && isset($_GET['tab']) && $_GET['tab'] == 'commercial')){
593
- return;
594
- }
595
-
596
- if(!empty($this->config['site_key_nags'])){
597
-
598
- foreach($this->config['site_key_nags'] as $nag){
599
-
600
- if(!$this->repository_has_subscription($nag['repository_id'] )){
601
- $show = true;
602
- if(!empty($nag['condition_cb'])){
603
- $show = call_user_func($nag['condition_cb']);
604
- }
605
-
606
- if(empty($this->settings['dismissed_nags'][$nag['repository_id']]) && $show){
607
- echo '<div class="updated error otgs-is-dismissible"><p>';
608
- printf(__("To get automatic updates, you need to register %s for this site. %sRegister %s%s", 'sitepress'),
609
- $nag['product_name'], '<a class="button-primary" href="' . $this->menu_url() . '">', $nag['product_name'], '</a>');
610
-
611
- echo '</p>';
612
- echo '<span class="installer-dismiss-nag notice-dismiss" data-repository="' . $nag['repository_id'] . '"><span class="screen-reader-text">' . __('Dismiss', 'sitepress') . '</span></span>';
613
- echo '</div>';
614
- }
615
- }
616
-
617
- }
618
-
619
- }
620
-
621
- }
622
-
623
- public function dismiss_nag(){
624
- $this->settings['dismissed_nags'][$_POST['repository']] = 1;
625
-
626
- $this->save_settings();
627
-
628
- echo json_encode(array());
629
- exit;
630
- }
631
-
632
- public function add_install_plugins_tab($tabs){
633
-
634
- $tabs['commercial'] = __('Commercial', 'installer');
635
-
636
- return $tabs;
637
- }
638
-
639
- public function load_repositories_list(){
640
- global $wp_installer_instances;
641
-
642
- foreach ($wp_installer_instances as $instance) {
643
-
644
- if (file_exists(dirname($instance['bootfile']) . '/repositories.xml')) {
645
- $config_file = dirname($instance['bootfile']) . '/repositories.xml';
646
-
647
- if (file_exists(dirname($instance['bootfile']) . '/repositories.sandbox.xml')) {
648
- $config_file = dirname($instance['bootfile']) . '/repositories.sandbox.xml';
649
- add_filter('https_ssl_verify', '__return_false');
650
- }
651
-
652
- $repos = simplexml_load_file($config_file);
653
-
654
- if($repos) {
655
- foreach ($repos as $repo) {
656
- $id = strval($repo->id);
657
-
658
- $data['api-url'] = strval($repo->apiurl);
659
- $data['products'] = strval($repo->products);
660
-
661
- // excludes rule;
662
- if (isset($this->config['repositories_exclude']) && in_array($id, $this->config['repositories_exclude'])) {
663
- continue;
664
- }
665
-
666
- // includes rule;
667
- if (isset($this->config['repositories_include']) && !in_array($id, $this->config['repositories_include'])) {
668
- continue;
669
- }
670
-
671
- $this->repositories[$id] = $data;
672
-
673
- }
674
- }
675
-
676
- }
677
- }
678
-
679
- }
680
-
681
- public function filter_repositories_list(){
682
-
683
- if(!empty($this->settings['repositories'])) {
684
- foreach ($this->settings['repositories'] as $id => $repo_data) {
685
-
686
- // excludes rule;
687
- if (isset($this->config['repositories_exclude']) && in_array($id, $this->config['repositories_exclude'])) {
688
- unset($this->settings['repositories'][$id]);
689
- }
690
-
691
- // includes rule;
692
- if (isset($this->config['repositories_include']) && !in_array($id, $this->config['repositories_include'])) {
693
- unset($this->settings['repositories'][$id]);
694
- }
695
-
696
-
697
- }
698
- }
699
-
700
-
701
- }
702
-
703
- public function refresh_repositories_data(){
704
- static $checked = false;
705
-
706
- if( defined('OTGS_DISABLE_AUTO_UPDATES') && OTGS_DISABLE_AUTO_UPDATES && empty($_GET['force-check']) || $checked ){
707
-
708
- if(empty($this->settings['repositories']) && $this->is_repositories_page()){
709
-
710
- foreach($this->repositories as $id => $data) {
711
- $repository_names[] = $id;
712
-
713
- }
714
-
715
- $error = sprintf(__("Installer cannot display the products information because the automatic updating for %s was explicitly disabled with the configuration below (usually in wp-config.php):", 'installer'), strtoupper( join(', ', $repository_names) ));
716
- $error .= '<br /><br /><code>define("OTGS_DISABLE_AUTO_UPDATES", true);</code><br /><br />';
717
- $error .= sprintf(__("In order to see the products information, please run the %smanual updates check%s to initialize the products list or (temporarily) remove the above code.", 'installer'), '<a href="' . admin_url('update-core.php') . '">', '</a>');
718
-
719
- $this->register_admin_message($error, 'error');
720
-
721
-
722
- }
723
-
724
- return;
725
- }
726
-
727
- $checked = true;
728
-
729
- foreach($this->repositories as $id => $data){
730
-
731
- $response = wp_remote_get($data['products']);
732
-
733
- if(is_wp_error($response)){
734
- // http fallback
735
- $data['products'] = preg_replace("@^https://@", 'http://', $data['products']);
736
- $response = wp_remote_get($data['products']);
737
- }
738
-
739
- if(is_wp_error($response)){
740
-
741
- $error = sprintf(__("Installer cannot contact our updates server to get information about the available products and check for new versions. If you are seeing this message for the first time, you can ignore it, as it may be a temporary communication problem. If the problem persists and your WordPress admin is slowing down, you can disable automated version checks. Add the following line to your wp-config.php file:", 'installer'), strtoupper($id));
742
- $error .= '<br /><br /><code>define("OTGS_DISABLE_AUTO_UPDATES", true);</code>';
743
-
744
- $this->register_admin_message($error, 'error');
745
-
746
- continue;
747
- }
748
-
749
- if($response && isset($response['response']['code']) && $response['response']['code'] == 200){
750
- $body = wp_remote_retrieve_body($response);
751
- if($body){
752
- $products = json_decode($body, true);
753
-
754
- if(is_array($products)){
755
- $this->settings['repositories'][$id]['data'] = $products;
756
- $this->settings = $this->_pre_1_6_backwards_compatibility($this->settings);
757
- }
758
- }
759
-
760
- }
761
-
762
- $this->log( sprintf("Checked for %s updates: %s", $id, $data['products']) );
763
-
764
-
765
- }
766
-
767
- // cleanup
768
- if(empty($this->settings['repositories'])){
769
- $this->settings['repositories'] = array();
770
- }
771
- foreach($this->settings['repositories'] as $id => $data){
772
- if(!in_array($id, array_keys($this->repositories))){
773
- unset($this->settings['repositories'][$id]);
774
- }
775
- }
776
-
777
- $this->settings['last_repositories_update']= time();
778
-
779
- $this->save_settings();
780
-
781
- }
782
-
783
- public function show_products($args = array()){
784
-
785
- $screen = get_current_screen();
786
-
787
- if($screen->base == 'settings_page_installer'){ // settings page
788
- echo '<div class="wrap">';
789
- echo '<h2>' . __('Installer', 'installer') . '</h2>';
790
- echo '<br />';
791
- }
792
-
793
- if(!is_array($args)) $args = array();
794
- if(empty($args['template'])) $args['template'] = 'default';
795
-
796
- $this->filter_repositories_list();
797
-
798
- if(!empty($this->settings['repositories'])){
799
-
800
- $this->localize_strings();
801
- $this->set_filtered_prices($args);
802
- $this->set_hierarchy_and_order();
803
-
804
- foreach($this->settings['repositories'] as $repository_id => $repository){
805
-
806
- if($args['template'] == 'compact'){
807
-
808
- if(isset($args['repository']) && $args['repository'] == $repository_id){
809
- include $this->plugin_path() . '/templates/products-compact.php';
810
- }
811
-
812
- }else{
813
-
814
- include $this->plugin_path() . '/templates/repository-listing.php';
815
-
816
- }
817
-
818
- unset($site_key, $subscription_type, $expired, $upgrade_options, $products_avaliable);
819
-
820
- }
821
-
822
- }else{
823
-
824
- echo '<center>' . __('No repositories defined.', 'installer') . '</center>';
825
-
826
- }
827
-
828
- if($screen->base == 'settings_page_installer'){ // settings page
829
- echo '</div>';
830
- }
831
-
832
-
833
- }
834
-
835
- public function get_product_price($repository_id, $package_id, $product_id, $incl_discount = false){
836
-
837
- $price = false;
838
-
839
- foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package ){
840
-
841
- if($package['id'] == $package_id){
842
- if(isset($package['products'][$product_id])){
843
- if($incl_discount && isset($package['products'][$product_id]['price_disc'])){
844
- $price = $package['products'][$product_id]['price_disc'];
845
- }elseif(isset($package['products'][$product_id]['price'])){
846
- $price = $package['products'][$product_id]['price'];
847
- }
848
- }
849
- break;
850
- }
851
- }
852
-
853
- return $price;
854
- }
855
-
856
- private function _render_product_packages($packages, $subscription_type, $expired, $upgrade_options, $repository_id){
857
-
858
- $data = array();
859
-
860
- foreach($packages as $package_id => $package){
861
-
862
- $row = array('products' => array(), 'downloads' => array());
863
- foreach($package['products'] as $product){
864
-
865
- // filter out free subscriptions from being displayed as buying options
866
- if( empty($product['price']) && (empty($subscription_type) || $expired) ){
867
- continue;
868
- }
869
-
870
- // buy base
871
- if(empty($subscription_type) || $expired) {
872
-
873
- $p['url'] = $this->append_parameters_to_buy_url($product['url'], $repository_id);
874
- if (!empty($product['price_disc'])) {
875
- $p['label'] = $product['call2action'] . ' - ' . sprintf('$%s %s$%d%s (USD)', $product['price_disc'], '&nbsp;&nbsp;<del>', $product['price'], '</del>');
876
- } else {
877
- $p['label'] = $product['call2action'] . ' - ' . sprintf('$%d (USD)', $product['price']);
878
- }
879
- $row['products'][] = $p;
880
-
881
- // renew
882
- } elseif(isset($subscription_type) && $product['subscription_type'] == $subscription_type){
883
-
884
- if($product['renewals']) {
885
- foreach ($product['renewals'] as $renewal) {
886
- $p['url'] = $this->append_parameters_to_buy_url($renewal['url'], $repository_id);
887
- $p['label'] = $renewal['call2action'] . ' - ' . sprintf('$%d (USD)', $renewal['price']);
888
- }
889
-
890
- $row['products'][] = $p;
891
- }
892
-
893
- }
894
-
895
- // upgrades
896
- if(!empty($upgrade_options[$product['subscription_type']])){
897
-
898
- foreach($upgrade_options[$product['subscription_type']] as $stype => $upgrade){
899
- if($stype != $subscription_type) continue;
900
-
901
- $p['url'] = $this->append_parameters_to_buy_url($upgrade['url'], $repository_id);
902
- if (!empty($upgrade['price_disc'])) {
903
- $p['label'] = $upgrade['call2action'] . ' - ' . sprintf('$%s %s$%d%s (USD)', $upgrade['price_disc'], '&nbsp;&nbsp;<del>', $upgrade['price'], '</del>');
904
- } else {
905
- $p['label'] = $upgrade['call2action'] . ' - ' . sprintf('$%d (USD)', $upgrade['price']);
906
- }
907
- $row['products'][] = $p;
908
-
909
- }
910
-
911
- }
912
-
913
- // downloads
914
- if(isset($subscription_type) && !$expired && $product['subscription_type'] == $subscription_type){
915
- foreach($product['plugins'] as $plugin_slug){
916
-
917
- $row['downloads'][] = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
918
-
919
- }
920
-
921
- }
922
-
923
- //subpackages
924
- if(!empty($package['sub-packages'])){
925
- $row['sub-packages'] = $package['sub-packages'];
926
- }
927
-
928
- }
929
-
930
- $row['id'] = $package['id'];
931
- $row['image_url'] = $package['image_url'];
932
- $row['name'] = $package['name'];
933
- $row['description'] = $package['description'];
934
-
935
- if(!empty($row['products']) || !empty($row['downloads']) || !empty($row['sub-packages'])){
936
- $data[] = $row;
937
- }
938
-
939
-
940
- }
941
-
942
- return $data;
943
-
944
- }
945
-
946
- public function get_extra_url_parameters(){
947
-
948
- $parameters = array();
949
-
950
- if(!empty($this->package_source)){
951
- foreach($this->package_source as $key => $val){
952
- $parameters[$key] = $val;
953
- }
954
- }
955
-
956
- $parameters['installer_version'] = WP_INSTALLER_VERSION;
957
- $parameters['theme'] = wp_get_theme()->get( 'Name' );
958
- $parameters['site_name'] = get_bloginfo( 'name' );
959
-
960
- return $parameters;
961
- }
962
-
963
- public function append_parameters_to_buy_url($url, $repository_id, $args = array()){
964
-
965
- $url = add_query_arg( array('icl_site_url' => $this->get_installer_site_url( $repository_id ) ), $url );
966
-
967
- $affiliate_id = false;
968
- $affiliate_key = false;
969
-
970
- // Add extra parameters for custom Installer packages
971
- if( !empty($this->package_source) ){
972
- $extra = $this->get_extra_url_parameters();
973
-
974
- if( !empty($extra['repository']) && $extra['repository'] == $repository_id ) {
975
-
976
- if( !empty($extra['affiliate_key']) && !empty($extra['user_id']) ){
977
- $this->config['affiliate_id:' . $repository_id] = $extra['user_id'];
978
- $this->config['affiliate_key:' . $repository_id] = $extra['affiliate_key'];
979
- unset($extra['affiliate_key'], $extra['user_id'], $extra['repository']); // no need to include these ones
980
- }
981
-
982
- $url = add_query_arg($extra, $url);
983
- }
984
-
985
- }
986
-
987
- if(isset($this->config['affiliate_id:' . $repository_id]) && isset($this->config['affiliate_key:' . $repository_id])){
988
-
989
- $affiliate_id = $this->config['affiliate_id:' . $repository_id];
990
- $affiliate_key = $this->config['affiliate_key:' . $repository_id];
991
-
992
- }elseif(isset($args['affiliate_id:' . $repository_id]) && isset($args['affiliate_key:' . $repository_id])){
993
-
994
- $affiliate_id = $args['affiliate_id:' . $repository_id];
995
- $affiliate_key = $args['affiliate_key:' . $repository_id];
996
-
997
- }elseif(defined('ICL_AFFILIATE_ID') && defined('ICL_AFFILIATE_KEY')){ //support for 1 repo
998
-
999
- $affiliate_id = ICL_AFFILIATE_ID;
1000
- $affiliate_key = ICL_AFFILIATE_KEY;
1001
-
1002
- }elseif(isset($this->config['affiliate_id']) && isset($this->config['affiliate_key'])) {
1003
- // BACKWARDS COMPATIBILITY
1004
- $affiliate_id = $this->config['affiliate_id'];
1005
- $affiliate_key = $this->config['affiliate_key'];
1006
- }
1007
-
1008
- if($affiliate_id && $affiliate_key){
1009
- $url = add_query_arg(array('aid' => $affiliate_id, 'affiliate_key' => $affiliate_key), $url);
1010
- }
1011
-
1012
- if($repository_id == 'wpml'){
1013
- $url = add_query_arg(array('using_icl' => $this->_using_icl, 'wpml_version' => $this->_wpml_version), $url);
1014
- }
1015
-
1016
- $url = apply_filters('wp_installer_buy_url', $url);
1017
-
1018
- $url = esc_url($url);
1019
-
1020
- return $url;
1021
-
1022
- }
1023
-
1024
- public function save_site_key($args = array()){
1025
-
1026
- $error = '';
1027
-
1028
- $repository_id = isset($args['repository_id']) ? $args['repository_id'] : (isset($_POST['repository_id']) ? $_POST['repository_id'] : false);
1029
- $nonce = isset($args['nonce']) ? $args['nonce'] : (isset($_POST['nonce']) ? $_POST['nonce'] : '');
1030
- $site_key = isset($args['site_key']) ? $args['site_key'] : $_POST['site_key_' . $repository_id];
1031
-
1032
- $site_key = preg_replace("/[^A-Za-z0-9]/", '', $site_key);
1033
-
1034
- if($repository_id && $nonce && wp_create_nonce('save_site_key_' . $repository_id) == $nonce){
1035
-
1036
- try {
1037
- $subscription_data = $this->fetch_subscription_data( $repository_id, $site_key, self::SITE_KEY_VALIDATION_SOURCE_REGISTRATION );
1038
-
1039
- if ( $subscription_data ) {
1040
- $this->settings['repositories'][$repository_id]['subscription'] = array('key' => $site_key, 'data' => $subscription_data);
1041
- $this->save_settings();
1042
- } else {
1043
- $error = __( 'Invalid site key for the current site.', 'installer' );
1044
- }
1045
-
1046
- } catch (Exception $e ){
1047
- $error = $e->getMessage();
1048
- if( preg_match('#Could not resolve host: (.*)#', $error, $matches) || preg_match('#Couldn\'t resolve host \'(.*)\'#', $error, $matches) ){
1049
- $error = sprintf(__("%s cannot access %s to register. Try again to see if it's a temporary problem. If the problem continues, make sure that this site has access to the Internet. You can still use the plugin without registration, but you will not receive automated updates.", 'installer'),
1050
- '<strong><i>' . $this->get_generic_product_name($repository_id) . '</i></strong>',
1051
- '<strong><i>' . $matches[1]. '</i></strong>'
1052
- ) ;
1053
- }
1054
- }
1055
-
1056
- }
1057
-
1058
- $return = array('error' => $error);
1059
-
1060
- if($this->api_debug){
1061
- $return['debug'] = $this->api_debug;
1062
- }
1063
-
1064
- if(!empty($args['return'])){
1065
- return $return;
1066
- }else{
1067
- echo json_encode($return);
1068
- exit;
1069
- }
1070
-
1071
- }
1072
-
1073
- /**
1074
- * Alias for WP_Installer::get_repository_site_key
1075
- * @see WP_Installer::get_repository_site_key()
1076
- *
1077
- * @param string $repository_id
1078
- * @return string (site key) or bool
1079
- */
1080
- public function get_site_key($repository_id){
1081
- return WP_Installer::get_repository_site_key( $repository_id );
1082
- }
1083
-
1084
- public function remove_site_key(){
1085
- if($_POST['nonce'] == wp_create_nonce('remove_site_key_' . $_POST['repository_id'])){
1086
- unset($this->settings['repositories'][$_POST['repository_id']]['subscription']);
1087
- $this->save_settings();
1088
-
1089
- $this->refresh_repositories_data();
1090
- }
1091
- exit;
1092
- }
1093
-
1094
- public function validate_repository_subscription(){
1095
- $repository_id = isset($_GET['validate_repository']) ? $_GET['validate_repository'] : false;
1096
- if($repository_id){
1097
-
1098
- $site_key = $this->get_site_key($repository_id);
1099
- if($site_key) {
1100
- $subscription_data = $this->fetch_subscription_data( $repository_id, $site_key, self::SITE_KEY_VALIDATION_SOURCE_REVALIDATION);
1101
- if(empty($subscription_data)){
1102
- unset($this->settings['repositories'][$repository_id]['subscription']);
1103
- delete_site_transient('update_plugins');
1104
- $this->save_settings();
1105
- }
1106
- }
1107
-
1108
- wp_redirect($this->menu_url() . '#repository-' . $repository_id);
1109
- exit;
1110
-
1111
- }
1112
-
1113
- }
1114
-
1115
- public function update_site_key(){
1116
-
1117
- $error = '';
1118
-
1119
- if($_POST['nonce'] == wp_create_nonce('update_site_key_' . $_POST['repository_id'])){
1120
-
1121
- $repository_id = $_POST['repository_id'];
1122
- $site_key = $this->get_site_key($_POST['repository_id']);
1123
-
1124
- if($site_key){
1125
- try {
1126
- $subscription_data = $this->fetch_subscription_data( $repository_id, $site_key, self::SITE_KEY_VALIDATION_SOURCE_UPDATES_CHECK );
1127
-
1128
- if ( $subscription_data ) {
1129
- $this->settings['repositories'][$repository_id]['subscription'] = array('key' => $site_key, 'data' => $subscription_data);
1130
-
1131
- //also refresh products information
1132
- $this->refresh_repositories_data();
1133
-
1134
- $this->save_settings();
1135
-
1136
- } else {
1137
- unset($this->settings['repositories'][$repository_id]['subscription']);
1138
- $error = __( 'Invalid site key for the current site. If the error persists, try to unregister first and then register again with the same site key.', 'installer' );
1139
- }
1140
-
1141
-
1142
- } catch (Exception $e ){
1143
- $error = $e->getMessage();
1144
- if( preg_match('#Could not resolve host: (.*)#', $error, $matches) || preg_match('#Couldn\'t resolve host \'(.*)\'#', $error, $matches) ){
1145
- $error = sprintf(__("%s cannot access %s to register. Try again to see if it's a temporary problem. If the problem continues, make sure that this site has access to the Internet. You can still use the plugin without registration, but you will not receive automated updates.", 'installer'),
1146
- '<strong><i>' . $this->get_generic_product_name($repository_id) . '</i></strong>',
1147
- '<strong><i>' . $matches[1]. '</i></strong>'
1148
- ) ;
1149
- }
1150
- }
1151
-
1152
- }
1153
-
1154
- }
1155
-
1156
- echo json_encode(array('error' => $error));
1157
-
1158
- exit;
1159
- }
1160
-
1161
- public function api_debug_log($text){
1162
-
1163
- if(defined('WPML_DEBUG_INSTALLER') && WPML_DEBUG_INSTALLER){
1164
-
1165
- if(!is_scalar($text)){
1166
- $text = print_r($text, 1);
1167
- }
1168
-
1169
- $this->api_debug .= $text . "\n";
1170
-
1171
- }
1172
-
1173
- }
1174
-
1175
- public function fetch_subscription_data( $repository_id, $site_key, $source = self::SITE_KEY_VALIDATION_SOURCE_OTHER ){
1176
-
1177
- $subscription_data = false;
1178
-
1179
- $args['body'] = array(
1180
- 'action' => 'site_key_validation',
1181
- 'site_key' => $site_key,
1182
- 'site_url' => $this->get_installer_site_url( $repository_id ),
1183
- 'source' => $source
1184
- );
1185
-
1186
- if($repository_id == 'wpml'){
1187
- $args['body']['using_icl'] = $this->_using_icl;
1188
- $args['body']['wpml_version'] = $this->_wpml_version;
1189
- }
1190
-
1191
- $args['body']['installer_version'] = WP_INSTALLER_VERSION;
1192
- $args['body']['theme'] = wp_get_theme()->get( 'Name' );
1193
- $args['body']['site_name'] = get_bloginfo( 'name' );
1194
-
1195
- $args['body']['versions'] = $this->get_local_product_versions( $repository_id );
1196
-
1197
- $args['timeout'] = 45;
1198
-
1199
- // Add extra parameters for custom Installer packages
1200
- if( !empty($this->package_source) ){
1201
- $extra = $this->get_extra_url_parameters();
1202
- if( !empty($extra['repository']) && $extra['repository'] == $repository_id ) {
1203
- unset($extra['repository']);
1204
- foreach($extra as $key => $val){
1205
- $args['body'][$key] = $val;
1206
- }
1207
- }
1208
- }
1209
-
1210
- $response = wp_remote_post($this->repositories[$repository_id]['api-url'], $args);
1211
-
1212
- $this->api_debug_log("POST {$this->repositories[$repository_id]['api-url']}");
1213
- $this->api_debug_log($args);
1214
-
1215
- $this->log("POST {$this->repositories[$repository_id]['api-url']} - fetch subscription data");
1216
-
1217
- if( !is_wp_error($response) ){
1218
- $datas = wp_remote_retrieve_body($response);
1219
-
1220
- if(is_serialized($datas)){
1221
- $data = unserialize($datas);
1222
- $this->api_debug_log($data);
1223
-
1224
- if( !empty( $data->subscription_data ) ){
1225
- $subscription_data = $data->subscription_data;
1226
- }
1227
-
1228
- do_action( 'installer_fetched_subscription_data', $data, $repository_id);
1229
-
1230
- }else{
1231
- $this->api_debug_log($datas);
1232
- }
1233
-
1234
- }else{
1235
-
1236
- $this->api_debug_log($response);
1237
- throw new Exception( $response->get_error_message() );
1238
- }
1239
-
1240
- return $subscription_data;
1241
-
1242
- }
1243
-
1244
- function get_local_product_versions( $repository_id ){
1245
-
1246
- $versions = array();
1247
-
1248
- foreach( $this->settings['repositories'][$repository_id]['data']['packages'] as $package_id => $package ){
1249
-
1250
- foreach( $package['products'] as $product_id => $product ){
1251
-
1252
- foreach( $product['plugins'] as $plugin_slug ){
1253
-
1254
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
1255
-
1256
- if( empty( $versions[$download['slug']] ) ) {
1257
- $v = $this->get_plugin_installed_version($download['name'], $download['slug']);
1258
- if($v){
1259
- $versions[$download['slug']] = $v;
1260
- }
1261
- }
1262
-
1263
- }
1264
-
1265
- }
1266
-
1267
- }
1268
-
1269
- return $versions;
1270
- }
1271
-
1272
- public function get_repository_site_key($repository_id){
1273
- $site_key = false;
1274
-
1275
- if(!empty($this->settings['repositories'][$repository_id]['subscription']['key'])){
1276
- $site_key = $this->settings['repositories'][$repository_id]['subscription']['key'];
1277
- }
1278
-
1279
- return $site_key;
1280
- }
1281
-
1282
- public function repository_has_valid_subscription($repository_id){
1283
-
1284
- $valid = false;
1285
-
1286
- if(!empty($this->settings['repositories'][$repository_id]['subscription'])){
1287
-
1288
- $subscription = $this->settings['repositories'][$repository_id]['subscription']['data'];
1289
- $valid = ( $subscription->status == 1 && (strtotime($subscription->expires) > time() || empty($subscription->expires)) ) || $subscription->status == 4;
1290
-
1291
- }
1292
- return $valid;
1293
-
1294
- }
1295
-
1296
- public function repository_has_subscription($repository_id){
1297
- $key = false;
1298
- if(!empty($this->settings['repositories'][$repository_id]['subscription']['key'])){
1299
- $key = $this->settings['repositories'][$repository_id]['subscription']['key'];
1300
- }
1301
-
1302
- return $key;
1303
-
1304
- }
1305
-
1306
- public function repository_has_expired_subscription($repository_id){
1307
-
1308
- return $this->repository_has_subscription($repository_id) && !$this->repository_has_valid_subscription($repository_id);
1309
-
1310
- }
1311
-
1312
- public function get_generic_product_name($repository_id){
1313
-
1314
- return $this->settings['repositories'][$repository_id]['data']['product-name'];
1315
-
1316
- }
1317
-
1318
- public function show_subscription_renew_warning($repository_id, $subscription_id){
1319
-
1320
- $show = false;
1321
-
1322
- $data = $this->settings['repositories'][$repository_id]['data'];
1323
- if(!empty($data['subscriptions_meta'])){
1324
- if(isset($data['subscriptions_meta']['expiration'])){
1325
-
1326
- if(!empty($data['subscriptions_meta']['expiration'][$subscription_id])){
1327
-
1328
- $days = $data['subscriptions_meta']['expiration'][$subscription_id]['days_warning'];
1329
- $message = $data['subscriptions_meta']['expiration'][$subscription_id]['warning_message'];
1330
-
1331
- }else{
1332
-
1333
- //defaults
1334
- $days = 30;
1335
- $message = __('You will have to renew your subscription in order to continue getting the updates and support.', 'installer');
1336
-
1337
- }
1338
-
1339
- if(!empty($this->settings['repositories'][$repository_id]['subscription'])){
1340
- $subscription = $this->settings['repositories'][$repository_id]['subscription'];
1341
-
1342
- if($subscription['data']->subscription_type == $subscription_id && !empty($subscription['data']->expires)){
1343
-
1344
- if(strtotime($subscription['data']->expires) < strtotime(sprintf("+%d day", $days))){
1345
-
1346
- $days_to_expiration = ceil((strtotime($subscription['data']->expires) - time()) / 86400);
1347
-
1348
- echo '<div><p class="installer-warn-box">' .
1349
- sprintf(_n('Your subscription expires in %d day.', 'Your subscription expires in %d days.', $days_to_expiration, 'installer'), $days_to_expiration) .
1350
- '<br />' . $message .
1351
- '</p></div>';
1352
-
1353
- $show = true;
1354
-
1355
- }
1356
-
1357
- }
1358
-
1359
- }
1360
-
1361
-
1362
- }
1363
- }
1364
-
1365
-
1366
- return $show;
1367
-
1368
- }
1369
-
1370
- public function setup_plugins_renew_warnings(){
1371
-
1372
- $plugins = get_plugins();
1373
-
1374
- $subscriptions_with_warnings = array();
1375
- foreach($this->settings['repositories'] as $repository_id => $repository){
1376
-
1377
- if($this->repository_has_valid_subscription($repository_id)){
1378
- $subscription_type = $this->settings['repositories'][$repository_id]['subscription']['data']->subscription_type;
1379
- $expires = $this->settings['repositories'][$repository_id]['subscription']['data']->expires;
1380
-
1381
- $never_expires = isset($this->settings['repositories'][$repository_id]['subscription'])
1382
- && empty($this->settings['repositories'][$repository_id]['subscription']['data']->expires)
1383
- && (
1384
- $this->settings['repositories'][$repository_id]['subscription']['data']->status == 4 ||
1385
- $this->settings['repositories'][$repository_id]['subscription']['data']->status == 1
1386
- );
1387
-
1388
- if(!$never_expires){
1389
- if(isset($this->settings['repositories'][$repository_id]['data']['subscriptions_meta']['expiration'][$subscription_type])){
1390
-
1391
- $days_warning = $this->settings['repositories'][$repository_id]['data']['subscriptions_meta']['expiration'][$subscription_type]['days_warning'];
1392
- $custom_message = $this->settings['repositories'][$repository_id]['data']['subscriptions_meta']['expiration'][$subscription_type]['warning_message'];
1393
-
1394
- }else{
1395
- //defaults
1396
- $days_warning = 30;
1397
- $custom_message = __('You will have to renew your subscription in order to continue getting the updates and support.', 'installer');
1398
- }
1399
-
1400
- if(strtotime($expires) < strtotime(sprintf('+%d day', $days_warning)) ){
1401
-
1402
- $days_to_expiration = ceil((strtotime($expires) - time()) / 86400);
1403
-
1404
- $message = sprintf(_n('Your subscription expires in %d day.', 'Your subscription expires in %d days.', $days_to_expiration, 'installer'), $days_to_expiration);
1405
- $subscriptions_with_warnings[$subscription_type] = $message . ' ' . $custom_message;
1406
-
1407
- }
1408
- }
1409
-
1410
- }
1411
-
1412
- }
1413
-
1414
-
1415
-
1416
- foreach($plugins as $plugin_id => $plugin){
1417
-
1418
- $slug = dirname($plugin_id);
1419
- if(empty($slug)) continue;
1420
-
1421
- foreach($this->settings['repositories'] as $repository_id => $repository){
1422
-
1423
- if($this->repository_has_valid_subscription($repository_id)){
1424
-
1425
- foreach($repository['data']['packages'] as $package){
1426
-
1427
- foreach($package['products'] as $product){
1428
-
1429
- foreach($product['plugins'] as $plugin_slug){
1430
-
1431
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
1432
-
1433
- if($download['slug'] == $slug || $download['name'] == $plugin['Name'] || $download['name'] == $plugin['Title']){ //match order: slug, name, title
1434
-
1435
- if(isset($subscriptions_with_warnings[$product['subscription_type']])){
1436
-
1437
- $this->_plugins_renew_warnings[$plugin_id] = $subscriptions_with_warnings[$product['subscription_type']];
1438
-
1439
- }
1440
-
1441
- }
1442
-
1443
- }
1444
-
1445
- }
1446
-
1447
- }
1448
-
1449
- }
1450
-
1451
- }
1452
-
1453
- }
1454
-
1455
- }
1456
-
1457
- public function queue_plugins_renew_warnings() {
1458
-
1459
- if(!empty($this->_plugins_renew_warnings)){
1460
-
1461
- foreach($this->_plugins_renew_warnings as $plugin_id => $message){
1462
-
1463
- add_action( "after_plugin_row_" . $plugin_id, array($this, 'plugins_renew_warning'), 10, 3 );
1464
- }
1465
-
1466
- }
1467
-
1468
- }
1469
-
1470
- public function plugins_renew_warning($plugin_file, $plugin_data, $status){
1471
-
1472
- if(empty($this->_plugins_renew_warnings[$plugin_file])) return;
1473
-
1474
- $wp_list_table = _get_list_table('WP_Plugins_List_Table');
1475
- ?>
1476
-
1477
- <tr class="plugin-update-tr"><td colspan="<?php echo $wp_list_table->get_column_count(); ?>" class="plugin-update colspanchange">
1478
- <div class="update-message">
1479
- <?php
1480
- echo $this->_plugins_renew_warnings[$plugin_file]. ' ';
1481
- printf(__('%sRenew here%s.', 'installer'),
1482
- '<a href="' . $this->menu_url() . '">', '</a>');
1483
- ?>
1484
- </div>
1485
- </tr>
1486
-
1487
- <?php
1488
-
1489
- }
1490
-
1491
- public function get_subscription_type_for_repository($repository_id){
1492
-
1493
- $subscription_type = false;
1494
-
1495
- if(!empty($this->settings['repositories'][$repository_id]['subscription'])){
1496
- $subscription_type = $this->settings['repositories'][$repository_id]['subscription']['data']->subscription_type;
1497
- }
1498
-
1499
- return $subscription_type;
1500
-
1501
- }
1502
-
1503
- public function have_superior_subscription($subscription_type, $product){
1504
-
1505
- $have = false;
1506
-
1507
- if(is_array($product['upgrades'])){
1508
- foreach($product['upgrades'] as $u){
1509
- if($u['subscription_type'] == $subscription_type){
1510
- $have = true;
1511
- break;
1512
- }
1513
- }
1514
- }
1515
-
1516
- return $have;
1517
- }
1518
-
1519
- public function is_product_available_for_download($product_name, $repository_id){
1520
-
1521
- $available = false;
1522
-
1523
- $subscription_type = $this->get_subscription_type_for_repository($repository_id);
1524
- $expired = $this->repository_has_expired_subscription($repository_id);
1525
-
1526
- if($this->repository_has_subscription($repository_id) && !$expired){
1527
-
1528
- $this->set_hierarchy_and_order();
1529
-
1530
- foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package_id => $package){
1531
-
1532
- $has_top_package = false;
1533
-
1534
- foreach($package['products'] as $product){
1535
-
1536
- if($subscription_type == $product['subscription_type']){
1537
- $has_top_package = true;
1538
- if($product['name'] == $product_name){
1539
- return $available = true;
1540
- }
1541
- }
1542
-
1543
- }
1544
-
1545
- if(!empty($package['sub-packages'])){
1546
- foreach($package['sub-packages'] as $sub_package){
1547
- foreach($sub_package['products'] as $product){
1548
- if($product['name'] == $product_name && ($subscription_type == $product['subscription_type'] || $has_top_package)){
1549
- return $available = true;
1550
- }
1551
- }
1552
- }
1553
- }
1554
-
1555
- }
1556
- }
1557
-
1558
- return $available;
1559
-
1560
- }
1561
-
1562
- public function get_upgrade_options($repository_id){
1563
- $all_upgrades = array();
1564
-
1565
- //get all products: packages and subpackages
1566
- $all_products = array();
1567
- foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package){
1568
- foreach($package['products'] as $product) {
1569
- $all_products[] = $product;
1570
- }
1571
- if(!empty($package['sub-packages'])){
1572
- foreach($package['sub-packages'] as $subpackage){
1573
- foreach($subpackage['products'] as $product) {
1574
- $all_products[] = $product;
1575
- }
1576
-
1577
- }
1578
-
1579
- }
1580
-
1581
- }
1582
-
1583
- foreach($all_products as $product) {
1584
- if ($product['upgrades']) {
1585
- foreach ($product['upgrades'] as $upgrade) {
1586
- if ($this->repository_has_valid_subscription($repository_id) || ($this->repository_has_subscription($repository_id) && $upgrade['including_expired'])) {
1587
- $all_upgrades[$upgrade['subscription_type']][$product['subscription_type']] = $upgrade;
1588
- }
1589
- }
1590
- }
1591
- }
1592
-
1593
- return $all_upgrades;
1594
-
1595
- }
1596
-
1597
- public function append_site_key_to_download_url($url, $key, $repository_id){
1598
-
1599
- $url_params['site_key'] = $key;
1600
- $url_params['site_url'] = $this->get_installer_site_url( $repository_id );
1601
-
1602
-
1603
- // Add extra parameters for custom Installer packages
1604
- if( !empty($this->package_source) ){
1605
- $extra = $this->get_extra_url_parameters();
1606
- if( !empty($extra['repository']) && $extra['repository'] == $repository_id ) {
1607
- unset($extra['repository']);
1608
- foreach($extra as $key => $val){
1609
- $url_params[$key] = $val;
1610
- }
1611
- }
1612
- }
1613
-
1614
- $url = add_query_arg($url_params, $url);
1615
-
1616
- if($repository_id == 'wpml'){
1617
- $url = add_query_arg(array('using_icl' => $this->_using_icl, 'wpml_version' => $this->_wpml_version), $url);
1618
- }
1619
-
1620
- return $url;
1621
-
1622
- }
1623
-
1624
- public function plugin_is_installed($name, $slug, $version = null){
1625
-
1626
- $is = false;
1627
-
1628
- $plugins = get_plugins();
1629
-
1630
- foreach($plugins as $plugin_id => $plugin){
1631
-
1632
- $wp_plugin_slug = dirname($plugin_id);
1633
-
1634
- // Exception: embedded plugins
1635
- if( $wp_plugin_slug == $slug || $plugin['Name'] == $name || $plugin['Title'] == $name || ( $wp_plugin_slug == $slug . '-embedded' || $plugin['Name'] == $name . ' Embedded' ) ){
1636
- if($version){
1637
- if(version_compare($plugin['Version'], $version, '>=')){
1638
- $is = $plugin['Version'];
1639
- }
1640
- }else{
1641
- $is = $plugin['Version'];
1642
- }
1643
-
1644
- break;
1645
- }
1646
-
1647
- }
1648
-
1649
- //exception: Types name difference
1650
- if(!$is && $name == 'Types'){
1651
- return $this->plugin_is_installed('Types - Complete Solution for Custom Fields and Types', $slug, $version);
1652
- }
1653
-
1654
- return $is;
1655
- }
1656
-
1657
- public function plugin_is_embedded_version($name, $slug){
1658
- $is = false;
1659
-
1660
- $plugins = get_plugins();
1661
-
1662
- //false if teh full version is also installed
1663
- $is_full_installed = false;
1664
- foreach($plugins as $plugin_id => $plugin){
1665
-
1666
- if(($plugin['Name'] == $name && !preg_match("#-embedded$#", $slug)) ){
1667
- $is_full_installed = true;
1668
- break;
1669
- }
1670
-
1671
- }
1672
-
1673
- if($is_full_installed){
1674
- return false;
1675
- }
1676
-
1677
- foreach($plugins as $plugin_id => $plugin){
1678
-
1679
- // TBD
1680
- $wp_plugin_slug = dirname($plugin_id);
1681
- if( $wp_plugin_slug == $slug . '-embedded' && $plugin['Name'] == $name . ' Embedded'){
1682
- $is = true;
1683
- break;
1684
- }
1685
-
1686
- }
1687
-
1688
- return $is;
1689
-
1690
- }
1691
-
1692
- //Alias for plugin_is_installed
1693
- public function get_plugin_installed_version($name, $slug){
1694
-
1695
- return $this->plugin_is_installed($name, $slug);
1696
-
1697
- }
1698
-
1699
- public function get_plugin_repository_version($repository_id, $slug){
1700
- $version = false;
1701
-
1702
- if(!empty($this->settings['repositories'][$repository_id]['data']['packages'])){
1703
- foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package){
1704
- foreach($package['products'] as $product) {
1705
-
1706
- foreach($product['plugins'] as $plugin_slug){
1707
-
1708
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
1709
-
1710
- if($download['slug'] == $slug){
1711
- $version = $download['version'];
1712
- break (3);
1713
- }
1714
-
1715
- }
1716
-
1717
- }
1718
- }
1719
- }
1720
-
1721
- return $version;
1722
- }
1723
-
1724
- public function is_uploading_allowed(){
1725
-
1726
- //_deprecated_function ( __FUNCTION__, '1.7.3', 'Installer_Dependencies::' . __FUNCTION__ );
1727
- return $this->dependencies->is_uploading_allowed();
1728
-
1729
- }
1730
-
1731
- public function download_plugin_ajax_handler(){
1732
-
1733
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1734
- require_once $this->plugin_path() . '/includes/installer-upgrader-skins.php';
1735
-
1736
- if(isset($_POST['data'])){
1737
-
1738
- $data = json_decode( base64_decode( $_POST['data'] ), true );
1739
-
1740
- }
1741
-
1742
- $ret = false;
1743
- $plugin_id = false;
1744
- $message = '';
1745
-
1746
- //validate subscription
1747
- $site_key = $this->get_repository_site_key($data['repository_id']);
1748
- $subscription_data = $this->fetch_subscription_data( $data['repository_id'], $site_key , self::SITE_KEY_VALIDATION_SOURCE_DOWNLOAD_REPORT);
1749
-
1750
- if($subscription_data && !is_wp_error($subscription_data) && $this->repository_has_valid_subscription($data['repository_id'])){
1751
-
1752
- if($data['nonce'] == wp_create_nonce('install_plugin_' . $data['url'])){
1753
-
1754
- $upgrader_skins = new Installer_Upgrader_Skins(); //use our custom (mute) Skin
1755
- $upgrader = new Plugin_Upgrader($upgrader_skins);
1756
-
1757
- remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
1758
-
1759
- $plugins = get_plugins();
1760
-
1761
- //upgrade or install?
1762
- foreach($plugins as $id => $plugin){
1763
- $wp_plugin_slug = dirname($id);
1764
- $is_embedded = $this->plugin_is_embedded_version(preg_replace('/ Embedded$/', '', $plugin['Name']), preg_replace('/-embedded$/', '', $wp_plugin_slug));
1765
-
1766
- if($wp_plugin_slug == $data['slug'] || $is_embedded && preg_replace('/-embedded$/', '', $wp_plugin_slug) == $data['slug']){
1767
- $plugin_id = $id;
1768
- break;
1769
- }
1770
- }
1771
-
1772
- if($plugin_id && empty($is_embedded)){ //upgrade
1773
- $response['upgrade'] = 1;
1774
-
1775
- $plugin_is_active = is_plugin_active($plugin_id);
1776
-
1777
- $ret = $upgrader->upgrade($plugin_id);
1778
-
1779
- if(!$ret && !empty($upgrader->skin->installer_error)){
1780
- if(is_wp_error($upgrader->skin->installer_error)){
1781
- $message = $upgrader->skin->installer_error->get_error_message() .
1782
- ' (' . $upgrader->skin->installer_error->get_error_data() . ')';
1783
- }
1784
- }
1785
-
1786
- if($plugin_is_active){
1787
- //prevent redirects
1788
- add_filter('wp_redirect', '__return_false');
1789
- activate_plugin($plugin_id);
1790
- }
1791
-
1792
- }else{ //install
1793
-
1794
- if($is_embedded){
1795
- delete_plugins(array($plugin_id));
1796
- }
1797
-
1798
- $response['install'] = 1;
1799
- $ret = $upgrader->install($data['url']);
1800
- if(!$ret && !empty($upgrader->skin->installer_error)){
1801
- if(is_wp_error($upgrader->skin->installer_error)){
1802
- $message = $upgrader->skin->installer_error->get_error_message() .
1803
- ' (' . $upgrader->skin->installer_error->get_error_data() . ')';
1804
- }
1805
- }
1806
- }
1807
-
1808
- $plugins = get_plugins(); //read again
1809
-
1810
- if($ret && !empty($_POST['activate'])){
1811
- foreach($plugins as $id => $plugin){
1812
- $wp_plugin_slug = dirname($id);
1813
- if($wp_plugin_slug == $data['slug']){
1814
- $plugin_version = $plugin['Version'];
1815
- $plugin_id = $id;
1816
- break;
1817
- }
1818
- }
1819
-
1820
- }
1821
-
1822
- }
1823
-
1824
- } else { //subscription not valid
1825
-
1826
- $ret = false;
1827
- $message = __('Your subscription appears to no longer be valid. Please try to register again using a valid site key.', 'installer');
1828
- }
1829
-
1830
- $response['version'] = isset($plugin_version) ? $plugin_version : 0;
1831
- $response['plugin_id'] = $plugin_id;
1832
- $response['nonce'] = wp_create_nonce('activate_' . $plugin_id);
1833
- $response['success'] = $ret;
1834
- $response['message'] = $message;
1835
-
1836
- echo json_encode( $response );
1837
- exit;
1838
-
1839
- }
1840
-
1841
- public function download_plugin($slug, $url){
1842
-
1843
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1844
- require_once $this->plugin_path() . '/includes/installer-upgrader-skins.php';
1845
-
1846
- $upgrader_skins = new Installer_Upgrader_Skins(); //use our custom (mute) Skin
1847
- $upgrader = new Plugin_Upgrader($upgrader_skins);
1848
-
1849
- remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
1850
-
1851
- $plugins = get_plugins();
1852
-
1853
- $plugin_id = false;
1854
-
1855
- //upgrade or install?
1856
- foreach($plugins as $id => $plugin){
1857
- $wp_plugin_slug = dirname($id);
1858
- if($wp_plugin_slug == $slug){
1859
- $plugin_id = $id;
1860
- break;
1861
- }
1862
- }
1863
-
1864
- if($plugin_id){ //upgrade
1865
-
1866
- $plugin_is_active = is_plugin_active($plugin_id);
1867
-
1868
- $ret = $upgrader->upgrade($plugin_id);
1869
-
1870
- if($plugin_is_active){
1871
- activate_plugin($plugin_id);
1872
- }
1873
-
1874
- }else{ //install
1875
- $ret = $upgrader->install($url);
1876
- }
1877
-
1878
- return $ret;
1879
-
1880
- }
1881
-
1882
- public function activate_plugin(){
1883
-
1884
- $error = '';
1885
-
1886
- if(isset($_POST['nonce']) && isset($_POST['plugin_id']) && $_POST['nonce'] == wp_create_nonce('activate_' . $_POST['plugin_id'])){
1887
-
1888
- $plugin_id = $_POST['plugin_id'];
1889
-
1890
- // Deactivate any embedded version
1891
- $plugin_slug = dirname($plugin_id);
1892
- $active_plugins = get_option('active_plugins');
1893
- foreach($active_plugins as $plugin){
1894
- $wp_plugin_slug = dirname($plugin);
1895
- if($wp_plugin_slug == $plugin_slug . '-embedded'){
1896
- deactivate_plugins(array($plugin));
1897
- break;
1898
- }
1899
- }
1900
-
1901
- //prevent redirects
1902
- add_filter('wp_redirect', '__return_false', 10000);
1903
-
1904
- $return = activate_plugin($plugin_id);
1905
-
1906
- if(is_wp_error($return)){
1907
- $error = $return->get_error_message();
1908
- }
1909
-
1910
- }else{
1911
- $error = 'error';
1912
- }
1913
-
1914
- $ret = array('error' => $error);
1915
-
1916
- echo json_encode($ret);
1917
- exit;
1918
-
1919
- }
1920
-
1921
- public function custom_plugins_api_call($false, $action, $args){
1922
-
1923
- if($action == 'plugin_information'){
1924
-
1925
- $slug = $args->slug;
1926
-
1927
- foreach($this->settings['repositories'] as $repository_id => $repository){
1928
-
1929
- if(!$this->repository_has_valid_subscription($repository_id)){
1930
- $site_key = false;
1931
- }else{
1932
- $site_key = $repository['subscription']['key'];
1933
- }
1934
-
1935
- foreach($repository['data']['packages'] as $package){
1936
-
1937
- foreach($package['products'] as $product){
1938
-
1939
- foreach($product['plugins'] as $plugin_slug){
1940
-
1941
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
1942
-
1943
- if($download['slug'] == $slug){
1944
-
1945
- $res = new stdClass();
1946
- $res->external = true;
1947
-
1948
- $res->name = $download['name'];
1949
- $res->slug = $slug;
1950
- $res->version = $download['version'];
1951
- $res->author = '';
1952
- $res->author_profile = '';
1953
- $res->last_updated = $download['date'];
1954
- //$res->homepage = $download['url'];
1955
-
1956
- if($site_key){
1957
- $res->download_link = $this->append_site_key_to_download_url($download['url'], $site_key, $repository_id);
1958
- }else{
1959
- // if(!empty($download['free-on-wporg'])
1960
- return false; //try somewhere else. e.g. wordpress.org
1961
- }
1962
-
1963
- $res->homepage = $repository['data']['url'];
1964
- $res->sections = array('Description' => $download['description'], 'Changelog' => $download['changelog']);
1965
-
1966
- return $res;
1967
-
1968
- }
1969
-
1970
- }
1971
-
1972
- }
1973
-
1974
- }
1975
-
1976
- }
1977
-
1978
- }
1979
-
1980
- return $false;
1981
-
1982
- }
1983
-
1984
- public function plugins_upgrade_check($update_plugins){
1985
-
1986
- if(!empty($this->settings['repositories'])){
1987
-
1988
- $plugins = get_plugins();
1989
-
1990
- foreach($plugins as $plugin_id => $plugin){
1991
-
1992
- $slug = dirname($plugin_id);
1993
- if(empty($slug)) continue;
1994
-
1995
- $version = $plugin['Version'];
1996
- $name = $plugin['Name'];
1997
-
1998
- foreach($this->settings['repositories'] as $repository_id => $repository){
1999
-
2000
-
2001
- if(!$this->repository_has_valid_subscription($repository_id)){
2002
- $site_key = false;
2003
- }else{
2004
- $site_key = $repository['subscription']['key'];
2005
- //$subscription_type = $this->get_subscription_type_for_repository($repository_id);
2006
- }
2007
-
2008
- foreach($repository['data']['packages'] as $package){
2009
-
2010
- foreach($package['products'] as $product){
2011
-
2012
- foreach($product['plugins'] as $plugin_slug){
2013
-
2014
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
2015
-
2016
- if(!empty($download['free-on-wporg'])) {
2017
- continue;
2018
- }
2019
-
2020
- if(empty($update_plugins->response[$plugin_id]) && ($download['slug'] == $slug || $download['name'] == $name ) && version_compare($download['version'], $version, '>')){
2021
-
2022
- $response = new stdClass();
2023
- $response->id = 0;
2024
- $response->slug = $slug;
2025
- $response->plugin = $plugin_id;
2026
- $response->new_version = $download['version'];
2027
- $response->upgrade_notice = '';
2028
- $response->url = $download['url'];
2029
- if($site_key){
2030
- $response->package = $this->append_site_key_to_download_url($download['url'], $site_key, $repository_id);
2031
- }
2032
- $update_plugins->checked[$plugin_id] = $version;
2033
- $update_plugins->response[$plugin_id] = $response;
2034
-
2035
- }
2036
-
2037
- }
2038
-
2039
- }
2040
-
2041
- }
2042
-
2043
- }
2044
-
2045
- }
2046
-
2047
- }
2048
-
2049
- return $update_plugins;
2050
-
2051
- }
2052
-
2053
- public function setup_plugins_page_notices(){
2054
-
2055
- $plugins = get_plugins();
2056
-
2057
- foreach($plugins as $plugin_id => $plugin){
2058
-
2059
- $slug = dirname($plugin_id);
2060
- if(empty($slug)) continue;
2061
-
2062
- $name = $plugin['Name'];
2063
-
2064
- foreach($this->settings['repositories'] as $repository_id => $repository){
2065
-
2066
- if(!$this->repository_has_valid_subscription($repository_id)){
2067
- $site_key = false;
2068
- }else{
2069
- $site_key = $repository['subscription']['key'];
2070
- }
2071
-
2072
- foreach($repository['data']['packages'] as $package){
2073
-
2074
- foreach($package['products'] as $product){
2075
-
2076
- foreach($product['plugins'] as $plugin_slug){
2077
-
2078
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
2079
-
2080
- if(!empty($download['free-on-wporg'])) {
2081
- continue;
2082
- }
2083
-
2084
- if( $download['slug'] == $slug || $download['name'] == $name ){
2085
-
2086
- if( !$site_key || !$this->plugin_is_registered($repository_id, $download['slug']) ){
2087
- add_action( "after_plugin_row_" . $plugin_id, array($this, 'show_purchase_notice_under_plugin'), 10, 3 );
2088
- }
2089
-
2090
- }
2091
-
2092
- }
2093
-
2094
- }
2095
-
2096
- }
2097
-
2098
- }
2099
-
2100
- }
2101
-
2102
- }
2103
-
2104
- public function show_purchase_notice_under_plugin($plugin_file, $plugin_data, $status){
2105
-
2106
- $wp_list_table = _get_list_table('WP_Plugins_List_Table');
2107
- $wp_version = preg_replace( '/-(.+)$/', '', $GLOBALS['wp_version'] );
2108
-
2109
- if( version_compare( $wp_version, '4.6', '>=' ) ){
2110
-
2111
- ?>
2112
- <tr class="plugin-update-tr installer-plugin-update-tr">
2113
- <td colspan="<?php echo $wp_list_table->get_column_count(); ?>" class="plugin-update colspanchange">
2114
- <div class="notice inline notice-warning notice-alt">
2115
- <p class="installer-q-icon">
2116
- <?php printf( __('You must have a valid subscription in order to get upgrades or support for this plugin. %sPurchase a subscription or enter an existing site key%s.', 'installer'),
2117
- '<a href="' . $this->menu_url() . '">', '</a>'); ?>
2118
- </p>
2119
- </div>
2120
- </td>
2121
- </tr>
2122
- <?php
2123
-
2124
- } else {
2125
-
2126
- ?>
2127
- <tr class="plugin-update-tr">
2128
- <td colspan="<?php echo $wp_list_table->get_column_count(); ?>" class="plugin-update colspanchange">
2129
- <div class="update-message installer-q-icon">
2130
- <?php printf( __('You must have a valid subscription in order to get upgrades or support for this plugin. %sPurchase a subscription or enter an existing site key%s.', 'installer'),
2131
- '<a href="' . $this->menu_url() . '">', '</a>'); ?>
2132
- </div>
2133
- </td>
2134
- </tr>
2135
- <?php
2136
-
2137
- }
2138
-
2139
- }
2140
-
2141
- public function localize_strings(){
2142
-
2143
- if(!empty($this->settings['repositories'])){
2144
- foreach($this->settings['repositories'] as $repository_id => $repository){
2145
- //set name as call2action when don't have any
2146
- //products
2147
- foreach($repository['data']['packages'] as $package_id => $package){
2148
- foreach($package['products'] as $product_id => $product){
2149
- if(empty($product['call2action'])){
2150
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['call2action'] = $product['name'];
2151
- }
2152
-
2153
- foreach($product['upgrades'] as $idx => $upg){
2154
- if(empty($upg['call2action'])){
2155
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['upgrades'][$idx]['call2action'] = $upg['name'];
2156
- }
2157
- }
2158
-
2159
- foreach($product['renewals'] as $idx => $rnw){
2160
- if(empty($rnw['call2action'])){
2161
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['renewals'][$idx]['call2action'] = $rnw['name'];
2162
- }
2163
-
2164
- }
2165
-
2166
- }
2167
- }
2168
- }
2169
- }
2170
-
2171
- global $sitepress;
2172
- if(is_null($sitepress)){
2173
- return;
2174
- }
2175
-
2176
- // default strings are always in English
2177
- $user_admin_language = $sitepress->get_admin_language();
2178
-
2179
- if($user_admin_language != 'en'){
2180
- foreach($this->settings['repositories'] as $repository_id => $repository){
2181
-
2182
- $localization = $repository['data']['localization'];
2183
-
2184
- //packages
2185
- foreach($repository['data']['packages'] as $package_id => $package){
2186
-
2187
- if( isset($localization['packages'][$package_id]['name'][$user_admin_language]) ){
2188
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['name'] = $localization['packages'][$package_id]['name'][$user_admin_language];
2189
- }
2190
- if( isset($localization['packages'][$package_id]['description'][$user_admin_language]) ){
2191
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['description'] = $localization['packages'][$package_id]['description'][$user_admin_language];
2192
- }
2193
-
2194
- }
2195
-
2196
- //products
2197
- foreach($repository['data']['packages'] as $package_id => $package){
2198
- foreach($package['products'] as $product_id => $product){
2199
-
2200
- if( isset($localization['products'][$product_id]['name'][$user_admin_language]) ){
2201
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['name']
2202
- = $localization['products'][$product_id]['name'][$user_admin_language];
2203
- }
2204
- if( isset($localization['products'][$product_id]['description'][$user_admin_language]) ){
2205
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['description']
2206
- = $localization['products'][$product_id]['description'][$user_admin_language];
2207
- }
2208
- if( isset($localization['products'][$product_id]['call2action'][$user_admin_language]) ){
2209
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['name']
2210
- = $localization['products'][$product_id]['call2action'][$user_admin_language];
2211
- }
2212
-
2213
-
2214
- }
2215
- }
2216
-
2217
- //subscription info
2218
- if(isset($repository['data']['subscriptions_meta']['expiration'])){
2219
- foreach($repository['data']['subscriptions_meta']['expiration'] as $subscription_id => $note){
2220
- if(isset($localization['subscriptions-notes'][$subscription_id]['expiration-warning'][$user_admin_language])){
2221
- $this->settings['repositories'][$repository_id]['data']['subscriptions_meta']['expiration'][$subscription_id]['warning_message']
2222
- = $localization['subscriptions-notes'][$subscription_id]['expiration-warning'][$user_admin_language];
2223
- }
2224
- }
2225
- }
2226
-
2227
- }
2228
- }
2229
-
2230
- }
2231
-
2232
- public function get_matching_cp($repository, $args = array()){
2233
- $match = false;
2234
-
2235
-
2236
- $cp_name = $cp_author = false;
2237
-
2238
- if(isset($this->config['src_name']) && isset($this->config['src_author'])){
2239
-
2240
- $cp_name = $this->config['src_name'];
2241
- $cp_author = $this->config['src_author'];
2242
-
2243
- }elseif(isset($args['src_name']) && isset($args['src_author'])){
2244
-
2245
- $cp_name = $args['src_name'];
2246
- $cp_author = $args['src_author'];
2247
-
2248
- }
2249
-
2250
- if(isset($repository['data']['marketing_cp'])){
2251
-
2252
- foreach($repository['data']['marketing_cp'] as $cp){
2253
-
2254
- if(!empty($cp['exp']) && time() > $cp['exp']){
2255
- continue;
2256
- }
2257
-
2258
- //Use theme_name for plugins too
2259
- if(!empty($cp['theme_name'])){
2260
- if($cp['author_name'] == $cp_author && $cp['theme_name'] == $cp_name){
2261
- $match = $cp;
2262
- continue;
2263
- }
2264
- }else{
2265
- if($cp['author_name'] == $cp_author){
2266
- $match = $cp;
2267
- continue;
2268
- }
2269
- }
2270
-
2271
- }
2272
-
2273
- }
2274
-
2275
- return $match;
2276
- }
2277
-
2278
- public function set_filtered_prices($args = array()){
2279
-
2280
- foreach($this->settings['repositories'] as $repository_id => $repository){
2281
-
2282
- $match = $this->get_matching_cp($repository, $args);
2283
-
2284
- if(empty($match)) continue;
2285
-
2286
- foreach($repository['data']['packages'] as $package_id => $package){
2287
-
2288
- foreach($package['products'] as $product_id => $product){
2289
-
2290
- if($match['dtp'] == '%'){
2291
- $fprice = round( $product['price'] * (1 - $match['amt']/100), 2 );
2292
- $fprice = $fprice != round($fprice) ? sprintf('%.2f', $fprice) : round($fprice, 0);
2293
- }elseif($match['dtp'] == '-'){
2294
- $fprice = $product['price'] - $match['amt'];
2295
- }else{
2296
- $fprice = $product['price'];
2297
- }
2298
-
2299
- if($fprice){
2300
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['price_disc'] = $fprice;
2301
-
2302
- $url_glue = false !== strpos($this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['url'], '?') ? '&' : '?';
2303
- $cpndata = base64_encode(json_encode(array('theme_author' => $match['author_name'], 'theme_name' => $match['theme_name'], 'vlc' => $match['vlc'])));
2304
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['url'] .= $url_glue . 'cpn=' . $cpndata;
2305
-
2306
- foreach($product['upgrades'] as $upgrade_id => $upgrade){
2307
-
2308
- $fprice = false;
2309
- if($match['dtp'] == '%'){
2310
- $fprice = round( $upgrade['price'] * (1 - $match['amt']/100), 2 );
2311
- $fprice = $fprice != round($fprice) ? sprintf('%.2f', $fprice) : round($fprice, 0);
2312
- }elseif($match['dtp'] == '-'){
2313
- $fprice = $upgrade['price'] - $match['amt'];
2314
- }
2315
- if($fprice){
2316
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['upgrades'][$upgrade_id]['price_disc'] = $fprice;
2317
- $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['upgrades'][$upgrade_id]['url'] .= $url_glue . 'cpn=' . $cpndata;
2318
- }
2319
-
2320
-
2321
- }
2322
-
2323
- }
2324
-
2325
- }
2326
-
2327
- }
2328
-
2329
- }
2330
-
2331
- }
2332
-
2333
- public function set_hierarchy_and_order(){
2334
-
2335
- //2 levels
2336
- if(!empty($this->settings['repositories'])) {
2337
- foreach ($this->settings['repositories'] as $repository_id => $repository) {
2338
-
2339
- if( empty( $repository['data']['packages'] ) ) continue;
2340
-
2341
- $all_packages = $repository['data']['packages'];
2342
- $ordered_packages = array();
2343
-
2344
- //backward compatibility - 'order'
2345
- foreach($all_packages as $k => $v){
2346
- if(!isset($v['order'])){
2347
- $all_packages[$k]['order'] = 0;
2348
- }
2349
- }
2350
-
2351
- //select parents
2352
- foreach ($all_packages as $package_id => $package) {
2353
- if(empty($package['parent'])){
2354
- $ordered_packages[$package_id] = $package;
2355
- }
2356
- }
2357
-
2358
- //add sub-packages
2359
- foreach($all_packages as $package_id => $package){
2360
- if(!empty($package['parent'])) {
2361
- if(isset($ordered_packages[$package['parent']])){
2362
- $ordered_packages[$package['parent']]['sub-packages'][$package_id] = $package;
2363
- }
2364
- }
2365
- }
2366
-
2367
- // order parents
2368
- usort($ordered_packages, array($this, '_order_packages_callback'));
2369
- //order sub-packages
2370
- foreach($ordered_packages as $package_id => $package){
2371
- if(!empty($package['sub-packages'])) {
2372
- usort($ordered_packages[$package_id]['sub-packages'], create_function('$a, $b', 'return $a[\'order\'] > $b[\'order\'];'));
2373
- }
2374
- }
2375
-
2376
- $this->settings['repositories'][$repository_id]['data']['packages'] = $ordered_packages;
2377
-
2378
-
2379
- }
2380
- }
2381
-
2382
-
2383
- }
2384
-
2385
- public function _order_packages_callback($a, $b){
2386
- return $a['order'] > $b['order'];
2387
- }
2388
-
2389
- public function get_support_tag_by_name( $name, $repository ){
2390
-
2391
- if( is_array($this->settings['repositories'][$repository]['data']['support_tags'] )){
2392
- foreach( $this->settings['repositories'][$repository]['data']['support_tags'] as $support_tag){
2393
- if( $support_tag['name'] == $name ){
2394
- return $support_tag['url'];
2395
- }
2396
- }
2397
- }
2398
-
2399
- return false;
2400
- }
2401
-
2402
- public function plugin_upgrade_custom_errors(){
2403
-
2404
- if ( isset($_REQUEST['action']) ) {
2405
-
2406
- $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
2407
-
2408
- //bulk mode
2409
- if('update-selected' == $action) {
2410
-
2411
- global $plugins;
2412
-
2413
- if(isset($plugins) && is_array($plugins)) {
2414
-
2415
- foreach ($plugins as $k => $plugin) {
2416
- $plugin_repository = false;
2417
-
2418
- $wp_plugin_slug = dirname($plugin);
2419
-
2420
- foreach ($this->settings['repositories'] as $repository_id => $repository) {
2421
-
2422
- foreach ($repository['data']['packages'] as $package) {
2423
-
2424
- foreach ($package['products'] as $product) {
2425
-
2426
- foreach ($product['plugins'] as $plugin_slug) {
2427
-
2428
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
2429
-
2430
- if ($download['slug'] == $wp_plugin_slug) {
2431
- $plugin_repository = $repository_id;
2432
- $product_name = $repository['data']['product-name'];
2433
- $plugin_name = $download['name'];
2434
- $free_on_wporg = !empty($download['free-on-wporg']);
2435
- break;
2436
- }
2437
-
2438
- }
2439
-
2440
- }
2441
-
2442
- }
2443
-
2444
- }
2445
-
2446
- if ($plugin_repository) {
2447
-
2448
- //validate subscription
2449
- static $sub_cache = array();
2450
-
2451
- if(empty($sub_cache[$plugin_repository])){
2452
- $site_key = $this->get_repository_site_key($plugin_repository);
2453
- if ($site_key) {
2454
- $subscription_data = $this->fetch_subscription_data( $plugin_repository, $site_key, self::SITE_KEY_VALIDATION_SOURCE_REVALIDATION );
2455
- }
2456
-
2457
- $sub_cache[$plugin_repository]['site_key'] = $site_key;
2458
- $sub_cache[$plugin_repository]['subscription_data'] = isset($subscription_data) ? $subscription_data : false;
2459
- }else{
2460
-
2461
- $site_key = $sub_cache[$plugin_repository]['site_key'];
2462
- $subscription_data = $sub_cache[$plugin_repository]['subscription_data'];
2463
-
2464
- }
2465
-
2466
- if(!$site_key && !empty($free_on_wporg)){ // allow the download from wp.org
2467
- continue;
2468
- }
2469
-
2470
- if (empty($site_key) || empty($subscription_data)) {
2471
-
2472
-
2473
- $error_message = sprintf(__("%s cannot update because your site's registration is not valid. Please %sregister %s%s again for this site first.", 'installer'),
2474
- '<strong>' . $plugin_name . '</strong>', '<a target="_top" href="' . $this->menu_url() . '&validate_repository=' . $plugin_repository .
2475
- '#repository-' . $plugin_repository . '">', $product_name, '</a>');
2476
-
2477
- echo '<div class="updated error"><p>' . $error_message . '</p></div>';
2478
-
2479
- unset($plugins[$k]);
2480
-
2481
-
2482
- }
2483
-
2484
- }
2485
-
2486
- }
2487
-
2488
- }
2489
-
2490
- }
2491
-
2492
-
2493
- if( 'upgrade-plugin' == $action || 'update-plugin' == $action ) {
2494
-
2495
- $plugin = isset($_REQUEST['plugin']) ? trim($_REQUEST['plugin']) : '';
2496
-
2497
- $wp_plugin_slug = dirname($plugin);
2498
-
2499
- $plugin_repository = false;
2500
-
2501
- foreach($this->settings['repositories'] as $repository_id => $repository){
2502
-
2503
- foreach($repository['data']['packages'] as $package){
2504
-
2505
- foreach($package['products'] as $product){
2506
-
2507
- foreach($product['plugins'] as $plugin_slug){
2508
- $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
2509
-
2510
- //match by folder, will change to match by name and folder
2511
- if($download['slug'] == $wp_plugin_slug) {
2512
- $plugin_repository = $repository_id;
2513
- $product_name = $repository['data']['product-name'];
2514
- $plugin_name = $download['name'];
2515
- $free_on_wporg = !empty($download['free-on-wporg']);
2516
- break;
2517
- }
2518
-
2519
- }
2520
-
2521
- }
2522
-
2523
- }
2524
-
2525
- }
2526
-
2527
- if($plugin_repository) {
2528
-
2529
- //validate subscription
2530
- $site_key = $this->get_repository_site_key($plugin_repository);
2531
- if ($site_key) {
2532
- $subscription_data = $this->fetch_subscription_data( $plugin_repository, $site_key, self::SITE_KEY_VALIDATION_SOURCE_REVALIDATION );
2533
- }
2534
-
2535
- if ( (empty($site_key) || empty($subscription_data)) && empty($free_on_wporg)) {
2536
-
2537
- $error_message = sprintf(__("%s cannot update because your site's registration is not valid. Please %sregister %s%s again for this site first.", 'installer'),
2538
- '<strong>'.$plugin_name . '</strong>', '<a href="' . $this->menu_url() . '&validate_repository=' . $plugin_repository .
2539
- '#repository-' . $plugin_repository . '">', $product_name, '</a>');
2540
-
2541
- if(defined('DOING_AJAX')){ //WP 4.2
2542
-
2543
- $status = array(
2544
- 'update' => 'plugin',
2545
- 'plugin' => $plugin,
2546
- 'slug' => sanitize_key( $_POST['slug'] ),
2547
- 'oldVersion' => '',
2548
- 'newVersion' => '',
2549
- );
2550
-
2551
- $status['errorCode'] = 'wp_installer_invalid_subscription';
2552
- $status['error'] = $error_message;
2553
-
2554
- wp_send_json_error( $status );
2555
-
2556
- } else { // WP 4.1.1
2557
- echo '<div class="updated error"><p>' . $error_message . '</p></div>';
2558
-
2559
-
2560
- echo '<div class="wrap">';
2561
- echo '<h2>' . __('Update Plugin') . '</h2>';
2562
- echo '<a href="' . admin_url('plugins.php') . '">' . __('Return to the plugins page') . '</a>';
2563
- echo '</div>';
2564
- require_once(ABSPATH . 'wp-admin/admin-footer.php');
2565
- exit;
2566
-
2567
- }
2568
-
2569
- }
2570
-
2571
-
2572
- }
2573
-
2574
- }
2575
- }
2576
-
2577
- }
2578
-
2579
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class WP_Installer{
4
+ protected static $_instance = null;
5
+
6
+ public $settings = array();
7
+
8
+ private $repositories = array();
9
+
10
+ protected $api_debug = '';
11
+
12
+ private $config = array();
13
+
14
+ protected $_plugins_renew_warnings = array();
15
+
16
+ protected $_gz_on = false;
17
+
18
+ private $admin_messages = array();
19
+
20
+ private $_using_icl = false;
21
+ private $_wpml_version = false;
22
+
23
+ private $package_source = array();
24
+
25
+ const SITE_KEY_VALIDATION_SOURCE_OTHER = 0;
26
+ const SITE_KEY_VALIDATION_SOURCE_DOWNLOAD_SPECIFIC = 1;
27
+ const SITE_KEY_VALIDATION_SOURCE_DOWNLOAD_REPORT = 2;
28
+ const SITE_KEY_VALIDATION_SOURCE_REGISTRATION = 3;
29
+ const SITE_KEY_VALIDATION_SOURCE_REVALIDATION = 4;
30
+ const SITE_KEY_VALIDATION_SOURCE_UPDATES_CHECK = 5;
31
+
32
+ public $dependencies;
33
+
34
+ public static function instance() {
35
+
36
+ if ( is_null( self::$_instance ) ) {
37
+ self::$_instance = new self();
38
+ }
39
+
40
+ return self::$_instance;
41
+ }
42
+
43
+ public function __construct(){
44
+
45
+ if(!is_admin() || !is_user_logged_in()) return; //Only for admin
46
+
47
+ $this->_gz_on = function_exists('gzuncompress') && function_exists('gzcompress');
48
+ $this->settings = $this->get_settings();
49
+
50
+ add_action('admin_notices', array($this, 'show_site_key_nags'));
51
+
52
+ add_action('admin_notices', array($this, 'show_admin_messages'));
53
+
54
+ add_action('admin_init', array($this, 'load_embedded_plugins'), 0);
55
+
56
+ add_action('admin_menu', array($this, 'menu_setup'));
57
+ add_action('network_admin_menu', array($this, 'menu_setup'));
58
+
59
+ if(defined('DOING_AJAX') && isset($_POST['action']) && $_POST['action'] == 'installer_download_plugin'){
60
+ add_filter( 'site_transient_update_plugins', array( $this, 'plugins_upgrade_check') );
61
+ }
62
+ add_filter('plugins_api', array( $this, 'custom_plugins_api_call'), 10, 3);
63
+ add_filter('pre_set_site_transient_update_plugins', array( $this, 'plugins_upgrade_check'));
64
+
65
+ // register repositories
66
+ $this->load_repositories_list();
67
+
68
+ if( empty($this->settings['last_repositories_update']) || time() - $this->settings['last_repositories_update'] > 86400
69
+ || ( isset($_GET['force-check']) && $_GET['force-check'] == 1 ) ){
70
+ $this->refresh_repositories_data();
71
+ }
72
+
73
+ // default config
74
+ $this->config['plugins_install_tab'] = false;
75
+
76
+ add_action('init', array($this, 'init'));
77
+
78
+ //add_filter('wp_installer_buy_url', array($this, 'append_parameters_to_buy_url'));
79
+
80
+ add_action('init', array($this,'load_locale'));
81
+
82
+ }
83
+
84
+ public function get_repositories() {
85
+
86
+ return $this->repositories;
87
+
88
+ }
89
+
90
+ public function set_config($key, $value){
91
+
92
+ $this->config[$key] = $value;
93
+
94
+ }
95
+
96
+ public function init(){
97
+ global $pagenow;
98
+
99
+ $this->dependencies = new Installer_Dependencies;
100
+
101
+ if(empty($this->settings['_pre_1_0_clean_up'])) {
102
+ $this->_pre_1_0_clean_up();
103
+ }
104
+
105
+ $this->settings = $this->_old_products_format_backwards_compatibility($this->settings);
106
+
107
+ if ( !function_exists( 'get_plugins' ) ) {
108
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
109
+ }
110
+
111
+ $this->_using_icl = function_exists('wpml_site_uses_icl') && wpml_site_uses_icl();
112
+ $this->_wpml_version = defined('ICL_SITEPRESS_VERSION') ? ICL_SITEPRESS_VERSION : '';
113
+
114
+ wp_enqueue_script('installer-admin', $this->res_url() . '/res/js/admin.js', array('jquery'), $this->version());
115
+ wp_enqueue_style('installer-admin', $this->res_url() . '/res/css/admin.css', array(), $this->version());
116
+
117
+ $translation_array = array(
118
+ 'installing' => __( 'Installing %s', 'installer' ),
119
+ 'updating' => __( 'Updating %s', 'installer' ),
120
+ 'activating' => __( 'Activating %s', 'installer' )
121
+ );
122
+
123
+ wp_localize_script( 'installer-admin', 'installer_strings', $translation_array );
124
+
125
+ if($pagenow == 'plugins.php'){
126
+ add_action('admin_notices', array($this, 'setup_plugins_page_notices'));
127
+ add_action('admin_notices', array($this, 'setup_plugins_renew_warnings'), 10);
128
+ add_action('admin_notices', array($this, 'queue_plugins_renew_warnings'), 20);
129
+
130
+ add_action('admin_init', array($this, 'setup_plugins_action_links'));
131
+
132
+ }
133
+
134
+ if($this->is_repositories_page()){
135
+ add_action('admin_init', array($this, 'validate_repository_subscription'));
136
+ }
137
+
138
+ if(defined('DOING_AJAX')){
139
+ add_action('wp_ajax_save_site_key', array($this, 'save_site_key'));
140
+ add_action('wp_ajax_remove_site_key', array($this, 'remove_site_key_ajax'));
141
+ add_action('wp_ajax_update_site_key', array($this, 'update_site_key'));
142
+
143
+ add_action('wp_ajax_installer_download_plugin', array($this, 'download_plugin_ajax_handler'));
144
+ add_action('wp_ajax_installer_activate_plugin', array($this, 'activate_plugin'));
145
+
146
+ add_action('wp_ajax_installer_dismiss_nag', array($this, 'dismiss_nag'));
147
+ }
148
+
149
+ if($pagenow == 'update.php'){
150
+ if(isset($_GET['action']) && $_GET['action'] == 'update-selected'){
151
+ add_action('admin_head', array($this, 'plugin_upgrade_custom_errors')); //iframe/bulk
152
+ }else{
153
+ add_action('all_admin_notices', array($this, 'plugin_upgrade_custom_errors')); //regular/singular
154
+ }
155
+ }
156
+
157
+ // WP 4.2
158
+ if(defined('DOING_AJAX')){
159
+ add_action('wp_ajax_update-plugin', array($this, 'plugin_upgrade_custom_errors'), 0); // high priority, before WP
160
+ }
161
+
162
+ //Include theme support
163
+ include_once $this->plugin_path() . '/includes/class-installer-theme.php';
164
+
165
+ // Extra information about the source of Installer
166
+ $package_source_file = $this->plugin_path() . '/installer-source.json';
167
+ if( file_exists( $package_source_file ) ){
168
+ $this->package_source = json_decode( file_get_contents( $package_source_file ) );
169
+ }
170
+ }
171
+
172
+ protected function log($message){
173
+ if( defined('WPML_INSTALLER_LOGGING') && WPML_INSTALLER_LOGGING ){
174
+ if($fh = @fopen( $this->plugin_path() . '/installer.log', 'a' )){
175
+ fwrite($fh, current_time( 'mysql' ) . "\t" . $message . "\n");
176
+ }
177
+ }
178
+ }
179
+
180
+ public function register_admin_message($text, $type = 'updated'){
181
+ $this->admin_messages[] = array('text' => $text, 'type' => $type);
182
+ }
183
+
184
+ public function show_admin_messages(){
185
+ if(!empty($this->admin_messages)){
186
+ $types = array( 'error', 'updated', 'notice' );
187
+ foreach($this->admin_messages as $message){
188
+ $class = in_array( $message['type'], $types ) ? $message['type'] : 'updated';
189
+ ?>
190
+ <div class="<?php echo $class ?>">
191
+ <p>
192
+ <?php echo $message['text'] ?>
193
+ </p>
194
+ </div>
195
+ <?php
196
+ }
197
+ }
198
+ }
199
+
200
+ public function load_locale(){
201
+ $locale = get_locale();
202
+ $locale = apply_filters( 'plugin_locale', $locale, 'installer' );
203
+ $mo_file = $this->plugin_path() . '/locale/installer-' . $locale . '.mo';
204
+ if(file_exists($mo_file)){
205
+ load_textdomain( 'installer', $mo_file );
206
+ }
207
+ }
208
+
209
+ public function load_embedded_plugins(){
210
+ if(file_exists($this->plugin_path() . '/embedded-plugins' )) {
211
+ include_once $this->plugin_path() . '/embedded-plugins/embedded-plugins.class.php';
212
+ $this->installer_embedded_plugins = new Installer_Embedded_Plugins();
213
+ }
214
+ }
215
+
216
+ public function menu_setup(){
217
+ global $pagenow;
218
+
219
+ if(is_multisite() && !is_network_admin()){
220
+ $this->menu_multisite_redirect();
221
+ add_options_page(__('Installer', 'installer'), __('Installer', 'installer'), 'manage_options', 'installer', array($this, 'show_products')) ;
222
+ }else{
223
+ if($this->config['plugins_install_tab'] && is_admin() && $pagenow == 'plugin-install.php'){
224
+ // Default GUI, under Plugins -> Install
225
+ add_filter('install_plugins_tabs', array($this, 'add_install_plugins_tab'));
226
+ add_action('install_plugins_commercial', array($this, 'show_products'));
227
+ }
228
+ }
229
+
230
+ }
231
+
232
+ public function menu_url(){
233
+ if(is_multisite()){
234
+ if(is_network_admin()){
235
+ $url = network_admin_url('plugin-install.php?tab=commercial');
236
+ }else{
237
+ $url = admin_url('options-general.php?page=installer');
238
+ }
239
+ }else{
240
+ $url = admin_url('plugin-install.php?tab=commercial');
241
+ }
242
+ return $url;
243
+ }
244
+
245
+ private function menu_multisite_redirect(){
246
+ global $pagenow;
247
+
248
+ if($pagenow == 'plugin-install.php' && isset($_GET['tab']) && $_GET['tab'] == 'commercial'){
249
+ wp_redirect($this->menu_url());
250
+ exit;
251
+ }
252
+
253
+ }
254
+
255
+ private function _pre_1_0_clean_up(){
256
+ global $wpdb;
257
+
258
+ if(!defined('WPRC_VERSION')){
259
+ $old_tables = array(
260
+ $wpdb->prefix . 'wprc_cached_requests',
261
+ $wpdb->prefix . 'wprc_extension_types',
262
+ $wpdb->prefix . 'wprc_extensions',
263
+ $wpdb->prefix . 'wprc_repositories',
264
+ $wpdb->prefix . 'wprc_repositories_relationships',
265
+ );
266
+
267
+ foreach($old_tables as $table){
268
+ $wpdb->query(sprintf("DROP TABLE IF EXISTS %s", $table));
269
+ }
270
+
271
+ }
272
+
273
+ $this->settings['_pre_1_0_clean_up'] = true;
274
+ $this->save_settings();
275
+ }
276
+
277
+ public function setup_plugins_action_links(){
278
+
279
+ $plugins = get_plugins();
280
+
281
+ $repositories_plugins = array();
282
+
283
+ if( !empty($this->settings['repositories']) ) {
284
+
285
+ foreach ( $this->settings['repositories'] as $repository_id => $repository ) {
286
+
287
+ foreach ( $repository['data']['packages'] as $package ) {
288
+
289
+ foreach ( $package['products'] as $product ) {
290
+
291
+ foreach ( $product['plugins'] as $plugin_slug ) {
292
+
293
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
294
+
295
+ if ( !isset($repositories_plugins[$repository_id][$download['slug']]) ) {
296
+ $repositories_plugins[$repository_id][$download['slug']] = array(
297
+ 'name' => $download['name'],
298
+ 'registered' => $this->plugin_is_registered( $repository_id, $download['slug'] ) ? 1 : 0
299
+ );
300
+ }
301
+
302
+ }
303
+
304
+ }
305
+
306
+ }
307
+
308
+ foreach ( $plugins as $plugin_id => $plugin ) {
309
+
310
+ $wp_plugin_slug = dirname( $plugin_id );
311
+ if ( empty($wp_plugin_slug) ) {
312
+ $wp_plugin_slug = basename( $plugin_id, '.php' );
313
+ }
314
+
315
+ foreach ( $repositories_plugins as $repository_id => $r_plugins ) {
316
+
317
+ foreach ( $r_plugins as $slug => $r_plugin ) {
318
+
319
+ if ( $wp_plugin_slug == $slug || $r_plugin['name'] == $plugin['Name'] || $r_plugin['name'] == $plugin['Title'] ) { //match order: slug, name, title
320
+
321
+ if ( $r_plugin['registered'] ) {
322
+ add_filter( 'plugin_action_links_' . $plugin_id, array($this, 'plugins_action_links_registered') );
323
+ } else {
324
+ add_filter( 'plugin_action_links_' . $plugin_id, array($this, 'plugins_action_links_not_registered') );
325
+ }
326
+
327
+ }
328
+
329
+ }
330
+
331
+ }
332
+
333
+
334
+ }
335
+
336
+ }
337
+ }
338
+
339
+ }
340
+
341
+ public function plugins_action_links_registered($links){
342
+ $links[] = '<a href="' . $this->menu_url() . '">' . __('Registered', 'installer') . '</a>';
343
+ return $links;
344
+ }
345
+
346
+ public function plugins_action_links_not_registered($links){
347
+ $links[] = '<a href="' . $this->menu_url() . '">' . __('Register', 'installer') . '</a>';
348
+ return $links;
349
+ }
350
+
351
+ public function plugin_is_registered($repository_id, $slug){
352
+
353
+ $registered = false;
354
+
355
+ if( $this->repository_has_valid_subscription($repository_id) ){
356
+
357
+ $subscription_type = $this->get_subscription_type_for_repository($repository_id);
358
+ $r_plugins = array();
359
+
360
+ foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package){
361
+
362
+ foreach($package['products'] as $product){
363
+
364
+ if( $product['subscription_type'] == $subscription_type || $this->have_superior_subscription($subscription_type, $product) ) {
365
+
366
+ foreach ($product['plugins'] as $plugin_slug) {
367
+
368
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
369
+
370
+ if (!isset($rep_plugins[$download['slug']])) {
371
+ $r_plugins[$download['slug']] = $download['slug'];
372
+ }
373
+
374
+ }
375
+
376
+ }
377
+
378
+ }
379
+
380
+ }
381
+
382
+ $registered = isset($r_plugins[$slug]);
383
+
384
+ }
385
+
386
+
387
+ return $registered;
388
+
389
+ }
390
+
391
+ public function version(){
392
+ return WP_INSTALLER_VERSION;
393
+ }
394
+
395
+ public function plugin_path() {
396
+ return untrailingslashit( plugin_dir_path( dirname(__FILE__) ) );
397
+ }
398
+
399
+ public function plugin_url() {
400
+ if(isset($this->config['in_theme_folder']) && !empty($this->config['in_theme_folder'])){
401
+ $url = untrailingslashit(get_template_directory_uri() . '/' . $this->config['in_theme_folder']);
402
+ }else{
403
+ $url = untrailingslashit( plugins_url( '/', dirname(__FILE__) ) );
404
+ }
405
+
406
+ return $url;
407
+ }
408
+
409
+ public function is_repositories_page(){
410
+ global $pagenow;
411
+
412
+ return $pagenow == 'plugin-install.php' && isset($_GET['tab']) && $_GET['tab'] == 'commercial';
413
+ }
414
+
415
+ public function res_url(){
416
+ if(isset($this->config['in_theme_folder']) && !empty($this->config['in_theme_folder'])){
417
+ $url = untrailingslashit(get_template_directory_uri() . '/' . $this->config['in_theme_folder']);
418
+ }else{
419
+ $url = $this->plugin_url();
420
+ }
421
+ return $url;
422
+ }
423
+
424
+ public function save_settings(){
425
+
426
+ $_settings = serialize($this->settings);
427
+ if($this->_gz_on){
428
+ $_settings = gzcompress($_settings);
429
+ }
430
+ $_settings = base64_encode($_settings);
431
+
432
+ update_option( 'wp_installer_settings', $_settings );
433
+
434
+ if( is_multisite() && is_main_site() && isset($this->settings['repositories']) ){
435
+ $network_settings = array();
436
+
437
+ foreach( $this->settings['repositories'] as $rep_id => $repository ){
438
+ if( isset($repository['subscription']) )
439
+ $network_settings[$rep_id] = $repository['subscription'];
440
+ }
441
+
442
+ update_site_option( 'wp_installer_network', $network_settings );
443
+
444
+
445
+ }
446
+
447
+ }
448
+
449
+ public function get_settings($refresh = false){
450
+
451
+ if($refresh || empty($this->settings)){
452
+
453
+ $_settings = get_option('wp_installer_settings');
454
+
455
+
456
+ if (is_array($_settings) || empty($_settings)) { //backward compatibility 1.1
457
+ $this->settings = $_settings;
458
+
459
+ } else {
460
+ $_settings = base64_decode($_settings);
461
+ if ($this->_gz_on) {
462
+ $_settings = gzuncompress($_settings);
463
+ }
464
+ $this->settings = unserialize($_settings);
465
+ }
466
+
467
+ if (is_multisite() && isset($this->settings['repositories'])) {
468
+ $network_settings = maybe_unserialize(get_site_option('wp_installer_network'));
469
+ if ($network_settings) {
470
+ foreach ($this->settings['repositories'] as $rep_id => $repository) {
471
+ if (isset($network_settings[$rep_id])) {
472
+ $this->settings['repositories'][$rep_id]['subscription'] = $network_settings[$rep_id];
473
+ }
474
+ }
475
+ }
476
+ }
477
+
478
+ $this->load_hardcoded_site_keys();
479
+
480
+ $this->settings = $this->_pre_1_6_backwards_compatibility($this->settings);
481
+
482
+ $this->settings = $this->_old_products_format_backwards_compatibility($this->settings);
483
+
484
+ }
485
+
486
+ return $this->settings;
487
+ }
488
+
489
+ private function load_hardcoded_site_keys(){
490
+
491
+ if( !empty( $this->settings['repositories'] ) ) {
492
+ foreach ( $this->settings['repositories'] as $repository_id => $repository ) {
493
+
494
+ if ( $site_key = self::get_repository_hardcoded_site_key( $repository_id ) ) {
495
+
496
+ $site_key_missing = empty($this->settings['repositories'][$repository_id]['subscription']['data']);
497
+ $site_key_changed = !$site_key_missing &&
498
+ $this->settings['repositories'][$repository_id]['subscription']['key'] != $site_key;
499
+
500
+ if ( $site_key_missing || $site_key_changed ) {
501
+
502
+ if ( !function_exists( 'get_plugins' ) ) {
503
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
504
+ }
505
+ $this->load_repositories_list();
506
+ $response = $this->save_site_key(
507
+ array(
508
+ 'repository_id' => $repository_id,
509
+ 'site_key' => $site_key,
510
+ 'return' => true,
511
+ 'nonce' => wp_create_nonce( 'save_site_key_' . $repository_id )
512
+ )
513
+ );
514
+
515
+ if ( !empty($response['error']) ) {
516
+ $this->remove_site_key( $repository_id );
517
+
518
+ $this->admin_messages[] = array(
519
+ 'type' => 'error',
520
+ 'text' => sprintf( __( 'You are using an invalid site key defined as the constant %s (most likely in wp-config.php).
521
+ Please remove it or use the correct value in order to be able to register correctly.', 'installer' ), 'OTGS_INSTALLER_SITE_KEY_' . strtoupper( $repository_id ) )
522
+ );
523
+
524
+ }
525
+
526
+ }
527
+
528
+ }
529
+
530
+ }
531
+ }
532
+
533
+ }
534
+
535
+ public static function get_repository_hardcoded_site_key( $repository_id ){
536
+
537
+ $site_key = false;
538
+
539
+ $site_key_constant = 'OTGS_INSTALLER_SITE_KEY_' . strtoupper( $repository_id );
540
+ if( defined( $site_key_constant ) ){
541
+ $site_key = constant( $site_key_constant );
542
+ }
543
+
544
+ return $site_key;
545
+ }
546
+
547
+ //backward compatibility, will remove 'basename' in version 1.8
548
+ private function _pre_1_6_backwards_compatibility($settings){
549
+
550
+ if( version_compare($this->version(), '1.8', '<') && !empty($settings['repositories']) ){
551
+
552
+ foreach ($settings['repositories'] as $repository_id => $repository) {
553
+
554
+ foreach ($repository['data']['downloads']['plugins'] as $slug => $download) {
555
+
556
+ $settings['repositories'][$repository_id]['data']['downloads']['plugins'][$slug]['slug'] = $download['basename'];
557
+
558
+ }
559
+ }
560
+
561
+ }
562
+
563
+ return $settings;
564
+
565
+ }
566
+
567
+ //backward compatibility - support old products list format (downloads under products instead of global downloads list)
568
+ private function _old_products_format_backwards_compatibility($settings){
569
+
570
+ if( version_compare($this->version(), '1.8', '<') && !empty($settings['repositories']) && empty($this->_old_products_format_backwards_compatibility) ) {
571
+
572
+ foreach ($settings['repositories'] as $repository_id => $repository) {
573
+
574
+ $populate_downloads = false;
575
+
576
+ foreach ($repository['data']['packages'] as $package_id => $package) {
577
+
578
+ foreach ($package['products'] as $product_id => $product) {
579
+
580
+ if (!isset($product['plugins'])) {
581
+
582
+ $populate_downloads = true;
583
+
584
+ foreach ($product['downloads'] as $download_id => $download) {
585
+
586
+ $settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['plugins'][] = $download['slug'];
587
+
588
+ }
589
+
590
+ }
591
+
592
+ }
593
+
594
+ }
595
+
596
+ if ($populate_downloads) {
597
+
598
+ // Add downloads branch
599
+ foreach ($repository['data']['packages'] as $package_id => $package) {
600
+
601
+ foreach ($package['products'] as $product_id => $product) {
602
+
603
+ foreach ($product['downloads'] as $download_id => $download) {
604
+
605
+ if (!isset($settings['repositories'][$repository_id]['data']['downloads']['plugins'][$download['slug']])) {
606
+ $settings['repositories'][$repository_id]['data']['downloads']['plugins'][$download['slug']] = $download;
607
+ }
608
+
609
+ $settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['plugins'][] = $download['slug'];
610
+ }
611
+
612
+ unset($settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['downloads']);
613
+
614
+ }
615
+
616
+ }
617
+
618
+ }
619
+
620
+ }
621
+
622
+ $this->_old_products_format_backwards_compatibility = true;
623
+
624
+ }
625
+
626
+ return $settings;
627
+
628
+ }
629
+
630
+ public function get_installer_site_url( $repository_id = false ){
631
+ global $current_site;
632
+
633
+ $site_url = get_site_url();
634
+
635
+ if( $repository_id && is_multisite() && isset( $this->settings['repositories'] ) ){
636
+ $network_settings = maybe_unserialize( get_site_option('wp_installer_network') );
637
+
638
+ if ( isset( $network_settings[$repository_id] ) ) {
639
+ $site_url = get_site_url( $current_site->blog_id );
640
+ }
641
+
642
+ }
643
+
644
+ return $site_url;
645
+ }
646
+
647
+ public function show_site_key_nags(){
648
+ $screen = get_current_screen();
649
+
650
+ if($screen->base == 'settings_page_installer' || ($screen->base == 'plugin-install' && isset($_GET['tab']) && $_GET['tab'] == 'commercial')){
651
+ return;
652
+ }
653
+
654
+ if(!empty($this->config['site_key_nags'])){
655
+
656
+ foreach($this->config['site_key_nags'] as $nag){
657
+
658
+ if(!$this->repository_has_subscription($nag['repository_id'] )){
659
+ $show = true;
660
+ if(!empty($nag['condition_cb'])){
661
+ $show = call_user_func($nag['condition_cb']);
662
+ }
663
+
664
+ if(empty($this->settings['dismissed_nags'][$nag['repository_id']]) && $show){
665
+ echo '<div class="updated error otgs-is-dismissible"><p>';
666
+ printf(__("To get automatic updates, you need to register %s for this site. %sRegister %s%s", 'sitepress'),
667
+ $nag['product_name'], '<a class="button-primary" href="' . $this->menu_url() . '">', $nag['product_name'], '</a>');
668
+
669
+ echo '</p>';
670
+ echo '<span class="installer-dismiss-nag notice-dismiss" data-repository="' . $nag['repository_id'] . '"><span class="screen-reader-text">' . __('Dismiss', 'sitepress') . '</span></span>';
671
+ echo '</div>';
672
+ }
673
+ }
674
+
675
+ }
676
+
677
+ }
678
+
679
+ }
680
+
681
+ public function dismiss_nag(){
682
+ $this->settings['dismissed_nags'][$_POST['repository']] = 1;
683
+
684
+ $this->save_settings();
685
+
686
+ echo json_encode(array());
687
+ exit;
688
+ }
689
+
690
+ public function add_install_plugins_tab($tabs){
691
+
692
+ $tabs['commercial'] = __('Commercial', 'installer');
693
+
694
+ return $tabs;
695
+ }
696
+
697
+ public function load_repositories_list(){
698
+ global $wp_installer_instances;
699
+
700
+ foreach ($wp_installer_instances as $instance) {
701
+
702
+ if (file_exists(dirname($instance['bootfile']) . '/repositories.xml')) {
703
+ $config_file = dirname($instance['bootfile']) . '/repositories.xml';
704
+
705
+ if (file_exists(dirname($instance['bootfile']) . '/repositories.sandbox.xml')) {
706
+ $config_file = dirname($instance['bootfile']) . '/repositories.sandbox.xml';
707
+ add_filter('https_ssl_verify', '__return_false');
708
+ }
709
+
710
+ $repos = simplexml_load_file($config_file);
711
+
712
+ if($repos) {
713
+ foreach ($repos as $repo) {
714
+ $id = strval($repo->id);
715
+
716
+ $data['api-url'] = strval($repo->apiurl);
717
+ $data['products'] = strval($repo->products);
718
+
719
+ // excludes rule;
720
+ if (isset($this->config['repositories_exclude']) && in_array($id, $this->config['repositories_exclude'])) {
721
+ continue;
722
+ }
723
+
724
+ // includes rule;
725
+ if (isset($this->config['repositories_include']) && !in_array($id, $this->config['repositories_include'])) {
726
+ continue;
727
+ }
728
+
729
+ $this->repositories[$id] = $data;
730
+
731
+ }
732
+ }
733
+
734
+ }
735
+ }
736
+
737
+ }
738
+
739
+ public function filter_repositories_list(){
740
+
741
+ if(!empty($this->settings['repositories'])) {
742
+ foreach ($this->settings['repositories'] as $id => $repo_data) {
743
+
744
+ // excludes rule;
745
+ if (isset($this->config['repositories_exclude']) && in_array($id, $this->config['repositories_exclude'])) {
746
+ unset($this->settings['repositories'][$id]);
747
+ }
748
+
749
+ // includes rule;
750
+ if (isset($this->config['repositories_include']) && !in_array($id, $this->config['repositories_include'])) {
751
+ unset($this->settings['repositories'][$id]);
752
+ }
753
+
754
+
755
+ }
756
+ }
757
+
758
+
759
+ }
760
+
761
+ public function refresh_repositories_data(){
762
+ static $checked = false;
763
+
764
+ if( defined('OTGS_DISABLE_AUTO_UPDATES') && OTGS_DISABLE_AUTO_UPDATES && empty($_GET['force-check']) || $checked ){
765
+
766
+ if(empty($this->settings['repositories']) && $this->is_repositories_page()){
767
+
768
+ foreach($this->repositories as $id => $data) {
769
+ $repository_names[] = $id;
770
+
771
+ }
772
+
773
+ $error = sprintf(__("Installer cannot display the products information because the automatic updating for %s was explicitly disabled with the configuration below (usually in wp-config.php):", 'installer'), strtoupper( join(', ', $repository_names) ));
774
+ $error .= '<br /><br /><code>define("OTGS_DISABLE_AUTO_UPDATES", true);</code><br /><br />';
775
+ $error .= sprintf(__("In order to see the products information, please run the %smanual updates check%s to initialize the products list or (temporarily) remove the above code.", 'installer'), '<a href="' . admin_url('update-core.php') . '">', '</a>');
776
+
777
+ $this->register_admin_message($error, 'error');
778
+
779
+
780
+ }
781
+
782
+ return;
783
+ }
784
+
785
+ $checked = true;
786
+
787
+ foreach($this->repositories as $id => $data){
788
+
789
+ $response = wp_remote_get($data['products']);
790
+
791
+ if(is_wp_error($response)){
792
+ // http fallback
793
+ $data['products'] = preg_replace("@^https://@", 'http://', $data['products']);
794
+ $response = wp_remote_get($data['products']);
795
+ }
796
+
797
+ if(is_wp_error($response)){
798
+
799
+ $error = sprintf(__("Installer cannot contact our updates server to get information about the available products and check for new versions. If you are seeing this message for the first time, you can ignore it, as it may be a temporary communication problem. If the problem persists and your WordPress admin is slowing down, you can disable automated version checks. Add the following line to your wp-config.php file:", 'installer'), strtoupper($id));
800
+ $error .= '<br /><br /><code>define("OTGS_DISABLE_AUTO_UPDATES", true);</code>';
801
+
802
+ $this->register_admin_message($error, 'error');
803
+
804
+ continue;
805
+ }
806
+
807
+ if($response && isset($response['response']['code']) && $response['response']['code'] == 200){
808
+ $body = wp_remote_retrieve_body($response);
809
+ if($body){
810
+ $products = json_decode($body, true);
811
+
812
+ if(is_array($products)){
813
+ $this->settings['repositories'][$id]['data'] = $products;
814
+ $this->settings = $this->_pre_1_6_backwards_compatibility($this->settings);
815
+ }
816
+ }
817
+
818
+ }
819
+
820
+ $this->log( sprintf("Checked for %s updates: %s", $id, $data['products']) );
821
+
822
+
823
+ }
824
+
825
+ // cleanup
826
+ if(empty($this->settings['repositories'])){
827
+ $this->settings['repositories'] = array();
828
+ }
829
+ foreach($this->settings['repositories'] as $id => $data){
830
+ if(!in_array($id, array_keys($this->repositories))){
831
+ unset($this->settings['repositories'][$id]);
832
+ }
833
+ }
834
+
835
+ $this->settings['last_repositories_update']= time();
836
+
837
+ $this->save_settings();
838
+
839
+ }
840
+
841
+ public function show_products($args = array()){
842
+
843
+ $screen = get_current_screen();
844
+
845
+ if($screen->base == 'settings_page_installer'){ // settings page
846
+ echo '<div class="wrap">';
847
+ echo '<h2>' . __('Installer', 'installer') . '</h2>';
848
+ echo '<br />';
849
+ }
850
+
851
+ if(!is_array($args)) $args = array();
852
+ if(empty($args['template'])) $args['template'] = 'default';
853
+
854
+ $this->filter_repositories_list();
855
+
856
+ if(!empty($this->settings['repositories'])){
857
+
858
+ $this->localize_strings();
859
+ $this->set_filtered_prices($args);
860
+ $this->set_hierarchy_and_order();
861
+
862
+ foreach($this->settings['repositories'] as $repository_id => $repository){
863
+
864
+ if($args['template'] == 'compact'){
865
+
866
+ if(isset($args['repository']) && $args['repository'] == $repository_id){
867
+ include $this->plugin_path() . '/templates/products-compact.php';
868
+ }
869
+
870
+ }else{
871
+
872
+ include $this->plugin_path() . '/templates/repository-listing.php';
873
+
874
+ }
875
+
876
+ unset($site_key, $subscription_type, $expired, $upgrade_options, $products_avaliable);
877
+
878
+ }
879
+
880
+ }else{
881
+
882
+ echo '<center>' . __('No repositories defined.', 'installer') . '</center>';
883
+
884
+ }
885
+
886
+ if($screen->base == 'settings_page_installer'){ // settings page
887
+ echo '</div>';
888
+ }
889
+
890
+
891
+ }
892
+
893
+ public function get_product_price($repository_id, $package_id, $product_id, $incl_discount = false){
894
+
895
+ $price = false;
896
+
897
+ foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package ){
898
+
899
+ if($package['id'] == $package_id){
900
+ if(isset($package['products'][$product_id])){
901
+ if($incl_discount && isset($package['products'][$product_id]['price_disc'])){
902
+ $price = $package['products'][$product_id]['price_disc'];
903
+ }elseif(isset($package['products'][$product_id]['price'])){
904
+ $price = $package['products'][$product_id]['price'];
905
+ }
906
+ }
907
+ break;
908
+ }
909
+ }
910
+
911
+ return $price;
912
+ }
913
+
914
+ private function _render_product_packages($packages, $subscription_type, $expired, $upgrade_options, $repository_id){
915
+
916
+ $data = array();
917
+
918
+ foreach($packages as $package_id => $package){
919
+
920
+ $row = array('products' => array(), 'downloads' => array());
921
+ foreach($package['products'] as $product){
922
+
923
+ // filter out free subscriptions from being displayed as buying options
924
+ if( empty($product['price']) && (empty($subscription_type) || $expired) ){
925
+ continue;
926
+ }
927
+
928
+ // buy base
929
+ if(empty($subscription_type) || $expired) {
930
+
931
+ $p['url'] = $this->append_parameters_to_buy_url($product['url'], $repository_id);
932
+ if (!empty($product['price_disc'])) {
933
+ $p['label'] = $product['call2action'] . ' - ' . sprintf('$%s %s$%d%s (USD)', $product['price_disc'], '&nbsp;&nbsp;<del>', $product['price'], '</del>');
934
+ } else {
935
+ $p['label'] = $product['call2action'] . ' - ' . sprintf('$%d (USD)', $product['price']);
936
+ }
937
+ $row['products'][] = $p;
938
+
939
+ // renew
940
+ } elseif(isset($subscription_type) && $product['subscription_type'] == $subscription_type){
941
+
942
+ if($product['renewals']) {
943
+ foreach ($product['renewals'] as $renewal) {
944
+ $p['url'] = $this->append_parameters_to_buy_url($renewal['url'], $repository_id);
945
+ $p['label'] = $renewal['call2action'] . ' - ' . sprintf('$%d (USD)', $renewal['price']);
946
+ }
947
+
948
+ $row['products'][] = $p;
949
+ }
950
+
951
+ }
952
+
953
+ // upgrades
954
+ if(!empty($upgrade_options[$product['subscription_type']])){
955
+
956
+ foreach($upgrade_options[$product['subscription_type']] as $stype => $upgrade){
957
+ if($stype != $subscription_type) continue;
958
+
959
+ $p['url'] = $this->append_parameters_to_buy_url($upgrade['url'], $repository_id);
960
+ if (!empty($upgrade['price_disc'])) {
961
+ $p['label'] = $upgrade['call2action'] . ' - ' . sprintf('$%s %s$%d%s (USD)', $upgrade['price_disc'], '&nbsp;&nbsp;<del>', $upgrade['price'], '</del>');
962
+ } else {
963
+ $p['label'] = $upgrade['call2action'] . ' - ' . sprintf('$%d (USD)', $upgrade['price']);
964
+ }
965
+ $row['products'][] = $p;
966
+
967
+ }
968
+
969
+ }
970
+
971
+ // downloads
972
+ if(isset($subscription_type) && !$expired && $product['subscription_type'] == $subscription_type){
973
+ foreach($product['plugins'] as $plugin_slug){
974
+
975
+ $row['downloads'][] = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
976
+
977
+ }
978
+
979
+ }
980
+
981
+ //subpackages
982
+ if(!empty($package['sub-packages'])){
983
+ $row['sub-packages'] = $package['sub-packages'];
984
+ }
985
+
986
+ }
987
+
988
+ $row['id'] = $package['id'];
989
+ $row['image_url'] = $package['image_url'];
990
+ $row['name'] = $package['name'];
991
+ $row['description'] = $package['description'];
992
+
993
+ if(!empty($row['products']) || !empty($row['downloads']) || !empty($row['sub-packages'])){
994
+ $data[] = $row;
995
+ }
996
+
997
+
998
+ }
999
+
1000
+ return $data;
1001
+
1002
+ }
1003
+
1004
+ public function get_extra_url_parameters(){
1005
+
1006
+ $parameters = array();
1007
+
1008
+ if(!empty($this->package_source)){
1009
+ foreach($this->package_source as $key => $val){
1010
+ $parameters[$key] = $val;
1011
+ }
1012
+ }
1013
+
1014
+ $parameters['installer_version'] = WP_INSTALLER_VERSION;
1015
+ $parameters['theme'] = wp_get_theme()->get( 'Name' );
1016
+ $parameters['site_name'] = get_bloginfo( 'name' );
1017
+
1018
+ return $parameters;
1019
+ }
1020
+
1021
+ public function append_parameters_to_buy_url($url, $repository_id, $args = array()){
1022
+
1023
+ $url = add_query_arg( array('icl_site_url' => $this->get_installer_site_url( $repository_id ) ), $url );
1024
+
1025
+ $affiliate_id = false;
1026
+ $affiliate_key = false;
1027
+
1028
+ // Add extra parameters for custom Installer packages
1029
+ if( !empty($this->package_source) ){
1030
+ $extra = $this->get_extra_url_parameters();
1031
+
1032
+ if( !empty($extra['repository']) && $extra['repository'] == $repository_id ) {
1033
+
1034
+ if( !empty($extra['affiliate_key']) && !empty($extra['user_id']) ){
1035
+ $this->config['affiliate_id:' . $repository_id] = $extra['user_id'];
1036
+ $this->config['affiliate_key:' . $repository_id] = $extra['affiliate_key'];
1037
+ unset($extra['affiliate_key'], $extra['user_id'], $extra['repository']); // no need to include these ones
1038
+ }
1039
+
1040
+ $url = add_query_arg($extra, $url);
1041
+ }
1042
+
1043
+ }
1044
+
1045
+ if(isset($this->config['affiliate_id:' . $repository_id]) && isset($this->config['affiliate_key:' . $repository_id])){
1046
+
1047
+ $affiliate_id = $this->config['affiliate_id:' . $repository_id];
1048
+ $affiliate_key = $this->config['affiliate_key:' . $repository_id];
1049
+
1050
+ }elseif(isset($args['affiliate_id:' . $repository_id]) && isset($args['affiliate_key:' . $repository_id])){
1051
+
1052
+ $affiliate_id = $args['affiliate_id:' . $repository_id];
1053
+ $affiliate_key = $args['affiliate_key:' . $repository_id];
1054
+
1055
+ }elseif(defined('ICL_AFFILIATE_ID') && defined('ICL_AFFILIATE_KEY')){ //support for 1 repo
1056
+
1057
+ $affiliate_id = ICL_AFFILIATE_ID;
1058
+ $affiliate_key = ICL_AFFILIATE_KEY;
1059
+
1060
+ }elseif(isset($this->config['affiliate_id']) && isset($this->config['affiliate_key'])) {
1061
+ // BACKWARDS COMPATIBILITY
1062
+ $affiliate_id = $this->config['affiliate_id'];
1063
+ $affiliate_key = $this->config['affiliate_key'];
1064
+ }
1065
+
1066
+ if($affiliate_id && $affiliate_key){
1067
+ $url = add_query_arg(array('aid' => $affiliate_id, 'affiliate_key' => $affiliate_key), $url);
1068
+ }
1069
+
1070
+ if($repository_id == 'wpml'){
1071
+ $url = add_query_arg(array('using_icl' => $this->_using_icl, 'wpml_version' => $this->_wpml_version), $url);
1072
+ }
1073
+
1074
+ $url = apply_filters('wp_installer_buy_url', $url);
1075
+
1076
+ $url = esc_url($url);
1077
+
1078
+ return $url;
1079
+
1080
+ }
1081
+
1082
+ public function save_site_key($args = array()){
1083
+
1084
+ $error = '';
1085
+
1086
+ if( isset( $args['repository_id'] ) ){
1087
+ $repository_id = $args['repository_id'];
1088
+ }elseif( isset( $_POST['repository_id'] ) ){
1089
+ $repository_id = sanitize_text_field( $_POST['repository_id'] );
1090
+ }else{
1091
+ $repository_id = false;
1092
+ }
1093
+
1094
+ if( isset( $args['nonce'] ) ){
1095
+ $nonce = $args['nonce'];
1096
+ }elseif( isset($_POST['nonce'] ) ){
1097
+ $nonce = sanitize_text_field( $_POST['nonce'] );
1098
+ }else{
1099
+ $nonce = '';
1100
+ }
1101
+
1102
+ if( isset( $args['site_key'] ) ){
1103
+ $site_key = $args['site_key'];
1104
+ } else {
1105
+ $site_key = sanitize_text_field( $_POST[ 'site_key_' . $repository_id] );
1106
+ }
1107
+ $site_key = preg_replace("/[^A-Za-z0-9]/", '', $site_key);
1108
+
1109
+ if($repository_id && $nonce && wp_create_nonce('save_site_key_' . $repository_id) == $nonce){
1110
+
1111
+ try {
1112
+ $subscription_data = $this->fetch_subscription_data( $repository_id, $site_key, self::SITE_KEY_VALIDATION_SOURCE_REGISTRATION );
1113
+
1114
+ if ( $subscription_data ) {
1115
+ $this->settings['repositories'][$repository_id]['subscription'] = array('key' => $site_key, 'data' => $subscription_data);
1116
+ $this->save_settings();
1117
+ } else {
1118
+ $error = __( 'Invalid site key for the current site.', 'installer' )
1119
+ . '<br /><div class="installer-footnote">' . __('Please note that the site key is case sensitive.', 'installer') . '</div>';
1120
+ }
1121
+
1122
+ } catch (Exception $e ){
1123
+ $error = $e->getMessage();
1124
+ if( preg_match('#Could not resolve host: (.*)#', $error, $matches) || preg_match('#Couldn\'t resolve host \'(.*)\'#', $error, $matches) ){
1125
+ $error = sprintf(__("%s cannot access %s to register. Try again to see if it's a temporary problem. If the problem continues, make sure that this site has access to the Internet. You can still use the plugin without registration, but you will not receive automated updates.", 'installer'),
1126
+ '<strong><i>' . $this->get_generic_product_name($repository_id) . '</i></strong>',
1127
+ '<strong><i>' . $matches[1]. '</i></strong>'
1128
+ ) ;
1129
+ }
1130
+ }
1131
+
1132
+ }
1133
+
1134
+ $return = array('error' => $error);
1135
+
1136
+ if($this->api_debug){
1137
+ $return['debug'] = $this->api_debug;
1138
+ }
1139
+
1140
+ if(!empty($args['return'])){
1141
+ return $return;
1142
+ }else{
1143
+ echo json_encode($return);
1144
+ exit;
1145
+ }
1146
+
1147
+ }
1148
+
1149
+ /**
1150
+ * Alias for WP_Installer::get_repository_site_key
1151
+ * @see WP_Installer::get_repository_site_key()
1152
+ *
1153
+ * @param string $repository_id
1154
+ * @return string (site key) or bool
1155
+ */
1156
+ public function get_site_key($repository_id){
1157
+ return WP_Installer::get_repository_site_key( $repository_id );
1158
+ }
1159
+
1160
+ public function remove_site_key( $repository_id ){
1161
+ if( isset( $this->settings['repositories'][$repository_id] ) ){
1162
+ unset($this->settings['repositories'][$repository_id]['subscription']);
1163
+ $this->save_settings();
1164
+ $this->refresh_repositories_data();
1165
+ }
1166
+ }
1167
+
1168
+ public function remove_site_key_ajax(){
1169
+ if($_POST['nonce'] == wp_create_nonce('remove_site_key_' . $_POST['repository_id'])){
1170
+ $this->remove_site_key( $_POST['repository_id'] );
1171
+ }
1172
+ exit;
1173
+ }
1174
+
1175
+ public function validate_repository_subscription(){
1176
+ $repository_id = isset($_GET['validate_repository']) ? sanitize_text_field( $_GET['validate_repository'] ) : false;
1177
+ if($repository_id){
1178
+
1179
+ $site_key = $this->get_site_key($repository_id);
1180
+ if($site_key) {
1181
+ $subscription_data = $this->fetch_subscription_data( $repository_id, $site_key, self::SITE_KEY_VALIDATION_SOURCE_REVALIDATION);
1182
+ if(empty($subscription_data)){
1183
+ unset($this->settings['repositories'][$repository_id]['subscription']);
1184
+ delete_site_transient('update_plugins');
1185
+ $this->save_settings();
1186
+ }
1187
+ }
1188
+
1189
+ wp_redirect($this->menu_url() . '#repository-' . $repository_id);
1190
+ exit;
1191
+
1192
+ }
1193
+
1194
+ }
1195
+
1196
+ public function update_site_key(){
1197
+
1198
+ $error = '';
1199
+
1200
+ $repository_id = sanitize_text_field ( $_POST['repository_id'] );
1201
+ if($_POST['nonce'] == wp_create_nonce('update_site_key_' . $repository_id )){
1202
+
1203
+ $site_key = $this->get_site_key($_POST['repository_id']);
1204
+
1205
+ if($site_key){
1206
+ try {
1207
+ $subscription_data = $this->fetch_subscription_data( $repository_id, $site_key, self::SITE_KEY_VALIDATION_SOURCE_UPDATES_CHECK );
1208
+
1209
+ if ( $subscription_data ) {
1210
+ $this->settings['repositories'][$repository_id]['subscription'] = array('key' => $site_key, 'data' => $subscription_data);
1211
+
1212
+ //also refresh products information
1213
+ $this->refresh_repositories_data();
1214
+
1215
+ $this->save_settings();
1216
+
1217
+ } else {
1218
+ unset($this->settings['repositories'][$repository_id]['subscription']);
1219
+ $error = __( 'Invalid site key for the current site. If the error persists, try to unregister first and then register again with the same site key.', 'installer' );
1220
+ }
1221
+
1222
+
1223
+ } catch (Exception $e ){
1224
+ $error = $e->getMessage();
1225
+ if( preg_match('#Could not resolve host: (.*)#', $error, $matches) || preg_match('#Couldn\'t resolve host \'(.*)\'#', $error, $matches) ){
1226
+ $error = sprintf(__("%s cannot access %s to register. Try again to see if it's a temporary problem. If the problem continues, make sure that this site has access to the Internet. You can still use the plugin without registration, but you will not receive automated updates.", 'installer'),
1227
+ '<strong><i>' . $this->get_generic_product_name($repository_id) . '</i></strong>',
1228
+ '<strong><i>' . $matches[1]. '</i></strong>'
1229
+ ) ;
1230
+ }
1231
+ }
1232
+
1233
+ }
1234
+
1235
+ }
1236
+
1237
+ echo json_encode(array('error' => $error));
1238
+
1239
+ exit;
1240
+ }
1241
+
1242
+ public function api_debug_log($text){
1243
+
1244
+ if(defined('WPML_DEBUG_INSTALLER') && WPML_DEBUG_INSTALLER){
1245
+
1246
+ if(!is_scalar($text)){
1247
+ $text = print_r($text, 1);
1248
+ }
1249
+
1250
+ $this->api_debug .= $text . "\n";
1251
+
1252
+ }
1253
+
1254
+ }
1255
+
1256
+ public function fetch_subscription_data( $repository_id, $site_key, $source = self::SITE_KEY_VALIDATION_SOURCE_OTHER ){
1257
+
1258
+ $subscription_data = false;
1259
+
1260
+ $args['body'] = array(
1261
+ 'action' => 'site_key_validation',
1262
+ 'site_key' => $site_key,
1263
+ 'site_url' => $this->get_installer_site_url( $repository_id ),
1264
+ 'source' => $source
1265
+ );
1266
+
1267
+ if($repository_id == 'wpml'){
1268
+ $args['body']['using_icl'] = $this->_using_icl;
1269
+ $args['body']['wpml_version'] = $this->_wpml_version;
1270
+ }
1271
+
1272
+ $args['body']['installer_version'] = WP_INSTALLER_VERSION;
1273
+ $args['body']['theme'] = wp_get_theme()->get( 'Name' );
1274
+ $args['body']['site_name'] = get_bloginfo( 'name' );
1275
+
1276
+ $args['body']['versions'] = $this->get_local_product_versions( $repository_id );
1277
+
1278
+ $args['timeout'] = 45;
1279
+
1280
+ // Add extra parameters for custom Installer packages
1281
+ if( !empty($this->package_source) ){
1282
+ $extra = $this->get_extra_url_parameters();
1283
+ if( !empty($extra['repository']) && $extra['repository'] == $repository_id ) {
1284
+ unset($extra['repository']);
1285
+ foreach($extra as $key => $val){
1286
+ $args['body'][$key] = $val;
1287
+ }
1288
+ }
1289
+ }
1290
+
1291
+ $response = wp_remote_post($this->repositories[$repository_id]['api-url'], $args);
1292
+
1293
+ $this->api_debug_log("POST {$this->repositories[$repository_id]['api-url']}");
1294
+ $this->api_debug_log($args);
1295
+
1296
+ $this->log("POST {$this->repositories[$repository_id]['api-url']} - fetch subscription data");
1297
+
1298
+ if( !is_wp_error($response) ){
1299
+ $datas = wp_remote_retrieve_body($response);
1300
+
1301
+ if(is_serialized($datas)){
1302
+ $data = unserialize($datas);
1303
+ $this->api_debug_log($data);
1304
+
1305
+ if( !empty( $data->subscription_data ) ){
1306
+ $subscription_data = $data->subscription_data;
1307
+ }
1308
+
1309
+ do_action( 'installer_fetched_subscription_data', $data, $repository_id);
1310
+
1311
+ }else{
1312
+ $this->api_debug_log($datas);
1313
+ }
1314
+
1315
+ }else{
1316
+
1317
+ $this->api_debug_log($response);
1318
+ throw new Exception( $response->get_error_message() );
1319
+ }
1320
+
1321
+ return $subscription_data;
1322
+
1323
+ }
1324
+
1325
+ function get_local_product_versions( $repository_id ){
1326
+
1327
+ $versions = array();
1328
+
1329
+ foreach( $this->settings['repositories'][$repository_id]['data']['packages'] as $package_id => $package ){
1330
+
1331
+ foreach( $package['products'] as $product_id => $product ){
1332
+
1333
+ foreach( $product['plugins'] as $plugin_slug ){
1334
+
1335
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
1336
+
1337
+ if( empty( $versions[$download['slug']] ) ) {
1338
+ $v = $this->get_plugin_installed_version($download['name'], $download['slug']);
1339
+ if($v){
1340
+ $versions[$download['slug']] = $v;
1341
+ }
1342
+ }
1343
+
1344
+ }
1345
+
1346
+ }
1347
+
1348
+ }
1349
+
1350
+ return $versions;
1351
+ }
1352
+
1353
+ public function get_repository_site_key($repository_id){
1354
+ $site_key = false;
1355
+
1356
+ if(!empty($this->settings['repositories'][$repository_id]['subscription']['key'])){
1357
+ $site_key = $this->settings['repositories'][$repository_id]['subscription']['key'];
1358
+ }
1359
+
1360
+ return $site_key;
1361
+ }
1362
+
1363
+ public function repository_has_valid_subscription($repository_id){
1364
+
1365
+ $valid = false;
1366
+
1367
+ if(!empty($this->settings['repositories'][$repository_id]['subscription'])){
1368
+
1369
+ $subscription = $this->settings['repositories'][$repository_id]['subscription']['data'];
1370
+ $valid = ( $subscription->status == 1 && (strtotime($subscription->expires) > time() || empty($subscription->expires)) ) || $subscription->status == 4;
1371
+
1372
+ }
1373
+ return $valid;
1374
+
1375
+ }
1376
+
1377
+ public function repository_has_subscription($repository_id){
1378
+ $key = false;
1379
+ if(!empty($this->settings['repositories'][$repository_id]['subscription']['key'])){
1380
+ $key = $this->settings['repositories'][$repository_id]['subscription']['key'];
1381
+ }
1382
+
1383
+ return $key;
1384
+
1385
+ }
1386
+
1387
+ public function repository_has_expired_subscription($repository_id){
1388
+
1389
+ return $this->repository_has_subscription($repository_id) && !$this->repository_has_valid_subscription($repository_id);
1390
+
1391
+ }
1392
+
1393
+ public function get_generic_product_name($repository_id){
1394
+
1395
+ return $this->settings['repositories'][$repository_id]['data']['product-name'];
1396
+
1397
+ }
1398
+
1399
+ public function show_subscription_renew_warning($repository_id, $subscription_id){
1400
+
1401
+ $show = false;
1402
+
1403
+ $data = $this->settings['repositories'][$repository_id]['data'];
1404
+ if(!empty($data['subscriptions_meta'])){
1405
+ if(isset($data['subscriptions_meta']['expiration'])){
1406
+
1407
+ if(!empty($data['subscriptions_meta']['expiration'][$subscription_id])){
1408
+
1409
+ $days = $data['subscriptions_meta']['expiration'][$subscription_id]['days_warning'];
1410
+ $message = $data['subscriptions_meta']['expiration'][$subscription_id]['warning_message'];
1411
+
1412
+ }else{
1413
+
1414
+ //defaults
1415
+ $days = 30;
1416
+ $message = __('You will have to renew your subscription in order to continue getting the updates and support.', 'installer');
1417
+
1418
+ }
1419
+
1420
+ if(!empty($this->settings['repositories'][$repository_id]['subscription'])){
1421
+ $subscription = $this->settings['repositories'][$repository_id]['subscription'];
1422
+
1423
+ if($subscription['data']->subscription_type == $subscription_id && !empty($subscription['data']->expires)){
1424
+
1425
+ if(strtotime($subscription['data']->expires) < strtotime(sprintf("+%d day", $days))){
1426
+
1427
+ $days_to_expiration = ceil((strtotime($subscription['data']->expires) - time()) / 86400);
1428
+
1429
+ echo '<div><p class="installer-warn-box">' .
1430
+ sprintf(_n('Your subscription expires in %d day.', 'Your subscription expires in %d days.', $days_to_expiration, 'installer'), $days_to_expiration) .
1431
+ '<br />' . $message .
1432
+ '</p></div>';
1433
+
1434
+ $show = true;
1435
+
1436
+ }
1437
+
1438
+ }
1439
+
1440
+ }
1441
+
1442
+
1443
+ }
1444
+ }
1445
+
1446
+
1447
+ return $show;
1448
+
1449
+ }
1450
+
1451
+ public function setup_plugins_renew_warnings(){
1452
+
1453
+ $plugins = get_plugins();
1454
+
1455
+ $subscriptions_with_warnings = array();
1456
+ foreach($this->settings['repositories'] as $repository_id => $repository){
1457
+
1458
+ if($this->repository_has_valid_subscription($repository_id)){
1459
+ $subscription_type = $this->settings['repositories'][$repository_id]['subscription']['data']->subscription_type;
1460
+ $expires = $this->settings['repositories'][$repository_id]['subscription']['data']->expires;
1461
+
1462
+ $never_expires = isset($this->settings['repositories'][$repository_id]['subscription'])
1463
+ && empty($this->settings['repositories'][$repository_id]['subscription']['data']->expires)
1464
+ && (
1465
+ $this->settings['repositories'][$repository_id]['subscription']['data']->status == 4 ||
1466
+ $this->settings['repositories'][$repository_id]['subscription']['data']->status == 1
1467
+ );
1468
+
1469
+ if(!$never_expires){
1470
+ if(isset($this->settings['repositories'][$repository_id]['data']['subscriptions_meta']['expiration'][$subscription_type])){
1471
+
1472
+ $days_warning = $this->settings['repositories'][$repository_id]['data']['subscriptions_meta']['expiration'][$subscription_type]['days_warning'];
1473
+ $custom_message = $this->settings['repositories'][$repository_id]['data']['subscriptions_meta']['expiration'][$subscription_type]['warning_message'];
1474
+
1475
+ }else{
1476
+ //defaults
1477
+ $days_warning = 30;
1478
+ $custom_message = __('You will have to renew your subscription in order to continue getting the updates and support.', 'installer');
1479
+ }
1480
+
1481
+ if(strtotime($expires) < strtotime(sprintf('+%d day', $days_warning)) ){
1482
+
1483
+ $days_to_expiration = ceil((strtotime($expires) - time()) / 86400);
1484
+
1485
+ $message = sprintf(_n('Your subscription expires in %d day.', 'Your subscription expires in %d days.', $days_to_expiration, 'installer'), $days_to_expiration);
1486
+ $subscriptions_with_warnings[$subscription_type] = $message . ' ' . $custom_message;
1487
+
1488
+ }
1489
+ }
1490
+
1491
+ }
1492
+
1493
+ }
1494
+
1495
+
1496
+
1497
+ foreach($plugins as $plugin_id => $plugin){
1498
+
1499
+ $slug = dirname($plugin_id);
1500
+ if(empty($slug)) continue;
1501
+
1502
+ foreach($this->settings['repositories'] as $repository_id => $repository){
1503
+
1504
+ if($this->repository_has_valid_subscription($repository_id)){
1505
+
1506
+ foreach($repository['data']['packages'] as $package){
1507
+
1508
+ foreach($package['products'] as $product){
1509
+
1510
+ foreach($product['plugins'] as $plugin_slug){
1511
+
1512
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
1513
+
1514
+ if($download['slug'] == $slug || $download['name'] == $plugin['Name'] || $download['name'] == $plugin['Title']){ //match order: slug, name, title
1515
+
1516
+ if(isset($subscriptions_with_warnings[$product['subscription_type']])){
1517
+
1518
+ $this->_plugins_renew_warnings[$plugin_id] = $subscriptions_with_warnings[$product['subscription_type']];
1519
+
1520
+ }
1521
+
1522
+ }
1523
+
1524
+ }
1525
+
1526
+ }
1527
+
1528
+ }
1529
+
1530
+ }
1531
+
1532
+ }
1533
+
1534
+ }
1535
+
1536
+ }
1537
+
1538
+ public function queue_plugins_renew_warnings() {
1539
+
1540
+ if(!empty($this->_plugins_renew_warnings)){
1541
+
1542
+ foreach($this->_plugins_renew_warnings as $plugin_id => $message){
1543
+
1544
+ add_action( "after_plugin_row_" . $plugin_id, array($this, 'plugins_renew_warning'), 10, 3 );
1545
+ }
1546
+
1547
+ }
1548
+
1549
+ }
1550
+
1551
+ public function plugins_renew_warning($plugin_file, $plugin_data, $status){
1552
+
1553
+ if(empty($this->_plugins_renew_warnings[$plugin_file])) return;
1554
+
1555
+ $wp_list_table = _get_list_table('WP_Plugins_List_Table');
1556
+ ?>
1557
+
1558
+ <tr class="plugin-update-tr"><td colspan="<?php echo $wp_list_table->get_column_count(); ?>" class="plugin-update colspanchange">
1559
+ <div class="update-message">
1560
+ <?php
1561
+ echo $this->_plugins_renew_warnings[$plugin_file]. ' ';
1562
+ printf(__('%sRenew here%s.', 'installer'),
1563
+ '<a href="' . $this->menu_url() . '">', '</a>');
1564
+ ?>
1565
+ </div>
1566
+ </tr>
1567
+
1568
+ <?php
1569
+
1570
+ }
1571
+
1572
+ public function get_subscription_type_for_repository($repository_id){
1573
+
1574
+ $subscription_type = false;
1575
+
1576
+ if(!empty($this->settings['repositories'][$repository_id]['subscription'])){
1577
+ $subscription_type = $this->settings['repositories'][$repository_id]['subscription']['data']->subscription_type;
1578
+ }
1579
+
1580
+ return $subscription_type;
1581
+
1582
+ }
1583
+
1584
+ public function have_superior_subscription($subscription_type, $product){
1585
+
1586
+ $have = false;
1587
+
1588
+ if(is_array($product['upgrades'])){
1589
+ foreach($product['upgrades'] as $u){
1590
+ if($u['subscription_type'] == $subscription_type){
1591
+ $have = true;
1592
+ break;
1593
+ }
1594
+ }
1595
+ }
1596
+
1597
+ return $have;
1598
+ }
1599
+
1600
+ public function is_product_available_for_download($product_name, $repository_id){
1601
+
1602
+ $available = false;
1603
+
1604
+ $subscription_type = $this->get_subscription_type_for_repository($repository_id);
1605
+ $expired = $this->repository_has_expired_subscription($repository_id);
1606
+
1607
+ if($this->repository_has_subscription($repository_id) && !$expired){
1608
+
1609
+ $this->set_hierarchy_and_order();
1610
+
1611
+ foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package_id => $package){
1612
+
1613
+ $has_top_package = false;
1614
+
1615
+ foreach($package['products'] as $product){
1616
+
1617
+ if($subscription_type == $product['subscription_type']){
1618
+ $has_top_package = true;
1619
+ if($product['name'] == $product_name){
1620
+ return $available = true;
1621
+ }
1622
+ }
1623
+
1624
+ }
1625
+
1626
+ if(!empty($package['sub-packages'])){
1627
+ foreach($package['sub-packages'] as $sub_package){
1628
+ foreach($sub_package['products'] as $product){
1629
+ if($product['name'] == $product_name && ($subscription_type == $product['subscription_type'] || $has_top_package)){
1630
+ return $available = true;
1631
+ }
1632
+ }
1633
+ }
1634
+ }
1635
+
1636
+ }
1637
+ }
1638
+
1639
+ return $available;
1640
+
1641
+ }
1642
+
1643
+ public function get_upgrade_options($repository_id){
1644
+ $all_upgrades = array();
1645
+
1646
+
1647
+ //get all products: packages and subpackages
1648
+ $all_products = array();
1649
+ foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package){
1650
+ foreach($package['products'] as $product) {
1651
+ $all_products[] = $product;
1652
+ }
1653
+ if(!empty($package['sub-packages'])){
1654
+ foreach($package['sub-packages'] as $subpackage){
1655
+ foreach($subpackage['products'] as $product) {
1656
+ $all_products[] = $product;
1657
+ }
1658
+
1659
+ }
1660
+
1661
+ }
1662
+
1663
+ }
1664
+
1665
+ foreach( $all_products as $product ) {
1666
+ if ($product['upgrades']) {
1667
+ foreach ($product['upgrades'] as $upgrade) {
1668
+ if ($this->repository_has_valid_subscription($repository_id) || ($this->repository_has_subscription($repository_id) && $upgrade['including_expired'])) {
1669
+ $all_upgrades[$upgrade['subscription_type']][$product['subscription_type']] = $upgrade;
1670
+ }
1671
+ }
1672
+ }
1673
+ }
1674
+
1675
+ return $all_upgrades;
1676
+
1677
+ }
1678
+
1679
+ public function append_site_key_to_download_url($url, $key, $repository_id){
1680
+
1681
+ $url_params['site_key'] = $key;
1682
+ $url_params['site_url'] = $this->get_installer_site_url( $repository_id );
1683
+
1684
+
1685
+ // Add extra parameters for custom Installer packages
1686
+ if( !empty($this->package_source) ){
1687
+ $extra = $this->get_extra_url_parameters();
1688
+ if( !empty($extra['repository']) && $extra['repository'] == $repository_id ) {
1689
+ unset($extra['repository']);
1690
+ foreach($extra as $key => $val){
1691
+ $url_params[$key] = $val;
1692
+ }
1693
+ }
1694
+ }
1695
+
1696
+ $url = add_query_arg($url_params, $url);
1697
+
1698
+ if($repository_id == 'wpml'){
1699
+ $url = add_query_arg(array('using_icl' => $this->_using_icl, 'wpml_version' => $this->_wpml_version), $url);
1700
+ }
1701
+
1702
+ return $url;
1703
+
1704
+ }
1705
+
1706
+ public function plugin_is_installed($name, $slug, $version = null){
1707
+
1708
+ $is = false;
1709
+
1710
+ $plugins = get_plugins();
1711
+
1712
+ foreach($plugins as $plugin_id => $plugin){
1713
+
1714
+ $wp_plugin_slug = dirname($plugin_id);
1715
+
1716
+ // Exception: embedded plugins
1717
+ if( $wp_plugin_slug == $slug || $plugin['Name'] == $name || $plugin['Title'] == $name || ( $wp_plugin_slug == $slug . '-embedded' || $plugin['Name'] == $name . ' Embedded' ) ){
1718
+ if($version){
1719
+ if(version_compare($plugin['Version'], $version, '>=')){
1720
+ $is = $plugin['Version'];
1721
+ }
1722
+ }else{
1723
+ $is = $plugin['Version'];
1724
+ }
1725
+
1726
+ break;
1727
+ }
1728
+
1729
+ }
1730
+
1731
+ //exception: Types name difference
1732
+ if(!$is && $name == 'Types'){
1733
+ return $this->plugin_is_installed('Types - Complete Solution for Custom Fields and Types', $slug, $version);
1734
+ }
1735
+
1736
+ return $is;
1737
+ }
1738
+
1739
+ public function plugin_is_embedded_version($name, $slug){
1740
+ $is = false;
1741
+
1742
+ $plugins = get_plugins();
1743
+
1744
+ //false if teh full version is also installed
1745
+ $is_full_installed = false;
1746
+ foreach($plugins as $plugin_id => $plugin){
1747
+
1748
+ if(($plugin['Name'] == $name && !preg_match("#-embedded$#", $slug)) ){
1749
+ $is_full_installed = true;
1750
+ break;
1751
+ }
1752
+
1753
+ }
1754
+
1755
+ if($is_full_installed){
1756
+ return false;
1757
+ }
1758
+
1759
+ foreach($plugins as $plugin_id => $plugin){
1760
+
1761
+ // TBD
1762
+ $wp_plugin_slug = dirname($plugin_id);
1763
+ if( $wp_plugin_slug == $slug . '-embedded' && $plugin['Name'] == $name . ' Embedded'){
1764
+ $is = true;
1765
+ break;
1766
+ }
1767
+
1768
+ }
1769
+
1770
+ return $is;
1771
+
1772
+ }
1773
+
1774
+ //Alias for plugin_is_installed
1775
+ public function get_plugin_installed_version($name, $slug){
1776
+
1777
+ return $this->plugin_is_installed($name, $slug);
1778
+
1779
+ }
1780
+
1781
+ public function get_plugin_repository_version($repository_id, $slug){
1782
+ $version = false;
1783
+
1784
+ if(!empty($this->settings['repositories'][$repository_id]['data']['packages'])){
1785
+ foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package){
1786
+ foreach($package['products'] as $product) {
1787
+
1788
+ foreach($product['plugins'] as $plugin_slug){
1789
+
1790
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
1791
+
1792
+ if($download['slug'] == $slug){
1793
+ $version = $download['version'];
1794
+ break (3);
1795
+ }
1796
+
1797
+ }
1798
+
1799
+ }
1800
+ }
1801
+ }
1802
+
1803
+ return $version;
1804
+ }
1805
+
1806
+ public function is_uploading_allowed(){
1807
+
1808
+ //_deprecated_function ( __FUNCTION__, '1.7.3', 'Installer_Dependencies::' . __FUNCTION__ );
1809
+ return $this->dependencies->is_uploading_allowed();
1810
+
1811
+ }
1812
+
1813
+ public function download_plugin_ajax_handler(){
1814
+
1815
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1816
+ require_once $this->plugin_path() . '/includes/installer-upgrader-skins.php';
1817
+
1818
+ $data = json_decode( base64_decode( sanitize_text_field ( $_POST['data'] ) ), true );
1819
+
1820
+ $ret = false;
1821
+ $plugin_id = false;
1822
+ $message = '';
1823
+
1824
+ //validate subscription
1825
+ $site_key = $this->get_repository_site_key($data['repository_id']);
1826
+ $subscription_data = $this->fetch_subscription_data( $data['repository_id'], $site_key , self::SITE_KEY_VALIDATION_SOURCE_DOWNLOAD_REPORT);
1827
+
1828
+ if($subscription_data && !is_wp_error($subscription_data) && $this->repository_has_valid_subscription($data['repository_id'])){
1829
+
1830
+ if($data['nonce'] == wp_create_nonce('install_plugin_' . $data['url'])){
1831
+
1832
+ $upgrader_skins = new Installer_Upgrader_Skins(); //use our custom (mute) Skin
1833
+ $upgrader = new Plugin_Upgrader($upgrader_skins);
1834
+
1835
+ remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
1836
+
1837
+ $plugins = get_plugins();
1838
+
1839
+ //upgrade or install?
1840
+ foreach($plugins as $id => $plugin){
1841
+ $wp_plugin_slug = dirname($id);
1842
+ $is_embedded = $this->plugin_is_embedded_version(preg_replace('/ Embedded$/', '', $plugin['Name']), preg_replace('/-embedded$/', '', $wp_plugin_slug));
1843
+
1844
+ if($wp_plugin_slug == $data['slug'] || $is_embedded && preg_replace('/-embedded$/', '', $wp_plugin_slug) == $data['slug']){
1845
+ $plugin_id = $id;
1846
+ break;
1847
+ }
1848
+ }
1849
+
1850
+ if($plugin_id && empty($is_embedded)){ //upgrade
1851
+ $response['upgrade'] = 1;
1852
+
1853
+ $plugin_is_active = is_plugin_active($plugin_id);
1854
+
1855
+ $ret = $upgrader->upgrade($plugin_id);
1856
+
1857
+ if(!$ret && !empty($upgrader->skin->installer_error)){
1858
+ if(is_wp_error($upgrader->skin->installer_error)){
1859
+ $message = $upgrader->skin->installer_error->get_error_message() .
1860
+ ' (' . $upgrader->skin->installer_error->get_error_data() . ')';
1861
+ }
1862
+ }
1863
+
1864
+ if($plugin_is_active){
1865
+ //prevent redirects
1866
+ add_filter('wp_redirect', '__return_false');
1867
+ activate_plugin($plugin_id);
1868
+ }
1869
+
1870
+ }else{ //install
1871
+
1872
+ if($is_embedded){
1873
+ delete_plugins(array($plugin_id));
1874
+ }
1875
+
1876
+ $response['install'] = 1;
1877
+ $ret = $upgrader->install($data['url']);
1878
+ if(!$ret && !empty($upgrader->skin->installer_error)){
1879
+ if(is_wp_error($upgrader->skin->installer_error)){
1880
+ $message = $upgrader->skin->installer_error->get_error_message() .
1881
+ ' (' . $upgrader->skin->installer_error->get_error_data() . ')';
1882
+ }
1883
+ }
1884
+ }
1885
+
1886
+ $plugins = get_plugins(); //read again
1887
+
1888
+ if($ret && !empty($_POST['activate'])){
1889
+ foreach($plugins as $id => $plugin){
1890
+ $wp_plugin_slug = dirname($id);
1891
+ if($wp_plugin_slug == $data['slug']){
1892
+ $plugin_version = $plugin['Version'];
1893
+ $plugin_id = $id;
1894
+ break;
1895
+ }
1896
+ }
1897
+
1898
+ }
1899
+
1900
+ }
1901
+
1902
+ } else { //subscription not valid
1903
+
1904
+ $ret = false;
1905
+ $message = __('Your subscription appears to no longer be valid. Please try to register again using a valid site key.', 'installer');
1906
+ }
1907
+
1908
+ $response['version'] = isset($plugin_version) ? $plugin_version : 0;
1909
+ $response['plugin_id'] = $plugin_id;
1910
+ $response['nonce'] = wp_create_nonce('activate_' . $plugin_id);
1911
+ $response['success'] = $ret;
1912
+ $response['message'] = $message;
1913
+
1914
+ echo json_encode( $response );
1915
+ exit;
1916
+
1917
+ }
1918
+
1919
+ public function download_plugin($slug, $url){
1920
+
1921
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1922
+ require_once $this->plugin_path() . '/includes/installer-upgrader-skins.php';
1923
+
1924
+ $upgrader_skins = new Installer_Upgrader_Skins(); //use our custom (mute) Skin
1925
+ $upgrader = new Plugin_Upgrader($upgrader_skins);
1926
+
1927
+ remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
1928
+
1929
+ $plugins = get_plugins();
1930
+
1931
+ $plugin_id = false;
1932
+
1933
+ //upgrade or install?
1934
+ foreach($plugins as $id => $plugin){
1935
+ $wp_plugin_slug = dirname($id);
1936
+ if($wp_plugin_slug == $slug){
1937
+ $plugin_id = $id;
1938
+ break;
1939
+ }
1940
+ }
1941
+
1942
+ if($plugin_id){ //upgrade
1943
+
1944
+ $plugin_is_active = is_plugin_active($plugin_id);
1945
+
1946
+ $ret = $upgrader->upgrade($plugin_id);
1947
+
1948
+ if($plugin_is_active){
1949
+ activate_plugin($plugin_id);
1950
+ }
1951
+
1952
+ }else{ //install
1953
+ $ret = $upgrader->install($url);
1954
+ }
1955
+
1956
+ return $ret;
1957
+
1958
+ }
1959
+
1960
+ public function activate_plugin(){
1961
+
1962
+ $error = '';
1963
+
1964
+ $plugin_id = sanitize_text_field ( $_POST['plugin_id'] );
1965
+ if(isset($_POST['nonce']) && $plugin_id && $_POST['nonce'] == wp_create_nonce('activate_' . $plugin_id )){
1966
+
1967
+ // Deactivate any embedded version
1968
+ $plugin_slug = dirname($plugin_id);
1969
+ $active_plugins = get_option('active_plugins');
1970
+ foreach($active_plugins as $plugin){
1971
+ $wp_plugin_slug = dirname($plugin);
1972
+ if($wp_plugin_slug == $plugin_slug . '-embedded'){
1973
+ deactivate_plugins(array($plugin));
1974
+ break;
1975
+ }
1976
+ }
1977
+
1978
+ //prevent redirects
1979
+ add_filter('wp_redirect', '__return_false', 10000);
1980
+
1981
+ $return = activate_plugin($plugin_id);
1982
+
1983
+ if(is_wp_error($return)){
1984
+ $error = $return->get_error_message();
1985
+ }
1986
+
1987
+ }else{
1988
+ $error = 'error';
1989
+ }
1990
+
1991
+ $ret = array('error' => $error);
1992
+
1993
+ echo json_encode($ret);
1994
+ exit;
1995
+
1996
+ }
1997
+
1998
+ public function custom_plugins_api_call($false, $action, $args){
1999
+
2000
+ if($action == 'plugin_information'){
2001
+
2002
+ $plugins = get_plugins();
2003
+ $plugin_names = array();
2004
+ foreach( $plugins as $plugin_id => $plugin ) {
2005
+ // plugins by WP slug which (plugin folder) which can be different
2006
+ // will use this to compare by title
2007
+ $plugin_names[ dirname( $plugin_id ) ] = array(
2008
+ 'name' => $plugin['Name'],
2009
+ 'title' => $plugin['Title'],
2010
+ );
2011
+ }
2012
+
2013
+ $slug = $args->slug;
2014
+
2015
+ foreach($this->settings['repositories'] as $repository_id => $repository){
2016
+
2017
+ if(!$this->repository_has_valid_subscription($repository_id)){
2018
+ $site_key = false;
2019
+ }else{
2020
+ $site_key = $repository['subscription']['key'];
2021
+ }
2022
+
2023
+ foreach($repository['data']['packages'] as $package){
2024
+
2025
+ foreach($package['products'] as $product){
2026
+
2027
+ foreach($product['plugins'] as $plugin_slug){
2028
+
2029
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
2030
+
2031
+ if( $download['slug'] == $slug ||
2032
+ isset( $plugin_names[$slug] ) && (
2033
+ $plugin_names[$slug]['name'] == $download['name'] ||
2034
+ $plugin_names[$slug]['title'] == $download['name']
2035
+ )
2036
+ ){
2037
+
2038
+ if( !empty( $download['free-on-wporg'] ) ){
2039
+ return false; // use data from wordpress.org
2040
+ }
2041
+
2042
+ $res = new stdClass();
2043
+ $res->external = true;
2044
+
2045
+ $res->name = $download['name'];
2046
+ $res->slug = $slug;
2047
+ $res->version = $download['version'];
2048
+ $res->author = '';
2049
+ $res->author_profile = '';
2050
+ $res->last_updated = $download['date'];
2051
+
2052
+ if($site_key){
2053
+ $res->download_link = $this->append_site_key_to_download_url($download['url'], $site_key, $repository_id);
2054
+ }
2055
+
2056
+ $res->homepage = $repository['data']['url'];
2057
+ $res->sections = array('Description' => $download['description'], 'Changelog' => $download['changelog']);
2058
+
2059
+ return $res;
2060
+
2061
+ }
2062
+
2063
+ }
2064
+
2065
+ }
2066
+
2067
+ }
2068
+
2069
+ }
2070
+
2071
+ }
2072
+
2073
+ return $false;
2074
+
2075
+ }
2076
+
2077
+ public function plugins_upgrade_check($update_plugins){
2078
+
2079
+ if(!empty($this->settings['repositories'])){
2080
+
2081
+ $plugins = get_plugins();
2082
+
2083
+ foreach($plugins as $plugin_id => $plugin){
2084
+
2085
+ $slug = dirname($plugin_id);
2086
+ if(empty($slug)) continue;
2087
+
2088
+ $version = $plugin['Version'];
2089
+ $name = $plugin['Name'];
2090
+
2091
+ foreach($this->settings['repositories'] as $repository_id => $repository){
2092
+
2093
+
2094
+ if(!$this->repository_has_valid_subscription($repository_id)){
2095
+ $site_key = false;
2096
+ }else{
2097
+ $site_key = $repository['subscription']['key'];
2098
+ //$subscription_type = $this->get_subscription_type_for_repository($repository_id);
2099
+ }
2100
+
2101
+ foreach($repository['data']['packages'] as $package){
2102
+
2103
+ foreach($package['products'] as $product){
2104
+
2105
+ foreach($product['plugins'] as $plugin_slug){
2106
+
2107
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
2108
+
2109
+ if(!empty($download['free-on-wporg'])) {
2110
+ continue;
2111
+ }
2112
+
2113
+ if(empty($update_plugins->response[$plugin_id]) && ($download['slug'] == $slug || $download['name'] == $name ) && version_compare($download['version'], $version, '>')){
2114
+
2115
+ $response = new stdClass();
2116
+ $response->id = 0;
2117
+ $response->slug = $slug;
2118
+ $response->plugin = $plugin_id;
2119
+ $response->new_version = $download['version'];
2120
+ $response->upgrade_notice = '';
2121
+ $response->url = $download['url'];
2122
+ if($site_key){
2123
+ $response->package = $this->append_site_key_to_download_url($download['url'], $site_key, $repository_id);
2124
+ }
2125
+ $update_plugins->checked[$plugin_id] = $version;
2126
+ $update_plugins->response[$plugin_id] = $response;
2127
+
2128
+ }
2129
+
2130
+ }
2131
+
2132
+ }
2133
+
2134
+ }
2135
+
2136
+ }
2137
+
2138
+ }
2139
+
2140
+ }
2141
+
2142
+ return $update_plugins;
2143
+
2144
+ }
2145
+
2146
+ public function setup_plugins_page_notices(){
2147
+
2148
+ $plugins = get_plugins();
2149
+
2150
+ foreach($plugins as $plugin_id => $plugin){
2151
+
2152
+ $slug = dirname($plugin_id);
2153
+ if(empty($slug)) continue;
2154
+
2155
+ $name = $plugin['Name'];
2156
+
2157
+ foreach($this->settings['repositories'] as $repository_id => $repository){
2158
+
2159
+ if(!$this->repository_has_valid_subscription($repository_id)){
2160
+ $site_key = false;
2161
+ }else{
2162
+ $site_key = $repository['subscription']['key'];
2163
+ }
2164
+
2165
+ foreach($repository['data']['packages'] as $package){
2166
+
2167
+ foreach($package['products'] as $product){
2168
+
2169
+ foreach($product['plugins'] as $plugin_slug){
2170
+
2171
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
2172
+
2173
+ if(!empty($download['free-on-wporg'])) {
2174
+ continue;
2175
+ }
2176
+
2177
+ if( $download['slug'] == $slug || $download['name'] == $name ){
2178
+
2179
+ if( !$site_key || !$this->plugin_is_registered($repository_id, $download['slug']) ){
2180
+ add_action( "after_plugin_row_" . $plugin_id, array($this, 'show_purchase_notice_under_plugin'), 10, 3 );
2181
+ }
2182
+
2183
+ }
2184
+
2185
+ }
2186
+
2187
+ }
2188
+
2189
+ }
2190
+
2191
+ }
2192
+
2193
+ }
2194
+
2195
+ }
2196
+
2197
+ public function show_purchase_notice_under_plugin($plugin_file, $plugin_data, $status){
2198
+
2199
+ $wp_list_table = _get_list_table('WP_Plugins_List_Table');
2200
+ $wp_version = preg_replace( '/-(.+)$/', '', $GLOBALS['wp_version'] );
2201
+
2202
+ if( version_compare( $wp_version, '4.6', '>=' ) ){
2203
+
2204
+ ?>
2205
+ <tr class="plugin-update-tr installer-plugin-update-tr">
2206
+ <td colspan="<?php echo $wp_list_table->get_column_count(); ?>" class="plugin-update colspanchange">
2207
+ <div class="notice inline notice-warning notice-alt">
2208
+ <p class="installer-q-icon">
2209
+ <?php printf( __('You must have a valid subscription in order to get upgrades or support for this plugin. %sPurchase a subscription or enter an existing site key%s.', 'installer'),
2210
+ '<a href="' . $this->menu_url() . '">', '</a>'); ?>
2211
+ </p>
2212
+ </div>
2213
+ </td>
2214
+ </tr>
2215
+ <?php
2216
+
2217
+ } else {
2218
+
2219
+ ?>
2220
+ <tr class="plugin-update-tr">
2221
+ <td colspan="<?php echo $wp_list_table->get_column_count(); ?>" class="plugin-update colspanchange">
2222
+ <div class="update-message installer-q-icon">
2223
+ <?php printf( __('You must have a valid subscription in order to get upgrades or support for this plugin. %sPurchase a subscription or enter an existing site key%s.', 'installer'),
2224
+ '<a href="' . $this->menu_url() . '">', '</a>'); ?>
2225
+ </div>
2226
+ </td>
2227
+ </tr>
2228
+ <?php
2229
+
2230
+ }
2231
+
2232
+ }
2233
+
2234
+ public function localize_strings(){
2235
+
2236
+ if(!empty($this->settings['repositories'])){
2237
+ foreach($this->settings['repositories'] as $repository_id => $repository){
2238
+ //set name as call2action when don't have any
2239
+ //products
2240
+ foreach($repository['data']['packages'] as $package_id => $package){
2241
+ foreach($package['products'] as $product_id => $product){
2242
+ if(empty($product['call2action'])){
2243
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['call2action'] = $product['name'];
2244
+ }
2245
+
2246
+ foreach($product['upgrades'] as $idx => $upg){
2247
+ if(empty($upg['call2action'])){
2248
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['upgrades'][$idx]['call2action'] = $upg['name'];
2249
+ }
2250
+ }
2251
+
2252
+ foreach($product['renewals'] as $idx => $rnw){
2253
+ if(empty($rnw['call2action'])){
2254
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['renewals'][$idx]['call2action'] = $rnw['name'];
2255
+ }
2256
+
2257
+ }
2258
+
2259
+ }
2260
+ }
2261
+ }
2262
+ }
2263
+
2264
+ global $sitepress;
2265
+ if(is_null($sitepress)){
2266
+ return;
2267
+ }
2268
+
2269
+ // default strings are always in English
2270
+ $user_admin_language = $sitepress->get_admin_language();
2271
+
2272
+ if($user_admin_language != 'en'){
2273
+ foreach($this->settings['repositories'] as $repository_id => $repository){
2274
+
2275
+ $localization = $repository['data']['localization'];
2276
+
2277
+ //packages
2278
+ foreach($repository['data']['packages'] as $package_id => $package){
2279
+
2280
+ if( isset($localization['packages'][$package_id]['name'][$user_admin_language]) ){
2281
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['name'] = $localization['packages'][$package_id]['name'][$user_admin_language];
2282
+ }
2283
+ if( isset($localization['packages'][$package_id]['description'][$user_admin_language]) ){
2284
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['description'] = $localization['packages'][$package_id]['description'][$user_admin_language];
2285
+ }
2286
+
2287
+ }
2288
+
2289
+ //products
2290
+ foreach($repository['data']['packages'] as $package_id => $package){
2291
+ foreach($package['products'] as $product_id => $product){
2292
+
2293
+ if( isset($localization['products'][$product_id]['name'][$user_admin_language]) ){
2294
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['name']
2295
+ = $localization['products'][$product_id]['name'][$user_admin_language];
2296
+ }
2297
+ if( isset($localization['products'][$product_id]['description'][$user_admin_language]) ){
2298
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['description']
2299
+ = $localization['products'][$product_id]['description'][$user_admin_language];
2300
+ }
2301
+ if( isset($localization['products'][$product_id]['call2action'][$user_admin_language]) ){
2302
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['name']
2303
+ = $localization['products'][$product_id]['call2action'][$user_admin_language];
2304
+ }
2305
+
2306
+
2307
+ }
2308
+ }
2309
+
2310
+ //subscription info
2311
+ if(isset($repository['data']['subscriptions_meta']['expiration'])){
2312
+ foreach($repository['data']['subscriptions_meta']['expiration'] as $subscription_id => $note){
2313
+ if(isset($localization['subscriptions-notes'][$subscription_id]['expiration-warning'][$user_admin_language])){
2314
+ $this->settings['repositories'][$repository_id]['data']['subscriptions_meta']['expiration'][$subscription_id]['warning_message']
2315
+ = $localization['subscriptions-notes'][$subscription_id]['expiration-warning'][$user_admin_language];
2316
+ }
2317
+ }
2318
+ }
2319
+
2320
+ }
2321
+ }
2322
+
2323
+ }
2324
+
2325
+ public function get_matching_cp($repository, $args = array()){
2326
+ $match = false;
2327
+
2328
+
2329
+ $cp_name = $cp_author = false;
2330
+
2331
+ if(isset($this->config['src_name']) && isset($this->config['src_author'])){
2332
+
2333
+ $cp_name = $this->config['src_name'];
2334
+ $cp_author = $this->config['src_author'];
2335
+
2336
+ }elseif(isset($args['src_name']) && isset($args['src_author'])){
2337
+
2338
+ $cp_name = $args['src_name'];
2339
+ $cp_author = $args['src_author'];
2340
+
2341
+ }
2342
+
2343
+ if(isset($repository['data']['marketing_cp'])){
2344
+
2345
+ foreach($repository['data']['marketing_cp'] as $cp){
2346
+
2347
+ if(!empty($cp['exp']) && time() > $cp['exp']){
2348
+ continue;
2349
+ }
2350
+
2351
+ //Use theme_name for plugins too
2352
+ if(!empty($cp['theme_name'])){
2353
+ if($cp['author_name'] == $cp_author && $cp['theme_name'] == $cp_name){
2354
+ $match = $cp;
2355
+ continue;
2356
+ }
2357
+ }else{
2358
+ if($cp['author_name'] == $cp_author){
2359
+ $match = $cp;
2360
+ continue;
2361
+ }
2362
+ }
2363
+
2364
+ }
2365
+
2366
+ }
2367
+
2368
+ return $match;
2369
+ }
2370
+
2371
+ public function set_filtered_prices($args = array()){
2372
+
2373
+ foreach($this->settings['repositories'] as $repository_id => $repository){
2374
+
2375
+ $match = $this->get_matching_cp($repository, $args);
2376
+
2377
+ if(empty($match)) continue;
2378
+
2379
+ foreach($repository['data']['packages'] as $package_id => $package){
2380
+
2381
+ foreach($package['products'] as $product_id => $product){
2382
+
2383
+ if($match['dtp'] == '%'){
2384
+ $fprice = round( $product['price'] * (1 - $match['amt']/100), 2 );
2385
+ $fprice = $fprice != round($fprice) ? sprintf('%.2f', $fprice) : round($fprice, 0);
2386
+ }elseif($match['dtp'] == '-'){
2387
+ $fprice = $product['price'] - $match['amt'];
2388
+ }else{
2389
+ $fprice = $product['price'];
2390
+ }
2391
+
2392
+ if($fprice){
2393
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['price_disc'] = $fprice;
2394
+
2395
+ $url_glue = false !== strpos($this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['url'], '?') ? '&' : '?';
2396
+ $cpndata = base64_encode(json_encode(array('theme_author' => $match['author_name'], 'theme_name' => $match['theme_name'], 'vlc' => $match['vlc'])));
2397
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['url'] .= $url_glue . 'cpn=' . $cpndata;
2398
+
2399
+ foreach($product['upgrades'] as $upgrade_id => $upgrade){
2400
+
2401
+ $fprice = false;
2402
+ if($match['dtp'] == '%'){
2403
+ $fprice = round( $upgrade['price'] * (1 - $match['amt']/100), 2 );
2404
+ $fprice = $fprice != round($fprice) ? sprintf('%.2f', $fprice) : round($fprice, 0);
2405
+ }elseif($match['dtp'] == '-'){
2406
+ $fprice = $upgrade['price'] - $match['amt'];
2407
+ }
2408
+ if($fprice){
2409
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['upgrades'][$upgrade_id]['price_disc'] = $fprice;
2410
+ $this->settings['repositories'][$repository_id]['data']['packages'][$package_id]['products'][$product_id]['upgrades'][$upgrade_id]['url'] .= $url_glue . 'cpn=' . $cpndata;
2411
+ }
2412
+
2413
+
2414
+ }
2415
+
2416
+ }
2417
+
2418
+ }
2419
+
2420
+ }
2421
+
2422
+ }
2423
+
2424
+ }
2425
+
2426
+ public function set_hierarchy_and_order(){
2427
+
2428
+ //2 levels
2429
+ if(!empty($this->settings['repositories'])) {
2430
+ foreach ($this->settings['repositories'] as $repository_id => $repository) {
2431
+
2432
+ if( empty( $repository['data']['packages'] ) ) continue;
2433
+
2434
+ $all_packages = $repository['data']['packages'];
2435
+ $ordered_packages = array();
2436
+
2437
+ //backward compatibility - 'order'
2438
+ foreach($all_packages as $k => $v){
2439
+ if(!isset($v['order'])){
2440
+ $all_packages[$k]['order'] = 0;
2441
+ }
2442
+ }
2443
+
2444
+ //select parents
2445
+ foreach ($all_packages as $package_id => $package) {
2446
+ if(empty($package['parent'])){
2447
+ $ordered_packages[$package_id] = $package;
2448
+ }
2449
+ }
2450
+
2451
+ //add sub-packages
2452
+ foreach($all_packages as $package_id => $package){
2453
+ if(!empty($package['parent'])) {
2454
+ if(isset($ordered_packages[$package['parent']])){
2455
+ $ordered_packages[$package['parent']]['sub-packages'][$package_id] = $package;
2456
+ }
2457
+ }
2458
+ }
2459
+
2460
+ // order parents
2461
+ usort($ordered_packages, array($this, '_order_packages_callback'));
2462
+ //order sub-packages
2463
+ foreach($ordered_packages as $package_id => $package){
2464
+ if(!empty($package['sub-packages'])) {
2465
+ usort($ordered_packages[$package_id]['sub-packages'], create_function('$a, $b', 'return $a[\'order\'] > $b[\'order\'];'));
2466
+ }
2467
+ }
2468
+
2469
+ $this->settings['repositories'][$repository_id]['data']['packages'] = $ordered_packages;
2470
+
2471
+
2472
+ }
2473
+ }
2474
+
2475
+
2476
+ }
2477
+
2478
+ public function _order_packages_callback($a, $b){
2479
+ return $a['order'] > $b['order'];
2480
+ }
2481
+
2482
+ public function get_support_tag_by_name( $name, $repository ){
2483
+
2484
+ if( is_array($this->settings['repositories'][$repository]['data']['support_tags'] )){
2485
+ foreach( $this->settings['repositories'][$repository]['data']['support_tags'] as $support_tag){
2486
+ if( $support_tag['name'] == $name ){
2487
+ return $support_tag['url'];
2488
+ }
2489
+ }
2490
+ }
2491
+
2492
+ return false;
2493
+ }
2494
+
2495
+ public function plugin_upgrade_custom_errors(){
2496
+
2497
+ if ( isset($_REQUEST['action']) ) {
2498
+
2499
+ $action = isset($_REQUEST['action']) ? sanitize_text_field ( $_REQUEST['action'] ) : '';
2500
+
2501
+ //bulk mode
2502
+ if('update-selected' == $action) {
2503
+
2504
+ global $plugins;
2505
+
2506
+ if(isset($plugins) && is_array($plugins)) {
2507
+
2508
+ foreach ($plugins as $k => $plugin) {
2509
+ $plugin_repository = false;
2510
+
2511
+ $wp_plugin_slug = dirname($plugin);
2512
+
2513
+ foreach ($this->settings['repositories'] as $repository_id => $repository) {
2514
+
2515
+ foreach ($repository['data']['packages'] as $package) {
2516
+
2517
+ foreach ($package['products'] as $product) {
2518
+
2519
+ foreach ($product['plugins'] as $plugin_slug) {
2520
+
2521
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
2522
+
2523
+ if ($download['slug'] == $wp_plugin_slug) {
2524
+ $plugin_repository = $repository_id;
2525
+ $product_name = $repository['data']['product-name'];
2526
+ $plugin_name = $download['name'];
2527
+ $free_on_wporg = !empty($download['free-on-wporg']);
2528
+ break;
2529
+ }
2530
+
2531
+ }
2532
+
2533
+ }
2534
+
2535
+ }
2536
+
2537
+ }
2538
+
2539
+ if ($plugin_repository) {
2540
+
2541
+ //validate subscription
2542
+ static $sub_cache = array();
2543
+
2544
+ if(empty($sub_cache[$plugin_repository])){
2545
+ $site_key = $this->get_repository_site_key($plugin_repository);
2546
+ if ($site_key) {
2547
+ $subscription_data = $this->fetch_subscription_data( $plugin_repository, $site_key, self::SITE_KEY_VALIDATION_SOURCE_REVALIDATION );
2548
+ }
2549
+
2550
+ $sub_cache[$plugin_repository]['site_key'] = $site_key;
2551
+ $sub_cache[$plugin_repository]['subscription_data'] = isset($subscription_data) ? $subscription_data : false;
2552
+ }else{
2553
+
2554
+ $site_key = $sub_cache[$plugin_repository]['site_key'];
2555
+ $subscription_data = $sub_cache[$plugin_repository]['subscription_data'];
2556
+
2557
+ }
2558
+
2559
+ if(!$site_key && !empty($free_on_wporg)){ // allow the download from wp.org
2560
+ continue;
2561
+ }
2562
+
2563
+ if (empty($site_key) || empty($subscription_data)) {
2564
+
2565
+
2566
+ $error_message = sprintf(__("%s cannot update because your site's registration is not valid. Please %sregister %s%s again for this site first.", 'installer'),
2567
+ '<strong>' . $plugin_name . '</strong>', '<a target="_top" href="' . $this->menu_url() . '&validate_repository=' . $plugin_repository .
2568
+ '#repository-' . $plugin_repository . '">', $product_name, '</a>');
2569
+
2570
+ echo '<div class="updated error"><p>' . $error_message . '</p></div>';
2571
+
2572
+ unset($plugins[$k]);
2573
+
2574
+
2575
+ }
2576
+
2577
+ }
2578
+
2579
+ }
2580
+
2581
+ }
2582
+
2583
+ }
2584
+
2585
+
2586
+ if( 'upgrade-plugin' == $action || 'update-plugin' == $action ) {
2587
+
2588
+ $plugin = isset($_REQUEST['plugin']) ? trim( sanitize_text_field ( $_REQUEST['plugin'] ) ) : '';
2589
+
2590
+ $wp_plugin_slug = dirname($plugin);
2591
+
2592
+ $plugin_repository = false;
2593
+
2594
+ foreach($this->settings['repositories'] as $repository_id => $repository){
2595
+
2596
+ foreach($repository['data']['packages'] as $package){
2597
+
2598
+ foreach($package['products'] as $product){
2599
+
2600
+ foreach($product['plugins'] as $plugin_slug){
2601
+ $download = $this->settings['repositories'][$repository_id]['data']['downloads']['plugins'][$plugin_slug];
2602
+
2603
+ //match by folder, will change to match by name and folder
2604
+ if($download['slug'] == $wp_plugin_slug) {
2605
+ $plugin_repository = $repository_id;
2606
+ $product_name = $repository['data']['product-name'];
2607
+ $plugin_name = $download['name'];
2608
+ $free_on_wporg = !empty($download['free-on-wporg']);
2609
+ break;
2610
+ }
2611
+
2612
+ }
2613
+
2614
+ }
2615
+
2616
+ }
2617
+
2618
+ }
2619
+
2620
+ if($plugin_repository) {
2621
+
2622
+ //validate subscription
2623
+ $site_key = $this->get_repository_site_key($plugin_repository);
2624
+ if ($site_key) {
2625
+ $subscription_data = $this->fetch_subscription_data( $plugin_repository, $site_key, self::SITE_KEY_VALIDATION_SOURCE_REVALIDATION );
2626
+ }
2627
+
2628
+ if ( (empty($site_key) || empty($subscription_data)) && empty($free_on_wporg)) {
2629
+
2630
+ $error_message = sprintf(__("%s cannot update because your site's registration is not valid. Please %sregister %s%s again for this site first.", 'installer'),
2631
+ '<strong>'.$plugin_name . '</strong>', '<a href="' . $this->menu_url() . '&validate_repository=' . $plugin_repository .
2632
+ '#repository-' . $plugin_repository . '">', $product_name, '</a>');
2633
+
2634
+ if(defined('DOING_AJAX')){ //WP 4.2
2635
+
2636
+ $status = array(
2637
+ 'update' => 'plugin',
2638
+ 'plugin' => $plugin,
2639
+ 'slug' => sanitize_key( $_POST['slug'] ),
2640
+ 'oldVersion' => '',
2641
+ 'newVersion' => '',
2642
+ );
2643
+
2644
+ $status['errorCode'] = 'wp_installer_invalid_subscription';
2645
+ $status['error'] = $error_message;
2646
+
2647
+ wp_send_json_error( $status );
2648
+
2649
+ } else { // WP 4.1.1
2650
+ echo '<div class="updated error"><p>' . $error_message . '</p></div>';
2651
+
2652
+
2653
+ echo '<div class="wrap">';
2654
+ echo '<h2>' . __('Update Plugin') . '</h2>';
2655
+ echo '<a href="' . admin_url('plugins.php') . '">' . __('Return to the plugins page') . '</a>';
2656
+ echo '</div>';
2657
+ require_once(ABSPATH . 'wp-admin/admin-footer.php');
2658
+ exit;
2659
+
2660
+ }
2661
+
2662
+ }
2663
+
2664
+
2665
+ }
2666
+
2667
+ }
2668
+ }
2669
+
2670
+ }
2671
+
2672
+ }
library/otgs/installer/includes/translation-service-info.class.php CHANGED
@@ -1,40 +1,40 @@
1
- <?php
2
-
3
- class TranslationServiceInfo{
4
-
5
- function __construct(){
6
-
7
- add_action('installer_fetched_subscription_data',array($this, 'save_info'), 10, 2);
8
-
9
- }
10
-
11
- function save_info($data, $repository_id) {
12
-
13
- $ts_info = isset( WP_Installer()->settings['repositories'][$repository_id]['ts_info'] ) ?
14
- WP_Installer()->settings['repositories'][$repository_id]['ts_info'] : false;
15
-
16
- $save_settings = false;
17
- if(isset($data->ts_info['preferred']) && empty($ts_info['preferred'])){
18
- WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'] = $data->ts_info['preferred'];
19
- $save_settings = true;
20
- }
21
-
22
- if(isset($data->ts_info['referal']) && empty($ts_info['referal'])){
23
- WP_Installer()->settings['repositories'][$repository_id]['ts_info']['referal'] = $data->ts_info['referal'];
24
- $save_settings = true;
25
- }
26
-
27
- if ( !empty( $data->ts_info['client_id'] ) ) { // can be updated
28
- WP_Installer()->settings['repositories'][$repository_id]['ts_info']['client_id'] = $data->ts_info['client_id'];
29
- $save_settings = true;
30
- }
31
-
32
- if($save_settings){
33
- WP_Installer()->save_settings();
34
- }
35
-
36
- }
37
-
38
- }
39
-
40
  new TranslationServiceInfo();
1
+ <?php
2
+
3
+ class TranslationServiceInfo{
4
+
5
+ function __construct(){
6
+
7
+ add_action('installer_fetched_subscription_data',array($this, 'save_info'), 10, 2);
8
+
9
+ }
10
+
11
+ function save_info($data, $repository_id) {
12
+
13
+ $ts_info = isset( WP_Installer()->settings['repositories'][$repository_id]['ts_info'] ) ?
14
+ WP_Installer()->settings['repositories'][$repository_id]['ts_info'] : false;
15
+
16
+ $save_settings = false;
17
+ if(isset($data->ts_info['preferred']) && empty($ts_info['preferred'])){
18
+ WP_Installer()->settings['repositories'][$repository_id]['ts_info']['preferred'] = $data->ts_info['preferred'];
19
+ $save_settings = true;
20
+ }
21
+
22
+ if(isset($data->ts_info['referal']) && empty($ts_info['referal'])){
23
+ WP_Installer()->settings['repositories'][$repository_id]['ts_info']['referal'] = $data->ts_info['referal'];
24
+ $save_settings = true;
25
+ }
26
+
27
+ if ( !empty( $data->ts_info['client_id'] ) ) { // can be updated
28
+ WP_Installer()->settings['repositories'][$repository_id]['ts_info']['client_id'] = $data->ts_info['client_id'];
29
+ $save_settings = true;
30
+ }
31
+
32
+ if($save_settings){
33
+ WP_Installer()->save_settings();
34
+ }
35
+
36
+ }
37
+
38
+ }
39
+
40
  new TranslationServiceInfo();
library/otgs/installer/installer.php CHANGED
@@ -1,22 +1,22 @@
1
- <?php
2
- define('WP_INSTALLER_VERSION', '1.7.11');
3
-
4
- include_once dirname(__FILE__) . '/includes/installer.class.php';
5
-
6
- function WP_Installer() {
7
- return WP_Installer::instance();
8
- }
9
-
10
-
11
- WP_Installer();
12
-
13
- include_once WP_Installer()->plugin_path() . '/includes/installer-api.php';
14
- include_once WP_Installer()->plugin_path() . '/includes/translation-service-info.class.php';
15
- include_once WP_Installer()->plugin_path() . '/includes/class-installer-dependencies.php';
16
-
17
- // Ext function
18
- function WP_Installer_Show_Products($args = array()){
19
-
20
- WP_Installer()->show_products($args);
21
-
22
  }
1
+ <?php
2
+ define('WP_INSTALLER_VERSION', '1.7.13');
3
+
4
+ include_once dirname(__FILE__) . '/includes/installer.class.php';
5
+
6
+ function WP_Installer() {
7
+ return WP_Installer::instance();
8
+ }
9
+
10
+
11
+ WP_Installer();
12
+
13
+ include_once WP_Installer()->plugin_path() . '/includes/installer-api.php';
14
+ include_once WP_Installer()->plugin_path() . '/includes/translation-service-info.class.php';
15
+ include_once WP_Installer()->plugin_path() . '/includes/class-installer-dependencies.php';
16
+
17
+ // Ext function
18
+ function WP_Installer_Show_Products($args = array()){
19
+
20
+ WP_Installer()->show_products($args);
21
+
22
  }
library/otgs/installer/loader.php CHANGED
@@ -1,151 +1,151 @@
1
- <?php
2
-
3
- if ( ! defined( 'ABSPATH' ) ) {
4
- exit; // Exit if accessed directly
5
- }
6
-
7
- //It should only be loaded on the admin side
8
- if( !is_admin() ){
9
- if(!function_exists('WP_Installer_Setup')){ function WP_Installer_Setup(){} }
10
- $wp_installer_instance = null;
11
- return;
12
- }
13
-
14
-
15
- $wp_installer_instance = dirname(__FILE__) . '/installer.php';
16
-
17
-
18
- // Global stack of instances
19
- global $wp_installer_instances;
20
- $wp_installer_instances[$wp_installer_instance] = array(
21
- 'bootfile' => $wp_installer_instance,
22
- 'version' => '1.7.11'
23
- );
24
-
25
-
26
- /* EXCEPTIONS ********************************************************************************************/
27
- // Exception: When WPML prior 3.2 is used, that instance must be used regardless of another newer instance
28
- // Case 1: WPML loaded before Types - eliminate other instances
29
- if( defined('ICL_SITEPRESS_VERSION') && version_compare(ICL_SITEPRESS_VERSION, '3.2', '<') ) {
30
- foreach($wp_installer_instances as $key => $instance) {
31
- if(isset($instance['args']['site_key_nags'])){
32
- $wp_installer_instances[$key]['version'] = '9.9';
33
- }else{
34
- $wp_installer_instances[$key]['version'] = '0';
35
- }
36
- }
37
- }
38
-
39
- // Exception: Types 1.8.9 (Installer 1.7.0) with WPML before 3.3 (Installer before 1.7.0)
40
- // New products file http://d2salfytceyqoe.cloudfront.net/wpml-products33.json overrides the old one
41
- // while the WPML's instance is being used
42
- // => Force using the new Installer Instance
43
- if( defined('ICL_SITEPRESS_VERSION') && version_compare(ICL_SITEPRESS_VERSION, '3.3.1', '<') ) {
44
-
45
- // if Installer 1.7.0+ is present, unregister Installer from old WPML
46
- // Force Installer 1.7.0+ being used over older Installer versions
47
- $installer_171_plus_on = false;
48
- foreach($wp_installer_instances as $key => $instance) {
49
- if( version_compare( $instance['version'], '1.7.1', '>=' ) ){
50
- $installer_171_plus_on = true;
51
- break;
52
- }
53
- }
54
-
55
- if( $installer_171_plus_on ){
56
- foreach($wp_installer_instances as $key => $instance) {
57
-
58
- if( version_compare( $instance['version'], '1.7.0', '<' ) ){
59
- unset( $wp_installer_instances[$key] );
60
- }
61
-
62
- }
63
- }
64
-
65
- }
66
-
67
- // Exception: When using the embedded plugins module allow the set up to run completely with the
68
- // Installer instance that triggers it
69
- if( isset( $_POST['installer_instance'] ) && isset( $wp_installer_instances[$_POST['installer_instance']] ) ){
70
- $wp_installer_instances[$_POST['installer_instance']]['version'] = '999';
71
- }
72
- /* EXCEPTIONS ********************************************************************************************/
73
-
74
-
75
- // Only one of these in the end
76
- remove_action('after_setup_theme', 'wpml_installer_instance_delegator', 1);
77
- add_action('after_setup_theme', 'wpml_installer_instance_delegator', 1);
78
-
79
- // When all plugins load pick the newest version
80
- if(!function_exists('wpml_installer_instance_delegator')){
81
- function wpml_installer_instance_delegator(){
82
- global $wp_installer_instances;
83
-
84
- // version based election
85
- foreach($wp_installer_instances as $instance){
86
-
87
- if(!isset($delegate)){
88
- $delegate = $instance;
89
- continue;
90
- }
91
-
92
- if(version_compare($instance['version'], $delegate['version'], '>')){
93
- $delegate = $instance;
94
- }
95
- }
96
-
97
- // priority based election
98
- $highest_priority = null;
99
- foreach($wp_installer_instances as $instance) {
100
- if(isset($instance['args']['high_priority'])){
101
- if(is_null($highest_priority) || $instance['args']['high_priority'] <= $highest_priority){
102
- $highest_priority = $instance['args']['high_priority'];
103
- $delegate = $instance;
104
- }
105
- }
106
- }
107
-
108
- // Exception: When WPML prior 3.2 is used, that instance must be used regardless of another newer instance
109
- // Case 2: WPML loaded after Types
110
- if( defined('ICL_SITEPRESS_VERSION') && version_compare(ICL_SITEPRESS_VERSION, '3.2', '<') ) {
111
- foreach($wp_installer_instances as $key => $instance) {
112
- if(isset($instance['args']['site_key_nags'])){
113
- $delegate = $instance;
114
- $wp_installer_instances = array($key => $delegate); //Eliminate other instances
115
- break;
116
- }
117
- }
118
- }
119
-
120
- include_once $delegate['bootfile'];
121
-
122
- // set configuration
123
- if(strpos(realpath($delegate['bootfile']), realpath(TEMPLATEPATH)) === 0){
124
- $delegate['args']['in_theme_folder'] = dirname(ltrim(str_replace(realpath(TEMPLATEPATH), '', realpath($delegate['bootfile'])), '\\/'));
125
- }
126
- if(isset($delegate['args']) && is_array($delegate['args'])){
127
- foreach($delegate['args'] as $key => $value){
128
- WP_Installer()->set_config($key, $value);
129
- }
130
- }
131
-
132
- }
133
- }
134
-
135
- if(!function_exists('WP_Installer_Setup')){
136
-
137
- // $args:
138
- // plugins_install_tab = true|false (default: true)
139
- // repositories_include = array() (default: all)
140
- // repositories_exclude = array() (default: none)
141
- // template = name (default: default)
142
- //
143
- // Ext function
144
- function WP_Installer_Setup($wp_installer_instance, $args = array()){
145
- global $wp_installer_instances;
146
-
147
- $wp_installer_instances[$wp_installer_instance]['args'] = $args;
148
-
149
- }
150
-
151
  }
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ //It should only be loaded on the admin side
8
+ if( !is_admin() ){
9
+ if(!function_exists('WP_Installer_Setup')){ function WP_Installer_Setup(){} }
10
+ $wp_installer_instance = null;
11
+ return;
12
+ }
13
+
14
+
15
+ $wp_installer_instance = dirname(__FILE__) . '/installer.php';
16
+
17
+
18
+ // Global stack of instances
19
+ global $wp_installer_instances;
20
+ $wp_installer_instances[$wp_installer_instance] = array(
21
+ 'bootfile' => $wp_installer_instance,
22
+ 'version' => '1.7.13'
23
+ );
24
+
25
+
26
+ /* EXCEPTIONS ********************************************************************************************/
27
+ // Exception: When WPML prior 3.2 is used, that instance must be used regardless of another newer instance
28
+ // Case 1: WPML loaded before Types - eliminate other instances
29
+ if( defined('ICL_SITEPRESS_VERSION') && version_compare(ICL_SITEPRESS_VERSION, '3.2', '<') ) {
30
+ foreach($wp_installer_instances as $key => $instance) {
31
+ if(isset($instance['args']['site_key_nags'])){
32
+ $wp_installer_instances[$key]['version'] = '9.9';
33
+ }else{
34
+ $wp_installer_instances[$key]['version'] = '0';
35
+ }
36
+ }
37
+ }
38
+
39
+ // Exception: Types 1.8.9 (Installer 1.7.0) with WPML before 3.3 (Installer before 1.7.0)
40
+ // New products file http://d2salfytceyqoe.cloudfront.net/wpml-products33.json overrides the old one
41
+ // while the WPML's instance is being used
42
+ // => Force using the new Installer Instance
43
+ if( defined('ICL_SITEPRESS_VERSION') && version_compare(ICL_SITEPRESS_VERSION, '3.3.1', '<') ) {
44
+
45
+ // if Installer 1.7.0+ is present, unregister Installer from old WPML
46
+ // Force Installer 1.7.0+ being used over older Installer versions
47
+ $installer_171_plus_on = false;
48
+ foreach($wp_installer_instances as $key => $instance) {
49
+ if( version_compare( $instance['version'], '1.7.1', '>=' ) ){
50
+ $installer_171_plus_on = true;
51
+ break;
52
+ }
53
+ }
54
+
55
+ if( $installer_171_plus_on ){
56
+ foreach($wp_installer_instances as $key => $instance) {
57
+
58
+ if( version_compare( $instance['version'], '1.7.0', '<' ) ){
59
+ unset( $wp_installer_instances[$key] );
60
+ }
61
+
62
+ }
63
+ }
64
+
65
+ }
66
+
67
+ // Exception: When using the embedded plugins module allow the set up to run completely with the
68
+ // Installer instance that triggers it
69
+ if( isset( $_POST['installer_instance'] ) && isset( $wp_installer_instances[$_POST['installer_instance']] ) ){
70
+ $wp_installer_instances[$_POST['installer_instance']]['version'] = '999';
71
+ }
72
+ /* EXCEPTIONS ********************************************************************************************/
73
+
74
+
75
+ // Only one of these in the end
76
+ remove_action('after_setup_theme', 'wpml_installer_instance_delegator', 1);
77
+ add_action('after_setup_theme', 'wpml_installer_instance_delegator', 1);
78
+
79
+ // When all plugins load pick the newest version
80
+ if(!function_exists('wpml_installer_instance_delegator')){
81
+ function wpml_installer_instance_delegator(){
82
+ global $wp_installer_instances;
83
+
84
+ // version based election
85
+ foreach($wp_installer_instances as $instance){
86
+
87
+ if(!isset($delegate)){
88
+ $delegate = $instance;
89
+ continue;
90
+ }
91
+
92
+ if(version_compare($instance['version'], $delegate['version'], '>')){
93
+ $delegate = $instance;
94
+ }
95
+ }
96
+
97
+ // priority based election
98
+ $highest_priority = null;
99
+ foreach($wp_installer_instances as $instance) {
100
+ if(isset($instance['args']['high_priority'])){
101
+ if(is_null($highest_priority) || $instance['args']['high_priority'] <= $highest_priority){
102
+ $highest_priority = $instance['args']['high_priority'];
103
+ $delegate = $instance;
104
+ }
105
+ }
106
+ }
107
+
108
+ // Exception: When WPML prior 3.2 is used, that instance must be used regardless of another newer instance
109
+ // Case 2: WPML loaded after Types
110
+ if( defined('ICL_SITEPRESS_VERSION') && version_compare(ICL_SITEPRESS_VERSION, '3.2', '<') ) {
111
+ foreach($wp_installer_instances as $key => $instance) {
112
+ if(isset($instance['args']['site_key_nags'])){
113
+ $delegate = $instance;
114
+ $wp_installer_instances = array($key => $delegate); //Eliminate other instances
115
+ break;
116
+ }
117
+ }
118
+ }
119
+
120
+ include_once $delegate['bootfile'];
121
+
122
+ // set configuration
123
+ if(strpos(realpath($delegate['bootfile']), realpath(TEMPLATEPATH)) === 0){
124
+ $delegate['args']['in_theme_folder'] = dirname(ltrim(str_replace(realpath(TEMPLATEPATH), '', realpath($delegate['bootfile'])), '\\/'));
125
+ }
126
+ if(isset($delegate['args']) && is_array($delegate['args'])){
127
+ foreach($delegate['args'] as $key => $value){
128
+ WP_Installer()->set_config($key, $value);
129
+ }
130
+ }
131
+
132
+ }
133
+ }
134
+
135
+ if(!function_exists('WP_Installer_Setup')){
136
+
137
+ // $args:
138
+ // plugins_install_tab = true|false (default: true)
139
+ // repositories_include = array() (default: all)
140
+ // repositories_exclude = array() (default: none)
141
+ // template = name (default: default)
142
+ //
143
+ // Ext function
144
+ function WP_Installer_Setup($wp_installer_instance, $args = array()){
145
+ global $wp_installer_instances;
146
+
147
+ $wp_installer_instances[$wp_installer_instance]['args'] = $args;
148
+
149
+ }
150
+
151
  }
library/otgs/installer/locale/orig/installer.po CHANGED
@@ -1,230 +1,230 @@
1
- # This file was generated by WPML
2
- # WPML is a WordPress plugin that can turn any WordPress site into a full featured multilingual content management system.
3
- # https://wpml.org
4
- msgid ""
5
- msgstr ""
6
- "Content-Type: text/plain; charset=utf-8\n"
7
- "Content-Transfer-Encoding: 8bit\n"
8
- "Project-Id-Version:WPML_EXPORT\n"
9
- "POT-Creation-Date: \n"
10
- "PO-Revision-Date: \n"
11
- "Last-Translator: \n"
12
- "Language-Team: \n"
13
- "Language:en\n"
14
- "MIME-Version: 1.0\n"
15
-
16
- msgid "Installer"
17
- msgstr ""
18
-
19
- msgid "Registered"
20
- msgstr ""
21
-
22
- msgid "Register"
23
- msgstr ""
24
-
25
- msgid "To get automatic updates, you need to register %s for this site. %sRegister %s%s"
26
- msgstr ""
27
-
28
- msgid "Dismiss"
29
- msgstr ""
30
-
31
- msgid "Commercial"
32
- msgstr ""
33
-
34
- msgid "Installer cannot contact our updates server to get information about the available products and check for new versions. If you are seeing this message for the first time, you can ignore it, as it may be a temporary communication problem. If the problem persists and your WordPress admin is slowing down, you can disable automated version checks. Add the following line to your wp-config.php file:"
35
- msgstr ""
36
-
37
- msgid "No repositories defined."
38
- msgstr ""
39
-
40
- msgid "%s cannot access %s to register. Try again to see if it's a temporary problem. If the problem continues, make sure that this site has access to the Internet. You can still use the plugin without registration, but you will not receive automated updates."
41
- msgstr ""
42
-
43
- msgid "Invalid site key for the current site."
44
- msgstr ""
45
-
46
- msgid "You will have to renew your subscription in order to continue getting the updates and support."
47
- msgstr ""
48
-
49
- msgid "%sRenew here%s."
50
- msgstr ""
51
-
52
- msgid "Your subscription appears to no longer be valid. Please try to register again using a valid site key."
53
- msgstr ""
54
-
55
- msgid "You need to have a valid subscription in order to get upgrades or support for this plugin. %sPurchase a subscription or enter an existing site key%s."
56
- msgstr ""
57
-
58
- msgid "%s cannot update because your site's registration is not valid. Please %sregister %s%s again for this site first."
59
- msgstr ""
60
-
61
- msgid "Update Plugin"
62
- msgstr ""
63
-
64
- msgid "Return to the plugins page"
65
- msgstr ""
66
-
67
- msgid "Your subscription expires in %d day."
68
- msgstr ""
69
-
70
- msgid "Your subscription expires in %d days."
71
- msgstr ""
72
-
73
- msgid "Downloading is not possible because WordPress cannot write into the plugins folder. %sHow to fix%s."
74
- msgstr ""
75
-
76
- msgid "Plugin"
77
- msgstr ""
78
-
79
- msgid "downloading..."
80
- msgstr ""
81
-
82
- msgid "failed!"
83
- msgstr ""
84
-
85
- msgid "downloaded"
86
- msgstr ""
87
-
88
- msgid "activating"
89
- msgstr ""
90
-
91
- msgid "activated"
92
- msgstr ""
93
-
94
- msgid "Activate after download"
95
- msgstr ""
96
-
97
- msgid "Operation complete!"
98
- msgstr ""
99
-
100
- msgid "Download failed!\n\nClick OK to revalidate your subscription or CANCEL to try again."
101
- msgstr ""
102
-
103
- msgid "Available"
104
- msgstr ""
105
-
106
- msgid "Installed"
107
- msgstr ""
108
-
109
- msgid "Downloading"
110
- msgstr ""
111
-
112
- msgid "Activate"
113
- msgstr ""
114
-
115
- msgid "Download"
116
- msgstr ""
117
-
118
- msgid "Downloads:"
119
- msgstr ""
120
-
121
- msgid "Current version"
122
- msgstr ""
123
-
124
- msgid "Released"
125
- msgstr ""
126
-
127
- msgid "Installed version"
128
- msgstr ""
129
-
130
- msgid "(embedded)"
131
- msgstr ""
132
-
133
- msgid "installing..."
134
- msgstr ""
135
-
136
- msgid "updating..."
137
- msgstr ""
138
-
139
- msgid "installed"
140
- msgstr ""
141
-
142
- msgid "updated"
143
- msgstr ""
144
-
145
- msgid "Download failed!\n\nPlease refresh the page and try again."
146
- msgstr ""
147
-
148
- msgid "Incorrect setup"
149
- msgstr ""
150
-
151
- msgid "Invalid product"
152
- msgstr ""
153
-
154
- msgid "Unknown repository"
155
- msgstr ""
156
-
157
- msgid " Your current site key (%s) does not match the selected product (%s)."
158
- msgstr ""
159
-
160
- msgid "Buy %s"
161
- msgstr ""
162
-
163
- msgid "Already bought %s?"
164
- msgstr ""
165
-
166
- msgid "Renew %s"
167
- msgstr ""
168
-
169
- msgid "Remove current site key (%s)"
170
- msgstr ""
171
-
172
- msgid "%s support on wpml.org"
173
- msgstr ""
174
-
175
- msgid "Enter site key"
176
- msgstr ""
177
-
178
- msgid "Subscription is expired."
179
- msgstr ""
180
-
181
- msgid "Add"
182
- msgstr ""
183
-
184
- msgid "Are you sure you want to remove this site key?"
185
- msgstr ""
186
-
187
- msgid "Register %s"
188
- msgstr ""
189
-
190
- msgid "1. Go to your %s%s account%s and add this site URL: %s"
191
- msgstr ""
192
-
193
- msgid "Unregister %s from this site"
194
- msgstr ""
195
-
196
- msgid "%s is registered on this site. You will receive automatic updates until %s"
197
- msgstr ""
198
-
199
- msgid "%s is registered on this site. Your Lifetime account gives you updates for life."
200
- msgstr ""
201
-
202
- msgid "This page lets you install plugins and update existing plugins. To remove any of these plugins, go to the %splugins%s page and if you have the permission to remove plugins you should be able to do this."
203
- msgstr ""
204
-
205
- msgid "Already bought?"
206
- msgstr ""
207
-
208
- msgid "2. Enter your site key"
209
- msgstr ""
210
-
211
- msgid "Subscription is expired. You need to either purchase a new subscription or upgrade if available."
212
- msgstr ""
213
-
214
- msgid "Check for updates"
215
- msgstr ""
216
-
217
- msgid "Individual components"
218
- msgstr ""
219
-
220
- msgid "OK"
221
- msgstr ""
222
-
223
- msgid "Cancel"
224
- msgstr ""
225
-
226
- msgid "Are you sure you want to unregister?"
227
- msgstr ""
228
-
229
- msgid "Click to see individual components options."
230
- msgstr ""
1
+ # This file was generated by WPML
2
+ # WPML is a WordPress plugin that can turn any WordPress site into a full featured multilingual content management system.
3
+ # https://wpml.org
4
+ msgid ""
5
+ msgstr ""
6
+ "Content-Type: text/plain; charset=utf-8\n"
7
+ "Content-Transfer-Encoding: 8bit\n"
8
+ "Project-Id-Version:WPML_EXPORT\n"
9
+ "POT-Creation-Date: \n"
10
+ "PO-Revision-Date: \n"
11
+ "Last-Translator: \n"
12
+ "Language-Team: \n"
13
+ "Language:en\n"
14
+ "MIME-Version: 1.0\n"
15
+
16
+ msgid "Installer"
17
+ msgstr ""
18
+
19
+ msgid "Registered"
20
+ msgstr ""
21
+
22
+ msgid "Register"
23
+ msgstr ""
24
+
25
+ msgid "To get automatic updates, you need to register %s for this site. %sRegister %s%s"
26
+ msgstr ""
27
+
28
+ msgid "Dismiss"
29
+ msgstr ""
30
+
31
+ msgid "Commercial"
32
+ msgstr ""
33
+
34
+ msgid "Installer cannot contact our updates server to get information about the available products and check for new versions. If you are seeing this message for the first time, you can ignore it, as it may be a temporary communication problem. If the problem persists and your WordPress admin is slowing down, you can disable automated version checks. Add the following line to your wp-config.php file:"
35
+ msgstr ""
36
+
37
+ msgid "No repositories defined."
38
+ msgstr ""
39
+
40
+ msgid "%s cannot access %s to register. Try again to see if it's a temporary problem. If the problem continues, make sure that this site has access to the Internet. You can still use the plugin without registration, but you will not receive automated updates."
41
+ msgstr ""
42
+
43
+ msgid "Invalid site key for the current site."
44
+ msgstr ""
45
+
46
+ msgid "You will have to renew your subscription in order to continue getting the updates and support."
47
+ msgstr ""
48
+
49
+ msgid "%sRenew here%s."
50
+ msgstr ""
51
+
52
+ msgid "Your subscription appears to no longer be valid. Please try to register again using a valid site key."
53
+ msgstr ""
54
+
55
+ msgid "You need to have a valid subscription in order to get upgrades or support for this plugin. %sPurchase a subscription or enter an existing site key%s."
56
+ msgstr ""
57
+
58
+ msgid "%s cannot update because your site's registration is not valid. Please %sregister %s%s again for this site first."
59
+ msgstr ""
60
+
61
+ msgid "Update Plugin"
62
+ msgstr ""
63
+
64
+ msgid "Return to the plugins page"
65
+ msgstr ""
66
+
67
+ msgid "Your subscription expires in %d day."
68
+ msgstr ""
69
+
70
+ msgid "Your subscription expires in %d days."
71
+ msgstr ""
72
+
73
+ msgid "Downloading is not possible because WordPress cannot write into the plugins folder. %sHow to fix%s."
74
+ msgstr ""
75
+
76
+ msgid "Plugin"
77
+ msgstr ""
78
+
79
+ msgid "downloading..."
80
+ msgstr ""
81
+
82
+ msgid "failed!"
83
+ msgstr ""
84
+
85
+ msgid "downloaded"
86
+ msgstr ""
87
+
88
+ msgid "activating"
89
+ msgstr ""
90
+
91
+ msgid "activated"
92
+ msgstr ""
93
+
94
+ msgid "Activate after download"
95
+ msgstr ""
96
+
97
+ msgid "Operation complete!"
98
+ msgstr ""
99
+
100
+ msgid "Download failed!\n\nClick OK to revalidate your subscription or CANCEL to try again."
101
+ msgstr ""
102
+
103
+ msgid "Available"
104
+ msgstr ""
105
+
106
+ msgid "Installed"
107
+ msgstr ""
108
+
109
+ msgid "Downloading"
110
+ msgstr ""
111
+
112
+ msgid "Activate"
113
+ msgstr ""
114
+
115
+ msgid "Download"
116
+ msgstr ""
117
+
118
+ msgid "Downloads:"
119
+ msgstr ""
120
+
121
+ msgid "Current version"
122
+ msgstr ""
123
+
124
+ msgid "Released"
125
+ msgstr ""
126
+
127
+ msgid "Installed version"
128
+ msgstr ""
129
+
130
+ msgid "(embedded)"
131
+ msgstr ""
132
+
133
+ msgid "installing..."
134
+ msgstr ""
135
+
136
+ msgid "updating..."
137
+ msgstr ""
138
+
139
+ msgid "installed"
140
+ msgstr ""
141
+
142
+ msgid "updated"
143
+ msgstr ""
144
+
145
+ msgid "Download failed!\n\nPlease refresh the page and try again."
146
+ msgstr ""
147
+
148
+ msgid "Incorrect setup"
149
+ msgstr ""
150
+
151
+ msgid "Invalid product"
152
+ msgstr ""
153
+
154
+ msgid "Unknown repository"
155
+ msgstr ""
156
+
157
+ msgid " Your current site key (%s) does not match the selected product (%s)."
158
+ msgstr ""
159
+
160
+ msgid "Buy %s"
161
+ msgstr ""
162
+
163
+ msgid "Already bought %s?"
164
+ msgstr ""
165
+
166
+ msgid "Renew %s"
167
+ msgstr ""
168
+
169
+ msgid "Remove current site key (%s)"
170
+ msgstr ""
171
+
172
+ msgid "%s support on wpml.org"
173
+ msgstr ""
174
+
175
+ msgid "Enter site key"
176
+ msgstr ""
177
+
178
+ msgid "Subscription is expired."
179
+ msgstr ""
180
+
181
+ msgid "Add"
182
+ msgstr ""
183
+
184
+ msgid "Are you sure you want to remove this site key?"
185
+ msgstr ""
186
+
187
+ msgid "Register %s"
188
+ msgstr ""
189
+
190
+ msgid "1. Go to your %s%s account%s and add this site URL: %s"
191
+ msgstr ""
192
+
193
+ msgid "Unregister %s from this site"
194
+ msgstr ""
195
+
196
+ msgid "%s is registered on this site. You will receive automatic updates until %s"
197
+ msgstr ""
198
+
199
+ msgid "%s is registered on this site. Your Lifetime account gives you updates for life."
200
+ msgstr ""
201
+
202
+ msgid "This page lets you install plugins and update existing plugins. To remove any of these plugins, go to the %splugins%s page and if you have the permission to remove plugins you should be able to do this."
203
+ msgstr ""
204
+
205
+ msgid "Already bought?"
206
+ msgstr ""
207
+
208
+ msgid "2. Enter your site key"
209
+ msgstr ""
210
+
211
+ msgid "Subscription is expired. You need to either purchase a new subscription or upgrade if available."
212
+ msgstr ""
213
+
214
+ msgid "Check for updates"
215
+ msgstr ""
216
+
217
+ msgid "Individual components"
218
+ msgstr ""
219
+
220
+ msgid "OK"
221
+ msgstr ""
222
+
223
+ msgid "Cancel"
224
+ msgstr ""
225
+
226
+ msgid "Are you sure you want to unregister?"
227
+ msgstr ""
228
+
229
+ msgid "Click to see individual components options."
230
+ msgstr ""
library/otgs/installer/repositories.xml CHANGED
@@ -1,13 +1,13 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <repositories>
3
- <repository>
4
- <id>wpml</id>
5
- <apiurl>https://api.wpml.org/</apiurl>
6
- <products>http://d2salfytceyqoe.cloudfront.net/wpml33-products.json</products>
7
- </repository>
8
- <repository>
9
- <id>toolset</id>
10
- <apiurl>https://api.wp-types.com/</apiurl>
11
- <products>http://d7j863fr5jhrr.cloudfront.net/toolset33-products.json</products>
12
- </repository>
13
- </repositories>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <repositories>
3
+ <repository>
4
+ <id>wpml</id>
5
+ <apiurl>https://api.wpml.org/</apiurl>
6
+ <products>http://d2salfytceyqoe.cloudfront.net/wpml33-products.json</products>
7
+ </repository>
8
+ <repository>
9
+ <id>toolset</id>
10
+ <apiurl>https://api.wp-types.com/</apiurl>
11
+ <products>http://d7j863fr5jhrr.cloudfront.net/toolset33-products.json</products>
12
+ </repository>
13
+ </repositories>
library/otgs/installer/res/css/admin.css CHANGED
@@ -1,193 +1,201 @@
1
- .otgsi_site_key_form{
2
- display:none;
3
- /*display:inline;*/
4
- }
5
-
6
- .installer-status-installing, .installer-status-installed, .installer-status-updating, .installer-status-updated, .installer-status-activating, .installer-status-activated, .installer-status-success{
7
- display: none;
8
- }
9
- .installer-status-installing{color: #FF9900; }
10
- .installer-status-installed{color: #003300; font-weight: bold; }
11
- .installer-status-updating{color: #FF9900; }
12
- .installer-status-updated{color: #003300; font-weight: bold; }
13
-
14
- .installer-status-activating{color: #996666 }
15
- .installer-status-activated{color: #333366; font-weight: bold; }
16
-
17
- .js-status-success p{
18
- color: #FF9900;
19
- padding: 4px;
20
- }
21
-
22
- .installer-green-text{
23
- color:#006600;
24
- font-weight:bold;
25
- }
26
-
27
- .installer-red-text{
28
- color:#b22121;
29
- font-weight:bold;
30
- }
31
-
32
- .installer-products-list li{
33
- display: inline;
34
- margin-right: 20px;
35
- }
36
-
37
- .otgs_wp_installer_table a.disabled{
38
- color:#888;
39
- }
40
-
41
- .otgs_wp_installer_subtable{
42
- clear: both;
43
- margin-left:-20px;
44
- }
45
- .otgs_wp_installer_subtable td p{
46
- font-size: 92%;
47
- }
48
-
49
- .installer-status-error{
50
- color: #f00;
51
- }
52
-
53
- .installer-status-note{
54
- color: #6F6E6D;
55
- font-style: italic;
56
- }
57
-
58
- .installer-warn-box{
59
- -webkit-border-radius: 5px;-moz-border-radius: 5px;border-radius: 5px;border:1px solid #962722;background-color:#F5C8C6;
60
- color: #333;
61
- padding: 5px;
62
- }
63
- .installer-warn-box span.details{
64
- font-style: italic;
65
- color:#777;
66
- }
67
-
68
- .installer-error-box{
69
- color:#962722;
70
- margin-top: 10px;
71
- }
72
- .installer-error-box p{
73
- margin: 10px 0 10px 0;
74
- -webkit-border-radius: 5px;-moz-border-radius: 5px;border-radius: 5px;border:1px solid #962722;background-color:#F5C8C6;
75
- color: #333;
76
- padding: 5px;
77
- text-align: center;
78
- }
79
-
80
- .spinner-inline{
81
- float: none;
82
- display: inline-block;
83
- visibility: visible;
84
- }
85
-
86
- .installer-q-icon:before{
87
- content: '\f223' !important;
88
- font-family: dashicons !important;
89
- color: #f56e28;
90
- font: 20px/1 "dashicons";
91
- }
92
-
93
- .installer-plugin-update-tr td{
94
- padding-left:3px !important;
95
- }
96
-
97
- .otgsi_yellow_bg{
98
- background-color: #f2f46b;
99
- }
100
-
101
- .otgs_wp_installer_table_compact{
102
- width:480px;
103
- border: solid 1px #999;
104
- padding:10px;
105
- border-radius: 5px;
106
- }
107
-
108
- .installer-plugins-list-compact{
109
- background-color: #fff;
110
- border-collapse: collapse;
111
- border:solid 1px #C1DAD7;
112
- width:100%;
113
- }
114
-
115
- .installer-plugins-list-compact tr th{
116
- padding-top:3px;
117
- background-color: #ccc;
118
- }
119
-
120
- .installer-plugins-list-compact tr{
121
- background-color: #ddd;
122
- }
123
-
124
- .installer-plugins-list-compact tr.even{
125
- background-color: #eee;
126
- }
127
-
128
- .installer-plugins-list-compact td{
129
- padding:2px 5px 2px 5px;
130
- border-right: 1px solid #C1DAD7;
131
- border-bottom: 1px solid #C1DAD7;
132
- }
133
-
134
- .installer-plugins-list-compact td.twelve{
135
- width:16px;
136
- }
137
-
138
- .otgs_wp_installer_table_compact .installer-status-downloading,
139
- .otgs_wp_installer_table_compact .installer-status-downloaded,
140
- .otgs_wp_installer_table_compact .installer-status-activating,
141
- .otgs_wp_installer_table_compact .installer-status-activated{
142
- display: none;
143
- color:transparent; width: 12px; padding:2px;
144
- }
145
-
146
- .otgs_wp_installer_table_compact .installer-status-success,
147
- .otgs_wp_installer_table_compact .installer-status-fail{
148
- display: none;
149
- }
150
-
151
- .installer-status-success{
152
- float: right;
153
- color: #006600;
154
- }
155
-
156
- .otgs_wp_installer_table_compact .installer-status-downloading{background: url(../img/dn.gif) no-repeat center; }
157
- .otgs_wp_installer_table_compact .installer-status-downloaded{background: url(../img/complete.png) no-repeat center;}
158
- .otgs_wp_installer_table_compact .installer-status-activating{background: url(../img/dn.gif) no-repeat center; }
159
- .otgs_wp_installer_table_compact .installer-status-activated{background: url(../img/complete.png) no-repeat center; }
160
- .otgs_wp_installer_table_compact .installer-status-error{background: url(../img/icon_error.gif) no-repeat center; }
161
-
162
- .installer_highlight{
163
- color:#c5510b;
164
- }
165
-
166
- .installer_highlight_package{
167
- background-color: #fff9c0;
168
- }
169
-
170
- .plugin_progress{
171
- font-style: italic;
172
- color: #777
173
- }
174
-
175
- .installer-download-progress-status{
176
- display: none;
177
- float:right;
178
- color: #006600;
179
- font-style: italic;
180
- background: url('../img/spinner.gif') no-repeat;
181
- padding-left:24px;
182
- }
183
-
184
- .otgs-is-dismissible {
185
- position: relative;
186
- padding-right: 38px;
187
- }
188
- .otgs-is-dismissible .notice-dismiss {
189
- text-decoration: none;
190
- }
191
- .otgs-is-dismissible p [class*="button-"] {
192
- margin: -5px 5px;
 
 
 
 
 
 
 
 
193
  }
1
+ .otgsi_site_key_form{
2
+ display:none;
3
+ /*display:inline;*/
4
+ }
5
+
6
+ .installer-status-installing, .installer-status-installed, .installer-status-updating, .installer-status-updated, .installer-status-activating, .installer-status-activated, .installer-status-success{
7
+ display: none;
8
+ }
9
+ .installer-status-installing{color: #FF9900; }
10
+ .installer-status-installed{color: #003300; font-weight: bold; }
11
+ .installer-status-updating{color: #FF9900; }
12
+ .installer-status-updated{color: #003300; font-weight: bold; }
13
+
14
+ .installer-status-activating{color: #996666 }
15
+ .installer-status-activated{color: #333366; font-weight: bold; }
16
+
17
+ .js-status-success p{
18
+ color: #FF9900;
19
+ padding: 4px;
20
+ }
21
+
22
+ .installer-green-text{
23
+ color:#006600;
24
+ font-weight:bold;
25
+ }
26
+
27
+ .installer-red-text{
28
+ color:#b22121;
29
+ font-weight:bold;
30
+ }
31
+
32
+ .installer-footnote{
33
+ color:#3d5e69;
34
+ display:block;
35
+ float: left;
36
+ font-style: italic;
37
+ font-size: 90%;
38
+ }
39
+
40
+ .installer-products-list li{
41
+ display: inline;
42
+ margin-right: 20px;
43
+ }
44
+
45
+ .otgs_wp_installer_table a.disabled{
46
+ color:#888;
47
+ }
48
+
49
+ .otgs_wp_installer_subtable{
50
+ clear: both;
51
+ margin-left:-20px;
52
+ }
53
+ .otgs_wp_installer_subtable td p{
54
+ font-size: 92%;
55
+ }
56
+
57
+ .installer-status-error{
58
+ color: #f00;
59
+ }
60
+
61
+ .installer-status-note{
62
+ color: #6F6E6D;
63
+ font-style: italic;
64
+ }
65
+
66
+ .installer-warn-box{
67
+ -webkit-border-radius: 5px;-moz-border-radius: 5px;border-radius: 5px;border:1px solid #962722;background-color:#F5C8C6;
68
+ color: #333;
69
+ padding: 5px;
70
+ }
71
+ .installer-warn-box span.details{
72
+ font-style: italic;
73
+ color:#777;
74
+ }
75
+
76
+ .installer-error-box{
77
+ color:#962722;
78
+ margin-top: 10px;
79
+ }
80
+ .installer-error-box p{
81
+ margin: 10px 0 10px 0;
82
+ -webkit-border-radius: 5px;-moz-border-radius: 5px;border-radius: 5px;border:1px solid #962722;background-color:#F5C8C6;
83
+ color: #333;
84
+ padding: 5px;
85
+ text-align: center;
86
+ }
87
+
88
+ .spinner-inline{
89
+ float: none;
90
+ display: inline-block;
91
+ visibility: visible;
92
+ }
93
+
94
+ .installer-q-icon:before{
95
+ content: '\f223' !important;
96
+ font-family: dashicons !important;
97
+ color: #f56e28;
98
+ font: 20px/1 "dashicons";
99
+ }
100
+
101
+ .installer-plugin-update-tr td{
102
+ padding-left:3px !important;
103
+ }
104
+
105
+ .otgsi_yellow_bg{
106
+ background-color: #f2f46b;
107
+ }
108
+
109
+ .otgs_wp_installer_table_compact{
110
+ width:480px;
111
+ border: solid 1px #999;
112
+ padding:10px;
113
+ border-radius: 5px;
114
+ }
115
+
116
+ .installer-plugins-list-compact{
117
+ background-color: #fff;
118
+ border-collapse: collapse;
119
+ border:solid 1px #C1DAD7;
120
+ width:100%;
121
+ }
122
+
123
+ .installer-plugins-list-compact tr th{
124
+ padding-top:3px;
125
+ background-color: #ccc;
126
+ }
127
+
128
+ .installer-plugins-list-compact tr{
129
+ background-color: #ddd;
130
+ }
131
+
132
+ .installer-plugins-list-compact tr.even{
133
+ background-color: #eee;
134
+ }
135
+
136
+ .installer-plugins-list-compact td{
137
+ padding:2px 5px 2px 5px;
138
+ border-right: 1px solid #C1DAD7;
139
+ border-bottom: 1px solid #C1DAD7;
140
+ }
141
+
142
+ .installer-plugins-list-compact td.twelve{
143
+ width:16px;
144
+ }
145
+
146
+ .otgs_wp_installer_table_compact .installer-status-downloading,
147
+ .otgs_wp_installer_table_compact .installer-status-downloaded,
148
+ .otgs_wp_installer_table_compact .installer-status-activating,
149
+ .otgs_wp_installer_table_compact .installer-status-activated{
150
+ display: none;
151
+ color:transparent; width: 12px; padding:2px;
152
+ }
153
+
154
+ .otgs_wp_installer_table_compact .installer-status-success,
155
+ .otgs_wp_installer_table_compact .installer-status-fail{
156
+ display: none;
157
+ }
158
+
159
+ .installer-status-success{
160
+ float: right;
161
+ color: #006600;
162
+ }
163
+
164
+ .otgs_wp_installer_table_compact .installer-status-downloading{background: url(../img/dn.gif) no-repeat center; }
165
+ .otgs_wp_installer_table_compact .installer-status-downloaded{background: url(../img/complete.png) no-repeat center;}
166
+ .otgs_wp_installer_table_compact .installer-status-activating{background: url(../img/dn.gif) no-repeat center; }
167
+ .otgs_wp_installer_table_compact .installer-status-activated{background: url(../img/complete.png) no-repeat center; }
168
+ .otgs_wp_installer_table_compact .installer-status-error{background: url(../img/icon_error.gif) no-repeat center; }
169
+
170
+ .installer_highlight{
171
+ color:#c5510b;
172
+ }
173
+
174
+ .installer_highlight_package{
175
+ background-color: #fff9c0;
176
+ }
177
+
178
+ .plugin_progress{
179
+ font-style: italic;
180
+ color: #777
181
+ }
182
+
183
+ .installer-download-progress-status{
184
+ display: none;
185
+ float:right;
186
+ color: #006600;
187
+ font-style: italic;
188
+ background: url('../img/spinner.gif') no-repeat;
189
+ padding-left:24px;
190
+ }
191
+
192
+ .otgs-is-dismissible {
193
+ position: relative;
194
+ padding-right: 38px;
195
+ }
196
+ .otgs-is-dismissible .notice-dismiss {
197
+ text-decoration: none;
198
+ }
199
+ .otgs-is-dismissible p [class*="button-"] {
200
+ margin: -5px 5px;
201
  }
library/otgs/installer/res/js/admin.js CHANGED
@@ -1,403 +1,417 @@
1
- var otgs_wp_installer = {
2
-
3
- init: function(){
4
-
5
- jQuery('.otgs_wp_installer_table').on('click', '.enter_site_key_js', otgs_wp_installer.show_site_key_form);
6
- jQuery('.otgs_wp_installer_table').on('click', '.cancel_site_key_js', otgs_wp_installer.hide_site_key_form);
7
-
8
- jQuery('.otgs_wp_installer_table').on('click', '.remove_site_key_js', otgs_wp_installer.remove_site_key);
9
- jQuery('.otgs_wp_installer_table').on('click', '.update_site_key_js', otgs_wp_installer.update_site_key);
10
-
11
- jQuery('.otgs_wp_installer_table').on('submit', '.otgsi_site_key_form', otgs_wp_installer.save_site_key);
12
- jQuery('.otgs_wp_installer_table').on('submit', '.otgsi_downloads_form', otgs_wp_installer.download_downloads);
13
- jQuery('.otgs_wp_installer_table').on('change', '.otgsi_downloads_form :checkbox[name="downloads[]"]', otgs_wp_installer.update_downloads_form);
14
-
15
- jQuery('.installer-dismiss-nag').click(otgs_wp_installer.dismiss_nag);
16
-
17
- jQuery('.otgs_wp_installer_table').on('click', '.installer_expand_button', otgs_wp_installer.toggle_subpackages);
18
-
19
- otgs_wp_installer.scroll_to_repository();
20
-
21
- if( typeof pagenow != 'undefined' && pagenow == 'plugins'){
22
-
23
- jQuery(document).ajaxSuccess(function(event, xhr, settings) {
24
- var data = otgs_wp_installer.getQueryParameters(settings.data);
25
- if(typeof data.action != 'undefined' && data.action == 'update-plugin'){
26
- response = xhr.responseJSON.data;
27
- console.log(typeof response.error);
28
- if(typeof response.error != 'undefined'){
29
- var default_error = jQuery('#' + response.slug + '-update .update-message').html();
30
- jQuery('#' + response.slug + '-update .update-message').html(default_error + ' &raquo;<span class="installer-red-text"> ' + response.error + '</span>');
31
- }
32
- }
33
- return false;
34
- });
35
-
36
- }
37
-
38
- },
39
-
40
- getQueryParameters : function(str) {
41
- return (str || document.location.search).replace(/(^\?)/,'').split("&").map(function(n){return n = n.split("="),this[n[0]] = n[1],this}.bind({}))[0];
42
- },
43
-
44
- reset_errors: function(){
45
- jQuery('.installer-error-box').html('').hide();
46
- },
47
-
48
- show_error: function(repo, text){
49
- jQuery('#installer_repo_' + repo).find('.installer-error-box').html(text).show();
50
- },
51
-
52
- show_site_key_form: function(){
53
- otgs_wp_installer.reset_errors();
54
-
55
- var form = jQuery(this).parent().find('form.otgsi_site_key_form');
56
- jQuery(this).prev().hide();
57
- jQuery(this).hide();
58
- form.css('display', 'inline');
59
- form.find('input[name^=site_key_]').focus().val('');
60
- form.find('input').removeAttr('disabled');
61
-
62
- form.closest('.otgsi_register_product_wrap').addClass('otgsi_yellow_bg');
63
-
64
- return false;
65
- },
66
-
67
- hide_site_key_form: function(){
68
- var form = jQuery(this).closest('form');
69
- form.hide();
70
- form.parent().find('.enter_site_key_js').show();
71
- form.parent().find('.enter_site_key_js').prev().show();
72
-
73
- form.closest('.otgsi_register_product_wrap').removeClass('otgsi_yellow_bg');
74
- otgs_wp_installer.reset_errors();
75
- return false;
76
- },
77
-
78
- save_site_key: function(){
79
-
80
- var thisf = jQuery(this);
81
- var data = jQuery(this).serialize();
82
- jQuery(this).find('input').attr('disabled', 'disabled');
83
-
84
- var spinner = jQuery('<span class="spinner"></span>');
85
- spinner.css({display: 'inline-block', float: 'none'}).prependTo(jQuery(this));
86
-
87
- otgs_wp_installer.reset_errors();
88
-
89
- jQuery.ajax({url: ajaxurl, type: 'POST', dataType:'json', data: data, success:
90
- function(ret){
91
- if(!ret.error){
92
- otgs_wp_installer.saved_site_key();
93
- }else{
94
- otgs_wp_installer.show_error(thisf.find('[name=repository_id]').val(), ret.error);
95
- thisf.find('input').removeAttr('disabled');
96
- }
97
-
98
- if(typeof ret.debug != 'undefined'){
99
- thisf.append('<textarea style="width:100%" rows="20">' + ret.debug + '</textarea>');
100
- }
101
-
102
- spinner.remove();
103
- }
104
- });
105
-
106
- return false;
107
-
108
- },
109
-
110
- saved_site_key: function(){
111
- location.reload();
112
- },
113
-
114
- remove_site_key: function(){
115
-
116
- if(confirm(jQuery(this).data('confirmation'))){
117
-
118
- jQuery('<span class="spinner"></span>').css({visibility: 'visible', float: 'none'}).prependTo(jQuery(this).parent());
119
- data = {action: 'remove_site_key', repository_id: jQuery(this).data('repository'), nonce: jQuery(this).data('nonce')}
120
- jQuery.ajax({url: ajaxurl, type: 'POST', data: data, success: otgs_wp_installer.removed_site_key});
121
- }
122
-
123
- return false;
124
- },
125
-
126
- removed_site_key: function(){
127
- location.reload();
128
- },
129
-
130
- update_site_key: function(){
131
- var error_wrap = jQuery(this).closest('.otgsi_register_product_wrap').find('.installer-error-box');
132
- error_wrap.html('');
133
-
134
- var spinner = jQuery('<span class="spinner"></span>');
135
-
136
- spinner.css({visibility: 'visible', float: 'none'}).prependTo(jQuery(this).parent());
137
- data = {action: 'update_site_key', repository_id: jQuery(this).data('repository'), nonce: jQuery(this).data('nonce')}
138
- jQuery.ajax({
139
- url: ajaxurl,
140
- type: 'POST',
141
- data: data,
142
- dataType: 'json',
143
- complete: function( event, xhr, settings ){
144
- var error = '';
145
- if(xhr == 'success') {
146
- var ret = event.responseJSON;
147
- if(ret.error){
148
- error = ret.error;
149
- }else{
150
- otgs_wp_installer.updated_site_key(ret);
151
- }
152
- }else{
153
- error = 'Error processing request (' + xhr + '). Please try again!';
154
- }
155
-
156
- if( error ){
157
- error_wrap.html('<p>' + error + '</p>').show();
158
- spinner.remove();
159
- }
160
-
161
- }
162
- });
163
-
164
- return false;
165
-
166
- },
167
-
168
- updated_site_key: function(ret){
169
- location.reload();
170
- },
171
-
172
- update_downloads_form: function(){
173
-
174
- var checked = jQuery('.otgsi_downloads_form :checkbox:checked[name="downloads[]"]').length;
175
-
176
- if(checked){
177
- jQuery(this).closest('form').find(':submit, :checkbox[name=activate]').removeAttr('disabled');
178
- }else{
179
- jQuery(this).closest('form').find(':submit, :checkbox[name=activate]').attr('disabled', 'disabled');
180
- }
181
-
182
-
183
- },
184
-
185
- download_downloads: function(){
186
-
187
- var activate = jQuery(this).find(":checkbox:checked[name=activate]").val(),
188
- action_button = jQuery(this).find('input[type="submit"]');
189
- downloads_form = jQuery(this),
190
- idx = 0,
191
- checkboxes = [];
192
-
193
- jQuery(this).find(':checkbox:checked[name="downloads[]"]').each(function(){
194
- if(jQuery(this).attr('disabled')) return;
195
- checkboxes[idx] = jQuery(this);
196
- idx++;
197
- jQuery(this).attr('disabled', 'disabled');
198
- });
199
-
200
- idx = 0;
201
-
202
- if( typeof checkboxes[idx] != 'undefined' ){
203
- download_and_activate( checkboxes[idx] );
204
- action_button.attr('disabled', 'disabled');
205
- }
206
-
207
- function download_and_activate( elem ){
208
-
209
- var this_tr = elem.closest('tr');
210
- var is_update = this_tr.find('.installer-red-text').length;
211
- if(is_update){
212
- var installing = this_tr.find('.installer-status-updating');
213
- var installed = this_tr.find('.installer-status-updated');
214
- }else{
215
- var installing = this_tr.find('.installer-status-installing');
216
- var installed = this_tr.find('.installer-status-installed');
217
-
218
- }
219
- if(activate){
220
- var activating = this_tr.find('.installer-status-activating');
221
- var activated = this_tr.find('.installer-status-activated');
222
- }
223
-
224
- if( this_tr.find('.for_spinner_js .spinner').length > 0 ){
225
- var spinner = this_tr.find('.for_spinner_js .spinner');
226
- }else{
227
- var spinner = this_tr.find('.installer-status-downloading');
228
- }
229
-
230
- otgs_wp_installer.reset_errors();
231
- downloads_form.find('div.installer-status-success').hide();
232
- spinner.css('visibility', 'visible');
233
- installing.show();
234
-
235
- var plugin_name = this_tr.find('.installer_plugin_name').html();
236
- if(is_update){
237
- otgs_wp_installer.show_download_progress_status(downloads_form, installer_strings.updating.replace('%s', plugin_name));
238
- }else{
239
- otgs_wp_installer.show_download_progress_status(downloads_form, installer_strings.installing.replace('%s', plugin_name));
240
- }
241
-
242
-
243
- data = {action: 'installer_download_plugin', data: elem.val(), activate: activate}
244
-
245
- jQuery.ajax({
246
- url: ajaxurl,
247
- type: 'POST',
248
- dataType: 'json',
249
- data: data,
250
- success: function(ret){
251
- installing.hide();
252
-
253
- if(!ret.success){
254
- installed.addClass('installer-status-error');
255
- installed.html(installed.data('fail'));
256
-
257
- if(ret.message){
258
- installed.closest('.otgs_wp_installer_table').find('.installer-error-box').html('<p>' + ret.message + '</p>').show();
259
- }else{
260
- installed.closest('.otgs_wp_installer_table').find('.installer-error-box').html('<p>' + downloads_form.find('.installer-revalidate-message').html() + '</p>').show();
261
- }
262
-
263
-
264
- }
265
-
266
- installed.show();
267
- spinner.fadeOut();
268
-
269
- if(ret.version){
270
- this_tr.find('.installer_version_installed').html('<span class="installer-green-text">' + ret.version + '</span>');
271
- }
272
-
273
- if(ret.success && activate){
274
-
275
- otgs_wp_installer.show_download_progress_status(downloads_form, installer_strings.activating.replace('%s', plugin_name));
276
- activating.show();
277
- spinner.show();
278
- this_tr.find('.installer-red-text').removeClass('installer-red-text').addClass('installer-green-text').html(ret.version);
279
-
280
- jQuery.ajax({
281
- url: ajaxurl,
282
- type: 'POST',
283
- dataType: 'json',
284
- data: {action: 'installer_activate_plugin', plugin_id: ret.plugin_id, nonce: ret.nonce},
285
- success: function(ret){
286
- activating.hide();
287
- if(!ret.error ){
288
- activated.show();
289
- }
290
-
291
- spinner.fadeOut();
292
-
293
- idx++;
294
- if( typeof checkboxes[idx] != 'undefined' ){
295
- download_and_activate( checkboxes[idx] );
296
- }else{
297
- otgs_wp_installer.hide_download_progress_status(downloads_form);
298
- downloads_form.find('div.installer-status-success').show();
299
- action_button.removeAttr('disabled');
300
- }
301
- }
302
- });
303
- }else{
304
- idx++;
305
- if( typeof checkboxes[idx] != 'undefined' ){
306
- download_and_activate( checkboxes[idx] );
307
- }else{
308
- otgs_wp_installer.hide_download_progress_status(downloads_form);
309
- downloads_form.find('div.installer-status-success').show();
310
- action_button.removeAttr('disabled');
311
- }
312
- }
313
- }
314
-
315
- });
316
-
317
- };
318
-
319
- return false;
320
- },
321
-
322
-
323
- show_download_progress_status: function(downloads_form, text){
324
-
325
- downloads_form.find('.installer-download-progress-status').html(text).fadeIn();
326
-
327
- },
328
-
329
- hide_download_progress_status: function(downloads_form){
330
-
331
- downloads_form.find('.installer-download-progress-status').html('').fadeOut();
332
-
333
- },
334
-
335
- dismiss_nag: function(){
336
-
337
- var thisa = jQuery(this);
338
-
339
- data = {action: 'installer_dismiss_nag', repository: jQuery(this).data('repository')}
340
-
341
- jQuery.ajax({url: ajaxurl, type: 'POST', dataType:'json', data: data, success:
342
- function(ret){
343
- thisa.closest('.otgs-is-dismissible').remove();
344
- }
345
- });
346
-
347
- return false;
348
- },
349
-
350
- toggle_subpackages: function(){
351
- var list = jQuery(this).closest('td').find('.otgs_wp_installer_subtable');
352
-
353
- if(list.is(':visible')){
354
- list.slideUp('fast');
355
- }else{
356
- list.slideDown('fast');
357
- }
358
-
359
-
360
- return false;
361
-
362
- },
363
-
364
- scroll_to_repository: function(){
365
-
366
- var ref = window.location.hash.replace('#', '');
367
-
368
- if(ref) {
369
- var split = ref.split('/');
370
- var repo = split[0];
371
-
372
- if(typeof split[1] != 'undefined'){
373
- var package = split[1];
374
- var repo_element = jQuery('#repository-' + repo);
375
-
376
-
377
-
378
- if(repo_element.length){
379
-
380
- jQuery('html, body').animate({
381
- scrollTop: repo_element.offset().top
382
- }, 1000);
383
-
384
- var package_element = jQuery('#repository-' + repo +'_' + package);
385
-
386
- if(package_element.length && !package_element.is(':visible')){
387
- package_element.parents('.otgs_wp_installer_subtable').slideDown();
388
- package_element.addClass('installer_highlight_package');
389
- }
390
-
391
- package_element.find('.button-secondary').removeClass('button-secondary').addClass('button-primary');
392
- }
393
- }
394
-
395
- }
396
-
397
- }
398
-
399
-
400
- }
401
-
402
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
  jQuery(document).ready(otgs_wp_installer.init);
1
+ var otgs_wp_installer = {
2
+
3
+ init: function(){
4
+
5
+ jQuery('.otgs_wp_installer_table').on('click', '.enter_site_key_js', otgs_wp_installer.show_site_key_form);
6
+ jQuery('.otgs_wp_installer_table').on('click', '.cancel_site_key_js', otgs_wp_installer.hide_site_key_form);
7
+
8
+ jQuery('.otgs_wp_installer_table').on('click', '.remove_site_key_js', otgs_wp_installer.remove_site_key);
9
+ jQuery('.otgs_wp_installer_table').on('click', '.update_site_key_js', otgs_wp_installer.update_site_key);
10
+
11
+ jQuery('.otgs_wp_installer_table').on('submit', '.otgsi_site_key_form', otgs_wp_installer.save_site_key);
12
+ jQuery('.otgs_wp_installer_table').on('submit', '.otgsi_downloads_form', otgs_wp_installer.download_downloads);
13
+ jQuery('.otgs_wp_installer_table').on('change', '.otgsi_downloads_form :checkbox[name="downloads[]"]', otgs_wp_installer.update_downloads_form);
14
+
15
+ jQuery('.installer-dismiss-nag').click(otgs_wp_installer.dismiss_nag);
16
+
17
+ jQuery('.otgs_wp_installer_table').on('click', '.installer_expand_button', otgs_wp_installer.toggle_subpackages);
18
+
19
+ otgs_wp_installer.scroll_to_repository();
20
+
21
+ if( typeof pagenow != 'undefined' && pagenow == 'plugins'){
22
+
23
+ jQuery(document).ajaxSuccess(function(event, xhr, settings) {
24
+ var data = otgs_wp_installer.getQueryParameters(settings.data);
25
+ if(typeof data.action != 'undefined' && data.action == 'update-plugin'){
26
+ response = xhr.responseJSON.data;
27
+ if(typeof response.error != 'undefined'){
28
+ var default_error = jQuery('#' + response.slug + '-update .update-message').html();
29
+ jQuery('#' + response.slug + '-update .update-message').html(default_error + ' &raquo;<span class="installer-red-text"> ' + response.error + '</span>');
30
+ }
31
+ }
32
+ return false;
33
+ });
34
+
35
+ }
36
+
37
+ },
38
+
39
+ getQueryParameters : function(str) {
40
+ return (str || document.location.search).replace(/(^\?)/,'').split("&").map(function(n){return n = n.split("="),this[n[0]] = n[1],this}.bind({}))[0];
41
+ },
42
+
43
+ reset_errors: function(){
44
+ jQuery('.installer-error-box').html('').hide();
45
+ },
46
+
47
+ show_error: function(repo, text){
48
+ jQuery('#installer_repo_' + repo).find('.installer-error-box').html(text).show();
49
+ },
50
+
51
+ show_site_key_form: function(){
52
+
53
+ if( jQuery(this).hasClass('disabled') ) {
54
+ alert( jQuery(this).attr('title') );
55
+ return false;
56
+ }
57
+
58
+ otgs_wp_installer.reset_errors();
59
+
60
+ var form = jQuery(this).parent().find('form.otgsi_site_key_form');
61
+ jQuery(this).prev().hide();
62
+ jQuery(this).hide();
63
+ form.css('display', 'inline');
64
+ form.find('input[name^=site_key_]').focus().val('');
65
+ form.find('input').removeAttr('disabled');
66
+
67
+ form.closest('.otgsi_register_product_wrap').addClass('otgsi_yellow_bg');
68
+
69
+ return false;
70
+ },
71
+
72
+ hide_site_key_form: function(){
73
+ var form = jQuery(this).closest('form');
74
+ form.hide();
75
+ form.parent().find('.enter_site_key_js').show();
76
+ form.parent().find('.enter_site_key_js').prev().show();
77
+
78
+ form.closest('.otgsi_register_product_wrap').removeClass('otgsi_yellow_bg');
79
+ otgs_wp_installer.reset_errors();
80
+ return false;
81
+ },
82
+
83
+ save_site_key: function(){
84
+
85
+ var thisf = jQuery(this);
86
+ var data = jQuery(this).serialize();
87
+ jQuery(this).find('input').attr('disabled', 'disabled');
88
+
89
+ var spinner = jQuery('<span class="spinner"></span>');
90
+ spinner.css({display: 'inline-block', float: 'none'}).prependTo(jQuery(this));
91
+
92
+ otgs_wp_installer.reset_errors();
93
+
94
+ jQuery.ajax({url: ajaxurl, type: 'POST', dataType:'json', data: data, success:
95
+ function(ret){
96
+ if(!ret.error){
97
+ otgs_wp_installer.saved_site_key();
98
+ }else{
99
+ otgs_wp_installer.show_error(thisf.find('[name=repository_id]').val(), ret.error);
100
+ thisf.find('input').removeAttr('disabled');
101
+ }
102
+
103
+ if(typeof ret.debug != 'undefined'){
104
+ thisf.append('<textarea style="width:100%" rows="20">' + ret.debug + '</textarea>');
105
+ }
106
+
107
+ spinner.remove();
108
+ }
109
+ });
110
+
111
+ return false;
112
+
113
+ },
114
+
115
+ saved_site_key: function(){
116
+ location.reload();
117
+ },
118
+
119
+ remove_site_key: function(){
120
+
121
+ if( jQuery(this).attr('disabled') == 'disabled' ){
122
+
123
+ alert( jQuery(this).attr('title') );
124
+ return false;
125
+
126
+ } else {
127
+
128
+ if(confirm(jQuery(this).data('confirmation'))){
129
+
130
+ jQuery('<span class="spinner"></span>').css({visibility: 'visible', float: 'none'}).prependTo(jQuery(this).parent());
131
+ data = {action: 'remove_site_key', repository_id: jQuery(this).data('repository'), nonce: jQuery(this).data('nonce')}
132
+ jQuery.ajax({url: ajaxurl, type: 'POST', data: data, success: otgs_wp_installer.removed_site_key});
133
+ }
134
+
135
+ }
136
+
137
+ return false;
138
+ },
139
+
140
+ removed_site_key: function(){
141
+ location.reload();
142
+ },
143
+
144
+ update_site_key: function(){
145
+ var error_wrap = jQuery(this).closest('.otgsi_register_product_wrap').find('.installer-error-box');
146
+ error_wrap.html('');
147
+
148
+ var spinner = jQuery('<span class="spinner"></span>');
149
+
150
+ spinner.css({visibility: 'visible', float: 'none'}).prependTo(jQuery(this).parent());
151
+ data = {action: 'update_site_key', repository_id: jQuery(this).data('repository'), nonce: jQuery(this).data('nonce')}
152
+ jQuery.ajax({
153
+ url: ajaxurl,
154
+ type: 'POST',
155
+ data: data,
156
+ dataType: 'json',
157
+ complete: function( event, xhr, settings ){
158
+ var error = '';
159
+ if(xhr == 'success') {
160
+ var ret = event.responseJSON;
161
+ if(ret.error){
162
+ error = ret.error;
163
+ }else{
164
+ otgs_wp_installer.updated_site_key(ret);
165
+ }
166
+ }else{
167
+ error = 'Error processing request (' + xhr + '). Please try again!';
168
+ }
169
+
170
+ if( error ){
171
+ error_wrap.html('<p>' + error + '</p>').show();
172
+ spinner.remove();
173
+ }
174
+
175
+ }
176
+ });
177
+
178
+ return false;
179
+
180
+ },
181
+
182
+ updated_site_key: function(ret){
183
+ location.reload();
184
+ },
185
+
186
+ update_downloads_form: function(){
187
+
188
+ var checked = jQuery('.otgsi_downloads_form :checkbox:checked[name="downloads[]"]').length;
189
+
190
+ if(checked){
191
+ jQuery(this).closest('form').find(':submit, :checkbox[name=activate]').removeAttr('disabled');
192
+ }else{
193
+ jQuery(this).closest('form').find(':submit, :checkbox[name=activate]').attr('disabled', 'disabled');
194
+ }
195
+
196
+
197
+ },
198
+
199
+ download_downloads: function(){
200
+
201
+ var activate = jQuery(this).find(":checkbox:checked[name=activate]").val(),
202
+ action_button = jQuery(this).find('input[type="submit"]');
203
+ downloads_form = jQuery(this),
204
+ idx = 0,
205
+ checkboxes = [];
206
+
207
+ jQuery(this).find(':checkbox:checked[name="downloads[]"]').each(function(){
208
+ if(jQuery(this).attr('disabled')) return;
209
+ checkboxes[idx] = jQuery(this);
210
+ idx++;
211
+ jQuery(this).attr('disabled', 'disabled');
212
+ });
213
+
214
+ idx = 0;
215
+
216
+ if( typeof checkboxes[idx] != 'undefined' ){
217
+ download_and_activate( checkboxes[idx] );
218
+ action_button.attr('disabled', 'disabled');
219
+ }
220
+
221
+ function download_and_activate( elem ){
222
+
223
+ var this_tr = elem.closest('tr');
224
+ var is_update = this_tr.find('.installer-red-text').length;
225
+ if(is_update){
226
+ var installing = this_tr.find('.installer-status-updating');
227
+ var installed = this_tr.find('.installer-status-updated');
228
+ }else{
229
+ var installing = this_tr.find('.installer-status-installing');
230
+ var installed = this_tr.find('.installer-status-installed');
231
+
232
+ }
233
+ if(activate){
234
+ var activating = this_tr.find('.installer-status-activating');
235
+ var activated = this_tr.find('.installer-status-activated');
236
+ }
237
+
238
+ if( this_tr.find('.for_spinner_js .spinner').length > 0 ){
239
+ var spinner = this_tr.find('.for_spinner_js .spinner');
240
+ }else{
241
+ var spinner = this_tr.find('.installer-status-downloading');
242
+ }
243
+
244
+ otgs_wp_installer.reset_errors();
245
+ downloads_form.find('div.installer-status-success').hide();
246
+ spinner.css('visibility', 'visible');
247
+ installing.show();
248
+
249
+ var plugin_name = this_tr.find('.installer_plugin_name').html();
250
+ if(is_update){
251
+ otgs_wp_installer.show_download_progress_status(downloads_form, installer_strings.updating.replace('%s', plugin_name));
252
+ }else{
253
+ otgs_wp_installer.show_download_progress_status(downloads_form, installer_strings.installing.replace('%s', plugin_name));
254
+ }
255
+
256
+
257
+ data = {action: 'installer_download_plugin', data: elem.val(), activate: activate}
258
+
259
+ jQuery.ajax({
260
+ url: ajaxurl,
261
+ type: 'POST',
262
+ dataType: 'json',
263
+ data: data,
264
+ success: function(ret){
265
+ installing.hide();
266
+
267
+ if(!ret.success){
268
+ installed.addClass('installer-status-error');
269
+ installed.html(installed.data('fail'));
270
+
271
+ if(ret.message){
272
+ installed.closest('.otgs_wp_installer_table').find('.installer-error-box').html('<p>' + ret.message + '</p>').show();
273
+ }else{
274
+ installed.closest('.otgs_wp_installer_table').find('.installer-error-box').html('<p>' + downloads_form.find('.installer-revalidate-message').html() + '</p>').show();
275
+ }
276
+
277
+
278
+ }
279
+
280
+ installed.show();
281
+ spinner.fadeOut();
282
+
283
+ if(ret.version){
284
+ this_tr.find('.installer_version_installed').html('<span class="installer-green-text">' + ret.version + '</span>');
285
+ }
286
+
287
+ if(ret.success && activate){
288
+
289
+ otgs_wp_installer.show_download_progress_status(downloads_form, installer_strings.activating.replace('%s', plugin_name));
290
+ activating.show();
291
+ spinner.show();
292
+ this_tr.find('.installer-red-text').removeClass('installer-red-text').addClass('installer-green-text').html(ret.version);
293
+
294
+ jQuery.ajax({
295
+ url: ajaxurl,
296
+ type: 'POST',
297
+ dataType: 'json',
298
+ data: {action: 'installer_activate_plugin', plugin_id: ret.plugin_id, nonce: ret.nonce},
299
+ success: function(ret){
300
+ activating.hide();
301
+ if(!ret.error ){
302
+ activated.show();
303
+ }
304
+
305
+ spinner.fadeOut();
306
+
307
+ idx++;
308
+ if( typeof checkboxes[idx] != 'undefined' ){
309
+ download_and_activate( checkboxes[idx] );
310
+ }else{
311
+ otgs_wp_installer.hide_download_progress_status(downloads_form);
312
+ downloads_form.find('div.installer-status-success').show();
313
+ action_button.removeAttr('disabled');
314
+ }
315
+ }
316
+ });
317
+ }else{
318
+ idx++;
319
+ if( typeof checkboxes[idx] != 'undefined' ){
320
+ download_and_activate( checkboxes[idx] );
321
+ }else{
322
+ otgs_wp_installer.hide_download_progress_status(downloads_form);
323
+ downloads_form.find('div.installer-status-success').show();
324
+ action_button.removeAttr('disabled');
325
+ }
326
+ }
327
+ }
328
+
329
+ });
330
+
331
+ };
332
+
333
+ return false;
334
+ },
335
+
336
+
337
+ show_download_progress_status: function(downloads_form, text){
338
+
339
+ downloads_form.find('.installer-download-progress-status').html(text).fadeIn();
340
+
341
+ },
342
+
343
+ hide_download_progress_status: function(downloads_form){
344
+
345
+ downloads_form.find('.installer-download-progress-status').html('').fadeOut();
346
+
347
+ },
348
+
349
+ dismiss_nag: function(){
350
+
351
+ var thisa = jQuery(this);
352
+
353
+ data = {action: 'installer_dismiss_nag', repository: jQuery(this).data('repository')}
354
+
355
+ jQuery.ajax({url: ajaxurl, type: 'POST', dataType:'json', data: data, success:
356
+ function(ret){
357
+ thisa.closest('.otgs-is-dismissible').remove();
358
+ }
359
+ });
360
+
361
+ return false;
362
+ },
363
+
364
+ toggle_subpackages: function(){
365
+ var list = jQuery(this).closest('td').find('.otgs_wp_installer_subtable');
366
+
367
+ if(list.is(':visible')){
368
+ list.slideUp('fast');
369
+ }else{
370
+ list.slideDown('fast');
371
+ }
372
+
373
+
374
+ return false;
375
+
376
+ },
377
+
378
+ scroll_to_repository: function(){
379
+
380
+ var ref = window.location.hash.replace('#', '');
381
+
382
+ if(ref) {
383
+ var split = ref.split('/');
384
+ var repo = split[0];
385
+
386
+ if(typeof split[1] != 'undefined'){
387
+ var package = split[1];
388
+ var repo_element = jQuery('#repository-' + repo);
389
+
390
+
391
+
392
+ if(repo_element.length){
393
+
394
+ jQuery('html, body').animate({
395
+ scrollTop: repo_element.offset().top
396
+ }, 1000);
397
+
398
+ var package_element = jQuery('#repository-' + repo +'_' + package);
399
+
400
+ if(package_element.length && !package_element.is(':visible')){
401
+ package_element.parents('.otgs_wp_installer_subtable').slideDown();
402
+ package_element.addClass('installer_highlight_package');
403
+ }
404
+
405
+ package_element.find('.button-secondary').removeClass('button-secondary').addClass('button-primary');
406
+ }
407
+ }
408
+
409
+ }
410
+
411
+ }
412
+
413
+
414
+ }
415
+
416
+
417
  jQuery(document).ready(otgs_wp_installer.init);
library/otgs/installer/res/js/iframeResizer.min.js CHANGED
@@ -1,10 +1,10 @@
1
- /*! iFrame Resizer (iframeSizer.min.js ) - v2.6.1 - 2014-09-03
2
- * Desc: Force cross domain iframes to size to content.
3
- * Requires: iframeResizer.contentWindow.min.js to be loaded into the target frame.
4
- * Copyright: (c) 2014 David J. Bradshaw - dave@bradshaw.net
5
- * License: MIT
6
- */
7
-
8
- !function(){"use strict";function a(a,b,c){"addEventListener"in window?a.addEventListener(b,c,!1):"attachEvent"in window&&a.attachEvent("on"+b,c)}function b(){var a,b=["moz","webkit","o","ms"];for(a=0;a<b.length&&!w;a+=1)w=window[b[a]+"RequestAnimationFrame"];w||c(" RequestAnimationFrame not supported")}function c(a){y.log&&"object"==typeof console&&console.log(s+"[Host page"+u+"]"+a)}function d(a){function b(){function a(){h(z),f(),y.resizedCallback(z)}i(a,z,"resetPage")}function d(a){var b=a.id;c(" Removing iFrame: "+b),a.parentNode.removeChild(a),y.closedCallback(b),c(" --")}function e(){var a=x.substr(t).split(":");return{iframe:document.getElementById(a[0]),id:a[0],height:a[1],width:a[2],type:a[3]}}function j(a){var b=Number(y["max"+a]),d=Number(y["min"+a]),e=a.toLowerCase(),f=Number(z[e]);if(d>b)throw new Error("Value for min"+a+" can not be greater than max"+a);c(" Checking "+e+" is in range "+d+"-"+b),d>f&&(f=d,c(" Set "+e+" to min value")),f>b&&(f=b,c(" Set "+e+" to max value")),z[e]=""+f}function k(){var b=a.origin,d=z.iframe.src.split("/").slice(0,3).join("/");if(y.checkOrigin&&(c(" Checking connection is from: "+d),""+b!="null"&&b!==d))throw new Error("Unexpected message received from: "+b+" for "+z.iframe.id+". Message was: "+a.data+". This error can be disabled by adding the checkOrigin: false option.");return!0}function l(){return s===(""+x).substr(0,t)}function m(){var a=z.type in{"true":1,"false":1};return a&&c(" Ignoring init message from meta parent page"),a}function n(){var a=x.substr(x.indexOf(":")+r+6);c(" MessageCallback passed: {iframe: "+z.iframe.id+", message: "+a+"}"),y.messageCallback({iframe:z.iframe,message:a}),c(" --")}function o(){if(null===z.iframe)throw new Error("iFrame ("+z.id+") does not exist on "+u);return!0}function q(){c(" Reposition requested from iFrame"),v={x:z.width,y:z.height},f()}function w(){switch(z.type){case"close":d(z.iframe),y.resizedCallback(z);break;case"message":n();break;case"scrollTo":q();break;case"reset":g(z);break;case"init":b(),y.initCallback(z.iframe);break;default:b()}}var x=a.data,z={};l()&&(c(" Received: "+x),z=e(),j("Height"),j("Width"),!m()&&o()&&k()&&(w(),p=!1))}function e(){null===v&&(v={x:void 0!==window.pageXOffset?window.pageXOffset:document.documentElement.scrollLeft,y:void 0!==window.pageYOffset?window.pageYOffset:document.documentElement.scrollTop},c(" Get position: "+v.x+","+v.y))}function f(){null!==v&&(window.scrollTo(v.x,v.y),c(" Set position: "+v.x+","+v.y),v=null)}function g(a){function b(){h(a),j("reset","reset",a.iframe)}c(" Size reset requested by "+("init"===a.type?"host page":"iFrame")),e(),i(b,a,"init")}function h(a){function b(b){a.iframe.style[b]=a[b]+"px",c(" IFrame ("+a.iframe.id+") "+b+" set to "+a[b]+"px")}y.sizeHeight&&b("height"),y.sizeWidth&&b("width")}function i(a,b,d){d!==b.type&&w?(c(" Requesting animation frame"),w(a)):a()}function j(a,b,d){c("["+a+"] Sending msg to iframe ("+b+")"),d.contentWindow.postMessage(s+b,"*")}function k(){function b(){function a(a){1/0!==y[a]&&0!==y[a]&&(k.style[a]=y[a]+"px",c(" Set "+a+" = "+y[a]+"px"))}a("maxHeight"),a("minHeight"),a("maxWidth"),a("minWidth")}function d(a){return""===a&&(k.id=a="iFrameResizer"+o++,c(" Added missing iframe ID: "+a+" ("+k.src+")")),a}function e(){c(" IFrame scrolling "+(y.scrolling?"enabled":"disabled")+" for "+l),k.style.overflow=!1===y.scrolling?"hidden":"auto",k.scrolling=!1===y.scrolling?"no":"yes"}function f(){("number"==typeof y.bodyMargin||"0"===y.bodyMargin)&&(y.bodyMarginV1=y.bodyMargin,y.bodyMargin=""+y.bodyMargin+"px")}function h(){return l+":"+y.bodyMarginV1+":"+y.sizeWidth+":"+y.log+":"+y.interval+":"+y.enablePublicMethods+":"+y.autoResize+":"+y.bodyMargin+":"+y.heightCalculationMethod+":"+y.bodyBackground+":"+y.bodyPadding+":"+y.tolerance}function i(b){a(k,"load",function(){var a=p;j("iFrame.onload",b,k),!a&&y.heightCalculationMethod in x&&g({iframe:k,height:0,width:0,type:"init"})}),j("init",b,k)}var k=this,l=d(k.id);e(),b(),f(),i(h())}function l(a){if("object"!=typeof a)throw new TypeError("Options is not an object.")}function m(){function a(a){if("IFRAME"!==a.tagName.toUpperCase())throw new TypeError("Expected <IFRAME> tag, found <"+a.tagName+">.");k.call(a)}function b(a){a=a||{},l(a);for(var b in z)z.hasOwnProperty(b)&&(y[b]=a.hasOwnProperty(b)?a[b]:z[b])}return function(c,d){b(c),Array.prototype.forEach.call(document.querySelectorAll(d||"iframe"),a)}}function n(a){a.fn.iFrameResize=function(b){return b=b||{},l(b),y=a.extend({},z,b),this.filter("iframe").each(k).end()}}var o=0,p=!0,q="message",r=q.length,s="[iFrameSizer]",t=s.length,u="",v=null,w=window.requestAnimationFrame,x={max:1,scroll:1,bodyScroll:1,documentElementScroll:1},y={},z={autoResize:!0,bodyBackground:null,bodyMargin:null,bodyMarginV1:8,bodyPadding:null,checkOrigin:!0,enablePublicMethods:!1,heightCalculationMethod:"offset",interval:32,log:!1,maxHeight:1/0,maxWidth:1/0,minHeight:0,minWidth:0,scrolling:!1,sizeHeight:!0,sizeWidth:!1,tolerance:0,closedCallback:function(){},initCallback:function(){},messageCallback:function(){},resizedCallback:function(){}};b(),a(window,"message",d),"jQuery"in window&&n(jQuery),"function"==typeof define&&define.amd?define(function(){return m()}):window.iFrameResize=m()}();
9
- //# sourceMappingURL=../src/iframeResizer.map
10
-
1
+ /*! iFrame Resizer (iframeSizer.min.js ) - v2.6.1 - 2014-09-03
2
+ * Desc: Force cross domain iframes to size to content.
3
+ * Requires: iframeResizer.contentWindow.min.js to be loaded into the target frame.
4
+ * Copyright: (c) 2014 David J. Bradshaw - dave@bradshaw.net
5
+ * License: MIT
6
+ */
7
+
8
+ !function(){"use strict";function a(a,b,c){"addEventListener"in window?a.addEventListener(b,c,!1):"attachEvent"in window&&a.attachEvent("on"+b,c)}function b(){var a,b=["moz","webkit","o","ms"];for(a=0;a<b.length&&!w;a+=1)w=window[b[a]+"RequestAnimationFrame"];w||c(" RequestAnimationFrame not supported")}function c(a){y.log&&"object"==typeof console&&console.log(s+"[Host page"+u+"]"+a)}function d(a){function b(){function a(){h(z),f(),y.resizedCallback(z)}i(a,z,"resetPage")}function d(a){var b=a.id;c(" Removing iFrame: "+b),a.parentNode.removeChild(a),y.closedCallback(b),c(" --")}function e(){var a=x.substr(t).split(":");return{iframe:document.getElementById(a[0]),id:a[0],height:a[1],width:a[2],type:a[3]}}function j(a){var b=Number(y["max"+a]),d=Number(y["min"+a]),e=a.toLowerCase(),f=Number(z[e]);if(d>b)throw new Error("Value for min"+a+" can not be greater than max"+a);c(" Checking "+e+" is in range "+d+"-"+b),d>f&&(f=d,c(" Set "+e+" to min value")),f>b&&(f=b,c(" Set "+e+" to max value")),z[e]=""+f}function k(){var b=a.origin,d=z.iframe.src.split("/").slice(0,3).join("/");if(y.checkOrigin&&(c(" Checking connection is from: "+d),""+b!="null"&&b!==d))throw new Error("Unexpected message received from: "+b+" for "+z.iframe.id+". Message was: "+a.data+". This error can be disabled by adding the checkOrigin: false option.");return!0}function l(){return s===(""+x).substr(0,t)}function m(){var a=z.type in{"true":1,"false":1};return a&&c(" Ignoring init message from meta parent page"),a}function n(){var a=x.substr(x.indexOf(":")+r+6);c(" MessageCallback passed: {iframe: "+z.iframe.id+", message: "+a+"}"),y.messageCallback({iframe:z.iframe,message:a}),c(" --")}function o(){if(null===z.iframe)throw new Error("iFrame ("+z.id+") does not exist on "+u);return!0}function q(){c(" Reposition requested from iFrame"),v={x:z.width,y:z.height},f()}function w(){switch(z.type){case"close":d(z.iframe),y.resizedCallback(z);break;case"message":n();break;case"scrollTo":q();break;case"reset":g(z);break;case"init":b(),y.initCallback(z.iframe);break;default:b()}}var x=a.data,z={};l()&&(c(" Received: "+x),z=e(),j("Height"),j("Width"),!m()&&o()&&k()&&(w(),p=!1))}function e(){null===v&&(v={x:void 0!==window.pageXOffset?window.pageXOffset:document.documentElement.scrollLeft,y:void 0!==window.pageYOffset?window.pageYOffset:document.documentElement.scrollTop},c(" Get position: "+v.x+","+v.y))}function f(){null!==v&&(window.scrollTo(v.x,v.y),c(" Set position: "+v.x+","+v.y),v=null)}function g(a){function b(){h(a),j("reset","reset",a.iframe)}c(" Size reset requested by "+("init"===a.type?"host page":"iFrame")),e(),i(b,a,"init")}function h(a){function b(b){a.iframe.style[b]=a[b]+"px",c(" IFrame ("+a.iframe.id+") "+b+" set to "+a[b]+"px")}y.sizeHeight&&b("height"),y.sizeWidth&&b("width")}function i(a,b,d){d!==b.type&&w?(c(" Requesting animation frame"),w(a)):a()}function j(a,b,d){c("["+a+"] Sending msg to iframe ("+b+")"),d.contentWindow.postMessage(s+b,"*")}function k(){function b(){function a(a){1/0!==y[a]&&0!==y[a]&&(k.style[a]=y[a]+"px",c(" Set "+a+" = "+y[a]+"px"))}a("maxHeight"),a("minHeight"),a("maxWidth"),a("minWidth")}function d(a){return""===a&&(k.id=a="iFrameResizer"+o++,c(" Added missing iframe ID: "+a+" ("+k.src+")")),a}function e(){c(" IFrame scrolling "+(y.scrolling?"enabled":"disabled")+" for "+l),k.style.overflow=!1===y.scrolling?"hidden":"auto",k.scrolling=!1===y.scrolling?"no":"yes"}function f(){("number"==typeof y.bodyMargin||"0"===y.bodyMargin)&&(y.bodyMarginV1=y.bodyMargin,y.bodyMargin=""+y.bodyMargin+"px")}function h(){return l+":"+y.bodyMarginV1+":"+y.sizeWidth+":"+y.log+":"+y.interval+":"+y.enablePublicMethods+":"+y.autoResize+":"+y.bodyMargin+":"+y.heightCalculationMethod+":"+y.bodyBackground+":"+y.bodyPadding+":"+y.tolerance}function i(b){a(k,"load",function(){var a=p;j("iFrame.onload",b,k),!a&&y.heightCalculationMethod in x&&g({iframe:k,height:0,width:0,type:"init"})}),j("init",b,k)}var k=this,l=d(k.id);e(),b(),f(),i(h())}function l(a){if("object"!=typeof a)throw new TypeError("Options is not an object.")}function m(){function a(a){if("IFRAME"!==a.tagName.toUpperCase())throw new TypeError("Expected <IFRAME> tag, found <"+a.tagName+">.");k.call(a)}function b(a){a=a||{},l(a);for(var b in z)z.hasOwnProperty(b)&&(y[b]=a.hasOwnProperty(b)?a[b]:z[b])}return function(c,d){b(c),Array.prototype.forEach.call(document.querySelectorAll(d||"iframe"),a)}}function n(a){a.fn.iFrameResize=function(b){return b=b||{},l(b),y=a.extend({},z,b),this.filter("iframe").each(k).end()}}var o=0,p=!0,q="message",r=q.length,s="[iFrameSizer]",t=s.length,u="",v=null,w=window.requestAnimationFrame,x={max:1,scroll:1,bodyScroll:1,documentElementScroll:1},y={},z={autoResize:!0,bodyBackground:null,bodyMargin:null,bodyMarginV1:8,bodyPadding:null,checkOrigin:!0,enablePublicMethods:!1,heightCalculationMethod:"offset",interval:32,log:!1,maxHeight:1/0,maxWidth:1/0,minHeight:0,minWidth:0,scrolling:!1,sizeHeight:!0,sizeWidth:!1,tolerance:0,closedCallback:function(){},initCallback:function(){},messageCallback:function(){},resizedCallback:function(){}};b(),a(window,"message",d),"jQuery"in window&&n(jQuery),"function"==typeof define&&define.amd?define(function(){return m()}):window.iFrameResize=m()}();
9
+ //# sourceMappingURL=../src/iframeResizer.map
10
+
library/otgs/installer/res/js/installer_theme_install.js CHANGED
@@ -1,97 +1,97 @@
1
- jQuery( document ).ready( function( $ ) {
2
-
3
- /** Append OTGS Theme tab */
4
- var js_array= installer_theme_install_localize.js_array_installer;
5
-
6
- if (!($.isEmptyObject(js_array))) {
7
- //Unempty
8
- for(var key in js_array) {
9
- //Dont append if we are on commercial plugins tab page and if there are no themes
10
- if ((!(js_array[key]['is_commercial_plugin_tab'])) && (!(installer_theme_install_localize.no_associated_themes))) {
11
- $('div.wp-filter ul.filter-links').append('<li><a data-sort="'+key+'" href="#">'+ js_array[key]['the_hyperlink_text'] +'</a></li>');
12
- }
13
- }
14
- }
15
-
16
- /** Page load event tab selected identifier */
17
- var loaded_browsing_tab=installer_theme_extended_object.getParameterByName('browse');
18
- if (loaded_browsing_tab.length > 0) {
19
-
20
- var frontend_tab_selected_tab = loaded_browsing_tab;
21
-
22
- } else if (0 == loaded_browsing_tab.length){
23
-
24
- //WordPress defaults to 'Featured' when theme install is loaded without the browse parameter
25
- var frontend_tab_selected_tab = 'featured';
26
- }
27
-
28
- /** Prepare data on page load event for AJAX */
29
- var data = {
30
- action: 'installer_theme_frontend_selected_tab',
31
- installer_theme_frontend_selected_tab_nonce: installer_theme_install_localize.installer_theme_frontend_selected_tab_nonce,
32
- frontend_tab_selected :frontend_tab_selected_tab
33
- };
34
-
35
- //Call AJAX
36
- installer_theme_extended_object.doAJAX(data,frontend_tab_selected_tab,js_array);
37
-
38
- /** When user clicks on any tab */
39
- $(document).on('click','.filter-links li > a',function () {
40
-
41
- //Get data_sort
42
- var data_sort =$(this).attr('data-sort');
43
-
44
- if (data_sort) {
45
- //data_sort is set, prepare data
46
- var data = {
47
- action: 'installer_theme_frontend_selected_tab',
48
- installer_theme_frontend_selected_tab_nonce: installer_theme_install_localize.installer_theme_frontend_selected_tab_nonce,
49
- frontend_tab_selected : data_sort
50
- };
51
-
52
- //Call AJAX
53
- installer_theme_extended_object.doAJAX(data,data_sort,js_array);
54
-
55
- }
56
- });
57
-
58
- var fullhash = window.location.hash;
59
- if (fullhash.length > 0) {
60
- var product_selector=fullhash+' '+'.enter_site_key_js';
61
- if ($(product_selector).length ) {
62
- $(product_selector).click();
63
- }
64
- }
65
- });
66
-
67
- //Installer theme extended JS object for methods
68
- var installer_theme_extended_object = {
69
-
70
- getParameterByName: function(name) {
71
- name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
72
- var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
73
- results = regex.exec(location.search);
74
- return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
75
- },
76
-
77
- doAJAX: function(data,data_sort,js_array) {
78
-
79
- //We only want to post to AJAX if its an OTGS tab
80
- jQuery.post(installer_theme_install_localize.ajaxurl, data, function(response) {
81
- //AJAX response
82
- var myObject = jQuery.parseJSON(response);
83
- if (typeof myObject != "undefined") {
84
- if(myObject.hasOwnProperty("output")){
85
- var tab_selected= myObject.output;
86
- if (data_sort in js_array) {
87
- if (!(installer_theme_install_localize.js_array_installer[tab_selected]['registration_status'])) {
88
- //Not registered, no theme response
89
- var unregistered_message= myObject.unregistered_messages;
90
- jQuery('.no-themes').html(unregistered_message);
91
- }
92
- }
93
- }
94
- }
95
- });
96
- }
97
  };
1
+ jQuery( document ).ready( function( $ ) {
2
+
3
+ /** Append OTGS Theme tab */
4
+ var js_array= installer_theme_install_localize.js_array_installer;
5
+
6
+ if (!($.isEmptyObject(js_array))) {
7
+ //Unempty
8
+ for(var key in js_array) {
9
+ //Dont append if we are on commercial plugins tab page and if there are no themes
10
+ if ((!(js_array[key]['is_commercial_plugin_tab'])) && (!(installer_theme_install_localize.no_associated_themes))) {
11
+ $('div.wp-filter ul.filter-links').append('<li><a data-sort="'+key+'" href="#">'+ js_array[key]['the_hyperlink_text'] +'</a></li>');
12
+ }
13
+ }
14
+ }
15
+
16
+ /** Page load event tab selected identifier */
17
+ var loaded_browsing_tab=installer_theme_extended_object.getParameterByName('browse');
18
+ if (loaded_browsing_tab.length > 0) {
19
+
20
+ var frontend_tab_selected_tab = loaded_browsing_tab;
21
+
22
+ } else if (0 == loaded_browsing_tab.length){
23
+
24
+ //WordPress defaults to 'Featured' when theme install is loaded without the browse parameter
25
+ var frontend_tab_selected_tab = 'featured';
26
+ }
27
+
28
+ /** Prepare data on page load event for AJAX */
29
+ var data = {
30
+ action: 'installer_theme_frontend_selected_tab',
31
+ installer_theme_frontend_selected_tab_nonce: installer_theme_install_localize.installer_theme_frontend_selected_tab_nonce,
32
+ frontend_tab_selected :frontend_tab_selected_tab
33
+ };
34
+
35
+ //Call AJAX
36
+ installer_theme_extended_object.doAJAX(data,frontend_tab_selected_tab,js_array);
37
+
38
+ /** When user clicks on any tab */
39
+ $(document).on('click','.filter-links li > a',function () {
40
+
41
+ //Get data_sort
42
+ var data_sort =$(this).attr('data-sort');
43
+
44
+ if (data_sort) {
45
+ //data_sort is set, prepare data
46
+ var data = {
47
+ action: 'installer_theme_frontend_selected_tab',
48
+ installer_theme_frontend_selected_tab_nonce: installer_theme_install_localize.installer_theme_frontend_selected_tab_nonce,
49
+ frontend_tab_selected : data_sort
50
+ };
51
+
52
+ //Call AJAX
53
+ installer_theme_extended_object.doAJAX(data,data_sort,js_array);
54
+
55
+ }
56
+ });
57
+
58
+ var fullhash = window.location.hash;
59
+ if (fullhash.length > 0) {
60
+ var product_selector=fullhash+' '+'.enter_site_key_js';
61
+ if ($(product_selector).length ) {
62
+ $(product_selector).click();
63
+ }
64
+ }
65
+ });
66
+
67
+ //Installer theme extended JS object for methods
68
+ var installer_theme_extended_object = {
69
+
70
+ getParameterByName: function(name) {
71
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
72
+ var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
73
+ results = regex.exec(location.search);
74
+ return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
75
+ },
76
+
77
+ doAJAX: function(data,data_sort,js_array) {
78
+
79
+ //We only want to post to AJAX if its an OTGS tab
80
+ jQuery.post(installer_theme_install_localize.ajaxurl, data, function(response) {
81
+ //AJAX response
82
+ var myObject = jQuery.parseJSON(response);
83
+ if (typeof myObject != "undefined") {
84
+ if(myObject.hasOwnProperty("output")){
85
+ var tab_selected= myObject.output;
86
+ if (data_sort in js_array) {
87
+ if (!(installer_theme_install_localize.js_array_installer[tab_selected]['registration_status'])) {
88
+ //Not registered, no theme response
89
+ var unregistered_message= myObject.unregistered_messages;
90
+ jQuery('.no-themes').html(unregistered_message);
91
+ }
92
+ }
93
+ }
94
+ }
95
+ });
96
+ }
97
  };
library/otgs/installer/templates/downloads-list-compact.php CHANGED
@@ -1,80 +1,80 @@
1
-
2
- <form method="post" class="otgsi_downloads_form">
3
-
4
- <table class="installer-plugins-list-compact">
5
- <thead>
6
- <tr>
7
- <th>&nbsp;</th>
8
- <th><?php _e('Plugin', 'installer') ?></th>
9
- <th><img src="<?php echo $this->plugin_url() ?>/res/img/globe.png" alt="<?php esc_attr_e('Available', 'installer') ?>" width="16" height="16"></th>
10
- <th><img src="<?php echo $this->plugin_url() ?>/res/img/computer.png" alt="<?php esc_attr_e('Installed', 'installer') ?>" width="16" height="16"></th>
11
- <th><img src="<?php echo $this->plugin_url() ?>/res/img/dn2.gif" alt="<?php esc_attr_e('Downloading', 'installer') ?>" width="16" height="16"></th>
12
- <th><img src="<?php echo $this->plugin_url() ?>/res/img/on.png" alt="<?php esc_attr_e('Activate', 'installer') ?>" width="16" height="16"></th>
13
- </tr>
14
- </thead>
15
- <tbody>
16
- <?php foreach($product['downloads'] as $download): ?>
17
- <?php if(empty($tr_oddeven) || $tr_oddeven == 'even') $tr_oddeven = 'odd'; else $tr_oddeven = 'even'; ?>
18
- <tr class="<?php echo $tr_oddeven ?>">
19
- <td>
20
- <label>
21
- <?php
22
- $url = $this->append_site_key_to_download_url($download['url'], $site_key, $repository_id );
23
-
24
- $download_data = array(
25
- 'url' => $url,
26
- 'slug' => $download['slug'],
27
- 'nonce' => wp_create_nonce('install_plugin_' . $url),
28
- 'repository_id' => $repository_id
29
- );
30
-
31
- $disabled = $expired ||
32
- (
33
- $this->plugin_is_installed($download['name'], $download['slug'], $download['version']) &&
34
- !$this->plugin_is_embedded_version($download['name'], $download['slug'])
35
- ) || WP_Installer()->dependencies->cant_download( $repository_id );
36
-
37
- ?>
38
- <input type="checkbox" name="downloads[]" value="<?php echo base64_encode(json_encode($download_data)); ?>" <?php
39
- if($disabled): ?>disabled="disabled"<?php endif; ?> />&nbsp;
40
-
41
- </label>
42
- </td>
43
- <td class="installer_plugin_name"><?php echo $download['name'] ?></td>
44
- <td><?php echo $download['version'] ?></td>
45
- <td class="installer_version_installed">
46
- <?php if($v = $this->plugin_is_installed($download['name'], $download['slug'])):
47
- $class = version_compare($v, $download['version'], '>=') ? 'installer-green-text' : 'installer-red-text'; ?>
48
- <span class="<?php echo $class ?>"><?php echo $v; ?></span>
49
- <?php endif; ?>
50
- </td>
51
- <td class="twelve">
52
- <div class="installer-status-downloading"><?php _e('downloading...', 'installer') ?></div>
53
- <div class="installer-status-downloaded" data-fail="<?php _e('failed!', 'installer') ?>"><?php _e('downloaded', 'installer') ?></div>
54
- </td>
55
- <td class="twelve">
56
- <div class="installer-status-activating"><?php _e('activating', 'installer') ?></div>
57
- <div class="installer-status-activated"><?php _e('activated', 'installer') ?></div>
58
- </td>
59
- </tr>
60
- <?php endforeach; ?>
61
- </tbody>
62
- </table>
63
-
64
- <?php if( !WP_Installer()->dependencies->is_uploading_allowed() ): ?>
65
- <p class="installer-error-box"><?php printf(__('Downloading is not possible because WordPress cannot write into the plugins folder. %sHow to fix%s.', 'installer'),
66
- '<a href="http://codex.wordpress.org/Changing_File_Permissions">', '</a>') ?></p>
67
- <?php elseif( WP_Installer()->dependencies->is_win_paths_exception($repository_id) ): ?>
68
- <p><?php echo WP_Installer()->dependencies->win_paths_exception_message() ?></p>
69
- <?php endif;?>
70
-
71
- <br />
72
- <input type="submit" class="button-secondary" value="<?php esc_attr_e('Download', 'installer') ?>" disabled="disabled" />
73
- &nbsp;
74
- <label><input name="activate" type="checkbox" value="1" disabled="disabled" />&nbsp;<?php _e('Activate after download', 'installer') ?></label>
75
-
76
- <div class="installer-download-progress-status"></div>
77
- <div class="installer-status-success"><?php _e('Operation complete!', 'installer') ?></div>
78
-
79
- <span class="installer-revalidate-message hidden"><?php _e("Download failed!\n\nClick OK to revalidate your subscription or CANCEL to try again.", 'installer') ?></span>
80
- </form>
1
+
2
+ <form method="post" class="otgsi_downloads_form">
3
+
4
+ <table class="installer-plugins-list-compact">
5
+ <thead>
6
+ <tr>
7
+ <th>&nbsp;</th>
8
+ <th><?php _e('Plugin', 'installer') ?></th>
9
+ <th><img src="<?php echo $this->plugin_url() ?>/res/img/globe.png" alt="<?php esc_attr_e('Available', 'installer') ?>" width="16" height="16"></th>
10
+ <th><img src="<?php echo $this->plugin_url() ?>/res/img/computer.png" alt="<?php esc_attr_e('Installed', 'installer') ?>" width="16" height="16"></th>
11
+ <th><img src="<?php echo $this->plugin_url() ?>/res/img/dn2.gif" alt="<?php esc_attr_e('Downloading', 'installer') ?>" width="16" height="16"></th>
12
+ <th><img src="<?php echo $this->plugin_url() ?>/res/img/on.png" alt="<?php esc_attr_e('Activate', 'installer') ?>" width="16" height="16"></th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <?php foreach($product['downloads'] as $download): ?>
17
+ <?php if(empty($tr_oddeven) || $tr_oddeven == 'even') $tr_oddeven = 'odd'; else $tr_oddeven = 'even'; ?>
18
+ <tr class="<?php echo $tr_oddeven ?>">
19
+ <td>
20
+ <label>
21
+ <?php
22
+ $url = $this->append_site_key_to_download_url($download['url'], $site_key, $repository_id );
23
+
24
+ $download_data = array(
25
+ 'url' => $url,
26
+ 'slug' => $download['slug'],
27
+ 'nonce' => wp_create_nonce('install_plugin_' . $url),
28
+ 'repository_id' => $repository_id
29
+ );
30
+
31
+ $disabled = $expired ||
32
+ (
33
+ $this->plugin_is_installed($download['name'], $download['slug'], $download['version']) &&
34
+ !$this->plugin_is_embedded_version($download['name'], $download['slug'])
35
+ ) || WP_Installer()->dependencies->cant_download( $repository_id );
36
+
37
+ ?>
38
+ <input type="checkbox" name="downloads[]" value="<?php echo base64_encode(json_encode($download_data)); ?>" <?php
39
+ if($disabled): ?>disabled="disabled"<?php endif; ?> />&nbsp;
40
+
41
+ </label>
42
+ </td>
43
+ <td class="installer_plugin_name"><?php echo $download['name'] ?></td>
44
+ <td><?php echo $download['version'] ?></td>
45
+ <td class="installer_version_installed">
46
+ <?php if($v = $this->plugin_is_installed($download['name'], $download['slug'])):
47
+ $class = version_compare($v, $download['version'], '>=') ? 'installer-green-text' : 'installer-red-text'; ?>
48
+ <span class="<?php echo $class ?>"><?php echo $v; ?></span>
49
+ <?php endif; ?>
50
+ </td>
51
+ <td class="twelve">
52
+ <div class="installer-status-downloading"><?php _e('downloading...', 'installer') ?></div>
53
+ <div class="installer-status-downloaded" data-fail="<?php _e('failed!', 'installer') ?>"><?php _e('downloaded', 'installer') ?></div>
54
+ </td>
55
+ <td class="twelve">
56
+ <div class="installer-status-activating"><?php _e('activating', 'installer') ?></div>
57
+ <div class="installer-status-activated"><?php _e('activated', 'installer') ?></div>
58
+ </td>
59
+ </tr>
60
+ <?php endforeach; ?>
61
+ </tbody>
62
+ </table>
63
+
64
+ <?php if( !WP_Installer()->dependencies->is_uploading_allowed() ): ?>
65
+ <p class="installer-error-box"><?php printf(__('Downloading is not possible because WordPress cannot write into the plugins folder. %sHow to fix%s.', 'installer'),
66
+ '<a href="http://codex.wordpress.org/Changing_File_Permissions">', '</a>') ?></p>
67
+ <?php elseif( WP_Installer()->dependencies->is_win_paths_exception($repository_id) ): ?>
68
+ <p><?php echo WP_Installer()->dependencies->win_paths_exception_message() ?></p>
69
+ <?php endif;?>
70
+
71
+ <br />
72
+ <input type="submit" class="button-secondary" value="<?php esc_attr_e('Download', 'installer') ?>" disabled="disabled" />
73
+ &nbsp;
74
+ <label><input name="activate" type="checkbox" value="1" disabled="disabled" />&nbsp;<?php _e('Activate after download', 'installer') ?></label>
75
+
76
+ <div class="installer-download-progress-status"></div>
77
+ <div class="installer-status-success"><?php _e('Operation complete!', 'installer') ?></div>
78
+
79
+ <span class="installer-revalidate-message hidden"><?php _e("Download failed!\n\nClick OK to revalidate your subscription or CANCEL to try again.", 'installer') ?></span>
80
+ </form>
library/otgs/installer/templates/downloads-list.php CHANGED
@@ -1,85 +1,85 @@
1
- <br clear="all" /><br />
2
- <strong><?php _e('Downloads:', 'installer') ?></strong>
3
-
4
- <form method="post" class="otgsi_downloads_form">
5
-
6
- <table class="widefat">
7
- <thead>
8
- <tr>
9
- <th>&nbsp;</th>
10
- <th><?php _e('Plugin', 'installer') ?></th>
11
- <th><?php _e('Current version', 'installer') ?></th>
12
- <th><?php _e('Released', 'installer') ?></th>
13
- <th><?php _e('Installed version', 'installer') ?></th>
14
- <th>&nbsp;</th>
15
- <th>&nbsp;</th>
16
- <th>&nbsp;</th>
17
- </tr>
18
- </thead>
19
- <tbody>
20
- <?php
21
- foreach($package['downloads'] as $download): ?>
22
- <tr>
23
- <td>
24
- <label>
25
- <?php
26
- $url = $this->append_site_key_to_download_url($download['url'], $site_key, $repository_id);
27
-
28
- $download_data = array(
29
- 'url' => $url,
30
- 'slug' => $download['slug'],
31
- 'nonce' => wp_create_nonce('install_plugin_' . $url),
32
- 'repository_id' => $repository_id
33
- );
34
- ?>
35
- <input type="checkbox" name="downloads[]" value="<?php echo base64_encode(json_encode($download_data)); ?>" <?php
36
- if($this->plugin_is_installed($download['name'], $download['slug'], $download['version']) && !$this->plugin_is_embedded_version($download['name'], $download['slug']) || WP_Installer()->dependencies->cant_download($repository_id) ): ?>disabled="disabled"<?php endif; ?> />&nbsp;
37
-
38
- </label>
39
- </td>
40
- <td class="installer_plugin_name"><?php echo $download['name'] ?></td>
41
- <td><?php echo $download['version'] ?></td>
42
- <td><?php echo date_i18n('F j, Y', strtotime($download['date'])) ?></td>
43
- <td class="installer_version_installed">
44
- <?php if($v = $this->plugin_is_installed($download['name'], $download['slug'])): $class = version_compare($v, $download['version'], '>=') ? 'installer-green-text' : 'installer-red-text'; ?>
45
- <span class="<?php echo $class ?>"><?php echo $v; ?></span>
46
- <?php if($this->plugin_is_embedded_version($download['name'], $download['slug'])): ?>&nbsp;<?php _e('(embedded)', 'installer'); ?><?php endif; ?>
47
- <?php endif; ?>
48
- </td>
49
- <td>
50
- <span class="installer-status-installing"><?php _e('installing...', 'installer') ?></span>
51
- <span class="installer-status-updating"><?php _e('updating...', 'installer') ?></span>
52
- <span class="installer-status-installed" data-fail="<?php _e('failed!', 'installer') ?>"><?php _e('installed', 'installer') ?></span>
53
- <span class="installer-status-updated" data-fail="<?php _e('failed!', 'installer') ?>"><?php _e('updated', 'installer') ?></span>
54
- </td>
55
- <td>
56
- <span class="installer-status-activating"><?php _e('activating', 'installer') ?></span>
57
- <span class="installer-status-activated"><?php _e('activated', 'installer') ?></span>
58
- </td>
59
- <td class="for_spinner_js"><span class="spinner"></span></td>
60
- </tr>
61
- <?php endforeach; ?>
62
- </tbody>
63
- </table>
64
-
65
- <br />
66
-
67
- <div class="installer-error-box">
68
- <?php if( !WP_Installer()->dependencies->is_uploading_allowed() ): ?>
69
- <p><?php printf(__('Downloading is not possible because WordPress cannot write into the plugins folder. %sHow to fix%s.', 'installer'),
70
- '<a href="http://codex.wordpress.org/Changing_File_Permissions">', '</a>') ?></p>
71
- <?php elseif( WP_Installer()->dependencies->is_win_paths_exception($repository_id) ): ?>
72
- <p><?php echo WP_Installer()->dependencies->win_paths_exception_message() ?></p>
73
- <?php endif; ?>
74
- </div>
75
-
76
- <input type="submit" class="button-secondary" value="<?php esc_attr_e('Download', 'installer') ?>" disabled="disabled" />
77
- &nbsp;
78
- <label><input name="activate" type="checkbox" value="1" disabled="disabled" />&nbsp;<?php _e('Activate after download', 'installer') ?></label>
79
-
80
- <div class="installer-download-progress-status"></div>
81
-
82
- <div class="installer-status-success"><?php _e('Operation complete!', 'installer') ?></div>
83
-
84
- <span class="installer-revalidate-message hidden"><?php _e("Download failed!\n\nPlease refresh the page and try again.", 'installer') ?></span>
85
- </form>
1
+ <br clear="all" /><br />
2
+ <strong><?php _e('Downloads:', 'installer') ?></strong>
3
+
4
+ <form method="post" class="otgsi_downloads_form">
5
+
6
+ <table class="widefat">
7
+ <thead>
8
+ <tr>
9
+ <th>&nbsp;</th>
10
+ <th><?php _e('Plugin', 'installer') ?></th>
11
+ <th><?php _e('Current version', 'installer') ?></th>
12
+ <th><?php _e('Released', 'installer') ?></th>
13
+ <th><?php _e('Installed version', 'installer') ?></th>
14
+ <th>&nbsp;</th>
15
+ <th>&nbsp;</th>
16
+ <th>&nbsp;</th>
17
+ </tr>
18
+ </thead>
19
+ <tbody>
20
+ <?php
21
+ foreach($package['downloads'] as $download): ?>
22
+ <tr>
23
+ <td>
24
+ <label>
25
+ <?php
26
+ $url = $this->append_site_key_to_download_url($download['url'], $site_key, $repository_id);
27
+
28
+ $download_data = array(
29
+ 'url' => $url,
30
+ 'slug' => $download['slug'],
31
+ 'nonce' => wp_create_nonce('install_plugin_' . $url),
32
+ 'repository_id' => $repository_id
33
+ );
34
+ ?>
35
+ <input type="checkbox" name="downloads[]" value="<?php echo base64_encode(json_encode($download_data)); ?>" <?php
36
+ if($this->plugin_is_installed($download['name'], $download['slug'], $download['version']) && !$this->plugin_is_embedded_version($download['name'], $download['slug']) || WP_Installer()->dependencies->cant_download($repository_id) ): ?>disabled="disabled"<?php endif; ?> />&nbsp;
37
+
38
+ </label>
39
+ </td>
40
+ <td class="installer_plugin_name"><?php echo $download['name'] ?></td>
41
+ <td><?php echo $download['version'] ?></td>
42
+ <td><?php echo date_i18n('F j, Y', strtotime($download['date'])) ?></td>
43
+ <td class="installer_version_installed">
44
+ <?php if($v = $this->plugin_is_installed($download['name'], $download['slug'])): $class = version_compare($v, $download['version'], '>=') ? 'installer-green-text' : 'installer-red-text'; ?>
45
+ <span class="<?php echo $class ?>"><?php echo $v; ?></span>
46
+ <?php if($this->plugin_is_embedded_version($download['name'], $download['slug'])): ?>&nbsp;<?php _e('(embedded)', 'installer'); ?><?php endif; ?>
47
+ <?php endif; ?>
48
+ </td>
49
+ <td>
50
+ <span class="installer-status-installing"><?php _e('installing...', 'installer') ?></span>
51
+ <span class="installer-status-updating"><?php _e('updating...', 'installer') ?></span>
52
+ <span class="installer-status-installed" data-fail="<?php _e('failed!', 'installer') ?>"><?php _e('installed', 'installer') ?></span>
53
+ <span class="installer-status-updated" data-fail="<?php _e('failed!', 'installer') ?>"><?php _e('updated', 'installer') ?></span>
54
+ </td>
55
+ <td>
56
+ <span class="installer-status-activating"><?php _e('activating', 'installer') ?></span>
57
+ <span class="installer-status-activated"><?php _e('activated', 'installer') ?></span>
58
+ </td>
59
+ <td class="for_spinner_js"><span class="spinner"></span></td>
60
+ </tr>
61
+ <?php endforeach; ?>
62
+ </tbody>
63
+ </table>
64
+
65
+ <br />
66
+
67
+ <div class="installer-error-box">
68
+ <?php if( !WP_Installer()->dependencies->is_uploading_allowed() ): ?>
69
+ <p><?php printf(__('Downloading is not possible because WordPress cannot write into the plugins folder. %sHow to fix%s.', 'installer'),
70
+ '<a href="http://codex.wordpress.org/Changing_File_Permissions">', '</a>') ?></p>
71
+ <?php elseif( WP_Installer()->dependencies->is_win_paths_exception($repository_id) ): ?>
72
+ <p><?php echo WP_Installer()->dependencies->win_paths_exception_message() ?></p>
73
+ <?php endif; ?>
74
+ </div>
75
+
76
+ <input type="submit" class="button-secondary" value="<?php esc_attr_e('Download', 'installer') ?>" disabled="disabled" />
77
+ &nbsp;
78
+ <label><input name="activate" type="checkbox" value="1" disabled="disabled" />&nbsp;<?php _e('Activate after download', 'installer') ?></label>
79
+
80
+ <div class="installer-download-progress-status"></div>
81
+
82
+ <div class="installer-status-success"><?php _e('Operation complete!', 'installer') ?></div>
83
+
84
+ <span class="installer-revalidate-message hidden"><?php _e("Download failed!\n\nPlease refresh the page and try again.", 'installer') ?></span>
85
+ </form>
library/otgs/installer/templates/products-compact.php CHANGED
@@ -1,129 +1,147 @@
1
- <h3><?php echo $args['box_title'] ?></h3>
2
-
3
- <?php
4
- if(empty($args['repository']) || empty($args['package']) || empty($args['product'])){
5
- echo __('Incorrect setup', 'installer');
6
- return;
7
- }
8
-
9
- $product = false;
10
- foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package_idx => $package){
11
-
12
- //pre 1.3 backwardds compatibility
13
- if(!isset($package['id'])){
14
- $package['id'] = sanitize_title_with_dashes($package['name']);
15
- }
16
-
17
- if($package['id'] == $args['package']){
18
- $product = $this->settings['repositories'][$repository_id]['data']['packages'][$package_idx]['products'][$args['product']];
19
- break;
20
- }
21
- }
22
-
23
-
24
- if(!$product){
25
- echo __('Invalid product', 'installer');
26
- return;
27
- }
28
-
29
- if(isset($this->settings['repositories'][$repository_id])){
30
- if(isset($this->settings['repositories'][$repository_id]['subscription']['key'])){
31
- $site_key = $this->settings['repositories'][$repository_id]['subscription']['key'];
32
- }else{
33
- $site_key = false;
34
- }
35
- }else{
36
- echo __('Unknown repository', 'installer');
37
- return;
38
- }
39
-
40
- $subscription_type = $this->get_subscription_type_for_repository($repository_id);
41
- $expired = false;
42
-
43
- if($subscription_type != $product['subscription_type'] && !$this->have_superior_subscription($subscription_type, $product) && $site_key){
44
- $subscription_no_match = sprintf(__(' Your current site key (%s) does not match the selected product (%s).', 'installer'), $site_key, $product['name']);
45
- }
46
-
47
- if(!isset($args['product_name'])) $args['product_name'] = $product['name'];
48
-
49
- ?>
50
-
51
- <div class="otgs_wp_installer_table otgs_wp_installer_table_compact">
52
-
53
- <p><?php echo $args['box_description'] ?></p>
54
-
55
-
56
- <?php if(!$this->repository_has_subscription($repository_id) || !empty($subscription_no_match)): ?>
57
-
58
- <?php if(!empty($subscription_no_match)): ?>
59
- <div class="installer-warn-box">
60
- <?php echo $subscription_no_match; ?>
61
- </div>
62
- <br />
63
- <?php endif; ?>
64
-
65
- <a class="button-primary" href="<?php echo $this->append_parameters_to_buy_url($product['url'], $repository_id, $args) ?>"><?php printf(__('Buy %s', 'installer'), $args['product_name']) ?></a>
66
-
67
- <div>
68
- <br />
69
- <?php printf(__('Already bought %s?', 'installer'), $args['product_name']) ?>
70
- <a class="enter_site_key_js" href="#"><?php _e('Enter site key', 'installer') ?></a>&nbsp;&nbsp;
71
-
72
- <form class="otgsi_site_key_form" method="post">
73
- <input type="hidden" name="action" value="save_site_key" />
74
- <input type="hidden" name="nonce" value="<?php echo wp_create_nonce('save_site_key_' . $repository_id) ?>" />
75
- <input type="hidden" name="repository_id" value="<?php echo $repository_id ?>">
76
- <input type="text" size="10" name="site_key_<?php echo $repository_id ?>" />
77
- <input class="button-secondary" type="submit" value="<?php esc_attr_e('Add', 'installer') ?>" />
78
- </form>
79
- <div class="installer-error-box hidden" style="margin-top:10px;"></div>
80
- </div>
81
-
82
- <?php else: ?>
83
-
84
- <?php if($this->repository_has_expired_subscription($repository_id)): $expired = true; ?>
85
-
86
- <div><p class="installer-warn-box"><?php _e('Subscription is expired.', 'installer') ?></p></div>
87
-
88
- <?php else: ?>
89
-
90
- <?php if($this->show_subscription_renew_warning($repository_id, $subscription_type)): ?>
91
-
92
- <ul class="installer-products-list">
93
- <?php foreach($product['renewals'] as $renewal): ?>
94
- <li>
95
- <a href="<?php echo $this->append_parameters_to_buy_url($renewal['url'], $repository_id, $args) ?>"><?php printf(__('Renew %s', 'installer'), $args['product_name']) ?></a>
96
- </li>
97
- <?php endforeach; ?>
98
- </ul>
99
-
100
- <?php endif; ?>
101
-
102
- <?php endif; ?>
103
-
104
- <center>
105
- <a class="remove_site_key_js" href="#" data-repository=<?php echo $repository_id ?> data-confirmation="<?php esc_attr_e('Are you sure you want to remove this site key?', 'installer') ?>" data-nonce="<?php echo wp_create_nonce('remove_site_key_' . $repository_id) ?>"><?php printf(__("Remove current site key (%s)", 'installer'), $site_key) ?></a>
106
- </center>
107
- <br />
108
-
109
- <?php include $this->plugin_path() . '/templates/downloads-list-compact.php'; ?>
110
-
111
-
112
-
113
- <?php endif; ?>
114
-
115
- <?php
116
- if( isset( $args[ 'name' ] ) ):
117
- $support_link = $this->get_support_tag_by_name($args['name'], $args['repository']); ?>
118
-
119
- <?php if($support_link): ?>
120
- <p><a href="<?php echo $support_link ?>" target="_blank"><?php printf(__('%s support on wpml.org', 'installer'), $args['name'] ) ?></a></p>
121
-
122
- <?php endif; ?>
123
- <?php
124
- // compatibility for installer 1.1
125
- elseif( isset( $args[ 'support_link' ] ) ): ?>
126
- <p><?php echo $args[ 'support_link' ]; ?></p>
127
- <?php endif; ?>
128
-
129
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3><?php echo $args['box_title'] ?></h3>
2
+
3
+ <?php
4
+ if(empty($args['repository']) || empty($args['package']) || empty($args['product'])){
5
+ echo __('Incorrect setup', 'installer');
6
+ return;
7
+ }
8
+
9
+ $product = false;
10
+ foreach($this->settings['repositories'][$repository_id]['data']['packages'] as $package_idx => $package){
11
+
12
+ //pre 1.3 backwardds compatibility
13
+ if(!isset($package['id'])){
14
+ $package['id'] = sanitize_title_with_dashes($package['name']);
15
+ }
16
+
17
+ if($package['id'] == $args['package']){
18
+ $product = $this->settings['repositories'][$repository_id]['data']['packages'][$package_idx]['products'][$args['product']];
19
+ break;
20
+ }
21
+ }
22
+
23
+
24
+ if(!$product){
25
+ echo __('Invalid product', 'installer');
26
+ return;
27
+ }
28
+
29
+ if(isset($this->settings['repositories'][$repository_id])){
30
+ if(isset($this->settings['repositories'][$repository_id]['subscription']['key'])){
31
+ $site_key = $this->settings['repositories'][$repository_id]['subscription']['key'];
32
+ }else{
33
+ $site_key = false;
34
+ }
35
+ }else{
36
+ echo __('Unknown repository', 'installer');
37
+ return;
38
+ }
39
+
40
+ $subscription_type = $this->get_subscription_type_for_repository($repository_id);
41
+ $expired = false;
42
+
43
+ if($subscription_type != $product['subscription_type'] && !$this->have_superior_subscription($subscription_type, $product) && $site_key){
44
+ $subscription_no_match = sprintf(__(' Your current site key (%s) does not match the selected product (%s).', 'installer'), $site_key, $product['name']);
45
+ }
46
+
47
+ if(!isset($args['product_name'])) $args['product_name'] = $product['name'];
48
+
49
+ ?>
50
+
51
+ <div class="otgs_wp_installer_table otgs_wp_installer_table_compact">
52
+
53
+ <p><?php echo $args['box_description'] ?></p>
54
+
55
+
56
+ <?php if(!$this->repository_has_subscription($repository_id) || !empty($subscription_no_match)): ?>
57
+
58
+ <?php if(!empty($subscription_no_match)): ?>
59
+ <div class="installer-warn-box">
60
+ <?php echo $subscription_no_match; ?>
61
+ </div>
62
+ <br />
63
+ <?php endif; ?>
64
+
65
+ <a class="button-primary" href="<?php echo $this->append_parameters_to_buy_url($product['url'], $repository_id, $args) ?>"><?php printf(__('Buy %s', 'installer'), $args['product_name']) ?></a>
66
+
67
+ <div>
68
+ <br />
69
+ <?php printf(__('Already bought %s?', 'installer'), $args['product_name']) ?>
70
+ <a class="enter_site_key_js<?php if( WP_Installer::get_repository_hardcoded_site_key( $repository_id ) ): ?> disabled<?php endif ?>" href="#"
71
+ <?php if( WP_Installer::get_repository_hardcoded_site_key( $repository_id ) ): ?>
72
+ style="cursor: help"
73
+ disabled="disabled"
74
+ title="<?php printf( esc_attr__("Site-key was set by %s, most likely in wp-config.php. Please remove the constant before attempting to register.", 'installer'), 'OTGS_INSTALLER_SITE_KEY_' . strtoupper($repository_id) ) ?>"
75
+ <?php endif; ?>
76
+ >
77
+ <?php _e('Enter site key', 'installer') ?>
78
+ </a>&nbsp;&nbsp;
79
+
80
+ <form class="otgsi_site_key_form" method="post">
81
+ <input type="hidden" name="action" value="save_site_key" />
82
+ <input type="hidden" name="nonce" value="<?php echo wp_create_nonce('save_site_key_' . $repository_id) ?>" />
83
+ <input type="hidden" name="repository_id" value="<?php echo $repository_id ?>">
84
+ <input type="text" size="10" name="site_key_<?php echo $repository_id ?>" />
85
+ <input class="button-secondary" type="submit" value="<?php esc_attr_e('Add', 'installer') ?>" />
86
+ </form>
87
+ <div class="installer-error-box hidden" style="margin-top:10px;"></div>
88
+ </div>
89
+
90
+ <?php else: ?>
91
+
92
+ <?php if($this->repository_has_expired_subscription($repository_id)): $expired = true; ?>
93
+
94
+ <div><p class="installer-warn-box"><?php _e('Subscription is expired.', 'installer') ?></p></div>
95
+
96
+ <?php else: ?>
97
+
98
+ <?php if($this->show_subscription_renew_warning($repository_id, $subscription_type)): ?>
99
+
100
+ <ul class="installer-products-list">
101
+ <?php foreach($product['renewals'] as $renewal): ?>
102
+ <li>
103
+ <a href="<?php echo $this->append_parameters_to_buy_url($renewal['url'], $repository_id, $args) ?>"><?php printf(__('Renew %s', 'installer'), $args['product_name']) ?></a>
104
+ </li>
105
+ <?php endforeach; ?>
106
+ </ul>
107
+
108
+ <?php endif; ?>
109
+
110
+ <?php endif; ?>
111
+
112
+ <center>
113
+ <a class="remove_site_key_js" href="#" data-repository=<?php echo $repository_id ?>
114
+ data-confirmation="<?php esc_attr_e('Are you sure you want to remove this site key?', 'installer') ?>"
115
+ data-nonce="<?php echo wp_create_nonce('remove_site_key_' . $repository_id) ?>"
116
+ <?php if( WP_Installer::get_repository_hardcoded_site_key( $repository_id ) ): ?>
117
+ style="cursor: help;color:#999999"
118
+ disabled="disabled"
119
+ title="<?php printf( esc_attr__("Site-key was set by %s, most likely in wp-config.php. Please remove the constant before attempting to unregister.", 'installer'), 'OTGS_INSTALLER_SITE_KEY_' . strtoupper($repository_id) ) ?>"
120
+ <?php endif; ?>
121
+ >
122
+ <?php printf(__("Remove current site key (%s)", 'installer'), $site_key) ?>
123
+ </a>
124
+ </center>
125
+ <br />
126
+
127
+ <?php include $this->plugin_path() . '/templates/downloads-list-compact.php'; ?>
128
+
129
+
130
+
131
+ <?php endif; ?>
132
+
133
+ <?php
134
+ if( isset( $args[ 'name' ] ) ):
135
+ $support_link = $this->get_support_tag_by_name($args['name'], $args['repository']); ?>
136
+
137
+ <?php if($support_link): ?>
138
+ <p><a href="<?php echo $support_link ?>" target="_blank"><?php printf(__('%s support on wpml.org', 'installer'), $args['name'] ) ?></a></p>
139
+
140
+ <?php endif; ?>
141
+ <?php
142
+ // compatibility for installer 1.1
143
+ elseif( isset( $args[ 'support_link' ] ) ): ?>
144
+ <p><?php echo $args[ 'support_link' ]; ?></p>
145
+ <?php endif; ?>
146
+
147
+ </div>
library/otgs/installer/templates/repository-listing.php CHANGED
@@ -1,179 +1,196 @@
1
- <?php if((!$this->repository_has_subscription($repository_id) && $match = $this->get_matching_cp($repository)) && $match['exp']): ?>
2
- <p class="alignright installer_highlight"><strong><?php printf('Price offers available until %s', date_i18n(get_option( 'date_format' ), $match['exp'])) ?></strong></p>
3
- <?php endif; ?>
4
-
5
- <h3 id="repository-<?php echo $repository_id ?>"><?php echo $repository['data']['name'] ?></h3>
6
- <?php
7
- $generic_product_name = $this->settings['repositories'][$repository_id]['data']['product-name'];
8
- ?>
9
- <table class="widefat otgs_wp_installer_table" id="installer_repo_<?php echo $repository_id ?>">
10
-
11
- <tr>
12
- <td>&nbsp;</td>
13
- <td class="otgsi_register_product_wrap" align="center" valign="top">
14
- <?php // IF NO SUBSCRIPTION ?>
15
- <?php if(!$this->repository_has_subscription($repository_id)): ?>
16
-
17
- <div style="text-align: right;">
18
- <span><?php _e('Already bought?', 'installer'); ?>&nbsp;</span>
19
- <a class="enter_site_key_js button-primary" href="#"><?php printf(__('Register %s', 'installer'), $generic_product_name); ?></a>&nbsp;&nbsp;
20
- <form class="otgsi_site_key_form" method="post">
21
- <input type="hidden" name="action" value="save_site_key" />
22
- <input type="hidden" name="nonce" value="<?php echo wp_create_nonce('save_site_key_' . $repository_id) ?>" />
23
- <input type="hidden" name="repository_id" value="<?php echo $repository_id ?>">
24
- <?php _e('2. Enter your site key', 'installer'); ?>
25
- <input type="text" size="10" name="site_key_<?php echo $repository_id ?>" placeholder="<?php echo esc_attr('site key') ?>" />
26
- <input class="button-primary" type="submit" value="<?php esc_attr_e('OK', 'installer') ?>" />
27
- <input class="button-secondary cancel_site_key_js" type="button" value="<?php esc_attr_e('Cancel', 'installer') ?>" />
28
-
29
- <div class="alignleft" style="margin-top:6px;"><?php printf(__('1. Go to your %s%s account%s and add this site URL: %s', 'installer'),
30
- '<a href="' . $this->settings['repositories'][$repository_id]['data']['site_keys_management_url'] . '?add='.urlencode($this->get_installer_site_url( $repository_id )).'">',
31
- $generic_product_name, '</a>', $this->get_installer_site_url( $repository_id )); ?></div>
32
- </form>
33
-
34
-
35
- </div>
36
-
37
- <?php
38
- $site_key = false;
39
-
40
- // IF SUBSCRIPTION
41
- else:
42
-
43
- $site_key = $this->settings['repositories'][$repository_id]['subscription']['key'];
44
- $subscription_type = $this->get_subscription_type_for_repository($repository_id);
45
-
46
- $upgrade_options = $this->get_upgrade_options($repository_id);
47
- $expired = false;
48
-
49
- ?>
50
-
51
- <?php if($this->repository_has_expired_subscription($repository_id)): $expired = true; ?>
52
- <div>
53
- <p class="installer-warn-box">
54
- <?php _e('Subscription expired. You need to either purchase a new subscription or upgrade if available.', 'installer') ?>
55
- <span class="alignright">
56
- <a class="update_site_key_js button-secondary" href="#" data-repository=<?php echo $repository_id ?> data-nonce="<?php echo wp_create_nonce('update_site_key_' . $repository_id) ?>">
57
- <?php _e('Revalidate subscription', 'installer'); ?>
58
- </a>
59
- </span>
60
- <br />
61
- <span class="details"><?php _e("If you have already purchased or renewed your subscription and you can still see this message, please revalidate your subscription", 'installer') ?></span>
62
- </p>
63
- </div>
64
- <?php else: ?>
65
- <?php $this->show_subscription_renew_warning($repository_id, $subscription_type); ?>
66
- <?php endif; ?>
67
-
68
- <div class="alignright">
69
- <a class="remove_site_key_js button-secondary" href="#" data-repository=<?php echo $repository_id ?> data-confirmation="<?php esc_attr_e('Are you sure you want to unregister?', 'installer') ?>" data-nonce="<?php echo wp_create_nonce('remove_site_key_' . $repository_id) ?>"><?php printf(__("Unregister %s from this site", 'installer'), $generic_product_name) ?></a>&nbsp;
70
- <a class="update_site_key_js button-secondary" href="#" data-repository=<?php echo $repository_id ?> data-nonce="<?php echo wp_create_nonce('update_site_key_' . $repository_id) ?>">
71
- <?php _e('Check for updates', 'installer'); ?>
72
- </a>
73
- </div>
74
-
75
- <?php if(empty($expired)): ?>
76
- <div class="alignleft">
77
- <?php if($expires = $this->settings['repositories'][$repository_id]['subscription']['data']->expires): ?>
78
- <?php printf(__('%s is registered on this site. You will receive automatic updates until %s', 'installer'), $generic_product_name, date_i18n('F j, Y', strtotime($expires))); ?>
79
- <?php else: ?>
80
- <?php printf(__('%s is registered on this site. Your Lifetime account gives you updates for life.', 'installer'), $generic_product_name); ?>
81
- <?php endif; ?>
82
- </div>
83
- <?php endif; //if(empty($expired)) ?>
84
-
85
- <?php endif; // if(!repository_has_subscription) ?>
86
- <br clear="all" />
87
- <div class="installer-error-box hidden"></div>
88
-
89
- </td>
90
- </tr>
91
-
92
- <?php
93
-
94
- $subscription_type = isset($subscription_type) ? $subscription_type : null;
95
- $expired = isset($expired) ? $expired : null;
96
- $upgrade_options = isset($upgrade_options) ? $upgrade_options : null;
97
- $packages = $this->_render_product_packages($repository['data']['packages'], $subscription_type, $expired, $upgrade_options, $repository_id);
98
- if(empty($subscription_type) || $expired){
99
- $subpackages_expandable = true;
100
- }else{
101
- $subpackages_expandable = false;
102
- }
103
-
104
- ?>
105
-
106
- <?php foreach($packages as $package): ?>
107
- <tr id="repository-<?php echo $repository_id ?>_<?php echo $package['id'] ?>">
108
- <td><img width="140" height="140" src="<?php echo $package['image_url'] ?>" /></td>
109
- <td>
110
- <p><strong><?php echo $package['name'] ?></strong></p>
111
- <p><?php echo $package['description'] ?></p>
112
-
113
- <?php if($package['products']): ?>
114
- <?php foreach($package['products'] as $product): ?>
115
- <ul class="installer-products-list" style="display:inline">
116
- <li>
117
- <a class="button-secondary" href="<?php echo $product['url'] ?>"><?php echo $product['label'] ?></a>
118
- </li>
119
- </ul>
120
- <?php endforeach; ?>
121
- <?php endif; ?>
122
-
123
- <?php if($package['downloads']): ?>
124
- <?php include $this->plugin_path() . '/templates/downloads-list.php'; ?>
125
- <?php endif; ?>
126
-
127
- <?php if(!empty($package['sub-packages'])): ?>
128
-
129
- <?php $subpackages = $this->_render_product_packages($package['sub-packages'], $subscription_type, $expired, $upgrade_options, $repository_id); ?>
130
-
131
- <?php if($subpackages): ?>
132
-
133
- <?php if($subpackages_expandable): ?>
134
- <h5><a class="installer_expand_button" href="#" title="<?php esc_attr_e('Click to see individual components options.', 'installer') ?>"><?php _e('Individual components', 'installer') ?></a></h5>
135
- <?php endif; ?>
136
-
137
- <table class="otgs_wp_installer_subtable" style="<?php if($subpackages_expandable) echo 'display:none' ?>">
138
- <?php foreach($subpackages as $package): ?>
139
- <tr id="repository-<?php echo $repository_id ?>_<?php echo $package['id'] ?>">
140
- <td><img width="70" height="70" src="<?php echo $package['image_url'] ?>" /></td>
141
- <td>
142
- <p><strong><?php echo $package['name'] ?></strong></p>
143
- <p><?php echo $package['description'] ?></p>
144
-
145
- <?php if($package['products']): ?>
146
- <?php foreach($package['products'] as $product): ?>
147
- <ul class="installer-products-list" style="display:inline">
148
- <li>
149
- <a class="button-secondary" href="<?php echo $product['url'] ?>"><?php echo $product['label'] ?></a>
150
- </li>
151
- </ul>
152
- <?php endforeach; ?>
153
- <?php endif; ?>
154
-
155
- <?php if($package['downloads']): ?>
156
- <?php include $this->plugin_path() . '/templates/downloads-list.php'; ?>
157
- <?php endif; ?>
158
- </td>
159
- </tr>
160
- <?php endforeach; ?>
161
- </table>
162
- <?php endif; ?>
163
-
164
- <?php endif; ?>
165
-
166
-
167
- </td>
168
- </tr>
169
-
170
- <?php endforeach; ?>
171
-
172
- </table>
173
-
174
-
175
- <p><i><?php printf(__('This page lets you install plugins and update existing plugins. To remove any of these plugins, go to the %splugins%s page and if you have the permission to remove plugins you should be able to do this.', 'installer'), '<a href="' . admin_url('plugins.php') . '">' , '</a>'); ?></i></p>
176
-
177
-
178
-
179
- <br />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if((!$this->repository_has_subscription($repository_id) && $match = $this->get_matching_cp($repository)) && $match['exp']): ?>
2
+ <p class="alignright installer_highlight"><strong><?php printf('Price offers available until %s', date_i18n(get_option( 'date_format' ), $match['exp'])) ?></strong></p>
3
+ <?php endif; ?>
4
+
5
+ <h3 id="repository-<?php echo $repository_id ?>"><?php echo $repository['data']['name'] ?></h3>
6
+ <?php
7
+ $generic_product_name = $this->settings['repositories'][$repository_id]['data']['product-name'];
8
+ ?>
9
+ <table class="widefat otgs_wp_installer_table" id="installer_repo_<?php echo $repository_id ?>">
10
+
11
+ <tr>
12
+ <td>&nbsp;</td>
13
+ <td class="otgsi_register_product_wrap" align="center" valign="top">
14
+ <?php // IF NO SUBSCRIPTION ?>
15
+ <?php if(!$this->repository_has_subscription($repository_id)): ?>
16
+
17
+ <div style="text-align: right;">
18
+ <span><?php _e('Already bought?', 'installer'); ?>&nbsp;</span>
19
+ <a class="enter_site_key_js button-primary<?php if( WP_Installer::get_repository_hardcoded_site_key( $repository_id ) ): ?> disabled<?php endif ?>" href="#"
20
+ <?php if( WP_Installer::get_repository_hardcoded_site_key( $repository_id ) ): ?>
21
+ style="cursor: help"
22
+ disabled="disabled"
23
+ title="<?php printf( esc_attr__("Site-key was set by %s, most likely in wp-config.php. Please remove the constant before attempting to register.", 'installer'), 'OTGS_INSTALLER_SITE_KEY_' . strtoupper($repository_id) ) ?>"
24
+ <?php endif; ?>
25
+ >
26
+ <?php printf(__('Register %s', 'installer'), $generic_product_name); ?>
27
+ </a>&nbsp;&nbsp;
28
+ <form class="otgsi_site_key_form" method="post">
29
+ <input type="hidden" name="action" value="save_site_key" />
30
+ <input type="hidden" name="nonce" value="<?php echo wp_create_nonce('save_site_key_' . $repository_id) ?>" />
31
+ <input type="hidden" name="repository_id" value="<?php echo $repository_id ?>">
32
+ <?php _e('2. Enter your site key', 'installer'); ?>
33
+ <input type="text" size="10" name="site_key_<?php echo $repository_id ?>" placeholder="<?php echo esc_attr('site key') ?>" />
34
+ <input class="button-primary" type="submit" value="<?php esc_attr_e('OK', 'installer') ?>" />
35
+ <input class="button-secondary cancel_site_key_js" type="button" value="<?php esc_attr_e('Cancel', 'installer') ?>" />
36
+
37
+ <div class="alignleft" style="margin-top:6px;"><?php printf(__('1. Go to your %s%s account%s and add this site URL: %s', 'installer'),
38
+ '<a href="' . $this->settings['repositories'][$repository_id]['data']['site_keys_management_url'] . '?add='.urlencode($this->get_installer_site_url( $repository_id )).'">',
39
+ $generic_product_name, '</a>', $this->get_installer_site_url( $repository_id )); ?></div>
40
+ </form>
41
+
42
+
43
+ </div>
44
+
45
+ <?php
46
+ $site_key = false;
47
+
48
+ // IF SUBSCRIPTION
49
+ else:
50
+
51
+ $site_key = $this->settings['repositories'][$repository_id]['subscription']['key'];
52
+ $subscription_type = $this->get_subscription_type_for_repository($repository_id);
53
+ $upgrade_options = $this->get_upgrade_options($repository_id);
54
+ $expired = false;
55
+
56
+ ?>
57
+
58
+ <?php if($this->repository_has_expired_subscription($repository_id)): $expired = true; ?>
59
+ <div>
60
+ <p class="installer-warn-box">
61
+ <?php _e('Subscription expired. You need to either purchase a new subscription or upgrade if available.', 'installer') ?>
62
+ <span class="alignright">
63
+ <a class="update_site_key_js button-secondary" href="#" data-repository=<?php echo $repository_id ?> data-nonce="<?php echo wp_create_nonce('update_site_key_' . $repository_id) ?>">
64
+ <?php _e('Revalidate subscription', 'installer'); ?>
65
+ </a>
66
+ </span>
67
+ <br />
68
+ <span class="details"><?php _e("If you have already purchased or renewed your subscription and you can still see this message, please revalidate your subscription", 'installer') ?></span>
69
+ </p>
70
+ </div>
71
+ <?php else: ?>
72
+ <?php $this->show_subscription_renew_warning($repository_id, $subscription_type); ?>
73
+ <?php endif; ?>
74
+
75
+ <div class="alignright">
76
+ <a class="remove_site_key_js button-secondary" href="#" data-repository=<?php echo $repository_id ?>
77
+ data-confirmation="<?php esc_attr_e('Are you sure you want to unregister?', 'installer') ?>"
78
+ data-nonce="<?php echo wp_create_nonce('remove_site_key_' . $repository_id) ?>"
79
+ <?php if( WP_Installer::get_repository_hardcoded_site_key( $repository_id ) ): ?>
80
+ style="cursor: help"
81
+ disabled="disabled"
82
+ title="<?php printf( esc_attr__("Site-key was set by %s, most likely in wp-config.php. Please remove the constant before attempting to unregister.", 'installer'), 'OTGS_INSTALLER_SITE_KEY_' . strtoupper($repository_id) ) ?>"
83
+ <?php endif; ?>
84
+ >
85
+ <?php printf(__("Unregister %s from this site", 'installer'), $generic_product_name) ?></a>&nbsp;
86
+ <a class="update_site_key_js button-secondary" href="#" data-repository=<?php echo $repository_id ?>
87
+ data-nonce="<?php echo wp_create_nonce('update_site_key_' . $repository_id) ?>">
88
+ <?php _e('Check for updates', 'installer'); ?>
89
+ </a>
90
+ </div>
91
+
92
+ <?php if(empty($expired)): ?>
93
+ <div class="alignleft">
94
+ <?php if($expires = $this->settings['repositories'][$repository_id]['subscription']['data']->expires): ?>
95
+ <?php printf(__('%s is registered on this site. You will receive automatic updates until %s', 'installer'), $generic_product_name, date_i18n('F j, Y', strtotime($expires))); ?>
96
+ <?php else: ?>
97
+ <?php printf(__('%s is registered on this site. Your Lifetime account gives you updates for life.', 'installer'), $generic_product_name); ?>
98
+ <?php endif; ?>
99
+ </div>
100
+ <?php endif; //if(empty($expired)) ?>
101
+
102
+ <?php endif; // if(!repository_has_subscription) ?>
103
+ <br clear="all" />
104
+ <div class="installer-error-box hidden"></div>
105
+
106
+ </td>
107
+ </tr>
108
+
109
+ <?php
110
+
111
+ $subscription_type = isset($subscription_type) ? $subscription_type : null;
112
+ $expired = isset($expired) ? $expired : null;
113
+ $upgrade_options = isset($upgrade_options) ? $upgrade_options : null;
114
+ $packages = $this->_render_product_packages($repository['data']['packages'], $subscription_type, $expired, $upgrade_options, $repository_id);
115
+ if(empty($subscription_type) || $expired){
116
+ $subpackages_expandable = true;
117
+ }else{
118
+ $subpackages_expandable = false;
119
+ }
120
+
121
+ ?>
122
+
123
+ <?php foreach($packages as $package): ?>
124
+ <tr id="repository-<?php echo $repository_id ?>_<?php echo $package['id'] ?>">
125
+ <td><img width="140" height="140" src="<?php echo $package['image_url'] ?>" /></td>
126
+ <td>
127
+ <p><strong><?php echo $package['name'] ?></strong></p>
128
+ <p><?php echo $package['description'] ?></p>
129
+
130
+ <?php if($package['products']): ?>
131
+ <?php foreach($package['products'] as $product): ?>
132
+ <ul class="installer-products-list" style="display:inline">
133
+ <li>
134
+ <a class="button-secondary" href="<?php echo $product['url'] ?>"><?php echo $product['label'] ?></a>
135
+ </li>
136
+ </ul>
137
+ <?php endforeach; ?>
138
+ <?php endif; ?>
139
+
140
+ <?php if($package['downloads']): ?>
141
+ <?php include $this->plugin_path() . '/templates/downloads-list.php'; ?>
142
+ <?php endif; ?>
143
+
144
+ <?php if(!empty($package['sub-packages'])): ?>
145
+
146
+ <?php $subpackages = $this->_render_product_packages($package['sub-packages'], $subscription_type, $expired, $upgrade_options, $repository_id); ?>
147
+
148
+ <?php if($subpackages): ?>
149
+
150
+ <?php if($subpackages_expandable): ?>
151
+ <h5><a class="installer_expand_button" href="#" title="<?php esc_attr_e('Click to see individual components options.', 'installer') ?>"><?php _e('Individual components', 'installer') ?></a></h5>
152
+ <?php endif; ?>
153
+
154
+ <table class="otgs_wp_installer_subtable" style="<?php if($subpackages_expandable) echo 'display:none' ?>">
155
+ <?php foreach($subpackages as $package): ?>
156
+ <tr id="repository-<?php echo $repository_id ?>_<?php echo $package['id'] ?>">
157
+ <td><img width="70" height="70" src="<?php echo $package['image_url'] ?>" /></td>
158
+ <td>
159
+ <p><strong><?php echo $package['name'] ?></strong></p>
160
+ <p><?php echo $package['description'] ?></p>
161
+
162
+ <?php if($package['products']): ?>
163
+ <?php foreach($package['products'] as $product): ?>
164
+ <ul class="installer-products-list" style="display:inline">
165
+ <li>
166
+ <a class="button-secondary" href="<?php echo $product['url'] ?>"><?php echo $product['label'] ?></a>
167
+ </li>
168
+ </ul>
169
+ <?php endforeach; ?>
170
+ <?php endif; ?>
171
+
172
+ <?php if($package['downloads']): ?>
173
+ <?php include $this->plugin_path() . '/templates/downloads-list.php'; ?>
174
+ <?php endif; ?>
175
+ </td>
176
+ </tr>
177
+ <?php endforeach; ?>
178
+ </table>
179
+ <?php endif; ?>
180
+
181
+ <?php endif; ?>
182
+
183
+
184
+ </td>
185
+ </tr>
186
+
187
+ <?php endforeach; ?>
188
+
189
+ </table>
190
+
191
+
192
+ <p><i><?php printf(__('This page lets you install plugins and update existing plugins. To remove any of these plugins, go to the %splugins%s page and if you have the permission to remove plugins you should be able to do this.', 'installer'), '<a href="' . admin_url('plugins.php') . '">' , '</a>'); ?></i></p>
193
+
194
+
195
+
196
+ <br />
library/toolset/types/embedded/frontend.php CHANGED
@@ -68,7 +68,7 @@ function wpcf_shortcode( $atts, $content = null, $code = '' ) {
68
  );
69
 
70
  if ( $atts['field'] ) {
71
- return types_render_field( $atts['field'], $atts, $content, $code );
72
  }
73
  if ( $atts['termmeta'] ) {
74
  return types_render_termmeta( $atts['termmeta'], $atts, $content, $code );
@@ -83,11 +83,15 @@ function wpcf_shortcode( $atts, $content = null, $code = '' ) {
83
  /**
84
  * Calls view function for specific field type.
85
  *
86
- * @param type $field
87
- * @param type $atts
88
- * @return type
 
 
 
 
89
  */
90
- function types_render_field( $field_id = null, $params = array(), $content = null, $code = '' )
91
  {
92
  if ( empty($field_id) ) {
93
  return '';
@@ -101,9 +105,13 @@ function types_render_field( $field_id = null, $params = array(), $content = nul
101
  // Set post ID to global
102
  $post_id = get_the_ID();
103
 
 
 
 
 
104
  // support also 'id' like our shortcode [types] does
105
  // @since 2.1
106
- if( ! isset( $params['post_id'] ) && isset( $params['id'] ) && substr( $params['id'], 0, 1) !== '$' )
107
  $params['post_id'] = $params['id'];
108
 
109
  // Check if other post required
@@ -367,17 +375,18 @@ function types_render_termmeta( $field_id, $params, $content = null, $code = ''
367
  /**
368
  * Calls view function for specific usermeta field type.
369
  *
370
- * @global object $wpdb
 
 
 
371
  *
372
- * @param type $field
373
- * @param type $atts (additional attributes: user_id, user_name, user_is_author, user_current)
374
- * @return type
375
  */
376
  function types_render_usermeta( $field_id, $params, $content = null, $code = '' ) {
377
 
378
  global $wpcf, $post, $wpdb, $WP_Views;
379
- // HTML var holds actual output
380
- $html = '';
381
  $current_user = wp_get_current_user();
382
  $current_user_id = $current_user->ID;
383
 
@@ -392,14 +401,14 @@ function types_render_usermeta( $field_id, $params, $content = null, $code = ''
392
  $post_id = $params['post_id'];
393
  }
394
 
395
- //Get User id from views loop
396
  if (
397
  isset( $WP_Views->users_data['term']->ID )
398
  && ! empty( $WP_Views->users_data['term']->ID )
399
  ) {
400
  $params['user_id'] = $WP_Views->users_data['term']->ID;
401
  }
402
- //print_r($params);exit;
403
  //Get user By ID
404
  if ( isset( $params['user_id'] ) ) {
405
  $user_id = $params['user_id'];
@@ -418,13 +427,14 @@ function types_render_usermeta( $field_id, $params, $content = null, $code = ''
418
  if ( !empty( $post_id ) ) {
419
  $user_id = $post->post_author;
420
  } else {
421
- return;
422
  }
423
  }
424
 
425
  if ( empty( $user_id ) ) {
426
- return;
427
  }
 
428
  // Get field
429
  $field = types_get_field( $field_id, 'usermeta' );
430
 
@@ -442,27 +452,18 @@ function types_render_usermeta( $field_id, $params, $content = null, $code = ''
442
  return '';
443
  }
444
 
445
- // See if repetitive
446
- if ( wpcf_admin_is_repetitive( $field ) ) {
447
 
448
  $wpcf->usermeta_repeater->set( $user_id, $field );
449
  $_meta = $wpcf->usermeta_repeater->_get_meta();
450
- $meta = '';
451
- if ( isset( $_meta['custom_order'] ) ) {
452
- $meta = $_meta['custom_order'];
453
- }
454
 
455
- if ( (count( $meta ) == 1 ) ) {
456
- $meta_id = key( $meta );
457
- $_temp = array_shift( $meta );
458
- if ( strval( $_temp ) == '' ) {
459
- return '';
460
- } else {
461
- $params['field_value'] = $_temp;
462
- return types_render_field_single( $field, $params, $content,
463
- $code, $meta_id );
464
- }
465
- } else if ( !empty( $meta ) ) {
466
  $output = '';
467
 
468
  if ( isset( $params['index'] ) ) {
@@ -524,9 +525,13 @@ function types_render_usermeta( $field_id, $params, $content = null, $code = ''
524
  /**
525
  * Calls view function for specific field type by single field.
526
  *
527
- * @param type $field
528
- * @param type $atts
529
- * @return type
 
 
 
 
530
  */
531
  function types_render_field_single( $field, $params, $content = null, $code = '', $meta_id = null )
532
  {
68
  );
69
 
70
  if ( $atts['field'] ) {
71
+ return types_render_field( $atts['field'], $atts, $content, $code, false );
72
  }
73
  if ( $atts['termmeta'] ) {
74
  return types_render_termmeta( $atts['termmeta'], $atts, $content, $code );
83
  /**
84
  * Calls view function for specific field type.
85
  *
86
+ * @param null|integer $field_id
87
+ * @param array $params
88
+ * @param null|string $content
89
+ * @param string $code
90
+ * @param bool $block_parent Used by the shortcode [types] which already change the global post before calling this
91
+ *
92
+ * @return string
93
  */
94
+ function types_render_field( $field_id = null, $params = array(), $content = null, $code = '', $allow_parent = true )
95
  {
96
  if ( empty($field_id) ) {
97
  return '';
105
  // Set post ID to global
106
  $post_id = get_the_ID();
107
 
108
+ // check if "$parent" for "id" is allowed
109
+ // OR if "id" does not contain a "$parent" selection
110
+ $parent_check = isset( $params['id'] ) && ( $allow_parent || substr( $params['id'], 0, 1) !== '$' );
111
+
112
  // support also 'id' like our shortcode [types] does
113
  // @since 2.1
114
+ if( ! isset( $params['post_id'] ) && isset( $params['id'] ) && $parent_check )
115
  $params['post_id'] = $params['id'];
116
 
117
  // Check if other post required
375
  /**
376
  * Calls view function for specific usermeta field type.
377
  *
378
+ * @param $field_id
379
+ * @param array $params (additional attributes: user_id, user_name, user_is_author, user_current)
380
+ * @param null $content
381
+ * @param string $code
382
  *
383
+ * @return string|void
384
+ * @since unknown
 
385
  */
386
  function types_render_usermeta( $field_id, $params, $content = null, $code = '' ) {
387
 
388
  global $wpcf, $post, $wpdb, $WP_Views;
389
+
 
390
  $current_user = wp_get_current_user();
391
  $current_user_id = $current_user->ID;
392
 
401
  $post_id = $params['post_id'];
402
  }
403
 
404
+ // Get User id from views loop
405
  if (
406
  isset( $WP_Views->users_data['term']->ID )
407
  && ! empty( $WP_Views->users_data['term']->ID )
408
  ) {
409
  $params['user_id'] = $WP_Views->users_data['term']->ID;
410
  }
411
+
412
  //Get user By ID
413
  if ( isset( $params['user_id'] ) ) {
414
  $user_id = $params['user_id'];
427
  if ( !empty( $post_id ) ) {
428
  $user_id = $post->post_author;
429
  } else {
430
+ return '';
431
  }
432
  }
433
 
434
  if ( empty( $user_id ) ) {
435
+ return '';
436
  }
437
+
438
  // Get field
439
  $field = types_get_field( $field_id, 'usermeta' );
440
 
452
  return '';
453
  }
454
 
455
+ if ( types_is_repetitive( $field ) ) {
 
456
 
457
  $wpcf->usermeta_repeater->set( $user_id, $field );
458
  $_meta = $wpcf->usermeta_repeater->_get_meta();
459
+ $meta = toolset_getarr( $_meta, 'custom_order', '' );
 
 
 
460
 
461
+ // Sometimes if meta is empty - array(0 => '') is returned
462
+ if ( count( $meta ) == 1 && reset( $meta ) == '' ) {
463
+ return '';
464
+ }
465
+
466
+ if ( !empty( $meta ) ) {
 
 
 
 
 
467
  $output = '';
468
 
469
  if ( isset( $params['index'] ) ) {
525
  /**
526
  * Calls view function for specific field type by single field.
527
  *
528
+ * @param array $field
529
+ * @param array $params
530
+ * @param mixed $content
531
+ * @param string $code
532
+ * @param null|int $meta_id
533
+ *
534
+ * @return string
535
  */
536
  function types_render_field_single( $field, $params, $content = null, $code = '', $meta_id = null )
537
  {
library/toolset/types/embedded/includes/api.php CHANGED
@@ -150,8 +150,9 @@ function types_field_get_meta_value_repetitive( $field, $post_id = null,
150
  /**
151
  * Check if field is repetitive.
152
  *
153
- * @param type $type
154
- * @return type
 
155
  */
156
  function types_is_repetitive( $field ) {
157
 
@@ -159,7 +160,7 @@ function types_is_repetitive( $field ) {
159
  if ( !is_array( $field ) ) {
160
  $field = types_get_field( $field );
161
  if ( empty( $field ) ) {
162
- return NULL;
163
  }
164
  }
165
 
150
  /**
151
  * Check if field is repetitive.
152
  *
153
+ * @param array|string $field Field definition or field slug
154
+ * @return bool|null
155
+ * @since unknown
156
  */
157
  function types_is_repetitive( $field ) {
158
 
160
  if ( !is_array( $field ) ) {
161
  $field = types_get_field( $field );
162
  if ( empty( $field ) ) {
163
+ return null;
164
  }
165
  }
166
 
library/toolset/types/embedded/includes/custom-taxonomies.php CHANGED
@@ -139,96 +139,124 @@ function wpcf_taxonomies_register($taxonomy, $data)
139
  /**
140
  * Registers custom taxonomies.
141
  *
142
- * @param type $post_type
143
- * @param type $data
 
 
 
144
  */
145
  function wpcf_custom_taxonomies_register( $taxonomy, $data ) {
146
- if ( !empty( $data['disabled'] ) ) {
147
- return false;
148
- }
149
- // Set object types
150
- if ( !empty( $data['supports'] ) && is_array( $data['supports'] ) ) {
151
- $object_types = array_keys( $data['supports'] );
152
- } else {
153
- $object_types = array();
154
- }
155
- $data = apply_filters( 'types_taxonomy', $data, $taxonomy );
156
- // Set labels
157
- if ( !empty( $data['labels'] ) ) {
158
- if ( !isset( $data['labels']['name'] ) ) {
159
- $data['labels']['name'] = $taxonomy;
160
- }
161
- if ( !isset( $data['labels']['singular_name'] ) ) {
162
- $data['labels']['singular_name'] = $data['labels']['name'];
163
- }
164
- foreach ( $data['labels'] as $label_key => $label ) {
165
- $data['labels'][$label_key] = $label = stripslashes( $label );
166
- switch ( $label_key ) {
167
- case 'parent_item':
168
- case 'parent_item_colon':
169
- case 'edit_item':
170
- case 'update_item':
171
- case 'add_new_item':
172
- case 'new_item_name':
173
- $data['labels'][$label_key] = sprintf( $label,
174
- $data['labels']['singular_name'] );
175
- break;
176
-
177
- case 'search_items':
178
- case 'popular_items':
179
- case 'all_items':
180
- case 'separate_items_with_commas':
181
- case 'add_or_remove_items':
182
- case 'choose_from_most_used':
183
- case 'menu_name':
184
- $data['labels'][$label_key] = sprintf( $label,
185
- $data['labels']['name'] );
186
- break;
187
- }
188
- }
189
- }
190
- $data['description'] = !empty( $data['description'] ) ? htmlspecialchars( stripslashes( $data['description'] ),
191
- ENT_QUOTES ) : '';
192
- $data['public'] = (empty( $data['public'] ) || strval( $data['public'] ) == 'hidden') ? false : true;
193
- $data['show_ui'] = (empty( $data['show_ui'] ) || !$data['public']) ? false : true;
194
- $data['hierarchical'] = (empty( $data['hierarchical'] ) || $data['hierarchical'] == 'flat') ? false : true;
195
- $data['show_in_nav_menus'] = !empty( $data['show_in_nav_menus'] );
196
- if ( empty( $data['query_var_enabled'] ) ) {
197
- $data['query_var'] = false;
198
- } else if ( empty( $data['query_var'] ) ) {
199
- $data['query_var'] = true;
200
- }
201
- if ( !empty( $data['rewrite']['enabled'] ) ) {
202
- $data['rewrite']['with_front'] = !empty( $data['rewrite']['with_front'] );
203
- $data['rewrite']['hierarchical'] = !empty( $data['rewrite']['hierarchical'] );
204
- // Make sure that rewrite/slug has a value
205
- if ( !isset( $data['rewrite']['slug'] ) || $data['rewrite']['slug'] == '' ) {
206
- $data['rewrite']['slug'] = $data['slug'];
207
- }
208
- } else {
209
- $data['rewrite'] = false;
210
- }
211
- /**
212
- * meta_box_cb
213
- */
214
- if ( isset($data['meta_box_cb']['disabled'])) {
215
- $data['meta_box_cb'] = false;
216
- } else if ( isset($data['meta_box_cb']['callback']) && !empty($data['meta_box_cb']['callback']) ){
217
- $data['meta_box_cb'] = $data['meta_box_cb']['callback'];
218
- } else {
219
- unset($data['meta_box_cb']);
220
- }
221
- // Force removing capabilities here
222
- unset( $data['capabilities'] );
223
- register_taxonomy( $taxonomy,
224
- apply_filters( 'wpcf_taxonomy_objects', $object_types, $taxonomy ),
225
- apply_filters( 'wpcf_taxonomy_data', $data, $taxonomy, $object_types ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  }
227
 
 
228
  /**
229
  * Returns only active taxonomies.
230
  *
231
- * @return type
232
  */
233
  function wpcf_get_active_custom_taxonomies() {
234
  $taxonomies = get_option(WPCF_OPTION_NAME_CUSTOM_TAXONOMIES, array());
139
  /**
140
  * Registers custom taxonomies.
141
  *
142
+ * @param string $taxonomy Taxonomy slug
143
+ * @param array $data Taxonomy settings as stored in options by Types.
144
+ *
145
+ * @return bool
146
+ * @since unknown
147
  */
148
  function wpcf_custom_taxonomies_register( $taxonomy, $data ) {
149
+ if ( ! empty( $data['disabled'] ) ) {
150
+ return false;
151
+ }
152
+
153
+ // Set object types
154
+ if ( ! empty( $data['supports'] ) && is_array( $data['supports'] ) ) {
155
+ $object_types = array_keys( $data['supports'] );
156
+ } else {
157
+ $object_types = array();
158
+ }
159
+
160
+ $data = apply_filters( 'types_taxonomy', $data, $taxonomy );
161
+
162
+ // Set labels
163
+ if ( ! empty( $data['labels'] ) ) {
164
+
165
+ if ( ! isset( $data['labels']['name'] ) ) {
166
+ $data['labels']['name'] = $taxonomy;
167
+ }
168
+
169
+ if ( ! isset( $data['labels']['singular_name'] ) ) {
170
+ $data['labels']['singular_name'] = $data['labels']['name'];
171
+ }
172
+
173
+ foreach ( $data['labels'] as $label_key => $label ) {
174
+ $data['labels'][ $label_key ] = $label = stripslashes( $label );
175
+
176
+ switch ( $label_key ) {
177
+
178
+ case 'parent_item':
179
+ case 'parent_item_colon':
180
+ case 'edit_item':
181
+ case 'update_item':
182
+ case 'add_new_item':
183
+ case 'new_item_name':
184
+ $data['labels'][ $label_key ] = sprintf( $label, $data['labels']['singular_name'] );
185
+ break;
186
+
187
+ case 'search_items':
188
+ case 'popular_items':
189
+ case 'all_items':
190
+ case 'separate_items_with_commas':
191
+ case 'add_or_remove_items':
192
+ case 'choose_from_most_used':
193
+ case 'menu_name':
194
+ $data['labels'][ $label_key ] = sprintf( $label, $data['labels']['name'] );
195
+ break;
196
+ }
197
+ }
198
+ }
199
+
200
+ $data['description'] = (
201
+ ! empty( $data['description'] )
202
+ ? htmlspecialchars( stripslashes( $data['description'] ), ENT_QUOTES )
203
+ : ''
204
+ );
205
+
206
+ $data['public'] = ( empty( $data['public'] ) || strval( $data['public'] ) == 'hidden' ) ? false : true;
207
+ $data['show_ui'] = ( empty( $data['show_ui'] ) || ! $data['public'] ) ? false : true;
208
+ $data['hierarchical'] = ( empty( $data['hierarchical'] ) || $data['hierarchical'] == 'flat' ) ? false : true;
209
+ $data['show_in_nav_menus'] = ! empty( $data['show_in_nav_menus'] );
210
+
211
+ if ( empty( $data['query_var_enabled'] ) ) {
212
+ $data['query_var'] = false;
213
+ } else if ( empty( $data['query_var'] ) ) {
214
+ $data['query_var'] = true;
215
+ }
216
+
217
+ if ( ! empty( $data['rewrite']['enabled'] ) ) {
218
+ $data['rewrite']['with_front'] = ! empty( $data['rewrite']['with_front'] );
219
+ $data['rewrite']['hierarchical'] = ! empty( $data['rewrite']['hierarchical'] );
220
+ // Make sure that rewrite/slug has a value
221
+ if ( ! isset( $data['rewrite']['slug'] ) || $data['rewrite']['slug'] == '' ) {
222
+ $data['rewrite']['slug'] = $data['slug'];
223
+ }
224
+ } else {
225
+ $data['rewrite'] = false;
226
+ }
227
+
228
+ // meta_box_cb
229
+ if ( isset( $data['meta_box_cb']['disabled'] ) ) {
230
+ $data['meta_box_cb'] = false;
231
+ } else if ( isset( $data['meta_box_cb']['callback'] ) && ! empty( $data['meta_box_cb']['callback'] ) ) {
232
+ $data['meta_box_cb'] = $data['meta_box_cb']['callback'];
233
+ } else {
234
+ unset( $data['meta_box_cb'] );
235
+ }
236
+
237
+ // Force removing capabilities here
238
+ unset( $data['capabilities'] );
239
+
240
+ $object_types_filtered = apply_filters( 'wpcf_taxonomy_objects', $object_types, $taxonomy );
241
+ $taxonomy_args = apply_filters( 'wpcf_taxonomy_data', $data, $taxonomy, $object_types );
242
+
243
+ // Suddenly, WordPress 4.7 (alpha) requires the 'name' argument and uses it as the slug.
244
+ if ( ( ! isset( $taxonomy_args['name'] ) ) || false == $taxonomy_args['name'] ) {
245
+ $taxonomy_args['name'] = $taxonomy;
246
+ }
247
+
248
+ $result = register_taxonomy( $taxonomy, $object_types_filtered, $taxonomy_args );
249
+
250
+ $is_success = ( $result instanceof WP_Error ? false : true );
251
+
252
+ return $is_success;
253
  }
254
 
255
+
256
  /**
257
  * Returns only active taxonomies.
258
  *
259
+ * @return array
260
  */
261
  function wpcf_get_active_custom_taxonomies() {
262
  $taxonomies = get_option(WPCF_OPTION_NAME_CUSTOM_TAXONOMIES, array());
library/toolset/types/embedded/includes/fields-post.php CHANGED
@@ -217,18 +217,20 @@ function wpcf_add_meta_boxes( $post_type, $post )
217
 
218
  // Check if hidden
219
  if ( !isset( $group['__show_meta_box'] ) || $group['__show_meta_box'] != false ) {
 
 
 
220
  // Add meta boxes
221
  if ( empty( $only_preview ) ) {
222
  add_meta_box( "wpcf-group-{$group['slug']}",
223
- wpcf_translate( 'group ' . $group['id'] . ' name',
224
- $group['name'] ), 'wpcf_admin_post_meta_box',
225
- $post_type, $group['meta_box_context'], 'high', $group );
226
  } else {
227
  add_meta_box( "wpcf-group-{$group['slug']}",
228
- wpcf_translate( 'group ' . $group['id'] . ' name',
229
- $group['name'] ),
230
- 'wpcf_admin_post_meta_box_preview', $post_type,
231
- $group['meta_box_context'], 'high', $group );
232
  }
233
  }
234
  }
@@ -407,6 +409,7 @@ function wpcf_admin_post_meta_box_preview( $post, $group, $echo = '' ){
407
  */
408
  function wpcf_admin_post_meta_box( $post, $group, $echo = '', $open_style_editor = false )
409
  {
 
410
 
411
  if (
412
  false === $open_style_editor
@@ -418,7 +421,7 @@ function wpcf_admin_post_meta_box( $post, $group, $echo = '', $open_style_editor
418
  */
419
  if ( array_key_exists('description', $group['args'] ) && !empty($group['args']['description'])) {
420
  echo '<div class="wpcf-meta-box-description">';
421
- echo wpautop( wpcf_translate( 'group ' . $group['args']['id'] . ' description', $group['args']['description'] ) );
422
  echo '</div>';
423
  }
424
  foreach ( $group['args']['html'] as $field ) {
@@ -487,8 +490,7 @@ function wpcf_admin_post_meta_box( $post, $group, $echo = '', $open_style_editor
487
  // Display description
488
  if ( !empty( $group['args']['description'] ) ) {
489
  $group_output .= '<div class="wpcf-meta-box-description">'
490
- . wpautop( wpcf_translate( 'group ' . $group['args']['id'] . ' description',
491
- $group['args']['description'] ) ) . '</div>';
492
  }
493
  foreach ( $group['args']['fields'] as $field_slug => $field ) {
494
  if ( empty( $field ) || !is_array( $field ) ) {
217
 
218
  // Check if hidden
219
  if ( !isset( $group['__show_meta_box'] ) || $group['__show_meta_box'] != false ) {
220
+
221
+ $group_wpml = new Types_Wpml_Field_Group( Types_Field_Group_Post_Factory::load( $group['slug'] ) );
222
+
223
  // Add meta boxes
224
  if ( empty( $only_preview ) ) {
225
  add_meta_box( "wpcf-group-{$group['slug']}",
226
+ $group_wpml->translate_name(),
227
+ 'wpcf_admin_post_meta_box',
228
+ $post_type, $group['meta_box_context'], 'high', $group );
229
  } else {
230
  add_meta_box( "wpcf-group-{$group['slug']}",
231
+ $group_wpml->translate_name(),
232
+ 'wpcf_admin_post_meta_box_preview', $post_type,
233
+ $group['meta_box_context'], 'high', $group );
 
234
  }
235
  }
236
  }
409
  */
410
  function wpcf_admin_post_meta_box( $post, $group, $echo = '', $open_style_editor = false )
411
  {
412
+ $group_wpml = new Types_Wpml_Field_Group( Types_Field_Group_Post_Factory::load( $group['args']['slug'] ) );
413
 
414
  if (
415
  false === $open_style_editor
421
  */
422
  if ( array_key_exists('description', $group['args'] ) && !empty($group['args']['description'])) {
423
  echo '<div class="wpcf-meta-box-description">';
424
+ echo wpautop( $group_wpml->translate_description() );
425
  echo '</div>';
426
  }
427
  foreach ( $group['args']['html'] as $field ) {
490
  // Display description
491
  if ( !empty( $group['args']['description'] ) ) {
492
  $group_output .= '<div class="wpcf-meta-box-description">'
493
+ . wpautop( $group_wpml->translate_description() ) . '</div>';
 
494
  }
495
  foreach ( $group['args']['fields'] as $field_slug => $field ) {
496
  if ( empty( $field ) || !is_array( $field ) ) {
library/toolset/types/embedded/includes/fields.php CHANGED
@@ -253,6 +253,64 @@ function wpcf_admin_fields_get_fields( $only_active = false,
253
  return $cache[$cache_key];
254
  }
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  function wpcf_admin_fields_get_field_by_meta_key( $meta_key )
257
  {
258
  $fields = wpcf_admin_fields_get_fields();
253
  return $cache[$cache_key];
254
  }
255
 
256
+
257
+ add_filter( 'types_fields', 'wpcf_add_mandatory_validation_rules' );
258
+
259
+
260
+ /**
261
+ * Modify field definitions when they are being loaded from the database.
262
+ *
263
+ * Hooked into types_fields. Not to be used elsewhere.
264
+ *
265
+ * Add mandatory validation rules that have not been stored in the database but are needed by Types and toolset-forms
266
+ * to work properly. Namely it's the URL validation for file fields. CRED handles these fields in its own way (front-end
267
+ * file upload), so this is a Types-specific problem.
268
+ *
269
+ * @param array $field_definitions An associative array of field definition arrays with field slugs as keys.
270
+ * @return array
271
+ * @since 2.2.4
272
+ */
273
+ function wpcf_add_mandatory_validation_rules( $field_definitions ) {
274
+
275
+ if( is_array( $field_definitions ) ) {
276
+ foreach( $field_definitions as $field_slug => $field_definition ) {
277
+
278
+ // Add URL validation to file fields (containing URLs).
279
+ //
280
+ // This doesn't include embed files because they are more variable and the URL validation can be
281
+ // configured on the Edit Field Group page.
282
+ $file_fields = array( 'file', 'image', 'audio', 'video' );
283
+
284
+ $field_type = toolset_getarr( $field_definition, 'type' );
285
+ $is_file_field = in_array( $field_type, $file_fields );
286
+ $validation_rules = wpcf_ensarr( wpcf_getnest( $field_definition, array( 'data', 'validate' ) ) );
287
+ $has_url_validation = array_key_exists( 'url', $validation_rules );
288
+
289
+ if( $is_file_field && ! $has_url_validation ) {
290
+
291
+ $default_validation_error_message = __( 'Please enter a valid URL address pointing to the file.', 'wpcf' );
292
+ $validation_error_messages = array(
293
+ 'file' => $default_validation_error_message,
294
+ 'audio' => __( 'Please enter a valid URL address pointing to the audio file.', 'wpcf' ),
295
+ 'image' => __( 'Please enter a valid URL address pointing to the image file.', 'wpcf' ),
296
+ 'video' => __( 'Please enter a valid URL address pointing to the video file.', 'wpcf' )
297
+ );
298
+
299
+ $validation_rules['url'] = array(
300
+ 'active' => '1',
301
+ 'message' => toolset_getarr( $validation_error_messages, $field_type, $default_validation_error_message )
302
+ );
303
+
304
+ $field_definitions[ $field_slug ]['data']['validate'] = $validation_rules;
305
+ }
306
+ }
307
+ }
308
+
309
+ return $field_definitions;
310
+ }
311
+
312
+
313
+
314
  function wpcf_admin_fields_get_field_by_meta_key( $meta_key )
315
  {
316
  $fields = wpcf_admin_fields_get_fields();
library/toolset/types/embedded/includes/fields/wysiwyg.php CHANGED
@@ -179,18 +179,32 @@ function wpcf_fields_wysiwyg_view( $params ) {
179
  }
180
 
181
  /**
182
- * Records the WP filter state.
 
 
 
 
 
183
  *
184
  * @since 1.9.1
 
185
  */
186
-
187
  class WPCF_WP_filter_state {
188
 
189
  private $current_index;
190
  private $tag;
 
191
 
192
  public function __construct( $tag ) {
193
- global $wp_filter;
 
 
 
 
 
 
 
 
194
 
195
  $this->tag = $tag;
196
 
@@ -200,6 +214,11 @@ class WPCF_WP_filter_state {
200
  }
201
 
202
  public function restore( ) {
 
 
 
 
 
203
  global $wp_filter;
204
 
205
  if ( isset( $wp_filter[$this->tag] ) && $this->current_index ) {
@@ -211,4 +230,4 @@ class WPCF_WP_filter_state {
211
 
212
  }
213
 
214
- }
179
  }
180
 
181
  /**
182
+ * Used for recording a current item of the callbacks in $wp_filter[ $tag ] and restoring it
183
+ * after applying a filter recursively.
184
+ *
185
+ * Workaround for https://core.trac.wordpress.org/ticket/17817.
186
+ *
187
+ * From WordPress 4.7 above, this does nothing.
188
  *
189
  * @since 1.9.1
190
+ * @deprecated No longer needed since WordPress 4.7
191
  */
 
192
  class WPCF_WP_filter_state {
193
 
194
  private $current_index;
195
  private $tag;
196
+ private $is_disabled = false;
197
 
198
  public function __construct( $tag ) {
199
+
200
+ global $wp_version;
201
+
202
+ if( version_compare( $wp_version, '4.6.9', '>' ) ) {
203
+ $this->is_disabled = true;
204
+ return;
205
+ }
206
+
207
+ global $wp_filter;
208
 
209
  $this->tag = $tag;
210
 
214
  }
215
 
216
  public function restore( ) {
217
+
218
+ if( $this->is_disabled ) {
219
+ return;
220
+ }
221
+
222
  global $wp_filter;
223
 
224
  if ( isset( $wp_filter[$this->tag] ) && $this->current_index ) {
230
 
231
  }
232
 
233
+ }
library/toolset/types/embedded/includes/module-manager.php CHANGED
@@ -661,6 +661,7 @@ function wpcf_admin_export_selected_data ( array $items, $_type = 'all', $return
661
  if( isset( $type['custom-field-group'] )
662
  && is_array( $type['custom-field-group'] )
663
  && !empty( $type['custom-field-group'] ) ) {
 
664
  foreach( $type['custom-field-group'] as $custom_field_group_id => $senseless_as_it_is_always_one ) {
665
  $custom_field_group = get_post( $custom_field_group_id );
666
 
@@ -671,8 +672,8 @@ function wpcf_admin_export_selected_data ( array $items, $_type = 'all', $return
671
  if( !is_object( $custom_field_group ) )
672
  continue;
673
 
674
- // set custom field USING SLUG AS KEY AND ID AS VALUE to custom post type
675
- $custom_types[$key]['custom-field-group'][$custom_field_group->post_name] = $custom_field_group_id;
676
  }
677
  }
678
 
@@ -742,9 +743,8 @@ function wpcf_admin_export_selected_data ( array $items, $_type = 'all', $return
742
  }
743
  if ( !empty( $custom_taxonomies ) ) {
744
  foreach ( $custom_taxonomies as $key => $tax ) {
745
- $custom_taxonomies[$key]['id'] = $key;
746
  $custom_taxonomies[$key] = apply_filters( 'wpcf_filter_export_custom_taxonomy', $custom_taxonomies[$key] );
747
-
748
  $custom_taxonomies[$key]['__types_id'] = $key;
749
  $custom_taxonomies[$key]['__types_title'] = $tax['labels']['name'];
750
  $custom_taxonomies[$key]['checksum'] = $wpcf->export->generate_checksum(
@@ -1320,28 +1320,44 @@ function wpcf_modman_set_submitted_id( $set, $id ) {
1320
  return '12' . $set . '21' . $id;
1321
  }
1322
 
 
 
 
 
1323
  /**
1324
- * wpcf_filter_export_custom_taxonomy_data
1325
- *
1326
- * Filter the data to be exported for custom taxonomies.
1327
- *
1328
- * We need to filter the data exported for custom taxonomies.
1329
- * In particular, associated CPTs slugs are stored as XML keys, so they can not start with a number.
1330
- * We force a prefix on all of them on export, and restore them on import.
1331
- */
1332
-
1333
- add_filter( 'wpcf_filter_export_custom_taxonomy', 'wpcf_filter_export_custom_taxonomy_data' );
1334
-
1335
- function wpcf_filter_export_custom_taxonomy_data( $data = array() ) {
1336
- if (
1337
- isset( $data['supports'] )
1338
- && is_array( $data['supports'] )
1339
- ) {
1340
- foreach ( $data['supports'] as $key => $value ) {
1341
- $new_key = '__types_cpt_supports_' . $key;
1342
- $data['supports'][ $new_key ] = $value;
1343
- unset( $data['supports'][ $key ] );
1344
- }
1345
  }
1346
- return $data;
 
 
 
 
 
 
 
 
 
 
 
 
 
1347
  }
661
  if( isset( $type['custom-field-group'] )
662
  && is_array( $type['custom-field-group'] )
663
  && !empty( $type['custom-field-group'] ) ) {
664
+
665
  foreach( $type['custom-field-group'] as $custom_field_group_id => $senseless_as_it_is_always_one ) {
666
  $custom_field_group = get_post( $custom_field_group_id );
667
 
672
  if( !is_object( $custom_field_group ) )
673
  continue;
674
 
675
+ // set custom field, generating an unique key (but without a particular meaning) AND ID AS VALUE to custom post type
676
+ $custom_types[ $key ]['custom-field-group'][ 'group_' . $custom_field_group_id ] = $custom_field_group_id;
677
  }
678
  }
679
 
743
  }
744
  if ( !empty( $custom_taxonomies ) ) {
745
  foreach ( $custom_taxonomies as $key => $tax ) {
746
+ $custom_taxonomies[$key]['id'] = $key;
747
  $custom_taxonomies[$key] = apply_filters( 'wpcf_filter_export_custom_taxonomy', $custom_taxonomies[$key] );
 
748
  $custom_taxonomies[$key]['__types_id'] = $key;
749
  $custom_taxonomies[$key]['__types_title'] = $tax['labels']['name'];
750
  $custom_taxonomies[$key]['checksum'] = $wpcf->export->generate_checksum(
1320
  return '12' . $set . '21' . $id;
1321
  }
1322
 
1323
+
1324
+ add_filter( 'wpcf_filter_export_custom_taxonomy', 'wpcf_fix_exported_taxonomy_assignment_to_cpt' );
1325
+
1326
+
1327
  /**
1328
+ * Filter the data to be exported for custom taxonomies.
1329
+ *
1330
+ * Ensure the settings of post types associated with the taxonomy is exported correctly, even with support of legacy
1331
+ * settings.
1332
+ *
1333
+ * @param array $taxonomy_data
1334
+ * @return array Modified taxonomy data.
1335
+ * @since unknown
1336
+ */
1337
+ function wpcf_fix_exported_taxonomy_assignment_to_cpt( $taxonomy_data = array() ) {
1338
+
1339
+ $setting_name_prefix = '__types_cpt_supports_';
1340
+ $post_type_support_settings = array();
1341
+
1342
+ // Associated CPTs slugs are stored as XML keys, so they can not start with a number.
1343
+ // We force a prefix on all of them on export, and restore them on import.
1344
+ $supported_post_types = wpcf_ensarr( wpcf_getarr( $taxonomy_data, 'supports' ) );
1345
+ foreach( $supported_post_types as $post_type_slug => $is_supported ) {
1346
+ $setting_name = $setting_name_prefix . $post_type_slug;
1347
+ $post_type_support_settings[ $setting_name ] = ( $is_supported ? 1 : 0 );
 
1348
  }
1349
+
1350
+ // Here, we will also process the legacy "object_type" setting, containing supported post type slugs as array items,
1351
+ // in the samve way.
1352
+ $legacy_supported_post_type_array = wpcf_ensarr( wpcf_getarr( $taxonomy_data, 'object_type' ) );
1353
+ foreach( $legacy_supported_post_type_array as $post_type_slug ) {
1354
+ $setting_name = $setting_name_prefix . $post_type_slug;
1355
+ $post_type_support_settings[ $setting_name ] = 1;
1356
+ }
1357
+
1358
+ // Now we need to remove this legacy setting to prevent producing invalid XML.
1359
+ unset( $taxonomy_data['object_type'] );
1360
+
1361
+ $taxonomy_data['supports'] = $post_type_support_settings;
1362
+ return $taxonomy_data;
1363
  }
library/toolset/types/embedded/includes/post-relationship.php CHANGED
@@ -5,7 +5,37 @@
5
  *
6
  */
7
  add_action( 'wpcf_admin_post_init', 'wpcf_pr_admin_post_init_action', 10, 4 );
 
8
  add_action( 'save_post', 'wpcf_pr_admin_save_post_hook', 20, 2 ); // Trigger afer main hook
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  if ( is_admin() ) {
10
  add_action('wp_ajax_wpcf_relationship_search', 'wpcf_pr_admin_wpcf_relationship_search');
11
  // Deprecated since the introduction of select v.4
@@ -94,37 +124,51 @@ function wpcf_pr_admin_post_init_action( $post_type, $post, $groups, $wpcf_activ
94
  }
95
  }
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  /**
98
  * Gets post types that belong to current post type.
99
  *
100
- * @param type $post_type
101
- * @return type
102
  */
103
- function wpcf_pr_admin_get_has( $post_type ) {
104
  static $cache = array();
105
- if ( isset( $cache[$post_type] ) ) {
106
- return $cache[$post_type];
107
  }
108
  $relationships = get_option( 'wpcf_post_relationship', array() );
109
- if ( empty( $relationships[$post_type] ) ) {
110
  return false;
111
  }
112
  // See if enabled
113
- foreach ( $relationships[$post_type] as $temp_post_type => $temp_post_type_data ) {
114
- $active = get_post_type_object( $temp_post_type );
115
- if ( !$active ) {
116
- unset( $relationships[$post_type][$temp_post_type] );
117
  }
118
  }
119
- $cache[$post_type] = !empty( $relationships[$post_type] ) ? $relationships[$post_type] : false;
120
- return $cache[$post_type];
121
  }
122
 
123
  /**
124
  * Gets post types that current post type belongs to.
125
  *
126
- * @param type $post_type
127
- * @return type
128
  */
129
  function wpcf_pr_admin_get_belongs( $post_type ) {
130
  static $cache = array();
@@ -135,11 +179,11 @@ function wpcf_pr_admin_get_belongs( $post_type ) {
135
  $results = array();
136
  if ( is_array( $relationships ) ) {
137
  foreach ( $relationships as $has => $belongs ) {
138
- // See if enabled
139
- $active = get_post_type_object( $has );
140
- if ( !$active ) {
141
  continue;
142
  }
 
143
  if ( array_key_exists( $post_type, $belongs ) ) {
144
  $results[$has] = $belongs[$post_type];
145
  }
@@ -346,15 +390,61 @@ function wpcf_pr_admin_post_meta_box_belongs_form( $post, $type, $belongs )
346
  $id = esc_attr(sprintf('wpcf_pr_belongs_%d_%s', $post->ID, $type));
347
  $belongs_id = isset( $belongs['belongs'][$type] ) ? $belongs['belongs'][$type] : 0;
348
 
349
- $options_array = array();
350
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  if ( $belongs_id ) {
 
 
352
  $options_array[ $belongs_id ] = array(
353
  '#title' => get_the_title( $belongs_id ),
354
  '#value' => $belongs_id,
355
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  }
357
 
 
358
  $form[$type] = array(
359
  '#type' => 'select',
360
  '#name' => 'wpcf_pr_belongs[' . $post->ID . '][' . $type . ']',
@@ -368,30 +458,9 @@ function wpcf_pr_admin_post_meta_box_belongs_form( $post, $type, $belongs )
368
  'data-placeholder' => esc_attr( sprintf( __('Search for %s', 'wpcf'), $temp_type->labels->name ) ),
369
  'data-post-id' => $post->ID,
370
  'data-post-type' => esc_attr($type),
 
371
  ),
372
  );
373
-
374
- /*
375
- $form[$type] = array(
376
- '#type' => 'textfield',
377
- '#name' => 'wpcf_pr_belongs[' . $post->ID . '][' . $type . ']',
378
- '#value' => isset( $belongs['belongs'][$type] ) ? $belongs['belongs'][$type] : 0,
379
- '#id' => $id,
380
- '#attributes' => array(
381
- 'class' => 'wpcf-pr-belongs',
382
- 'data-loading' => esc_attr__('Please Wait, Loading…', 'wpcf'),
383
- 'data-nounce' => wp_create_nonce($id),
384
- 'data-placeholder' => esc_attr__('Search for a entries', 'wpcf'),
385
- 'data-post-id' => $post->ID,
386
- 'data-post-type' => esc_attr($type),
387
- 'data-input-too-short' => esc_attr(__('Please enter 1 or more character.', 'wpcf')),
388
- ),
389
- );
390
-
391
- if( $belongs_id != 0 && get_post_status( $belongs_id ) ) {
392
- //$form[$type]['#attributes']['data-belongs-title'] = get_the_title( $belongs_id );
393
- }
394
- */
395
 
396
  return $form;
397
  }
@@ -637,7 +706,7 @@ function wpcf_pr_admin_wpcf_relationship_check($keys_to_check = array())
637
 
638
  function wpcf_pr_admin_wpcf_relationship_search()
639
  {
640
- wpcf_pr_admin_wpcf_relationship_check(array('s'));
641
 
642
  global $wpdb;
643
  $values_to_prepare = array();
@@ -660,7 +729,10 @@ function wpcf_pr_admin_wpcf_relationship_search()
660
 
661
  $search_where = "";
662
 
663
- if ( isset( $_REQUEST['s'] ) ) {
 
 
 
664
  $search_term = "";
665
  if ( method_exists( $wpdb, 'esc_like' ) ) {
666
  $search_term = '%' . $wpdb->esc_like( $_REQUEST['s'] ) . '%';
@@ -669,13 +741,16 @@ function wpcf_pr_admin_wpcf_relationship_search()
669
  }
670
  $search_where = " AND p.post_title LIKE %s ";
671
  $values_to_prepare[] = $search_term;
 
 
 
672
  }
673
 
674
  if (
675
  isset( $_REQUEST['page'] )
676
  && preg_match( '/^\d+$/', $_REQUEST['page'] )
677
  ) {
678
- $values_to_prepare[] = (int) $_REQUEST['page'] * $posts_per_page;
679
  } else {
680
  $values_to_prepare[] = 0;
681
  }
@@ -689,7 +764,7 @@ function wpcf_pr_admin_wpcf_relationship_search()
689
  {$wpml_where}
690
  AND p.post_type = %s
691
  {$search_where}
692
- ORDER BY p.post_title
693
  LIMIT %d,%d",
694
  $values_to_prepare
695
  )
5
  *
6
  */
7
  add_action( 'wpcf_admin_post_init', 'wpcf_pr_admin_post_init_action', 10, 4 );
8
+
9
  add_action( 'save_post', 'wpcf_pr_admin_save_post_hook', 20, 2 ); // Trigger afer main hook
10
+
11
+ /*
12
+ * Temporary fix for https://core.trac.wordpress.org/ticket/17817
13
+ *
14
+ * WordPress 4.7 fixes this issue and the code below is not needed anymore.
15
+ *
16
+ * Supported by WPML 3.6.0 and above.
17
+ */
18
+ $wp_version = get_bloginfo( 'version' );
19
+ if( version_compare( $wp_version, '4.7' ) == -1 ) {
20
+
21
+ global $sitepress;
22
+ $is_wpml_active = (
23
+ defined( 'ICL_SITEPRESS_VERSION' )
24
+ && ! ICL_PLUGIN_INACTIVE
25
+ && ! is_null( $sitepress )
26
+ && class_exists( 'SitePress' )
27
+ );
28
+
29
+ $is_wpml_required_version = ( defined( 'ICL_SITEPRESS_VERSION' ) && version_compare( ICL_SITEPRESS_VERSION, '3.6.0' ) >= 0 );
30
+
31
+ if( $is_wpml_active && $is_wpml_required_version ) {
32
+ // WPML *guarantees* that the wpml_after_save_post action will be fired for each post.
33
+ remove_action( 'save_post', 'wpcf_pr_admin_save_post_hook', 20 );
34
+ add_action( 'wpml_after_save_post', 'wpcf_pr_admin_save_post_hook', 10, 2 );
35
+ }
36
+ }
37
+
38
+
39
  if ( is_admin() ) {
40
  add_action('wp_ajax_wpcf_relationship_search', 'wpcf_pr_admin_wpcf_relationship_search');
41
  // Deprecated since the introduction of select v.4
124
  }
125
  }
126
 
127
+
128
+ /**
129
+ * Determine if a post type can take a part in a post relationship.
130
+ *
131
+ * @param string $post_type_slug
132
+ * @return bool
133
+ * @since 2.3
134
+ */
135
+ function wpcf_pr_is_post_type_available_for_relationships( $post_type_slug ) {
136
+ $is_active = ( null != get_post_type_object( $post_type_slug ) );
137
+ $is_excluded_from_relationships = ( 'attachment' == $post_type_slug );
138
+ return ( $is_active && ! $is_excluded_from_relationships );
139
+ }
140
+
141
+
142
  /**
143
  * Gets post types that belong to current post type.
144
  *
145
+ * @param string $parent_post_type_slug
146
+ * @return array|false
147
  */
148
+ function wpcf_pr_admin_get_has( $parent_post_type_slug ) {
149
  static $cache = array();
150
+ if ( isset( $cache[$parent_post_type_slug] ) ) {
151
+ return $cache[$parent_post_type_slug];
152
  }
153
  $relationships = get_option( 'wpcf_post_relationship', array() );
154
+ if ( empty( $relationships[$parent_post_type_slug] ) ) {
155
  return false;
156
  }
157
  // See if enabled
158
+ foreach ( $relationships[ $parent_post_type_slug ] as $child_post_type_slug => $ignored ) {
159
+ if ( ! wpcf_pr_is_post_type_available_for_relationships( $child_post_type_slug ) ) {
160
+ unset( $relationships[ $parent_post_type_slug ][ $child_post_type_slug ] );
 
161
  }
162
  }
163
+ $cache[$parent_post_type_slug] = !empty( $relationships[$parent_post_type_slug] ) ? $relationships[$parent_post_type_slug] : false;
164
+ return $cache[$parent_post_type_slug];
165
  }
166
 
167
  /**
168
  * Gets post types that current post type belongs to.
169
  *
170
+ * @param string $post_type
171
+ * @return array|false
172
  */
173
  function wpcf_pr_admin_get_belongs( $post_type ) {
174
  static $cache = array();
179
  $results = array();
180
  if ( is_array( $relationships ) ) {
181
  foreach ( $relationships as $has => $belongs ) {
182
+
183
+ if ( ! wpcf_pr_is_post_type_available_for_relationships( $has ) ) {
 
184
  continue;
185
  }
186
+
187
  if ( array_key_exists( $post_type, $belongs ) ) {
188
  $results[$has] = $belongs[$post_type];
189
  }
390
  $id = esc_attr(sprintf('wpcf_pr_belongs_%d_%s', $post->ID, $type));
391
  $belongs_id = isset( $belongs['belongs'][$type] ) ? $belongs['belongs'][$type] : 0;
392
 
393
+ $options_array = array();
394
 
395
+ $values_to_prepare = array();
396
+
397
+ $post_status = array( 'publish', 'private' );
398
+
399
+ $wpml_join = $wpml_where = "";
400
+ $is_translated_post_type = apply_filters( 'wpml_is_translated_post_type', false, $type );
401
+
402
+ if ( $is_translated_post_type ) {
403
+ $wpml_current_language = apply_filters( 'wpml_current_language', '' );
404
+ $wpml_join = " JOIN {$wpdb->prefix}icl_translations t ";
405
+ $wpml_where = " AND p.ID = t.element_id AND t.language_code = %s ";
406
+ $values_to_prepare[] = $wpml_current_language;
407
+ }
408
+
409
+ $values_to_prepare[] = sanitize_text_field( $type );
410
+
411
+ $not_in_selected = '';
412
  if ( $belongs_id ) {
413
+ $not_in_selected = ' AND p.ID != %d';
414
+ $values_to_prepare[] = (int) $belongs_id;
415
  $options_array[ $belongs_id ] = array(
416
  '#title' => get_the_title( $belongs_id ),
417
  '#value' => $belongs_id,
418
  );
419
+ } else {
420
+ $options_array[ '' ] = array(
421
+ '#title' => '',
422
+ '#value' => '',
423
+ );
424
+ }
425
+
426
+ $parents_available = $wpdb->get_results(
427
+ $wpdb->prepare(
428
+ "SELECT p.ID, p.post_title
429
+ FROM {$wpdb->posts} p {$wpml_join}
430
+ WHERE p.post_status IN ('" . implode( "','" , $post_status ) . "')
431
+ {$wpml_where}
432
+ AND p.post_type = %s
433
+ {$not_in_selected}
434
+ ORDER BY p.post_date DESC
435
+ LIMIT 15",
436
+ $values_to_prepare
437
+ )
438
+ );
439
+
440
+ foreach ( $parents_available as $parent_option ) {
441
+ $options_array[ $parent_option->ID ] = array(
442
+ '#title' => $parent_option->post_title,
443
+ '#value' => $parent_option->ID,
444
+ );
445
  }
446
 
447
+
448
  $form[$type] = array(
449
  '#type' => 'select',
450
  '#name' => 'wpcf_pr_belongs[' . $post->ID . '][' . $type . ']',
458
  'data-placeholder' => esc_attr( sprintf( __('Search for %s', 'wpcf'), $temp_type->labels->name ) ),
459
  'data-post-id' => $post->ID,
460
  'data-post-type' => esc_attr($type),
461
+ 'autocomplete' => 'off'
462
  ),
463
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
 
465
  return $form;
466
  }
706
 
707
  function wpcf_pr_admin_wpcf_relationship_search()
708
  {
709
+ wpcf_pr_admin_wpcf_relationship_check();
710
 
711
  global $wpdb;
712
  $values_to_prepare = array();
729
 
730
  $search_where = "";
731
 
732
+ if (
733
+ isset( $_REQUEST['s'] )
734
+ && $_REQUEST['s'] != ''
735
+ ) {
736
  $search_term = "";
737
  if ( method_exists( $wpdb, 'esc_like' ) ) {
738
  $search_term = '%' . $wpdb->esc_like( $_REQUEST['s'] ) . '%';
741
  }
742
  $search_where = " AND p.post_title LIKE %s ";
743
  $values_to_prepare[] = $search_term;
744
+ $orderby = ' ORDER BY p.post_title ';
745
+ } else {
746
+ $orderby = ' ORDER BY p.post_date DESC ';
747
  }
748
 
749
  if (
750
  isset( $_REQUEST['page'] )
751
  && preg_match( '/^\d+$/', $_REQUEST['page'] )
752
  ) {
753
+ $values_to_prepare[] = ( (int) $_REQUEST['page'] - 1 ) * $posts_per_page;
754
  } else {
755
  $values_to_prepare[] = 0;
756
  }
764
  {$wpml_where}
765
  AND p.post_type = %s
766
  {$search_where}
767
+ {$orderby}
768
  LIMIT %d,%d",
769
  $values_to_prepare
770
  )
library/toolset/types/embedded/includes/usermeta-post.php CHANGED
@@ -66,6 +66,9 @@ function wpcf_admin_userprofile_init($user_id){
66
 
67
  // Process fields
68
  if ( empty($profile_only_preview) ){
 
 
 
69
  if ( defined( 'WPTOOLSET_FORMS_VERSION' ) ) {
70
  $errors = get_user_meta( $user_id->ID, '__wpcf-invalid-fields',
71
  true );
@@ -76,11 +79,10 @@ function wpcf_admin_userprofile_init($user_id){
76
 
77
  $output = '<div class="wpcf-group-area wpcf-group-area_'
78
  . $group['slug'] . '">' . "\n\n" . '<h3>'
79
- . wpcf_translate( 'group ' . $group['id'] . ' name',
80
- $group['name'] ) . '</h3>' . "\n\n";
81
 
82
  if ( !empty( $group['description'] ) ) {
83
- $output .= '<span>' . wpautop( wpcf_translate( 'group ' . $group['id'] . ' description', $group['description'] ) )
84
  . '</span>' . "\n\n";
85
  }
86
 
@@ -197,8 +199,10 @@ function wpcf_usermeta_preview_profile( $user_id, $group, $echo = ''){
197
  global $wpcf;
198
  //print_r($group);exit;
199
  $fields = $group['fields'];
 
 
200
  $group_output = '<div class="wpcf-group-area wpcf-group-area-' . $group['slug'] . '">' . "\n\n";
201
- $group_output .= '<h3 class="wpcf-group-header-'. $group['slug'] .'">'.wpcf_translate( 'group ' . $group['id'] . ' name', $group['name']).'</h3>'. "\n\n";
202
 
203
 
204
  foreach ( $fields as $field ) {
@@ -582,14 +586,15 @@ function wpcf_admin_userprofilesave_init($user_id){
582
  function wpcf_admin_render_fields( $group, $user_id, $echo = '') {
583
 
584
  global $wpcf;
 
 
585
  $output = '<div class="wpcf-group-area wpcf-group-area_' . $group['slug'] . '">' . "\n\n";
586
- $output .= '<h3>'.wpcf_translate( 'group ' . $group['id'] . ' name', $group['name']).'</h3>' . "\n\n";
587
  if ( !empty( $group['fields'] ) ) {
588
  // Display description
589
  if ( !empty( $group['description'] ) ) {
590
  $output .= '<span>'
591
- . wpautop( wpcf_translate( 'group ' . $group['id'] . ' description',
592
- $group['description'] ) ) . '</span>' . "\n\n";
593
  }
594
 
595
  $output .= '<div class="wpcf-profile-field-line">' . "\n\n";
66
 
67
  // Process fields
68
  if ( empty($profile_only_preview) ){
69
+
70
+ $group_wpml = new Types_Wpml_Field_Group( Types_Field_Group_User_Factory::load( $group['slug'] ) );
71
+
72
  if ( defined( 'WPTOOLSET_FORMS_VERSION' ) ) {
73
  $errors = get_user_meta( $user_id->ID, '__wpcf-invalid-fields',
74
  true );
79
 
80
  $output = '<div class="wpcf-group-area wpcf-group-area_'
81
  . $group['slug'] . '">' . "\n\n" . '<h3>'
82
+ . $group_wpml->translate_name() . '</h3>' . "\n\n";
 
83
 
84
  if ( !empty( $group['description'] ) ) {
85
+ $output .= '<span>' . wpautop( $group_wpml->translate_description() )
86
  . '</span>' . "\n\n";
87
  }
88
 
199
  global $wpcf;
200
  //print_r($group);exit;
201
  $fields = $group['fields'];
202
+ $group_wpml = new Types_Wpml_Field_Group( Types_Field_Group_User_Factory::load( $group['slug'] ) );
203
+
204
  $group_output = '<div class="wpcf-group-area wpcf-group-area-' . $group['slug'] . '">' . "\n\n";
205
+ $group_output .= '<h3 class="wpcf-group-header-'. $group['slug'] .'">'. $group_wpml->translate_name() .'</h3>'. "\n\n";
206
 
207
 
208
  foreach ( $fields as $field ) {
586
  function wpcf_admin_render_fields( $group, $user_id, $echo = '') {
587
 
588
  global $wpcf;
589
+ $group_wpml = new Types_Wpml_Field_Group( Types_Field_Group_User_Factory::load( $group['slug'] ) );
590
+
591
  $output = '<div class="wpcf-group-area wpcf-group-area_' . $group['slug'] . '">' . "\n\n";
592
+ $output .= '<h3>'. $group_wpml->translate_name() .'</h3>' . "\n\n";
593
  if ( !empty( $group['fields'] ) ) {
594
  // Display description
595
  if ( !empty( $group['description'] ) ) {
596
  $output .= '<span>'
597
+ . wpautop( $group_wpml->translate_description() ) . '</span>' . "\n\n";
 
598
  }
599
 
600
  $output .= '<div class="wpcf-profile-field-line">' . "\n\n";
library/toolset/types/embedded/includes/wpml.php CHANGED
@@ -183,7 +183,7 @@ function wpcf_translate_register_string( $context, $name, $value,
183
  $allow_empty_value = false ) {
184
  if ( function_exists( 'icl_register_string' ) ) {
185
  icl_register_string( $context, $name, stripslashes( $value ),
186
- $allow_empty_value );
187
  }
188
  }
189
 
@@ -300,13 +300,8 @@ function wpcf_admin_bulk_string_translation() {
300
  // Register groups
301
  $groups = wpcf_admin_fields_get_groups();
302
  foreach ( $groups as $group_id => $group ) {
303
- $group_id = $group['id'];
304
- wpcf_translate_register_string( 'plugin Types',
305
- 'group ' . $group_id . ' name', $group['name'] );
306
- if ( isset( $group['description'] ) ) {
307
- wpcf_translate_register_string( 'plugin Types',
308
- 'group ' . $group_id . ' description', $group['description'] );
309
- }
310
  }
311
 
312
  // Register fields
@@ -1236,7 +1231,7 @@ function wpcf_wpml_warnings_init()
1236
  */
1237
  function wpcf_wpml_warning()
1238
  {
1239
- if(!defined('WPML_ST_PATH') || !class_exists( 'ICL_AdminNotifier' )) return;
1240
  ICL_AdminNotifier::displayMessages('wp-types');
1241
  }
1242
 
183
  $allow_empty_value = false ) {
184
  if ( function_exists( 'icl_register_string' ) ) {
185
  icl_register_string( $context, $name, stripslashes( $value ),
186
+ $allow_empty_value );
187
  }
188
  }
189
 
300
  // Register groups
301
  $groups = wpcf_admin_fields_get_groups();
302
  foreach ( $groups as $group_id => $group ) {
303
+ $group_wpml = new Types_Wpml_Field_Group( Types_Field_Group_Post_Factory::load( $group['slug'] ) );
304
+ $group_wpml->register();
 
 
 
 
 
305
  }
306
 
307
  // Register fields
1231
  */
1232
  function wpcf_wpml_warning()
1233
  {
1234
+ if(!defined('ICL_SITEPRESS_VERSION') || !defined('WPML_ST_PATH') || !class_exists( 'ICL_AdminNotifier' )) return;
1235
  ICL_AdminNotifier::displayMessages('wp-types');
1236
  }
1237
 
library/toolset/types/embedded/resources/css/basic.css CHANGED
@@ -1450,4 +1450,9 @@ input#slug+p.wpcf-form-description {
1450
  }
1451
  .ui-sortable-handle .description{
1452
  font-weight: normal;
 
 
 
 
 
1453
  }
1450
  }
1451
  .ui-sortable-handle .description{
1452
  font-weight: normal;
1453
+ }
1454
+
1455
+ /* todo move to common */
1456
+ .ui-datepicker {
1457
+ z-index: 10000 !important;
1458
  }
library/toolset/types/embedded/resources/js/post-relationship.js CHANGED
@@ -1021,94 +1021,113 @@ function wpcfBindSelect2($) {
1021
  }
1022
 
1023
  function wpcfBindSelect2For( element ) {
1024
- var $ = jQuery;
1025
- element.toolset_select2({
1026
- allowClear: true,
1027
- ajax: {
1028
- url: ajaxurl + '?action=wpcf_relationship_search&nounce='+element.data('nounce'),
1029
- dataType: 'json',
1030
- delay: 250,
1031
- type: 'post',
1032
- data: function (params) {
1033
- return {
1034
- s: params.term,
1035
- page: params.page,
1036
- post_id: element.data('post-id'),
1037
- post_type: element.data('post-type')
1038
- };
1039
- },
1040
- processResults: function (data, params) {
1041
- params.page = params.page || 1;
1042
- return {
1043
- results: data.items,
1044
- pagination: {
1045
- more: ( params.page * wpcf_post_relationship_messages.parent_per_page ) < data.total_count
1046
- }
1047
- };
1048
- },
1049
- cache: false
1050
- },
1051
- escapeMarkup: function (markup) { return markup; },
1052
- minimumInputLength: 2,
1053
- triggerChange: true,
1054
- })
1055
- .on('toolset_select2:select', function( evt ) {
1056
- $.ajax({
1057
- url: ajaxurl,
1058
- dataType: "json",
1059
- data: {
1060
- action: 'wpcf_relationship_update',
1061
- nounce: element.data('nounce'),
1062
- post_id: element.data('post-id'),
1063
- post_type: element.data('post-type'),
1064
- p: element.val()
1065
- },
1066
- success: function( response ) {
1067
- var parent_edit_button = element
1068
- .closest( '.form-item' )
1069
- .find( '.js-wpcf-pr-parent-edit' );
1070
- parent_edit_button
1071
- .removeClass( 'disabled' )
1072
- .fadeIn( 'fast' )
1073
- .addClass( 'wpcf-saved' )
1074
- .attr( 'href', response.data.edit_link + '?post=' + element.val() + '&action=edit' );
1075
- setTimeout( function() {
1076
- parent_edit_button.removeClass( 'wpcf-saved' );
1077
- },
1078
- 1000
1079
- );
1080
- }
1081
  });
1082
- })
1083
- .on('toolset_select2:unselect', function( evt ) {
1084
- $.ajax({
1085
- url: ajaxurl,
1086
- dataType: "json",
1087
- data: {
1088
- action: 'wpcf_relationship_update',
1089
- nounce: element.data('nounce'),
1090
- post_id: element.data('post-id'),
1091
- post_type: element.data('post-type'),
1092
- p: 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1093
  },
1094
- success: function() {
1095
- var parent_edit_button = element
1096
- .closest( '.form-item' )
1097
- .find( '.js-wpcf-pr-parent-edit' );
1098
- parent_edit_button
1099
- .addClass( 'disabled wpcf-deleted' )
1100
- .attr( 'href', '#');
1101
- setTimeout( function() {
1102
- parent_edit_button
1103
- .fadeOut( 500, function() {
1104
- parent_edit_button.removeClass( 'wpcf-deleted' );
1105
- });
1106
- },
1107
- 1000
1108
- );
1109
  }
1110
  });
1111
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1112
  }
1113
  jQuery(document).ready(function($) {
1114
  wpcfBindSelect2($);
1021
  }
1022
 
1023
  function wpcfBindSelect2For( element ) {
1024
+ var $ = jQuery,
1025
+ options = element.find( 'option' ),
1026
+ element_s2_instance;
1027
+
1028
+ if ( options.length < 16 ) {
1029
+ element_s2_instance = element.toolset_select2({
1030
+ allowClear: true,
1031
+ triggerChange: true,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1032
  });
1033
+ } else {
1034
+ element_s2_instance = element.toolset_select2({
1035
+ allowClear: true,
1036
+ ajax: {
1037
+ url: ajaxurl + '?action=wpcf_relationship_search&nounce='+element.data('nounce'),
1038
+ dataType: 'json',
1039
+ delay: 250,
1040
+ type: 'post',
1041
+ data: function (params) {
1042
+ return {
1043
+ s: params.term,
1044
+ page: params.page,
1045
+ post_id: element.data('post-id'),
1046
+ post_type: element.data('post-type')
1047
+ };
1048
+ },
1049
+ processResults: function (data, params) {
1050
+ params.page = params.page || 1;
1051
+ return {
1052
+ results: data.items,
1053
+ pagination: {
1054
+ more: ( params.page * wpcf_post_relationship_messages.parent_per_page ) < data.total_count
1055
+ }
1056
+ };
1057
+ },
1058
+ cache: false
1059
  },
1060
+ escapeMarkup: function (markup) { return markup; },
1061
+ //minimumInputLength: 2,// No minimum input length so we can search by empty terms, hece offer latests parents
1062
+ triggerChange: true,
1063
+ defaultResults: function() {
1064
+ var results = {};
1065
+ results.items = [],
1066
+ $.each( options, function( index, option ) {
1067
+ results.items.push( { id: option.value, text: option.text } );
1068
+ });
1069
+ return results;
 
 
 
 
 
1070
  }
1071
  });
1072
+ }
1073
+ element_s2_instance
1074
+ .on('toolset_select2:select', function( evt ) {
1075
+ $.ajax({
1076
+ url: ajaxurl,
1077
+ dataType: "json",
1078
+ data: {
1079
+ action: 'wpcf_relationship_update',
1080
+ nounce: element.data('nounce'),
1081
+ post_id: element.data('post-id'),
1082
+ post_type: element.data('post-type'),
1083
+ p: element.val()
1084
+ },
1085
+ success: function( response ) {
1086
+ var parent_edit_button = element
1087
+ .closest( '.form-item' )
1088
+ .find( '.js-wpcf-pr-parent-edit' );
1089
+ parent_edit_button
1090
+ .removeClass( 'disabled' )
1091
+ .fadeIn( 'fast' )
1092
+ .addClass( 'wpcf-saved' )
1093
+ .attr( 'href', response.data.edit_link + '?post=' + element.val() + '&action=edit' );
1094
+ setTimeout( function() {
1095
+ parent_edit_button.removeClass( 'wpcf-saved' );
1096
+ },
1097
+ 1000
1098
+ );
1099
+ }
1100
+ });
1101
+ })
1102
+ .on('toolset_select2:unselect', function( evt ) {
1103
+ $.ajax({
1104
+ url: ajaxurl,
1105
+ dataType: "json",
1106
+ data: {
1107
+ action: 'wpcf_relationship_update',
1108
+ nounce: element.data('nounce'),
1109
+ post_id: element.data('post-id'),
1110
+ post_type: element.data('post-type'),
1111
+ p: 0
1112
+ },
1113
+ success: function() {
1114
+ var parent_edit_button = element
1115
+ .closest( '.form-item' )
1116
+ .find( '.js-wpcf-pr-parent-edit' );
1117
+ parent_edit_button
1118
+ .addClass( 'disabled wpcf-deleted' )
1119
+ .attr( 'href', '#');
1120
+ setTimeout( function() {
1121
+ parent_edit_button
1122
+ .fadeOut( 500, function() {
1123
+ parent_edit_button.removeClass( 'wpcf-deleted' );
1124
+ });
1125
+ },
1126
+ 1000
1127
+ );
1128
+ }
1129
+ });
1130
+ });
1131
  }
1132
  jQuery(document).ready(function($) {
1133
  wpcfBindSelect2($);
library/toolset/types/embedded/usermeta-init.php CHANGED
@@ -345,155 +345,17 @@ function wpcf_admin_post_add_usermeta_to_editor_js( $menu, $views_callback = fal
345
  /**
346
  * Calls view function for specific field type.
347
  *
348
- * @global object $wpdb
 
 
 
349
  *
350
- * @param type $field
351
- * @param type $atts
352
- * @return type
353
  *
354
- * @deprecated I can not find where it is being used, we use types_render_usermeta() instead
355
  */
356
- function types_render_usermeta_field( $field_id, $params, $content = null,
357
- $code = '' ) {
358
-
359
- require_once WPCF_EMBEDDED_INC_ABSPATH . '/fields.php';
360
- global $wpcf, $post, $wpdb;
361
-
362
- // HTML var holds actual output
363
- $html = '';
364
-
365
- // Set post ID
366
- $post_id = $post->ID;
367
- if ( isset( $params['post_id'] ) && !empty( $params['post_id'] ) ) {
368
- $post_id = $params['post_id'];
369
- }
370
-
371
- // Get field
372
- $field = wpcf_fields_get_field_by_slug( $field_id, 'wpcf-usermeta' );
373
-
374
- // If field not found return empty string
375
- if ( empty( $field ) ) {
376
-
377
- // Log
378
- if ( !function_exists( 'wplogger' ) ) {
379
- require_once WPCF_EMBEDDED_TOOLSET_ABSPATH . '/toolset-common/wplogger.php';
380
- }
381
- global $wplogger;
382
- $wplogger->log( 'types_render_usermeta_field call for missing field \''
383
- . $field_id . '\'', WPLOG_DEBUG );
384
-
385
- return '';
386
- }
387
-
388
- //Get user By ID
389
- if ( isset( $params['user_id'] ) ) {
390
- $user_id = $params['user_id'];
391
- } else if ( isset( $params['user_name'] ) ) { //Get user by login
392
- $user_id = $wpdb->get_var(
393
- $wpdb->prepare(
394
- "SELECT * FROM " . $wpdb->users . " WHERE user_login = %s",
395
- $params['user_name']
396
- )
397
- );
398
- } else if ( isset( $params['user_is_author'] ) ) { //Get Post author
399
- $user_id = $post->post_author;
400
- } else if ( isset( $params['user_current'] ) ) {//Get current logged user
401
- $user_id = wpcf_usermeta_get_user();
402
- } else { //If empty get post author, if no post, return empty
403
- if ( !empty( $post_id ) ) {
404
- $user_id = $post->post_author;
405
- } else {
406
- return;
407
- }
408
- }
409
-
410
- if ( empty( $user_id ) ) {
411
- return;
412
- }
413
-
414
- // Set field
415
- $wpcf->usermeta_field->set( $user_id, $field );
416
-
417
- // See if repetitive
418
- if ( wpcf_admin_is_repetitive( $field ) ) {
419
- $wpcf->usermeta_repeater->set( $user_id, $field );
420
- $_meta = $wpcf->usermeta_repeater->_get_meta();
421
- $meta = $_meta['custom_order'];
422
- // $meta = get_post_meta( $post_id,
423
- // wpcf_types_get_meta_prefix( $field ) . $field['slug'], false );
424
- // Sometimes if meta is empty - array(0 => '') is returned
425
- if ( (count( $meta ) == 1 ) ) {
426
- $meta_id = key( $meta );
427
- $_temp = array_shift( $meta );
428
- if ( strval( $_temp ) == '' ) {
429
- return '';
430
- } else {
431
-
432
- $params['field_value'] = $_temp;
433
- return types_render_field_single( $field, $params, $content,
434
- $code, $meta_id );
435
- }
436
- } else if ( !empty( $meta ) ) {
437
- $output = '';
438
-
439
- if ( isset( $params['index'] ) ) {
440
- $index = $params['index'];
441
- } else {
442
- $index = '';
443
- }
444
-
445
- // Allow wpv-for-each shortcode to set the index
446
- $index = apply_filters( 'wpv-for-each-index', $index );
447
-
448
- if ( $index === '' ) {
449
- $output = array();
450
- foreach ( $meta as $temp_key => $temp_value ) {
451
- $params['field_value'] = $temp_value;
452
- $temp_output = types_render_field_single( $field, $params,
453
- $content, $code, $temp_key );
454
- if ( !empty( $temp_output ) ) {
455
- $output[] = $temp_output;
456
- }
457
- }
458
- if ( !empty( $output ) && isset( $params['separator'] ) ) {
459
- $output = implode( html_entity_decode( $params['separator'] ),
460
- $output );
461
- } else if ( !empty( $output ) ) {
462
- $output = implode( '', $output );
463
- } else {
464
- return '';
465
- }
466
- } else {
467
- // Make sure indexed right
468
- $_index = 0;
469
- foreach ( $meta as $temp_key => $temp_value ) {
470
- if ( $_index == $index ) {
471
- $params['field_value'] = $temp_value;
472
- return types_render_field_single( $field, $params,
473
- $content, $code, $temp_key );
474
- }
475
- $_index++;
476
- }
477
- // If missed index
478
- return '';
479
- }
480
- $html = $output;
481
- } else {
482
- return '';
483
- }
484
- } else {
485
- $params['field_value'] = get_user_meta( $user_id,
486
- wpcf_types_get_meta_prefix( $field ) . $field['slug'], true );
487
- if ( $params['field_value'] == '' && $field['type'] != 'checkbox' ) {
488
- return '';
489
- }
490
- $html = types_render_field_single( $field, $params, $content, $code,
491
- $wpcf->usermeta_field->meta_object->umeta_id );
492
- }
493
-
494
- // API filter
495
- // $wpcf->usermeta_field->set( $user_id, $field );
496
- return $wpcf->usermeta_field->html( $html, $params );
497
  }
498
 
499
  /**
345
  /**
346
  * Calls view function for specific field type.
347
  *
348
+ * @param $field_id
349
+ * @param $params
350
+ * @param null $content
351
+ * @param string $code
352
  *
353
+ * @return string
 
 
354
  *
355
+ * @deprecated Use types_render_usermeta() instead.
356
  */
357
+ function types_render_usermeta_field( $field_id, $params, $content = null, $code = '' ) {
358
+ return types_render_usermeta( $field_id, $params, $content, $code );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  }
360
 
361
  /**
library/toolset/types/includes/fields.php CHANGED
@@ -246,6 +246,8 @@ function wpcf_admin_fields_save_group( $group, $post_type = TYPES_CUSTOM_FIELD_G
246
  }
247
 
248
  $update = false;
 
 
249
  if ( isset( $group['id'] ) && !empty($group['id']) ) {
250
  $update = true;
251
  $post_to_update = get_post( $group['id'] );
@@ -253,6 +255,7 @@ function wpcf_admin_fields_save_group( $group, $post_type = TYPES_CUSTOM_FIELD_G
253
  return false;
254
  }
255
  $post['ID'] = $post_to_update->ID;
 
256
  $post['post_status'] = $post_to_update->post_status;
257
  }
258
 
@@ -281,10 +284,21 @@ function wpcf_admin_fields_save_group( $group, $post_type = TYPES_CUSTOM_FIELD_G
281
 
282
  // WPML register strings
283
  if ( function_exists( 'icl_register_string' ) ) {
284
- wpcf_translate_register_string( 'plugin Types',
285
- 'group ' . $group_id . ' name', $group['name'] );
286
- wpcf_translate_register_string( 'plugin Types',
287
- 'group ' . $group_id . ' description', $group['description'] );
 
 
 
 
 
 
 
 
 
 
 
288
  }
289
 
290
  // admin message
246
  }
247
 
248
  $update = false;
249
+ $slug_pre_save = false;
250
+
251
  if ( isset( $group['id'] ) && !empty($group['id']) ) {
252
  $update = true;
253
  $post_to_update = get_post( $group['id'] );
255
  return false;
256
  }
257
  $post['ID'] = $post_to_update->ID;
258
+ $slug_pre_save = $post_to_update->post_name;
259
  $post['post_status'] = $post_to_update->post_status;
260
  }
261
 
284
 
285
  // WPML register strings
286
  if ( function_exists( 'icl_register_string' ) ) {
287
+
288
+ try {
289
+ // Legacy function gives us only the underlying post type of the field group.
290
+ $group_factory = Types_Field_Utils::get_group_factory_by_post_type( $post_type );
291
+ $field_group = $group_factory->load_field_group( sanitize_title( $group['name'] ) );
292
+
293
+ // Skip registering if the group does not exist.
294
+ if( null != $field_group ) {
295
+ $group_wpml = new Types_Wpml_Field_Group( $field_group );
296
+ $group_wpml->register( $slug_pre_save );
297
+ }
298
+
299
+ } catch( InvalidArgumentException $e ) {
300
+ // Something is seriously wrong - there's no field group factory for given post type, bail.
301
+ }
302
  }
303
 
304
  // admin message
library/toolset/types/includes/post-relationship.php CHANGED
@@ -331,32 +331,47 @@ function wpcf_admin_metabox_relationship($post_type)
331
  )
332
  )
333
  );
334
- $custom_types = get_option( WPCF_OPTION_NAME_CUSTOM_TYPES, array() );
335
- if ( !isset($_REQUEST['wpcf-post-type']) || !isset( $custom_types[$_REQUEST['wpcf-post-type']] ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  $form['alert'] = array(
337
  '#type' => 'notice',
338
- '#markup' => __( 'Please save first, before you can edit the relationship.', 'wpcf' ),
339
  );
340
- $form = wpcf_form(__FUNCTION__, $form);
341
  echo $form->renderForm();
342
- return false;
343
  }
344
 
345
- $post_type = false;;
346
-
347
  $post_type = $custom_types[$_REQUEST['wpcf-post-type']];
 
348
  unset($custom_types);
349
 
350
- /**
351
- * belongs/children section
352
- */
353
  $has = wpcf_pr_admin_get_has( $post_type['slug'] );
354
  $belongs = wpcf_pr_admin_get_belongs( $post_type['slug'] );
355
  $post_types = get_post_types( '', 'objects' );
356
 
357
- /**
358
- * parents
359
- */
360
  $form['parent-h3'] = array(
361
  '#type' => 'markup',
362
  '#markup' => sprintf(
@@ -374,40 +389,40 @@ function wpcf_admin_metabox_relationship($post_type)
374
  );
375
  $options = array();
376
 
377
- /**
378
- * build excluded post types
379
- */
380
  global $wpcf;
381
  $excluded_post_types = $wpcf->excluded_post_types;
382
  $excluded_post_types[] = $post_type['slug'];
 
 
 
383
 
384
- foreach ( $post_types as $temp_post_type_slug => $temp_post_type ) {
385
- if (
386
- in_array( $temp_post_type_slug, $excluded_post_types )
387
- || (
388
- !$temp_post_type->show_ui
389
- && !apply_filters('wpcf_show_ui_hide_in_relationships', true)
390
- )
391
- ) {
392
  continue;
393
  }
394
- $options[$temp_post_type_slug] = array(
395
- '#name' => 'ct[post_relationship][belongs][' . $temp_post_type_slug . ']',
396
- '#title' => $temp_post_type->labels->singular_name,
397
- '#default_value' => isset( $belongs[$temp_post_type_slug] ),
 
398
  '#inline' => true,
399
  '#before' => '<li>',
400
  '#after' => '</li>',
401
  '#attributes' => array(
402
  'class' => 'js-wpcf-relationship-checkbox',
403
  'data-wpcf-type' => 'belongs',
404
- 'data-wpcf-value' => esc_attr($temp_post_type_slug),
405
  'data-wpcf-message-disabled' => esc_attr__('This post type is disabled, becouse is used as child post.', 'wpcf'),
406
  ),
407
  );
408
- if ( isset( $has[$temp_post_type_slug] ) ) {
409
- $options[$temp_post_type_slug]['#before'] = '<li class="disabled">';
410
- $options[$temp_post_type_slug]['#attributes']['disabled'] = 'disabled';
411
  }
412
  }
413
  $form['table-pr-belongs-form'] = array(
@@ -419,9 +434,8 @@ function wpcf_admin_metabox_relationship($post_type)
419
  '#after' => '</ul>',
420
  );
421
 
422
- /**
423
- * child posts
424
- */
425
  $form['child-h3'] = array(
426
  '#type' => 'markup',
427
  '#markup' => sprintf(
@@ -438,11 +452,11 @@ function wpcf_admin_metabox_relationship($post_type)
438
  )
439
  );
440
  $options = array();
441
- foreach ( $post_types as $temp_post_type_slug => $temp_post_type ) {
442
  if (
443
- in_array( $temp_post_type_slug, $excluded_post_types )
444
  || (
445
- !$temp_post_type->show_ui
446
  && !apply_filters('wpcf_show_ui_hide_in_relationships', true)
447
  )
448
  ) {
@@ -452,46 +466,46 @@ function wpcf_admin_metabox_relationship($post_type)
452
  $nonce = sprintf(
453
  'child-post-fields-%s-%s',
454
  $post_type['slug'],
455
- $temp_post_type_slug
456
  );
457
  $a = sprintf(
458
  ' <span>(<a class="js-wpcf-edit-child-post-fields" href="#" data-wpcf-nonce="%s" data-wpcf-child="%s" data-wpcf-parent="%s" data-wpcf-title="%s" data-wpcf-buttons-apply="%s" data-wpcf-buttons-cancel="%s" data-wpcf-message-loading="%s" data-wpcf-save-status="%s">%s</a>)</span>',
459
  esc_attr(wp_create_nonce($nonce)),
460
- esc_attr($temp_post_type_slug),
461
  esc_attr($post_type['slug']),
462
  esc_attr(
463
  sprintf(
464
  __('Select child fields from %s to be displayed in Post Relationship table', 'wpcf'),
465
- $temp_post_type->labels->singular_name
466
  )
467
  ),
468
  esc_attr__('Apply', 'wpcf'),
469
  esc_attr__('Cancel', 'wpcf'),
470
  esc_attr__('Please Wait, Loading…', 'wpcf'),
471
- esc_attr(isset( $has[$temp_post_type_slug] )?'saved':'new'),
472
  esc_attr__('Select fields', 'wpcf')
473
  );
474
 
475
- $options[$temp_post_type_slug] = array(
476
- '#name' => 'ct[post_relationship][has][' . $temp_post_type_slug . ']',
477
- '#title' => $temp_post_type->labels->singular_name,
478
  '#inline' => true,
479
  '#before' => '<li>',
480
  '#after' => $a.'</li>',
481
  '#attributes' => array(
482
  'class' => 'js-wpcf-relationship-checkbox',
483
  'data-wpcf-type' => 'has',
484
- 'data-wpcf-value' => esc_attr($temp_post_type_slug),
485
  'data-wpcf-message-disabled' => esc_attr__('This post type is disabled, becouse is used as parent post.', 'wpcf'),
486
  ),
487
  );
488
  // Check if it already belongs
489
- if ( isset( $belongs[$temp_post_type_slug] ) ) {
490
- $options[$temp_post_type_slug]['#before'] = '<li class="disabled">';
491
- $options[$temp_post_type_slug]['#attributes']['disabled'] = 'disabled';
492
- } else if ( isset( $has[$temp_post_type_slug] ) ) {
493
- $options[$temp_post_type_slug]['#default_value'] = true;
494
- $options[$temp_post_type_slug]['#before'] = '<li class="active">';
495
  }
496
  }
497
  $form['table-pr-has-form'] = array(
@@ -503,7 +517,7 @@ function wpcf_admin_metabox_relationship($post_type)
503
  '#after' => '</ul>',
504
  );
505
 
506
- $form = wpcf_form(__FUNCTION__, $form);
507
  echo $form->renderForm();
508
  }
509
 
331
  )
332
  )
333
  );
334
+
335
+ $custom_types = get_option( WPCF_OPTION_NAME_CUSTOM_TYPES, array() );
336
+
337
+ $is_error = false;
338
+ $error_message = '';
339
+
340
+ // Detect situations when we cannot configure post relationships yet. Render a message and finish.
341
+ $is_unsaved_post_type = ( ! isset( $_REQUEST['wpcf-post-type'] ) || ! isset( $custom_types[ $_REQUEST['wpcf-post-type'] ] ) );
342
+ if ( $is_unsaved_post_type ) {
343
+ $is_error = true;
344
+ $error_message = __( 'Please save first, before you can edit the relationship.', 'wpcf' );
345
+ }
346
+
347
+ $is_attachment = ( isset( $_REQUEST['wpcf-post-type'] ) && 'attachment' == $_REQUEST['wpcf-post-type'] );
348
+ if( $is_attachment ) {
349
+ $is_error = true;
350
+ $error_message = __( 'Post relationships are not allowed for the Media post type.', 'wpcf' );
351
+ }
352
+
353
+ if( $is_error ) {
354
  $form['alert'] = array(
355
  '#type' => 'notice',
356
+ '#markup' => $error_message,
357
  );
358
+ $form = wpcf_form( __FUNCTION__, $form );
359
  echo $form->renderForm();
360
+ return;
361
  }
362
 
 
 
363
  $post_type = $custom_types[$_REQUEST['wpcf-post-type']];
364
+
365
  unset($custom_types);
366
 
367
+ // No problems detected, go ahead and render the options.
368
+
369
+ // belongs/children section
370
  $has = wpcf_pr_admin_get_has( $post_type['slug'] );
371
  $belongs = wpcf_pr_admin_get_belongs( $post_type['slug'] );
372
  $post_types = get_post_types( '', 'objects' );
373
 
374
+ // parents
 
 
375
  $form['parent-h3'] = array(
376
  '#type' => 'markup',
377
  '#markup' => sprintf(
389
  );
390
  $options = array();
391
 
392
+ // Build excluded post types
 
 
393
  global $wpcf;
394
  $excluded_post_types = $wpcf->excluded_post_types;
395
  $excluded_post_types[] = $post_type['slug'];
396
+ // Explicitly exclude attachments for post relationships because there is no GUI for it
397
+ // (but we're not excluding them from all Types functionality)
398
+ $excluded_post_types[] = 'attachment';
399
 
400
+ foreach ( $post_types as $post_type_option_slug => $post_type_option ) {
401
+
402
+ $is_excluded = in_array( $post_type_option_slug, $excluded_post_types );
403
+ $has_no_ui = ( ! $post_type_option->show_ui && ! apply_filters('wpcf_show_ui_hide_in_relationships', true ) );
404
+
405
+ if ( $is_excluded || $has_no_ui ) {
 
 
406
  continue;
407
  }
408
+
409
+ $options[$post_type_option_slug] = array(
410
+ '#name' => 'ct[post_relationship][belongs][' . $post_type_option_slug . ']',
411
+ '#title' => $post_type_option->labels->singular_name,
412
+ '#default_value' => isset( $belongs[$post_type_option_slug] ),
413
  '#inline' => true,
414
  '#before' => '<li>',
415
  '#after' => '</li>',
416
  '#attributes' => array(
417
  'class' => 'js-wpcf-relationship-checkbox',
418
  'data-wpcf-type' => 'belongs',
419
+ 'data-wpcf-value' => esc_attr($post_type_option_slug),
420
  'data-wpcf-message-disabled' => esc_attr__('This post type is disabled, becouse is used as child post.', 'wpcf'),
421
  ),
422
  );
423
+ if ( isset( $has[$post_type_option_slug] ) ) {
424
+ $options[$post_type_option_slug]['#before'] = '<li class="disabled">';
425
+ $options[$post_type_option_slug]['#attributes']['disabled'] = 'disabled';
426
  }
427
  }
428
  $form['table-pr-belongs-form'] = array(
434
  '#after' => '</ul>',
435
  );
436
 
437
+
438
+ // child posts
 
439
  $form['child-h3'] = array(
440
  '#type' => 'markup',
441
  '#markup' => sprintf(
452
  )
453
  );
454
  $options = array();
455
+ foreach ( $post_types as $post_type_option_slug => $post_type_option ) {
456
  if (
457
+ in_array( $post_type_option_slug, $excluded_post_types )
458
  || (
459
+ !$post_type_option->show_ui
460
  && !apply_filters('wpcf_show_ui_hide_in_relationships', true)
461
  )
462
  ) {
466
  $nonce = sprintf(
467
  'child-post-fields-%s-%s',
468
  $post_type['slug'],
469
+ $post_type_option_slug
470
  );
471
  $a = sprintf(
472
  ' <span>(<a class="js-wpcf-edit-child-post-fields" href="#" data-wpcf-nonce="%s" data-wpcf-child="%s" data-wpcf-parent="%s" data-wpcf-title="%s" data-wpcf-buttons-apply="%s" data-wpcf-buttons-cancel="%s" data-wpcf-message-loading="%s" data-wpcf-save-status="%s">%s</a>)</span>',
473
  esc_attr(wp_create_nonce($nonce)),
474
+ esc_attr($post_type_option_slug),
475
  esc_attr($post_type['slug']),
476
  esc_attr(
477
  sprintf(
478
  __('Select child fields from %s to be displayed in Post Relationship table', 'wpcf'),
479
+ $post_type_option->labels->singular_name
480
  )
481
  ),
482
  esc_attr__('Apply', 'wpcf'),
483
  esc_attr__('Cancel', 'wpcf'),
484
  esc_attr__('Please Wait, Loading…', 'wpcf'),
485
+ esc_attr(isset( $has[$post_type_option_slug] )?'saved':'new'),
486
  esc_attr__('Select fields', 'wpcf')
487
  );
488
 
489
+ $options[$post_type_option_slug] = array(
490
+ '#name' => 'ct[post_relationship][has][' . $post_type_option_slug . ']',
491
+ '#title' => $post_type_option->labels->singular_name,
492
  '#inline' => true,
493
  '#before' => '<li>',
494
  '#after' => $a.'</li>',
495
  '#attributes' => array(
496
  'class' => 'js-wpcf-relationship-checkbox',
497
  'data-wpcf-type' => 'has',
498
+ 'data-wpcf-value' => esc_attr($post_type_option_slug),
499
  'data-wpcf-message-disabled' => esc_attr__('This post type is disabled, becouse is used as parent post.', 'wpcf'),
500
  ),
501
  );
502
  // Check if it already belongs
503
+ if ( isset( $belongs[$post_type_option_slug] ) ) {
504
+ $options[$post_type_option_slug]['#before'] = '<li class="disabled">';
505
+ $options[$post_type_option_slug]['#attributes']['disabled'] = 'disabled';
506
+ } else if ( isset( $has[$post_type_option_slug] ) ) {
507
+ $options[$post_type_option_slug]['#default_value'] = true;
508
+ $options[$post_type_option_slug]['#before'] = '<li class="active">';
509
  }
510
  }
511
  $form['table-pr-has-form'] = array(
517
  '#after' => '</ul>',
518
  );
519
 
520
+ $form = wpcf_form( __FUNCTION__, $form );
521
  echo $form->renderForm();
522
  }
523
 
library/twig/twig/.travis.yml CHANGED
@@ -5,7 +5,7 @@ sudo: false
5
  cache:
6
  directories:
7
  - vendor
8
- - $HOME/.composer/cache
9
 
10
  php:
11
  - 5.2
@@ -14,12 +14,16 @@ php:
14
  - 5.5
15
  - 5.6
16
  - 7.0
 
17
  - hhvm
18
 
19
  env:
20
  - TWIG_EXT=no
21
  - TWIG_EXT=yes
22
 
 
 
 
23
  install:
24
  # Composer is not available on PHP 5.2
25
  - if [ ${TRAVIS_PHP_VERSION:0:3} != "5.2" ]; then travis_retry composer install; fi
@@ -34,6 +38,7 @@ matrix:
34
  exclude:
35
  - php: hhvm
36
  env: TWIG_EXT=yes
37
- allow_failures:
38
  - php: 7.0
39
  env: TWIG_EXT=yes
 
 
5
  cache:
6
  directories:
7
  - vendor
8
+ - $HOME/.composer/cache/files
9
 
10
  php:
11
  - 5.2
14
  - 5.5
15
  - 5.6
16
  - 7.0
17
+ - 7.1
18
  - hhvm
19
 
20
  env:
21
  - TWIG_EXT=no
22
  - TWIG_EXT=yes
23
 
24
+ before_install:
25
+ - if [[ ! $TRAVIS_PHP_VERSION = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi
26
+
27
  install:
28
  # Composer is not available on PHP 5.2
29
  - if [ ${TRAVIS_PHP_VERSION:0:3} != "5.2" ]; then travis_retry composer install; fi
38
  exclude:
39
  - php: hhvm
40
  env: TWIG_EXT=yes
 
41
  - php: 7.0
42
  env: TWIG_EXT=yes
43
+ - php: 7.1
44
+ env: TWIG_EXT=yes
library/twig/twig/CHANGELOG CHANGED
@@ -1,3 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  * 1.25.0 (2016-09-21)
2
 
3
  * changed the way we store template source in template classes
1
+ * 1.28.2 (2016-11-23)
2
+
3
+ * fixed precedence between getFoo() and isFoo() in Twig_Template::getAttribute()
4
+ * improved a deprecation message
5
+
6
+ * 1.28.1 (2016-11-18)
7
+
8
+ * fixed block() function when used with a template argument
9
+
10
+ * 1.28.0 (2016-11-17)
11
+
12
+ * added support for the PHP 7 null coalescing operator for the ?? Twig implementation
13
+ * exposed a way to access template data and methods in a portable way
14
+ * changed context access to use the PHP 7 null coalescing operator when available
15
+ * added the "with" tag
16
+ * added support for a custom template on the block() function
17
+ * added "is defined" support for block() and constant()
18
+ * optimized the way attributes are fetched
19
+
20
+ * 1.27.0 (2016-10-25)
21
+
22
+ * deprecated Twig_Parser::getEnvironment()
23
+ * deprecated Twig_Parser::addHandler() and Twig_Parser::addNodeVisitor()
24
+ * deprecated Twig_Compiler::addIndentation()
25
+ * fixed regression when registering two extensions having the same class name
26
+ * deprecated Twig_LoaderInterface::getSource() (implement Twig_SourceContextLoaderInterface instead)
27
+ * fixed the filesystem loader with relative paths
28
+ * deprecated Twig_Node::getLine() in favor of Twig_Node::getTemplateLine()
29
+ * deprecated Twig_Template::getSource() in favor of Twig_Template::getSourceContext()
30
+ * deprecated Twig_Node::getFilename() in favor of Twig_Node::getTemplateName()
31
+ * deprecated the "filename" escaping strategy (use "name" instead)
32
+ * added Twig_Source to hold information about the original template
33
+ * deprecated Twig_Error::getTemplateFile() and Twig_Error::setTemplateFile() in favor of Twig_Error::getTemplateName() and Twig_Error::setTemplateName()
34
+ * deprecated Parser::getFilename()
35
+ * fixed template paths when a template name contains a protocol like vfs://
36
+ * improved debugging with Twig_Sandbox_SecurityError exceptions for disallowed methods and properties
37
+
38
+ * 1.26.1 (2016-10-05)
39
+
40
+ * removed template source code from generated template classes when debug is disabled
41
+ * fixed default implementation of Twig_Template::getDebugInfo() for better BC
42
+ * fixed regression on static calls for functions/filters/tests
43
+
44
+ * 1.26.0 (2016-10-02)
45
+
46
+ * added template cache invalidation based on more environment options
47
+ * added a missing deprecation notice
48
+ * fixed template paths when a template is stored in a PHAR file
49
+ * allowed filters/functions/tests implementation to use a different class than the extension they belong to
50
+ * deprecated Twig_ExtensionInterface::getName()
51
+
52
  * 1.25.0 (2016-09-21)
53
 
54
  * changed the way we store template source in template classes
library/twig/twig/doc/advanced.rst CHANGED
@@ -136,7 +136,13 @@ Creating a filter is as simple as associating a name with a PHP callable::
136
  // or a simple PHP function
137
  $filter = new Twig_SimpleFilter('rot13', 'str_rot13');
138
 
 
 
 
 
139
  // or a class method
 
 
140
  $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
141
 
142
  The first argument passed to the ``Twig_SimpleFilter`` constructor is the name
@@ -525,10 +531,6 @@ reusable class like adding support for internationalization. An extension can
525
  define tags, filters, tests, operators, global variables, functions, and node
526
  visitors.
527
 
528
- Creating an extension also makes for a better separation of code that is
529
- executed at compilation time and code needed at runtime. As such, it makes
530
- your code faster.
531
-
532
  Most of the time, it is useful to create a single extension for your project,
533
  to host all the specific tags and filters you want to add to Twig.
534
 
@@ -552,8 +554,6 @@ An extension is a class that implements the following interface::
552
  *
553
  * This is where you can load some file that contains filter functions for instance.
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);
@@ -561,35 +561,35 @@ An extension is a class that implements the following interface::
561
  /**
562
  * Returns the token parser instances to add to the existing list.
563
  *
564
- * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
565
  */
566
  function getTokenParsers();
567
 
568
  /**
569
  * Returns the node visitor instances to add to the existing list.
570
  *
571
- * @return array An array of Twig_NodeVisitorInterface instances
572
  */
573
  function getNodeVisitors();
574
 
575
  /**
576
  * Returns a list of filters to add to the existing list.
577
  *
578
- * @return array An array of filters
579
  */
580
  function getFilters();
581
 
582
  /**
583
  * Returns a list of tests to add to the existing list.
584
  *
585
- * @return array An array of tests
586
  */
587
  function getTests();
588
 
589
  /**
590
  * Returns a list of functions to add to the existing list.
591
  *
592
- * @return array An array of functions
593
  */
594
  function getFunctions();
595
 
@@ -613,32 +613,27 @@ An extension is a class that implements the following interface::
613
  * Returns the name of the extension.
614
  *
615
  * @return string The extension name
 
 
616
  */
617
  function getName();
618
  }
619
 
620
- To keep your extension class clean and lean, it can inherit from the built-in
621
- ``Twig_Extension`` class instead of implementing the whole interface. That
622
- way, you just need to implement the ``getName()`` method as the
623
- ``Twig_Extension`` provides empty implementations for all other methods.
624
-
625
- The ``getName()`` method must return a unique identifier for your extension.
626
-
627
- Now, with this information in mind, let's create the most basic extension
628
- possible::
629
 
630
  class Project_Twig_Extension extends Twig_Extension
631
  {
632
- public function getName()
633
- {
634
- return 'project';
635
- }
636
  }
637
 
 
 
 
638
  .. note::
639
 
640
- Of course, this extension does nothing for now. We will customize it in
641
- the next sections.
642
 
643
  Twig does not care where you save your extension on the filesystem, as all
644
  extensions must be registered explicitly to be available in your templates.
@@ -651,7 +646,7 @@ main ``Environment`` object::
651
 
652
  .. tip::
653
 
654
- The bundled extensions are great examples of how extensions work.
655
 
656
  Globals
657
  ~~~~~~~
@@ -770,6 +765,101 @@ The ``getTests()`` method lets you add new test functions::
770
  // ...
771
  }
772
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
  Overloading
774
  -----------
775
 
@@ -790,11 +880,6 @@ possible** (order matters)::
790
  {
791
  // do something different from the built-in date filter
792
  }
793
-
794
- public function getName()
795
- {
796
- return 'project';
797
- }
798
  }
799
 
800
  $twig = new Twig_Environment($loader);
@@ -802,7 +887,7 @@ possible** (order matters)::
802
 
803
  Here, we have overloaded the built-in ``date`` filter with a custom one.
804
 
805
- If you do the same on the Twig_Environment itself, beware that it takes
806
  precedence over any other registered extensions::
807
 
808
  $twig = new Twig_Environment($loader);
136
  // or a simple PHP function
137
  $filter = new Twig_SimpleFilter('rot13', 'str_rot13');
138
 
139
+ // or a class static method
140
+ $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
141
+ $filter = new Twig_SimpleFilter('rot13', 'SomeClass::rot13Filter');
142
+
143
  // or a class method
144
+ $filter = new Twig_SimpleFilter('rot13', array($this, 'rot13Filter'));
145
+ // the one below needs a runtime implementation (see below for more information)
146
  $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
147
 
148
  The first argument passed to the ``Twig_SimpleFilter`` constructor is the name
531
  define tags, filters, tests, operators, global variables, functions, and node
532
  visitors.
533
 
 
 
 
 
534
  Most of the time, it is useful to create a single extension for your project,
535
  to host all the specific tags and filters you want to add to Twig.
536
 
554
  *
555
  * This is where you can load some file that contains filter functions for 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);
561
  /**
562
  * Returns the token parser instances to add to the existing list.
563
  *
564
+ * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[]
565
  */
566
  function getTokenParsers();
567
 
568
  /**
569
  * Returns the node visitor instances to add to the existing list.
570
  *
571
+ * @return Twig_NodeVisitorInterface[]
572
  */
573
  function getNodeVisitors();
574
 
575
  /**
576
  * Returns a list of filters to add to the existing list.
577
  *
578
+ * @return Twig_SimpleFilter[]
579
  */
580
  function getFilters();
581
 
582
  /**
583
  * Returns a list of tests to add to the existing list.
584
  *
585
+ * @return Twig_SimpleTest[]
586
  */
587
  function getTests();
588
 
589
  /**
590
  * Returns a list of functions to add to the existing list.
591
  *
592
+ * @return Twig_SimpleFunction[]
593
  */
594
  function getFunctions();
595
 
613
  * Returns the name of the extension.
614
  *
615
  * @return string The extension name
616
+ *
617
+ * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
618
  */
619
  function getName();
620
  }
621
 
622
+ To keep your extension class clean and lean, inherit from the built-in
623
+ ``Twig_Extension`` class instead of implementing the interface as it provides
624
+ empty implementations for all methods:
 
 
 
 
 
 
625
 
626
  class Project_Twig_Extension extends Twig_Extension
627
  {
 
 
 
 
628
  }
629
 
630
+ Of course, this extension does nothing for now. We will customize it in the
631
+ next sections.
632
+
633
  .. note::
634
 
635
+ Prior to Twig 1.26, you must implement the ``getName()`` method which must
636
+ return a unique identifier for the extension.
637
 
638
  Twig does not care where you save your extension on the filesystem, as all
639
  extensions must be registered explicitly to be available in your templates.
646
 
647
  .. tip::
648
 
649
+ The Twig core extensions are great examples of how extensions work.
650
 
651
  Globals
652
  ~~~~~~~
765
  // ...
766
  }
767
 
768
+ Definition vs Runtime
769
+ ~~~~~~~~~~~~~~~~~~~~~
770
+
771
+ Twig filters, functions, and tests runtime implementations can be defined as
772
+ any valid PHP callable:
773
+
774
+ * **functions/static methods**: Simple to implement and fast (used by all Twig
775
+ core extensions); but it is hard for the runtime to depend on external
776
+ objects;
777
+
778
+ * **closures**: Simple to implement;
779
+
780
+ * **object methods**: More flexible and required if your runtime code depends
781
+ on external objects.
782
+
783
+ The simplest way to use methods is to define them on the extension itself::
784
+
785
+ class Project_Twig_Extension extends Twig_Extension
786
+ {
787
+ private $rot13Provider;
788
+
789
+ public function __construct($rot13Provider)
790
+ {
791
+ $this->rot13Provider = $rot13Provider;
792
+ }
793
+
794
+ public function getFunctions()
795
+ {
796
+ return array(
797
+ new Twig_SimpleFunction('rot13', array($this, 'rot13')),
798
+ );
799
+ }
800
+
801
+ public function rot13($value)
802
+ {
803
+ return $rot13Provider->rot13($value);
804
+ }
805
+ }
806
+
807
+ This is very convenient but not recommended as it makes template compilation
808
+ depend on runtime dependencies even if they are not needed (think for instance
809
+ as a dependency that connects to a database engine).
810
+
811
+ As of Twig 1.26, you can easily decouple the extension definitions from their
812
+ runtime implementations by registering a ``Twig_RuntimeLoaderInterface``
813
+ instance on the environment that knows how to instantiate such runtime classes
814
+ (runtime classes must be autoload-able)::
815
+
816
+ class RuntimeLoader implements Twig_RuntimeLoaderInterface
817
+ {
818
+ public function load($class)
819
+ {
820
+ // implement the logic to create an instance of $class
821
+ // and inject its dependencies
822
+ // most of the time, it means using your dependency injection container
823
+ if ('Project_Twig_RuntimeExtension' === $class) {
824
+ return new $class(new Rot13Provider());
825
+ } else {
826
+ // ...
827
+ }
828
+ }
829
+ }
830
+
831
+ $twig->addRuntimeLoader(new RuntimeLoader());
832
+
833
+ It is now possible to move the runtime logic to a new
834
+ ``Project_Twig_RuntimeExtension`` class and use it directly in the extension::
835
+
836
+ class Project_Twig_RuntimeExtension extends Twig_Extension
837
+ {
838
+ private $rot13Provider;
839
+
840
+ public function __construct($rot13Provider)
841
+ {
842
+ $this->rot13Provider = $rot13Provider;
843
+ }
844
+
845
+ public function rot13($value)
846
+ {
847
+ return $rot13Provider->rot13($value);
848
+ }
849
+ }
850
+
851
+ class Project_Twig_Extension extends Twig_Extension
852
+ {
853
+ public function getFunctions()
854
+ {
855
+ return array(
856
+ new Twig_SimpleFunction('rot13', array('Project_Twig_RuntimeExtension', 'rot13')),
857
+ // or
858
+ new Twig_SimpleFunction('rot13', 'Project_Twig_RuntimeExtension::rot13'),
859
+ );
860
+ }
861
+ }
862
+
863
  Overloading
864
  -----------
865
 
880
  {
881
  // do something different from the built-in date filter
882
  }
 
 
 
 
 
883
  }
884
 
885
  $twig = new Twig_Environment($loader);
887
 
888
  Here, we have overloaded the built-in ``date`` filter with a custom one.
889
 
890
+ If you do the same on the ``Twig_Environment`` itself, beware that it takes
891
  precedence over any other registered extensions::
892
 
893
  $twig = new Twig_Environment($loader);
library/twig/twig/doc/advanced_legacy.rst CHANGED
@@ -539,43 +539,41 @@ An extension is a class that implements the following interface::
539
  * Initializes the runtime environment.
540
  *
541
  * This is where you can load some file that contains filter functions for instance.
542
- *
543
- * @param Twig_Environment $environment The current Twig_Environment instance
544
  */
545
  function initRuntime(Twig_Environment $environment);
546
 
547
  /**
548
  * Returns the token parser instances to add to the existing list.
549
  *
550
- * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
551
  */
552
  function getTokenParsers();
553
 
554
  /**
555
  * Returns the node visitor instances to add to the existing list.
556
  *
557
- * @return array An array of Twig_NodeVisitorInterface instances
558
  */
559
  function getNodeVisitors();
560
 
561
  /**
562
  * Returns a list of filters to add to the existing list.
563
  *
564
- * @return array An array of filters
565
  */
566
  function getFilters();
567
 
568
  /**
569
  * Returns a list of tests to add to the existing list.
570
  *
571
- * @return array An array of tests
572
  */
573
  function getTests();
574
 
575
  /**
576
  * Returns a list of functions to add to the existing list.
577
  *
578
- * @return array An array of functions
579
  */
580
  function getFunctions();
581
 
539
  * Initializes the runtime environment.
540
  *
541
  * This is where you can load some file that contains filter functions for instance.
 
 
542
  */
543
  function initRuntime(Twig_Environment $environment);
544
 
545
  /**
546
  * Returns the token parser instances to add to the existing list.
547
  *
548
+ * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[]
549
  */
550
  function getTokenParsers();
551
 
552
  /**
553
  * Returns the node visitor instances to add to the existing list.
554
  *
555
+ * @return Twig_NodeVisitorInterface[]
556
  */
557
  function getNodeVisitors();
558
 
559
  /**
560
  * Returns a list of filters to add to the existing list.
561
  *
562
+ * @return Twig_SimpleFilter[]
563
  */
564
  function getFilters();
565
 
566
  /**
567
  * Returns a list of tests to add to the existing list.
568
  *
569
+ * @return Twig_SimpleTest[]
570
  */
571
  function getTests();
572
 
573
  /**
574
  * Returns a list of functions to add to the existing list.
575
  *
576
+ * @return Twig_SimpleFunction[]
577
  */
578
  function getFunctions();
579
 
library/twig/twig/doc/api.rst CHANGED
@@ -43,10 +43,18 @@ templates from a database or other resources.
43
  the evaluated templates. For such a need, you can use any available PHP
44
  cache library.
45
 
46
- To load a template from this environment you just have to call the
47
- ``loadTemplate()`` method which then returns a ``Twig_Template`` instance::
 
 
 
 
 
 
 
48
 
49
- $template = $twig->loadTemplate('index.html');
 
50
 
51
  To render the template with some variables, call the ``render()`` method::
52
 
@@ -60,6 +68,14 @@ You can also load and render the template in one fell swoop::
60
 
61
  echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here'));
62
 
 
 
 
 
 
 
 
 
63
  .. _environment_options:
64
 
65
  Environment Options
@@ -115,14 +131,14 @@ The following options are available:
115
  ``false`` to disable).
116
 
117
  As of Twig 1.9, you can set the escaping strategy to use (``css``, ``url``,
118
- ``html_attr``, or a PHP callback that takes the template "filename" and must
119
  return the escaping strategy to use -- the callback cannot be a function name
120
  to avoid collision with built-in escaping strategies).
121
 
122
- As of Twig 1.17, the ``filename`` escaping strategy determines the escaping
123
- strategy to use for a template based on the template filename extension (this
124
- strategy does not incur any overhead at runtime as auto-escaping is done at
125
- compilation time.)
126
 
127
  * ``optimizations`` *integer*
128
 
@@ -156,6 +172,9 @@ Here is a list of the built-in loaders Twig provides:
156
  .. versionadded:: 1.10
157
  The ``prependPath()`` and support for namespaces were added in Twig 1.10.
158
 
 
 
 
159
  ``Twig_Loader_Filesystem`` loads templates from the file system. This loader
160
  can find templates in folders on the file system and is the preferred way to
161
  load them::
@@ -190,6 +209,18 @@ Namespaced templates can be accessed via the special
190
 
191
  $twig->render('@admin/index.html', array());
192
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  ``Twig_Loader_Array``
194
  .....................
195
 
@@ -256,6 +287,8 @@ All loaders implement the ``Twig_LoaderInterface``::
256
  * @param string $name string The name of the template to load
257
  *
258
  * @return string The template source code
 
 
259
  */
260
  function getSource($name);
261
 
@@ -280,6 +313,11 @@ All loaders implement the ``Twig_LoaderInterface``::
280
  The ``isFresh()`` method must return ``true`` if the current cached template
281
  is still fresh, given the last modification time, or ``false`` otherwise.
282
 
 
 
 
 
 
283
  .. tip::
284
 
285
  As of Twig 1.11.0, you can also implement ``Twig_ExistsLoaderInterface``
43
  the evaluated templates. For such a need, you can use any available PHP
44
  cache library.
45
 
46
+ Rendering Templates
47
+ -------------------
48
+
49
+ To load a template from a Twig environment, call the ``load()`` method which
50
+ returns a ``Twig_TemplateWrapper`` instance::
51
+
52
+ $template = $twig->load('index.html');
53
+
54
+ .. note::
55
 
56
+ Before Twig 1.28, you should use ``loadTemplate()`` instead which returns a
57
+ ``Twig_Template`` instance.
58
 
59
  To render the template with some variables, call the ``render()`` method::
60
 
68
 
69
  echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here'));
70
 
71
+ .. versionadded:: 1.28
72
+ The possibility to render blocks from the API was added in Twig 1.28.
73
+
74
+ If a template defines blocks, they can be rendered individually via the
75
+ ``renderBlock()`` call::
76
+
77
+ echo $template->renderBlock('block_name', array('the' => 'variables', 'go' => 'here'));
78
+
79
  .. _environment_options:
80
 
81
  Environment Options
131
  ``false`` to disable).
132
 
133
  As of Twig 1.9, you can set the escaping strategy to use (``css``, ``url``,
134
+ ``html_attr``, or a PHP callback that takes the template name and must
135
  return the escaping strategy to use -- the callback cannot be a function name
136
  to avoid collision with built-in escaping strategies).
137
 
138
+ As of Twig 1.17, the ``filename`` escaping strategy (renamed to ``name`` as
139
+ of Twig 1.27) determines the escaping strategy to use for a template based on
140
+ the template filename extension (this strategy does not incur any overhead at
141
+ runtime as auto-escaping is done at compilation time.)
142
 
143
  * ``optimizations`` *integer*
144
 
172
  .. versionadded:: 1.10
173
  The ``prependPath()`` and support for namespaces were added in Twig 1.10.
174
 
175
+ .. versionadded:: 1.27
176
+ Relative paths support was added in Twig 1.27.
177
+
178
  ``Twig_Loader_Filesystem`` loads templates from the file system. This loader
179
  can find templates in folders on the file system and is the preferred way to
180
  load them::
209
 
210
  $twig->render('@admin/index.html', array());
211
 
212
+ ``Twig_Loader_Filesystem`` support absolute and relative paths. Using relative
213
+ paths is preferred as it makes the cache keys independent of the project root
214
+ directory (for instance, it allows warming the cache from a build server where
215
+ the directory might be different from the one used on production servers)::
216
+
217
+ $loader = new Twig_Loader_Filesystem('templates', getcwd().'/..');
218
+
219
+ .. note::
220
+
221
+ When not passing the root path as a second argument, Twig uses ``getcwd()``
222
+ for relative paths.
223
+
224
  ``Twig_Loader_Array``
225
  .....................
226
 
287
  * @param string $name string The name of the template to load
288
  *
289
  * @return string The template source code
290
+ *
291
+ * @deprecated since 1.27 (to be removed in 2.0), implement Twig_SourceContextLoaderInterface
292
  */
293
  function getSource($name);
294
 
313
  The ``isFresh()`` method must return ``true`` if the current cached template
314
  is still fresh, given the last modification time, or ``false`` otherwise.
315
 
316
+ .. note::
317
+
318
+ As of Twig 1.27, you should also implement
319
+ ``Twig_SourceContextLoaderInterface`` to avoid deprecation notices.
320
+
321
  .. tip::
322
 
323
  As of Twig 1.11.0, you can also implement ``Twig_ExistsLoaderInterface``
library/twig/twig/doc/deprecated.rst CHANGED
@@ -20,6 +20,11 @@ Token Parsers
20
  * ``Twig_TokenParserBrokerInterface``
21
  * ``Twig_TokenParserBroker``
22
 
 
 
 
 
 
23
  Extensions
24
  ----------
25
 
@@ -37,6 +42,9 @@ Extensions
37
  deprecated. Implement ``Twig_Extension_GlobalsInterface`` to avoid
38
  deprecation notices.
39
 
 
 
 
40
  PEAR
41
  ----
42
 
@@ -109,6 +117,17 @@ Nodes
109
  * As of Twig 1.x, ``Node::toXml()`` is deprecated and will be removed in Twig
110
  2.0.
111
 
 
 
 
 
 
 
 
 
 
 
 
112
  Interfaces
113
  ----------
114
 
@@ -120,16 +139,30 @@ Interfaces
120
  * ``Twig_NodeInterface`` (use ``Twig_Node`` instead)
121
  * ``Twig_ParserInterface`` (use ``Twig_Parser`` instead)
122
  * ``Twig_ExistsLoaderInterface`` (merged with ``Twig_LoaderInterface``)
 
123
  * ``Twig_TemplateInterface`` (use ``Twig_Template`` instead, and use
124
  those constants Twig_Template::ANY_CALL, Twig_Template::ARRAY_CALL,
125
  Twig_Template::METHOD_CALL)
126
 
 
 
 
 
 
 
 
 
 
127
  Loaders
128
  -------
129
 
130
  * As of Twig 1.x, ``Twig_Loader_String`` is deprecated and will be removed in
131
  2.0. You can render a string via ``Twig_Environment::createTemplate()``.
132
 
 
 
 
 
133
  Node Visitors
134
  -------------
135
 
@@ -153,10 +186,25 @@ Globals
153
  Miscellaneous
154
  -------------
155
 
156
- * As of Twig 1.x, ``Twig_Environment::clearTemplateCache()``, ``Twig_Environment::writeCacheFile()``,
157
- ``Twig_Environment::clearCacheFiles()``, ``Twig_Environment::getCacheFilename()``, and
158
- ``Twig_Environment::getTemplateClassPrefix()`` are deprecated and will be removed in 2.0.
 
 
 
 
159
 
160
  * As of Twig 1.x, ``Twig_Template::getEnvironment()`` and
161
  ``Twig_TemplateInterface::getEnvironment()`` are deprecated and will be
162
  removed in 2.0.
 
 
 
 
 
 
 
 
 
 
 
20
  * ``Twig_TokenParserBrokerInterface``
21
  * ``Twig_TokenParserBroker``
22
 
23
+ * As of Twig 1.27, ``Twig_Parser::getFilename()`` is deprecated. From a token
24
+ parser, use ``$this->parser->getStream()->getSourceContext()->getPath()`` instead.
25
+
26
+ * As of Twig 1.27, ``Twig_Parser::getEnvironment()`` is deprecated.
27
+
28
  Extensions
29
  ----------
30
 
42
  deprecated. Implement ``Twig_Extension_GlobalsInterface`` to avoid
43
  deprecation notices.
44
 
45
+ * As of Twig 1.26, the ``Twig_ExtensionInterface::getName()`` method is
46
+ deprecated and it is not used internally anymore.
47
+
48
  PEAR
49
  ----
50
 
117
  * As of Twig 1.x, ``Node::toXml()`` is deprecated and will be removed in Twig
118
  2.0.
119
 
120
+ * As of Twig 1.26, ``Node::$nodes`` should only contains ``Twig_Node``
121
+ instances, storing a ``null`` value is deprecated and won't be possible in
122
+ Twig 2.x.
123
+
124
+ * As of Twig 1.27, the ``filename`` attribute on ``Twig_Node_Module`` is
125
+ deprecated. Use ``getName()`` instead.
126
+
127
+ * As of Twig 1.27, the ``Twig_Node::getFilename()/Twig_Node::getLine()``
128
+ methods are deprecated, use
129
+ ``Twig_Node::getTemplateName()/Twig_Node::getTemplateLine()`` instead.
130
+
131
  Interfaces
132
  ----------
133
 
139
  * ``Twig_NodeInterface`` (use ``Twig_Node`` instead)
140
  * ``Twig_ParserInterface`` (use ``Twig_Parser`` instead)
141
  * ``Twig_ExistsLoaderInterface`` (merged with ``Twig_LoaderInterface``)
142
+ * ``Twig_SourceContextLoaderInterface`` (merged with ``Twig_LoaderInterface``)
143
  * ``Twig_TemplateInterface`` (use ``Twig_Template`` instead, and use
144
  those constants Twig_Template::ANY_CALL, Twig_Template::ARRAY_CALL,
145
  Twig_Template::METHOD_CALL)
146
 
147
+ Compiler
148
+ --------
149
+
150
+ * As of Twig 1.26, the ``Twig_Compiler::getFilename()`` has been deprecated.
151
+ You should not use it anyway as its values is not reliable.
152
+
153
+ * As of Twig 1.27, the ``Twig_Compiler::addIndentation()`` has been deprecated.
154
+ Use ``Twig_Compiler::write('')`` instead.
155
+
156
  Loaders
157
  -------
158
 
159
  * As of Twig 1.x, ``Twig_Loader_String`` is deprecated and will be removed in
160
  2.0. You can render a string via ``Twig_Environment::createTemplate()``.
161
 
162
+ * As of Twig 1.27, ``Twig_LoaderInterface::getSource()`` is deprecated.
163
+ Implement ``Twig_SourceContextLoaderInterface`` instead and use
164
+ ``getSourceContext()``.
165
+
166
  Node Visitors
167
  -------------
168
 
186
  Miscellaneous
187
  -------------
188
 
189
+ * As of Twig 1.x, ``Twig_Environment::clearTemplateCache()``,
190
+ ``Twig_Environment::writeCacheFile()``,
191
+ ``Twig_Environment::clearCacheFiles()``,
192
+ ``Twig_Environment::getCacheFilename()``,
193
+ ``Twig_Environment::getTemplateClassPrefix()``,
194
+ ``Twig_Environment::getLexer()``, ``Twig_Environment::getParser()``, and
195
+ ``Twig_Environment::getCompiler()`` are deprecated and will be removed in 2.0.
196
 
197
  * As of Twig 1.x, ``Twig_Template::getEnvironment()`` and
198
  ``Twig_TemplateInterface::getEnvironment()`` are deprecated and will be
199
  removed in 2.0.
200
+
201
+ * As of Twig 1.27, ``Twig_Error::getTemplateFile()`` and
202
+ ``Twig_Error::setTemplateFile()`` are deprecated. Use
203
+ ``Twig_Error::getTemplateName()`` and ``Twig_Error::setTemplateName()``
204
+ instead.
205
+
206
+ * As of Twig 1.27, ``Twig_Template::getSource()`` is deprecated. Use
207
+ ``Twig_Template::getSourceContext()`` instead.
208
+
209
+ * As of Twig 1.27, ``Twig_Parser::addHandler()`` and
210
+ ``Twig_Parser::addNodeVisitor()`` are deprecated and will be removed in 2.0.
library/twig/twig/doc/filters/date.rst CHANGED
@@ -54,6 +54,9 @@ dates and the second one is the default format for date intervals:
54
  .. code-block:: php
55
 
56
  $twig = new Twig_Environment($loader);
 
 
 
57
  $twig->getExtension('core')->setDateFormat('d/m/Y', '%d days');
58
 
59
  Timezone
@@ -79,6 +82,9 @@ The default timezone can also be set globally by calling ``setTimezone()``:
79
  .. code-block:: php
80
 
81
  $twig = new Twig_Environment($loader);
 
 
 
82
  $twig->getExtension('core')->setTimezone('Europe/Paris');
83
 
84
  Arguments
54
  .. code-block:: php
55
 
56
  $twig = new Twig_Environment($loader);
57
+ $twig->getExtension('Twig_Extension_Core')->setDateFormat('d/m/Y', '%d days');
58
+
59
+ // before Twig 1.26
60
  $twig->getExtension('core')->setDateFormat('d/m/Y', '%d days');
61
 
62
  Timezone
82
  .. code-block:: php
83
 
84
  $twig = new Twig_Environment($loader);
85
+ $twig->getExtension('Twig_Extension_Core')->setTimezone('Europe/Paris');
86
+
87
+ // before Twig 1.26
88
  $twig->getExtension('core')->setTimezone('Europe/Paris');
89
 
90
  Arguments
library/twig/twig/doc/filters/escape.rst CHANGED
@@ -97,6 +97,9 @@ used in the ``escape`` call) and the second one must be a valid PHP callable:
97
  .. code-block:: php
98
 
99
  $twig = new Twig_Environment($loader);
 
 
 
100
  $twig->getExtension('core')->setEscaper('csv', 'csv_escaper');
101
 
102
  When called by Twig, the callable receives the Twig environment instance, the
97
  .. code-block:: php
98
 
99
  $twig = new Twig_Environment($loader);
100
+ $twig->getExtension('Twig_Extension_Core')->setEscaper('csv', 'csv_escaper');
101
+
102
+ // before Twig 1.26
103
  $twig->getExtension('core')->setEscaper('csv', 'csv_escaper');
104
 
105
  When called by Twig, the callable receives the Twig environment instance, the
library/twig/twig/doc/filters/number_format.rst CHANGED
@@ -30,6 +30,9 @@ These defaults can be easily changed through the core extension:
30
  .. code-block:: php
31
 
32
  $twig = new Twig_Environment($loader);
 
 
 
33
  $twig->getExtension('core')->setNumberFormat(3, '.', ',');
34
 
35
  The defaults set for ``number_format`` can be over-ridden upon each call using the
30
  .. code-block:: php
31
 
32
  $twig = new Twig_Environment($loader);
33
+ $twig->getExtension('Twig_Extension_Core')->setNumberFormat(3, '.', ',');
34
+
35
+ // before Twig 1.26
36
  $twig->getExtension('core')->setNumberFormat(3, '.', ',');
37
 
38
  The defaults set for ``number_format`` can be over-ridden upon each call using the
library/twig/twig/doc/functions/block.rst CHANGED
@@ -1,6 +1,12 @@
1
  ``block``
2
  =========
3
 
 
 
 
 
 
 
4
  When a template uses inheritance and if you want to print a block multiple
5
  times, use the ``block`` function:
6
 
@@ -12,4 +18,24 @@ times, use the ``block`` function:
12
 
13
  {% block body %}{% endblock %}
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  .. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>`
1
  ``block``
2
  =========
3
 
4
+ .. versionadded: 1.28
5
+ Using ``block`` with the ``defined`` test was added in Twig 1.28.
6
+
7
+ .. versionadded: 1.28
8
+ Support for the template argument was added in Twig 1.28.
9
+
10
  When a template uses inheritance and if you want to print a block multiple
11
  times, use the ``block`` function:
12
 
18
 
19
  {% block body %}{% endblock %}
20
 
21
+ The ``block`` function can also be used to display one block of another
22
+ template:
23
+
24
+ .. code-block:: jinja
25
+
26
+ {{ block("title", "common_blocks.twig") }}
27
+
28
+ Use the ``defined`` test to check if a block exists in the context of the
29
+ current template:
30
+
31
+ .. code-block:: jinja
32
+
33
+ {% if block("footer") is defined %}
34
+ ...
35
+ {% endif %}
36
+
37
+ {% if block("footer", "common_blocks.twig") is defined %}
38
+ ...
39
+ {% endif %}
40
+
41
  .. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>`
library/twig/twig/doc/functions/constant.rst CHANGED
@@ -4,6 +4,9 @@
4
  .. versionadded: 1.12.1
5
  constant now accepts object instances as the second argument.
6
 
 
 
 
7
  ``constant`` returns the constant value for a given string:
8
 
9
  .. code-block:: jinja
@@ -16,3 +19,11 @@ As of 1.12.1 you can read constants from object instances as well:
16
  .. code-block:: jinja
17
 
18
  {{ constant('RSS', date) }}
 
 
 
 
 
 
 
 
4
  .. versionadded: 1.12.1
5
  constant now accepts object instances as the second argument.
6
 
7
+ .. versionadded: 1.28
8
+ Using ``constant`` with the ``defined`` test was added in Twig 1.28.
9
+
10
  ``constant`` returns the constant value for a given string:
11
 
12
  .. code-block:: jinja
19
  .. code-block:: jinja
20
 
21
  {{ constant('RSS', date) }}
22
+
23
+ Use the ``defined`` test to check if a constant is defined:
24
+
25
+ .. code-block:: jinja
26
+
27
+ {% if constant('SOME_CONST') is defined %}
28
+ ...
29
+ {% endif %}
library/twig/twig/doc/functions/date.rst CHANGED
@@ -41,6 +41,9 @@ If no argument is passed, the function returns the current date:
41
  .. code-block:: php
42
 
43
  $twig = new Twig_Environment($loader);
 
 
 
44
  $twig->getExtension('core')->setTimezone('Europe/Paris');
45
 
46
  Arguments
41
  .. code-block:: php
42
 
43
  $twig = new Twig_Environment($loader);
44
+ $twig->getExtension('Twig_Extension_Core')->setTimezone('Europe/Paris');
45
+
46
+ // before Twig 1.26
47
  $twig->getExtension('core')->setTimezone('Europe/Paris');
48
 
49
  Arguments
library/twig/twig/doc/functions/include.rst CHANGED
@@ -37,14 +37,18 @@ You can disable access to the context by setting ``with_context`` to
37
  {# no variables will be accessible #}
38
  {{ include('template.html', with_context = false) }}
39
 
40
- And if the expression evaluates to a ``Twig_Template`` object, Twig will use it
41
- directly::
42
 
43
  // {{ include(template) }}
44
 
 
45
  $template = $twig->loadTemplate('some_template.twig');
46
 
47
- $twig->loadTemplate('template.twig')->display(array('template' => $template));
 
 
 
48
 
49
  When you set the ``ignore_missing`` flag, Twig will return an empty string if
50
  the template does not exist:
37
  {# no variables will be accessible #}
38
  {{ include('template.html', with_context = false) }}
39
 
40
+ And if the expression evaluates to a ``Twig_Template`` or a
41
+ ``Twig_TemplateWrapper`` instance, Twig will use it directly::
42
 
43
  // {{ include(template) }}
44
 
45
+ // deprecated as of Twig 1.28
46
  $template = $twig->loadTemplate('some_template.twig');
47
 
48
+ // as of Twig 1.28
49
+ $template = $twig->load('some_template.twig');
50
+
51
+ $twig->display('template.twig', array('template' => $template));
52
 
53
  When you set the ``ignore_missing`` flag, Twig will return an empty string if
54
  the template does not exist:
library/twig/twig/doc/internals.rst CHANGED
@@ -44,7 +44,11 @@ an instance of ``Twig_Token``, and the stream is an instance of
44
  You can manually convert a source code into a token stream by calling the
45
  ``tokenize()`` method of an environment::
46
 
47
- $stream = $twig->tokenize($source, $identifier);
 
 
 
 
48
 
49
  As the stream has a ``__toString()`` method, you can have a textual
50
  representation of it by echoing the object::
44
  You can manually convert a source code into a token stream by calling the
45
  ``tokenize()`` method of an environment::
46
 
47
+ $stream = $twig->tokenize(new Twig_Source($source, $identifier));
48
+
49
+ .. versionadded:: 1.27
50
+ ``Twig_Source`` was introduced in version 1.27, pass the source and the
51
+ identifier directly on previous versions.
52
 
53
  As the stream has a ``__toString()`` method, you can have a textual
54
  representation of it by echoing the object::
library/twig/twig/doc/recipes.rst CHANGED
@@ -306,7 +306,7 @@ saving it. If the template code is stored in a `$template` variable, here is
306
  how you can do it::
307
 
308
  try {
309
- $twig->parse($twig->tokenize($template));
310
 
311
  // the $template is valid
312
  } catch (Twig_Error_Syntax $e) {
@@ -318,7 +318,7 @@ If you iterate over a set of files, you can pass the filename to the
318
 
319
  foreach ($files as $file) {
320
  try {
321
- $twig->parse($twig->tokenize($template, $file));
322
 
323
  // the $template is valid
324
  } catch (Twig_Error_Syntax $e) {
@@ -326,6 +326,10 @@ If you iterate over a set of files, you can pass the filename to the
326
  }
327
  }
328
 
 
 
 
 
329
  .. note::
330
 
331
  This method won't catch any sandbox policy violations because the policy
@@ -413,7 +417,7 @@ We have created a simple ``templates`` table that hosts two templates:
413
 
414
  Now, let's define a loader able to use this database::
415
 
416
- class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
417
  {
418
  protected $dbh;
419
 
@@ -431,6 +435,16 @@ Now, let's define a loader able to use this database::
431
  return $source;
432
  }
433
 
 
 
 
 
 
 
 
 
 
 
434
  // Twig_ExistsLoaderInterface as of Twig 1.11
435
  public function exists($name)
436
  {
@@ -515,4 +529,40 @@ From PHP, it's also possible to load a template stored in a string via
515
 
516
  Never use the ``Twig_Loader_String`` loader, which has severe limitations.
517
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  .. _callback: http://www.php.net/manual/en/function.is-callable.php
306
  how you can do it::
307
 
308
  try {
309
+ $twig->parse($twig->tokenize(new Twig_Source($template)));
310
 
311
  // the $template is valid
312
  } catch (Twig_Error_Syntax $e) {
318
 
319
  foreach ($files as $file) {
320
  try {
321
+ $twig->parse($twig->tokenize(new Twig_Source($template, $file->getFilename(), $file)));
322
 
323
  // the $template is valid
324
  } catch (Twig_Error_Syntax $e) {
326
  }
327
  }
328
 
329
+ .. versionadded:: 1.27
330
+ ``Twig_Source`` was introduced in version 1.27, pass the source and the
331
+ identifier directly on previous versions.
332
+
333
  .. note::
334
 
335
  This method won't catch any sandbox policy violations because the policy
417
 
418
  Now, let's define a loader able to use this database::
419
 
420
+ class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
421
  {
422
  protected $dbh;
423
 
435
  return $source;
436
  }
437
 
438
+ // Twig_SourceContextLoaderInterface as of Twig 1.27
439
+ public function getSourceContext($name)
440
+ {
441
+ if (false === $source = $this->getValue('source', $name)) {
442
+ throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
443
+ }
444
+
445
+ return new Twig_Source($source, $name);
446
+ }
447
+
448
  // Twig_ExistsLoaderInterface as of Twig 1.11
449
  public function exists($name)
450
  {
529
 
530
  Never use the ``Twig_Loader_String`` loader, which has severe limitations.
531
 
532
+ Using Twig and AngularJS in the same Templates
533
+ ----------------------------------------------
534
+
535
+ Mixing different template syntaxes in the same file is not a recommended
536
+ practice as both AngularJS and Twig use the same delimiters in their syntax:
537
+ ``{{`` and ``}}``.
538
+
539
+ Still, if you want to use AngularJS and Twig in the same template, there are
540
+ two ways to make it work depending on the amount of AngularJS you need to
541
+ include in your templates:
542
+
543
+ * Escaping the AngularJS delimiters by wrapping AngularJS sections with the
544
+ ``{% verbatim %}`` tag or by escaping each delimiter via ``{{ '{{' }}`` and
545
+ ``{{ '}}' }}``;
546
+
547
+ * Changing the delimiters of one of the template engines (depending on which
548
+ engine you introduced last):
549
+
550
+ * For AngularJS, change the interpolation tags using the
551
+ ``interpolateProvider`` service, for instance at the module initialization
552
+ time:
553
+
554
+ ..code-block:: javascript
555
+
556
+ angular.module('myApp', []).config(function($interpolateProvider) {
557
+ $interpolateProvider.startSymbol('{[').endSymbol(']}');
558
+ });
559
+
560
+ * For Twig, change the delimiters via the ``tag_variable`` Lexer option:
561
+
562
+ ..code-block:: php
563
+
564
+ $env->setLexer(new Twig_Lexer($env, array(
565
+ 'tag_variable' => array('{[', ']}'),
566
+ )));
567
+
568
  .. _callback: http://www.php.net/manual/en/function.is-callable.php
library/twig/twig/doc/tags/autoescape.rst CHANGED
@@ -6,8 +6,6 @@ template to be escaped or not by using the ``autoescape`` tag:
6
 
7
  .. code-block:: jinja
8
 
9
- {# The following syntax works as of Twig 1.8 -- see the note below for previous versions #}
10
-
11
  {% autoescape %}
12
  Everything will be automatically escaped in this block
13
  using the HTML strategy
6
 
7
  .. code-block:: jinja
8
 
 
 
9
  {% autoescape %}
10
  Everything will be automatically escaped in this block
11
  using the HTML strategy
library/twig/twig/doc/tags/embed.rst CHANGED
@@ -170,9 +170,9 @@ The ``embed`` tag takes the exact same arguments as the ``include`` tag:
170
  .. warning::
171
 
172
  As embedded templates do not have "names", auto-escaping strategies based
173
- on the template "filename" won't work as expected if you change the
174
- context (for instance, if you embed a CSS/JavaScript template into an HTML
175
- one). In that case, explicitly set the default auto-escaping strategy with
176
- the ``autoescape`` tag.
177
 
178
  .. seealso:: :doc:`include<../tags/include>`
170
  .. warning::
171
 
172
  As embedded templates do not have "names", auto-escaping strategies based
173
+ on the template name won't work as expected if you change the context (for
174
+ instance, if you embed a CSS/JavaScript template into an HTML one). In that
175
+ case, explicitly set the default auto-escaping strategy with the
176
+ ``autoescape`` tag.
177
 
178
  .. seealso:: :doc:`include<../tags/include>`
library/twig/twig/doc/tags/extends.rst CHANGED
@@ -153,13 +153,17 @@ Twig supports dynamic inheritance by using a variable as the base template:
153
 
154
  {% extends some_var %}
155
 
156
- If the variable evaluates to a ``Twig_Template`` object, Twig will use it as
157
- the parent template::
158
 
159
  // {% extends layout %}
160
 
 
161
  $layout = $twig->loadTemplate('some_layout_template.twig');
162
 
 
 
 
163
  $twig->display('template.twig', array('layout' => $layout));
164
 
165
  .. versionadded:: 1.2
153
 
154
  {% extends some_var %}
155
 
156
+ If the variable evaluates to a ``Twig_Template`` or a ``Twig_TemplateWraper``
157
+ instance, Twig will use it as the parent template::
158
 
159
  // {% extends layout %}
160
 
161
+ // deprecated as of Twig 1.28
162
  $layout = $twig->loadTemplate('some_layout_template.twig');
163
 
164
+ // as of Twig 1.28
165
+ $layout = $twig->load('some_layout_template.twig');
166
+
167
  $twig->display('template.twig', array('layout' => $layout));
168
 
169
  .. versionadded:: 1.2
library/twig/twig/doc/tags/include.rst CHANGED
@@ -50,14 +50,18 @@ The template name can be any valid Twig expression:
50
  {% include some_var %}
51
  {% include ajax ? 'ajax.html' : 'not_ajax.html' %}
52
 
53
- And if the expression evaluates to a ``Twig_Template`` object, Twig will use it
54
- directly::
55
 
56
  // {% include template %}
57
 
 
58
  $template = $twig->loadTemplate('some_template.twig');
59
 
60
- $twig->loadTemplate('template.twig')->display(array('template' => $template));
 
 
 
61
 
62
  .. versionadded:: 1.2
63
  The ``ignore missing`` feature has been added in Twig 1.2.
50
  {% include some_var %}
51
  {% include ajax ? 'ajax.html' : 'not_ajax.html' %}
52
 
53
+ And if the expression evaluates to a ``Twig_Template`` or a
54
+ ``Twig_TemplateWrapper`` instance, Twig will use it directly::
55
 
56
  // {% include template %}
57
 
58
+ // deprecated as of Twig 1.28
59
  $template = $twig->loadTemplate('some_template.twig');
60
 
61
+ // as of Twig 1.28
62
+ $template = $twig->load('some_template.twig');
63
+
64
+ $twig->display('template.twig', array('template' => $template));
65
 
66
  .. versionadded:: 1.2
67
  The ``ignore missing`` feature has been added in Twig 1.2.
library/twig/twig/doc/tags/index.rst CHANGED
@@ -22,3 +22,4 @@ Tags
22
  spaceless
23
  use
24
  verbatim
 
22
  spaceless
23
  use
24
  verbatim
25
+ with
library/twig/twig/doc/tags/with.rst ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``with``
2
+ ========
3
+
4
+ .. versionadded:: 1.28
5
+ The ``with`` tag was added in Twig 1.28.
6
+
7
+ Use the ``with`` tag to create a new inner scope. Variables set within this
8
+ scope are not visible outside of the scope:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {% with %}
13
+ {% set foo = 42 %}
14
+ {{ foo }} foo is 42 here
15
+ {% endwith %}
16
+ foo is not visible here any longer
17
+
18
+ Instead of defining variables at the beginning of the scope, you can pass a
19
+ hash of variables you want to define in the ``with`` tag; the previous example
20
+ is equivalent to the following one:
21
+
22
+ .. code-block:: jinja
23
+
24
+ {% with { foo: 42 } %}
25
+ {{ foo }} foo is 42 here
26
+ {% endwith %}
27
+ foo is not visible here any longer
28
+
29
+ {# it works with any expression that resolves to a hash #}
30
+ {% set vars = { foo: 42 } %}
31
+ {% with vars %}
32
+ ...
33
+ {% endwith %}
34
+
35
+ By default, the inner scope has access to the outer scope context; you can
36
+ disable this behavior by appending the ``only`` keyword:
37
+
38
+ .. code-block:: jinja
39
+
40
+ {% set bar = 'bar' %}
41
+ {% with { foo: 42 } only %}
42
+ {# only foo is defined #}
43
+ {# bar is not defined #}
44
+ {% endwith %}
library/twig/twig/doc/templates.rst CHANGED
@@ -424,10 +424,8 @@ everything by default.
424
 
425
  Twig supports both, automatic escaping is enabled by default.
426
 
427
- .. note::
428
-
429
- Automatic escaping is only supported if the *escaper* extension has been
430
- enabled (which is the default).
431
 
432
  Working with Manual Escaping
433
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
424
 
425
  Twig supports both, automatic escaping is enabled by default.
426
 
427
+ The automatic escaping strategy can be configured via the
428
+ :ref:`autoescape<environment_options>` option and defaults to ``html``.
 
 
429
 
430
  Working with Manual Escaping
431
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library/twig/twig/doc/tests/empty.rst CHANGED
@@ -6,7 +6,6 @@ hash, exactly ``false``, or exactly ``null``:
6
 
7
  .. code-block:: jinja
8
 
9
- {# evaluates to true if the foo variable is null, false, an empty array, or the empty string #}
10
  {% if foo is empty %}
11
  ...
12
  {% endif %}
6
 
7
  .. code-block:: jinja
8
 
 
9
  {% if foo is empty %}
10
  ...
11
  {% endif %}
library/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.25.0"
19
 
20
  #include "php.h"
21
 
15
  #ifndef PHP_TWIG_H
16
  #define PHP_TWIG_H
17
 
18
+ #define PHP_TWIG_VERSION "1.28.2"
19
 
20
  #include "php.h"
21
 
library/twig/twig/ext/twig/twig.c CHANGED
@@ -144,7 +144,7 @@ static zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
144
 
145
  if (!retval) {
146
  if (!EG(exception)) {
147
- zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
148
  }
149
  return NULL;
150
  }
@@ -167,7 +167,7 @@ static int TWIG_ISSET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
167
 
168
  if (!retval) {
169
  if (!EG(exception)) {
170
- zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
171
  }
172
  return 0;
173
  }
@@ -811,15 +811,15 @@ PHP_FUNCTION(twig_template_get_attributes)
811
  }
812
  */
813
  if (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC)) {
814
- TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" in object with ArrayAccess of class \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
815
  } else if (Z_TYPE_P(object) == IS_OBJECT) {
816
- TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to access a key \"%s\" on an object of class \"%s\" that does not implement ArrayAccess interface", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
817
  } else if (Z_TYPE_P(object) == IS_ARRAY) {
818
  if (0 == zend_hash_num_elements(Z_ARRVAL_P(object))) {
819
- TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" does not exist as the array is empty", item);
820
  } else {
821
  char *array_keys = TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC);
822
- TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist", item, array_keys);
823
  efree(array_keys);
824
  }
825
  } else {
@@ -829,15 +829,15 @@ PHP_FUNCTION(twig_template_get_attributes)
829
  convert_to_string(object);
830
  TWIG_RUNTIME_ERROR(template TSRMLS_CC,
831
  (strcmp("array", type) == 0)
832
- ? "Impossible to access a key (\"%s\") on a %s variable"
833
- : "Impossible to access an attribute (\"%s\") on a %s variable",
834
  item, type_name);
835
  } else {
836
  convert_to_string(object);
837
  TWIG_RUNTIME_ERROR(template TSRMLS_CC,
838
  (strcmp("array", type) == 0)
839
- ? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")"
840
- : "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\")",
841
  item, type_name, Z_STRVAL_P(object));
842
  }
843
  zval_ptr_dtor(&object);
@@ -883,11 +883,11 @@ PHP_FUNCTION(twig_template_get_attributes)
883
  if (Z_TYPE_P(object) == IS_NULL) {
884
  convert_to_string_ex(&object);
885
 
886
- TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable", item, type_name);
887
  } else {
888
  convert_to_string_ex(&object);
889
 
890
- TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\")", item, type_name, Z_STRVAL_P(object));
891
  }
892
 
893
  zval_ptr_dtor(&object);
@@ -916,8 +916,8 @@ PHP_FUNCTION(twig_template_get_attributes)
916
  return true;
917
  }
918
 
919
- if ($this->env->hasExtension('sandbox')) {
920
- $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
921
  }
922
 
923
  return $object->$item;
@@ -935,8 +935,8 @@ PHP_FUNCTION(twig_template_get_attributes)
935
  efree(item);
936
  RETURN_TRUE;
937
  }
938
- if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
939
- TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC);
940
  }
941
  if (EG(exception)) {
942
  efree(item);
@@ -1020,7 +1020,7 @@ PHP_FUNCTION(twig_template_get_attributes)
1020
  return null;
1021
  }
1022
 
1023
- throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
1024
  }
1025
 
1026
  if ($isDefinedTest) {
@@ -1040,7 +1040,7 @@ PHP_FUNCTION(twig_template_get_attributes)
1040
  efree(item);
1041
  return;
1042
  }
1043
- TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Neither the property \"%s\" nor one of the methods \"%s()\", \"get%s()\"/\"is%s()\" or \"__call()\" exist and have public access in class \"%s\"", item, item, item, item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
1044
  efree(item);
1045
  return;
1046
  }
@@ -1052,14 +1052,14 @@ PHP_FUNCTION(twig_template_get_attributes)
1052
  RETURN_TRUE;
1053
  }
1054
  /*
1055
- if ($this->env->hasExtension('sandbox')) {
1056
- $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
1057
  }
1058
  */
1059
  MAKE_STD_ZVAL(zmethod);
1060
  ZVAL_STRING(zmethod, method, 1);
1061
- if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
1062
- TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, zmethod TSRMLS_CC);
1063
  }
1064
  zval_ptr_dtor(&zmethod);
1065
  if (EG(exception)) {
144
 
145
  if (!retval) {
146
  if (!EG(exception)) {
147
+ zend_error(E_ERROR, "Undefined offset for object of type %s used as array.", ce->name);
148
  }
149
  return NULL;
150
  }
167
 
168
  if (!retval) {
169
  if (!EG(exception)) {
170
+ zend_error(E_ERROR, "Undefined offset for object of type %s used as array.", ce->name);
171
  }
172
  return 0;
173
  }
811
  }
812
  */
813
  if (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC)) {
814
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" in object with ArrayAccess of class \"%s\" does not exist.", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
815
  } else if (Z_TYPE_P(object) == IS_OBJECT) {
816
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to access a key \"%s\" on an object of class \"%s\" that does not implement ArrayAccess interface.", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
817
  } else if (Z_TYPE_P(object) == IS_ARRAY) {
818
  if (0 == zend_hash_num_elements(Z_ARRVAL_P(object))) {
819
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" does not exist as the array is empty.", item);
820
  } else {
821
  char *array_keys = TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC);
822
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist.", item, array_keys);
823
  efree(array_keys);
824
  }
825
  } else {
829
  convert_to_string(object);
830
  TWIG_RUNTIME_ERROR(template TSRMLS_CC,
831
  (strcmp("array", type) == 0)
832
+ ? "Impossible to access a key (\"%s\") on a %s variable."
833
+ : "Impossible to access an attribute (\"%s\") on a %s variable.",
834
  item, type_name);
835
  } else {
836
  convert_to_string(object);
837
  TWIG_RUNTIME_ERROR(template TSRMLS_CC,
838
  (strcmp("array", type) == 0)
839
+ ? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")."
840
+ : "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\").",
841
  item, type_name, Z_STRVAL_P(object));
842
  }
843
  zval_ptr_dtor(&object);
883
  if (Z_TYPE_P(object) == IS_NULL) {
884
  convert_to_string_ex(&object);
885
 
886
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable.", item, type_name);
887
  } else {
888
  convert_to_string_ex(&object);
889
 
890
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\").", item, type_name, Z_STRVAL_P(object));
891
  }
892
 
893
  zval_ptr_dtor(&object);
916
  return true;
917
  }
918
 
919
+ if ($this->env->hasExtension('Twig_Extension_Sandbox')) {
920
+ $this->env->getExtension('Twig_Extension_Sandbox')->checkPropertyAllowed($object, $item);
921
  }
922
 
923
  return $object->$item;
935
  efree(item);
936
  RETURN_TRUE;
937
  }
938
+ if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "Twig_Extension_Sandbox" TSRMLS_CC)) {
939
+ TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "Twig_Extension_Sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC);
940
  }
941
  if (EG(exception)) {
942
  efree(item);
1020
  return null;
1021
  }
1022
 
1023
+ throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist.', $item, get_class($object)), -1, $this->getTemplateName());
1024
  }
1025
 
1026
  if ($isDefinedTest) {
1040
  efree(item);
1041
  return;
1042
  }
1043
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Neither the property \"%s\" nor one of the methods \"%s()\", \"get%s()\"/\"is%s()\" or \"__call()\" exist and have public access in class \"%s\".", item, item, item, item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
1044
  efree(item);
1045
  return;
1046
  }
1052
  RETURN_TRUE;
1053
  }
1054
  /*
1055
+ if ($this->env->hasExtension('Twig_Extension_Sandbox')) {
1056
+ $this->env->getExtension('Twig_Extension_Sandbox')->checkMethodAllowed($object, $method);
1057
  }
1058
  */
1059
  MAKE_STD_ZVAL(zmethod);
1060
  ZVAL_STRING(zmethod, method, 1);
1061
+ if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "Twig_Extension_Sandbox" TSRMLS_CC)) {
1062
+ TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "Twig_Extension_Sandbox" TSRMLS_CC), "checkMethodAllowed", object, zmethod TSRMLS_CC);
1063
  }
1064
  zval_ptr_dtor(&zmethod);
1065
  if (EG(exception)) {
library/twig/twig/lib/Twig/BaseNodeVisitor.php CHANGED
@@ -43,9 +43,6 @@ abstract class Twig_BaseNodeVisitor implements Twig_NodeVisitorInterface
43
  /**
44
  * Called before child nodes are visited.
45
  *
46
- * @param Twig_Node $node The node to visit
47
- * @param Twig_Environment $env The Twig environment instance
48
- *
49
  * @return Twig_Node The modified node
50
  */
51
  abstract protected function doEnterNode(Twig_Node $node, Twig_Environment $env);
@@ -53,9 +50,6 @@ abstract class Twig_BaseNodeVisitor implements Twig_NodeVisitorInterface
53
  /**
54
  * Called after child nodes are visited.
55
  *
56
- * @param Twig_Node $node The node to visit
57
- * @param Twig_Environment $env The Twig environment instance
58
- *
59
  * @return Twig_Node|false The modified node or false if the node must be removed
60
  */
61
  abstract protected function doLeaveNode(Twig_Node $node, Twig_Environment $env);
43
  /**
44
  * Called before child nodes are visited.
45
  *
 
 
 
46
  * @return Twig_Node The modified node
47
  */
48
  abstract protected function doEnterNode(Twig_Node $node, Twig_Environment $env);
50
  /**
51
  * Called after child nodes are visited.
52
  *
 
 
 
53
  * @return Twig_Node|false The modified node or false if the node must be removed
54
  */
55
  abstract protected function doLeaveNode(Twig_Node $node, Twig_Environment $env);
library/twig/twig/lib/Twig/Compiler.php CHANGED
@@ -26,11 +26,6 @@ class Twig_Compiler implements Twig_CompilerInterface
26
  protected $sourceLine;
27
  protected $filename;
28
 
29
- /**
30
- * Constructor.
31
- *
32
- * @param Twig_Environment $env The twig environment instance
33
- */
34
  public function __construct(Twig_Environment $env)
35
  {
36
  $this->env = $env;
@@ -49,7 +44,7 @@ class Twig_Compiler implements Twig_CompilerInterface
49
  /**
50
  * Returns the environment instance related to this compiler.
51
  *
52
- * @return Twig_Environment The environment instance
53
  */
54
  public function getEnvironment()
55
  {
@@ -72,7 +67,7 @@ class Twig_Compiler implements Twig_CompilerInterface
72
  * @param Twig_NodeInterface $node The node to compile
73
  * @param int $indentation The current indentation
74
  *
75
- * @return Twig_Compiler The current compiler instance
76
  */
77
  public function compile(Twig_NodeInterface $node, $indentation = 0)
78
  {
@@ -85,10 +80,8 @@ class Twig_Compiler implements Twig_CompilerInterface
85
  $this->indentation = $indentation;
86
 
87
  if ($node instanceof Twig_Node_Module) {
88
- $node->setFilename($node->getAttribute('filename'));
89
-
90
  // to be removed in 2.0
91
- $this->filename = $node->getAttribute('filename');
92
  }
93
 
94
  $node->compile($this);
@@ -99,7 +92,7 @@ class Twig_Compiler implements Twig_CompilerInterface
99
  public function subcompile(Twig_NodeInterface $node, $raw = true)
100
  {
101
  if (false === $raw) {
102
- $this->addIndentation();
103
  }
104
 
105
  $node->compile($this);
@@ -112,7 +105,7 @@ class Twig_Compiler implements Twig_CompilerInterface
112
  *
113
  * @param string $string The string
114
  *
115
- * @return Twig_Compiler The current compiler instance
116
  */
117
  public function raw($string)
118
  {
@@ -124,14 +117,13 @@ class Twig_Compiler implements Twig_CompilerInterface
124
  /**
125
  * Writes a string to the compiled code by adding indentation.
126
  *
127
- * @return Twig_Compiler The current compiler instance
128
  */
129
  public function write()
130
  {
131
  $strings = func_get_args();
132
  foreach ($strings as $string) {
133
- $this->addIndentation();
134
- $this->source .= $string;
135
  }
136
 
137
  return $this;
@@ -140,10 +132,14 @@ class Twig_Compiler implements Twig_CompilerInterface
140
  /**
141
  * Appends an indentation to the current PHP code after compilation.
142
  *
143
- * @return Twig_Compiler The current compiler instance
 
 
144
  */
145
  public function addIndentation()
146
  {
 
 
147
  $this->source .= str_repeat(' ', $this->indentation * 4);
148
 
149
  return $this;
@@ -154,7 +150,7 @@ class Twig_Compiler implements Twig_CompilerInterface
154
  *
155
  * @param string $value The string
156
  *
157
- * @return Twig_Compiler The current compiler instance
158
  */
159
  public function string($value)
160
  {
@@ -168,7 +164,7 @@ class Twig_Compiler implements Twig_CompilerInterface
168
  *
169
  * @param mixed $value The value to convert
170
  *
171
- * @return Twig_Compiler The current compiler instance
172
  */
173
  public function repr($value)
174
  {
@@ -209,14 +205,12 @@ class Twig_Compiler implements Twig_CompilerInterface
209
  /**
210
  * Adds debugging information.
211
  *
212
- * @param Twig_NodeInterface $node The related twig node
213
- *
214
- * @return Twig_Compiler The current compiler instance
215
  */
216
  public function addDebugInfo(Twig_NodeInterface $node)
217
  {
218
- if ($node->getLine() != $this->lastLine) {
219
- $this->write(sprintf("// line %d\n", $node->getLine()));
220
 
221
  // when mbstring.func_overload is set to 2
222
  // mb_substr_count() replaces substr_count()
@@ -228,9 +222,9 @@ class Twig_Compiler implements Twig_CompilerInterface
228
  $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
229
  }
230
  $this->sourceOffset = strlen($this->source);
231
- $this->debugInfo[$this->sourceLine] = $node->getLine();
232
 
233
- $this->lastLine = $node->getLine();
234
  }
235
 
236
  return $this;
@@ -248,7 +242,7 @@ class Twig_Compiler implements Twig_CompilerInterface
248
  *
249
  * @param int $step The number of indentation to add
250
  *
251
- * @return Twig_Compiler The current compiler instance
252
  */
253
  public function indent($step = 1)
254
  {
@@ -262,7 +256,7 @@ class Twig_Compiler implements Twig_CompilerInterface
262
  *
263
  * @param int $step The number of indentation to remove
264
  *
265
- * @return Twig_Compiler The current compiler instance
266
  *
267
  * @throws LogicException When trying to outdent too much so the indentation would become negative
268
  */
@@ -270,7 +264,7 @@ class Twig_Compiler implements Twig_CompilerInterface
270
  {
271
  // can't outdent by more steps than the current indentation level
272
  if ($this->indentation < $step) {
273
- throw new LogicException('Unable to call outdent() as the indentation would become negative');
274
  }
275
 
276
  $this->indentation -= $step;
26
  protected $sourceLine;
27
  protected $filename;
28
 
 
 
 
 
 
29
  public function __construct(Twig_Environment $env)
30
  {
31
  $this->env = $env;
44
  /**
45
  * Returns the environment instance related to this compiler.
46
  *
47
+ * @return Twig_Environment
48
  */
49
  public function getEnvironment()
50
  {
67
  * @param Twig_NodeInterface $node The node to compile
68
  * @param int $indentation The current indentation
69
  *
70
+ * @return $this
71
  */
72
  public function compile(Twig_NodeInterface $node, $indentation = 0)
73
  {
80
  $this->indentation = $indentation;
81
 
82
  if ($node instanceof Twig_Node_Module) {
 
 
83
  // to be removed in 2.0
84
+ $this->filename = $node->getTemplateName();
85
  }
86
 
87
  $node->compile($this);
92
  public function subcompile(Twig_NodeInterface $node, $raw = true)
93
  {
94
  if (false === $raw) {
95
+ $this->source .= str_repeat(' ', $this->indentation * 4);
96
  }
97
 
98
  $node->compile($this);
105
  *
106
  * @param string $string The string
107
  *
108
+ * @return $this
109
  */
110
  public function raw($string)
111
  {
117
  /**
118
  * Writes a string to the compiled code by adding indentation.
119
  *
120
+ * @return $this
121
  */
122
  public function write()
123
  {
124
  $strings = func_get_args();
125
  foreach ($strings as $string) {
126
+ $this->source .= str_repeat(' ', $this->indentation * 4).$string;
 
127
  }
128
 
129
  return $this;
132
  /**
133
  * Appends an indentation to the current PHP code after compilation.
134
  *
135
+ * @return $this
136
+ *
137
+ * @deprecated since 1.27 (to be removed in 2.0).
138
  */
139
  public function addIndentation()
140
  {
141
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use write(\'\') instead.', E_USER_DEPRECATED);
142
+
143
  $this->source .= str_repeat(' ', $this->indentation * 4);
144
 
145
  return $this;
150
  *
151
  * @param string $value The string
152
  *
153
+ * @return $this
154
  */
155
  public function string($value)
156
  {
164
  *
165
  * @param mixed $value The value to convert
166
  *
167
+ * @return $this
168
  */
169
  public function repr($value)
170
  {
205
  /**
206
  * Adds debugging information.
207
  *
208
+ * @return $this
 
 
209
  */
210
  public function addDebugInfo(Twig_NodeInterface $node)
211
  {
212
+ if ($node->getTemplateLine() != $this->lastLine) {
213
+ $this->write(sprintf("// line %d\n", $node->getTemplateLine()));
214
 
215
  // when mbstring.func_overload is set to 2
216
  // mb_substr_count() replaces substr_count()
222
  $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
223
  }
224
  $this->sourceOffset = strlen($this->source);
225
+ $this->debugInfo[$this->sourceLine] = $node->getTemplateLine();
226
 
227
+ $this->lastLine = $node->getTemplateLine();
228
  }
229
 
230
  return $this;
242
  *
243
  * @param int $step The number of indentation to add
244
  *
245
+ * @return $this
246
  */
247
  public function indent($step = 1)
248
  {
256
  *
257
  * @param int $step The number of indentation to remove
258
  *
259
+ * @return $this
260
  *
261
  * @throws LogicException When trying to outdent too much so the indentation would become negative
262
  */
264
  {
265
  // can't outdent by more steps than the current indentation level
266
  if ($this->indentation < $step) {
267
+ throw new LogicException('Unable to call outdent() as the indentation would become negative.');
268
  }
269
 
270
  $this->indentation -= $step;
library/twig/twig/lib/Twig/CompilerInterface.php CHANGED
@@ -21,9 +21,7 @@ interface Twig_CompilerInterface
21
  /**
22
  * Compiles a node.
23
  *
24
- * @param Twig_NodeInterface $node The node to compile
25
- *
26
- * @return Twig_CompilerInterface The current compiler instance
27
  */
28
  public function compile(Twig_NodeInterface $node);
29
 
21
  /**
22
  * Compiles a node.
23
  *
24
+ * @return $this
 
 
25
  */
26
  public function compile(Twig_NodeInterface $node);
27
 
library/twig/twig/lib/Twig/Environment.php CHANGED
@@ -16,7 +16,12 @@
16
  */
17
  class Twig_Environment
18
  {
19
- const VERSION = '1.25.0';
 
 
 
 
 
20
 
21
  protected $charset;
22
  protected $loader;
@@ -49,6 +54,10 @@ class Twig_Environment
49
  private $bcWriteCacheFile = false;
50
  private $bcGetCacheFilename = false;
51
  private $lastModifiedExtension = 0;
 
 
 
 
52
 
53
  /**
54
  * Constructor.
@@ -78,14 +87,14 @@ class Twig_Environment
78
  * * false: disable auto-escaping
79
  * * true: equivalent to html
80
  * * html, js: set the autoescaping to one of the supported strategies
81
- * * filename: set the autoescaping strategy based on the template filename extension
82
- * * PHP callback: a PHP callback that returns an escaping strategy based on the template "filename"
83
  *
84
  * * optimizations: A flag that indicates which optimizations to apply
85
  * (default to -1 which means that all optimizations are enabled;
86
  * set it to 0 to disable).
87
  *
88
- * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
89
  * @param array $options An array of options
90
  */
91
  public function __construct(Twig_LoaderInterface $loader = null, $options = array())
@@ -155,6 +164,7 @@ class Twig_Environment
155
  public function setBaseTemplateClass($class)
156
  {
157
  $this->baseTemplateClass = $class;
 
158
  }
159
 
160
  /**
@@ -163,6 +173,7 @@ class Twig_Environment
163
  public function enableDebug()
164
  {
165
  $this->debug = true;
 
166
  }
167
 
168
  /**
@@ -171,6 +182,7 @@ class Twig_Environment
171
  public function disableDebug()
172
  {
173
  $this->debug = false;
 
174
  }
175
 
176
  /**
@@ -215,6 +227,7 @@ class Twig_Environment
215
  public function enableStrictVariables()
216
  {
217
  $this->strictVariables = true;
 
218
  }
219
 
220
  /**
@@ -223,6 +236,7 @@ class Twig_Environment
223
  public function disableStrictVariables()
224
  {
225
  $this->strictVariables = false;
 
226
  }
227
 
228
  /**
@@ -300,7 +314,10 @@ class Twig_Environment
300
  *
301
  * * The cache key for the given template;
302
  * * The currently enabled extensions;
303
- * * Whether the Twig C extension is available or not.
 
 
 
304
  *
305
  * @param string $name The name for which to calculate the template class name
306
  * @param int|null $index The index if it is an embedded template
@@ -309,10 +326,7 @@ class Twig_Environment
309
  */
310
  public function getTemplateClass($name, $index = null)
311
  {
312
- $key = $this->getLoader()->getCacheKey($name);
313
- $key .= json_encode(array_keys($this->extensions));
314
- $key .= function_exists('twig_template_get_attributes');
315
- $key .= ':'.PHP_MAJOR_VERSION.':'.PHP_MINOR_VERSION;
316
 
317
  return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '_'.$index);
318
  }
@@ -364,7 +378,30 @@ class Twig_Environment
364
  }
365
 
366
  /**
367
- * Loads a template by name.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  *
369
  * @param string $name The template name
370
  * @param int $index The index if it is an embedded template
@@ -373,6 +410,8 @@ class Twig_Environment
373
  *
374
  * @throws Twig_Error_Loader When the template cannot be found
375
  * @throws Twig_Error_Syntax When an error occurred during compilation
 
 
376
  */
377
  public function loadTemplate($name, $index = null)
378
  {
@@ -394,14 +433,30 @@ class Twig_Environment
394
  }
395
 
396
  if (!class_exists($cls, false)) {
397
- $content = $this->compileSource($this->getLoader()->getSource($name), $name);
 
 
 
 
 
 
 
 
398
  if ($this->bcWriteCacheFile) {
399
  $this->writeCacheFile($key, $content);
400
  } else {
401
  $this->cache->write($key, $content);
 
402
  }
403
 
404
- eval('?>'.$content);
 
 
 
 
 
 
 
405
  }
406
  }
407
 
@@ -546,7 +601,7 @@ class Twig_Environment
546
  /**
547
  * Gets the Lexer instance.
548
  *
549
- * @return Twig_LexerInterface A Twig_LexerInterface instance
550
  *
551
  * @deprecated since 1.25 (to be removed in 2.0)
552
  */
@@ -561,11 +616,6 @@ class Twig_Environment
561
  return $this->lexer;
562
  }
563
 
564
- /**
565
- * Sets the Lexer instance.
566
- *
567
- * @param Twig_LexerInterface $lexer A Twig_LexerInterface instance
568
- */
569
  public function setLexer(Twig_LexerInterface $lexer)
570
  {
571
  $this->lexer = $lexer;
@@ -574,26 +624,31 @@ class Twig_Environment
574
  /**
575
  * Tokenizes a source code.
576
  *
577
- * @param string $source The template source code
578
- * @param string $name The template name
579
  *
580
- * @return Twig_TokenStream A Twig_TokenStream instance
581
  *
582
  * @throws Twig_Error_Syntax When the code is syntactically wrong
583
  */
584
  public function tokenize($source, $name = null)
585
  {
 
 
 
 
 
586
  if (null === $this->lexer) {
587
  $this->lexer = new Twig_Lexer($this);
588
  }
589
 
590
- return $this->lexer->tokenize($source, $name);
591
  }
592
 
593
  /**
594
  * Gets the Parser instance.
595
  *
596
- * @return Twig_ParserInterface A Twig_ParserInterface instance
597
  *
598
  * @deprecated since 1.25 (to be removed in 2.0)
599
  */
@@ -608,11 +663,6 @@ class Twig_Environment
608
  return $this->parser;
609
  }
610
 
611
- /**
612
- * Sets the Parser instance.
613
- *
614
- * @param Twig_ParserInterface $parser A Twig_ParserInterface instance
615
- */
616
  public function setParser(Twig_ParserInterface $parser)
617
  {
618
  $this->parser = $parser;
@@ -621,9 +671,7 @@ class Twig_Environment
621
  /**
622
  * Converts a token stream to a node tree.
623
  *
624
- * @param Twig_TokenStream $stream A token stream instance
625
- *
626
- * @return Twig_Node_Module A node tree
627
  *
628
  * @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
629
  */
@@ -639,7 +687,7 @@ class Twig_Environment
639
  /**
640
  * Gets the Compiler instance.
641
  *
642
- * @return Twig_CompilerInterface A Twig_CompilerInterface instance
643
  *
644
  * @deprecated since 1.25 (to be removed in 2.0)
645
  */
@@ -654,11 +702,6 @@ class Twig_Environment
654
  return $this->compiler;
655
  }
656
 
657
- /**
658
- * Sets the Compiler instance.
659
- *
660
- * @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance
661
- */
662
  public function setCompiler(Twig_CompilerInterface $compiler)
663
  {
664
  $this->compiler = $compiler;
@@ -667,8 +710,6 @@ class Twig_Environment
667
  /**
668
  * Compiles a node and returns the PHP code.
669
  *
670
- * @param Twig_NodeInterface $node A Twig_NodeInterface instance
671
- *
672
  * @return string The compiled PHP source code
673
  */
674
  public function compile(Twig_NodeInterface $node)
@@ -683,8 +724,8 @@ class Twig_Environment
683
  /**
684
  * Compiles a template source code.
685
  *
686
- * @param string $source The template source code
687
- * @param string $name The template name
688
  *
689
  * @return string The compiled PHP source code
690
  *
@@ -692,30 +733,34 @@ class Twig_Environment
692
  */
693
  public function compileSource($source, $name = null)
694
  {
 
 
 
 
 
695
  try {
696
- return $this->compile($this->parse($this->tokenize($source, $name)));
697
  } catch (Twig_Error $e) {
698
- $e->setTemplateFile($name);
699
  throw $e;
700
  } catch (Exception $e) {
701
- throw new Twig_Error_Syntax(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
702
  }
703
  }
704
 
705
- /**
706
- * Sets the Loader instance.
707
- *
708
- * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
709
- */
710
  public function setLoader(Twig_LoaderInterface $loader)
711
  {
 
 
 
 
712
  $this->loader = $loader;
713
  }
714
 
715
  /**
716
  * Gets the Loader instance.
717
  *
718
- * @return Twig_LoaderInterface A Twig_LoaderInterface instance
719
  */
720
  public function getLoader()
721
  {
@@ -771,51 +816,100 @@ class Twig_Environment
771
  /**
772
  * Returns true if the given extension is registered.
773
  *
774
- * @param string $name The extension name
775
  *
776
  * @return bool Whether the extension is registered or not
777
  */
778
- public function hasExtension($name)
779
  {
780
- return isset($this->extensions[$name]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781
  }
782
 
783
  /**
784
- * Gets an extension by name.
785
  *
786
- * @param string $name The extension name
787
  *
788
- * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance
789
  */
790
- public function getExtension($name)
791
  {
792
- if (!isset($this->extensions[$name])) {
793
- throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name));
 
 
 
 
 
 
 
 
 
 
794
  }
795
 
796
- return $this->extensions[$name];
797
  }
798
 
799
  /**
800
- * Registers an extension.
801
  *
802
- * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance
 
 
 
 
803
  */
804
- public function addExtension(Twig_ExtensionInterface $extension)
805
  {
806
- $name = $extension->getName();
 
 
 
 
 
 
 
 
807
 
 
 
 
 
 
808
  if ($this->extensionInitialized) {
809
- throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $name));
810
  }
811
 
812
- if (isset($this->extensions[$name])) {
813
- @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);
 
 
 
 
814
  }
815
 
816
  $this->lastModifiedExtension = 0;
817
-
818
- $this->extensions[$name] = $extension;
 
819
  }
820
 
821
  /**
@@ -835,7 +929,17 @@ class Twig_Environment
835
  throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
836
  }
837
 
838
- unset($this->extensions[$name]);
 
 
 
 
 
 
 
 
 
 
839
  }
840
 
841
  /**
@@ -853,18 +957,13 @@ class Twig_Environment
853
  /**
854
  * Returns all registered extensions.
855
  *
856
- * @return array An array of extensions
857
  */
858
  public function getExtensions()
859
  {
860
  return $this->extensions;
861
  }
862
 
863
- /**
864
- * Registers a Token Parser.
865
- *
866
- * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
867
- */
868
  public function addTokenParser(Twig_TokenParserInterface $parser)
869
  {
870
  if ($this->extensionInitialized) {
@@ -877,7 +976,7 @@ class Twig_Environment
877
  /**
878
  * Gets the registered Token Parsers.
879
  *
880
- * @return Twig_TokenParserBrokerInterface A broker containing token parsers
881
  *
882
  * @internal
883
  */
@@ -895,7 +994,7 @@ class Twig_Environment
895
  *
896
  * Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes.
897
  *
898
- * @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances
899
  *
900
  * @internal
901
  */
@@ -911,11 +1010,6 @@ class Twig_Environment
911
  return $tags;
912
  }
913
 
914
- /**
915
- * Registers a Node Visitor.
916
- *
917
- * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
918
- */
919
  public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
920
  {
921
  if ($this->extensionInitialized) {
@@ -928,7 +1022,7 @@ class Twig_Environment
928
  /**
929
  * Gets the registered Node Visitors.
930
  *
931
- * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
932
  *
933
  * @internal
934
  */
@@ -945,12 +1039,12 @@ class Twig_Environment
945
  * Registers a Filter.
946
  *
947
  * @param string|Twig_SimpleFilter $name The filter name or a Twig_SimpleFilter instance
948
- * @param Twig_FilterInterface|Twig_SimpleFilter $filter A Twig_FilterInterface instance or a Twig_SimpleFilter instance
949
  */
950
  public function addFilter($name, $filter = null)
951
  {
952
  if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) {
953
- throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter');
954
  }
955
 
956
  if ($name instanceof Twig_SimpleFilter) {
@@ -1021,7 +1115,7 @@ class Twig_Environment
1021
  *
1022
  * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback.
1023
  *
1024
- * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances
1025
  *
1026
  * @see registerUndefinedFilterCallback
1027
  *
@@ -1045,7 +1139,7 @@ class Twig_Environment
1045
  public function addTest($name, $test = null)
1046
  {
1047
  if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) {
1048
- throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest');
1049
  }
1050
 
1051
  if ($name instanceof Twig_SimpleTest) {
@@ -1065,7 +1159,7 @@ class Twig_Environment
1065
  /**
1066
  * Gets the registered Tests.
1067
  *
1068
- * @return Twig_TestInterface[] An array of Twig_TestInterface instances
1069
  *
1070
  * @internal
1071
  */
@@ -1104,12 +1198,12 @@ class Twig_Environment
1104
  * Registers a Function.
1105
  *
1106
  * @param string|Twig_SimpleFunction $name The function name or a Twig_SimpleFunction instance
1107
- * @param Twig_FunctionInterface|Twig_SimpleFunction $function A Twig_FunctionInterface instance or a Twig_SimpleFunction instance
1108
  */
1109
  public function addFunction($name, $function = null)
1110
  {
1111
  if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) {
1112
- throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction');
1113
  }
1114
 
1115
  if ($name instanceof Twig_SimpleFunction) {
@@ -1180,7 +1274,7 @@ class Twig_Environment
1180
  *
1181
  * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
1182
  *
1183
- * @return Twig_FunctionInterface[] An array of Twig_FunctionInterface instances
1184
  *
1185
  * @see registerUndefinedFunctionCallback
1186
  *
@@ -1407,7 +1501,7 @@ class Twig_Environment
1407
 
1408
  $this->parsers->addTokenParserBroker($parser);
1409
  } else {
1410
- throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
1411
  }
1412
  }
1413
 
@@ -1434,4 +1528,21 @@ class Twig_Environment
1434
  {
1435
  $this->cache->write($file, $content);
1436
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1437
  }
16
  */
17
  class Twig_Environment
18
  {
19
+ const VERSION = '1.28.2';
20
+ const VERSION_ID = 12802;
21
+ const MAJOR_VERSION = 1;
22
+ const MINOR_VERSION = 28;
23
+ const RELEASE_VERSION = 2;
24
+ const EXTRA_VERSION = '';
25
 
26
  protected $charset;
27
  protected $loader;
54
  private $bcWriteCacheFile = false;
55
  private $bcGetCacheFilename = false;
56
  private $lastModifiedExtension = 0;
57
+ private $extensionsByClass = array();
58
+ private $runtimeLoaders = array();
59
+ private $runtimes = array();
60
+ private $optionsHash;
61
 
62
  /**
63
  * Constructor.
87
  * * false: disable auto-escaping
88
  * * true: equivalent to html
89
  * * html, js: set the autoescaping to one of the supported strategies
90
+ * * name: set the autoescaping strategy based on the template name extension
91
+ * * PHP callback: a PHP callback that returns an escaping strategy based on the template "name"
92
  *
93
  * * optimizations: A flag that indicates which optimizations to apply
94
  * (default to -1 which means that all optimizations are enabled;
95
  * set it to 0 to disable).
96
  *
97
+ * @param Twig_LoaderInterface $loader
98
  * @param array $options An array of options
99
  */
100
  public function __construct(Twig_LoaderInterface $loader = null, $options = array())
164
  public function setBaseTemplateClass($class)
165
  {
166
  $this->baseTemplateClass = $class;
167
+ $this->updateOptionsHash();
168
  }
169
 
170
  /**
173
  public function enableDebug()
174
  {
175
  $this->debug = true;
176
+ $this->updateOptionsHash();
177
  }
178
 
179
  /**
182
  public function disableDebug()
183
  {
184
  $this->debug = false;
185
+ $this->updateOptionsHash();
186
  }
187
 
188
  /**
227
  public function enableStrictVariables()
228
  {
229
  $this->strictVariables = true;
230
+ $this->updateOptionsHash();
231
  }
232
 
233
  /**
236
  public function disableStrictVariables()
237
  {
238
  $this->strictVariables = false;
239
+ $this->updateOptionsHash();
240
  }
241
 
242
  /**
314
  *
315
  * * The cache key for the given template;
316
  * * The currently enabled extensions;
317
+ * * Whether the Twig C extension is available or not;
318
+ * * PHP version;
319
+ * * Twig version;
320
+ * * Options with what environment was created.
321
  *
322
  * @param string $name The name for which to calculate the template class name
323
  * @param int|null $index The index if it is an embedded template
326
  */
327
  public function getTemplateClass($name, $index = null)
328
  {
329
+ $key = $this->getLoader()->getCacheKey($name).$this->optionsHash;
 
 
 
330
 
331
  return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '_'.$index);
332
  }
378
  }
379
 
380
  /**
381
+ * Loads a template.
382
+ *
383
+ * @param string|Twig_TemplateWrapper|Twig_Template $name The template name
384
+ *
385
+ * @return Twig_TemplateWrapper
386
+ */
387
+ public function load($name)
388
+ {
389
+ if ($name instanceof Twig_TemplateWrapper) {
390
+ return $name;
391
+ }
392
+
393
+ if ($name instanceof Twig_Template) {
394
+ return new Twig_TemplateWrapper($this, $name);
395
+ }
396
+
397
+ return new Twig_TemplateWrapper($this, $this->loadTemplate($name));
398
+ }
399
+
400
+ /**
401
+ * Loads a template internal representation.
402
+ *
403
+ * This method is for internal use only and should never be called
404
+ * directly.
405
  *
406
  * @param string $name The template name
407
  * @param int $index The index if it is an embedded template
410
  *
411
  * @throws Twig_Error_Loader When the template cannot be found
412
  * @throws Twig_Error_Syntax When an error occurred during compilation
413
+ *
414
+ * @internal
415
  */
416
  public function loadTemplate($name, $index = null)
417
  {
433
  }
434
 
435
  if (!class_exists($cls, false)) {
436
+ $loader = $this->getLoader();
437
+ if (!$loader instanceof Twig_SourceContextLoaderInterface) {
438
+ $source = new Twig_Source($loader->getSource($name), $name);
439
+ } else {
440
+ $source = $loader->getSourceContext($name);
441
+ }
442
+
443
+ $content = $this->compileSource($source);
444
+
445
  if ($this->bcWriteCacheFile) {
446
  $this->writeCacheFile($key, $content);
447
  } else {
448
  $this->cache->write($key, $content);
449
+ $this->cache->load($key);
450
  }
451
 
452
+ if (!class_exists($cls, false)) {
453
+ /* Last line of defense if either $this->bcWriteCacheFile was used,
454
+ * $this->cache is implemented as a no-op or we have a race condition
455
+ * where the cache was cleared between the above calls to write to and load from
456
+ * the cache.
457
+ */
458
+ eval('?>'.$content);
459
+ }
460
  }
461
  }
462
 
601
  /**
602
  * Gets the Lexer instance.
603
  *
604
+ * @return Twig_LexerInterface
605
  *
606
  * @deprecated since 1.25 (to be removed in 2.0)
607
  */
616
  return $this->lexer;
617
  }
618
 
 
 
 
 
 
619
  public function setLexer(Twig_LexerInterface $lexer)
620
  {
621
  $this->lexer = $lexer;
624
  /**
625
  * Tokenizes a source code.
626
  *
627
+ * @param string|Twig_Source $source The template source code
628
+ * @param string $name The template name (deprecated)
629
  *
630
+ * @return Twig_TokenStream
631
  *
632
  * @throws Twig_Error_Syntax When the code is syntactically wrong
633
  */
634
  public function tokenize($source, $name = null)
635
  {
636
+ if (!$source instanceof Twig_Source) {
637
+ @trigger_error(sprintf('Passing a string as the $source argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
638
+ $source = new Twig_Source($source, $name);
639
+ }
640
+
641
  if (null === $this->lexer) {
642
  $this->lexer = new Twig_Lexer($this);
643
  }
644
 
645
+ return $this->lexer->tokenize($source);
646
  }
647
 
648
  /**
649
  * Gets the Parser instance.
650
  *
651
+ * @return Twig_ParserInterface
652
  *
653
  * @deprecated since 1.25 (to be removed in 2.0)
654
  */
663
  return $this->parser;
664
  }
665
 
 
 
 
 
 
666
  public function setParser(Twig_ParserInterface $parser)
667
  {
668
  $this->parser = $parser;
671
  /**
672
  * Converts a token stream to a node tree.
673
  *
674
+ * @return Twig_Node_Module
 
 
675
  *
676
  * @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
677
  */
687
  /**
688
  * Gets the Compiler instance.
689
  *
690
+ * @return Twig_CompilerInterface
691
  *
692
  * @deprecated since 1.25 (to be removed in 2.0)
693
  */
702
  return $this->compiler;
703
  }
704
 
 
 
 
 
 
705
  public function setCompiler(Twig_CompilerInterface $compiler)
706
  {
707
  $this->compiler = $compiler;
710
  /**
711
  * Compiles a node and returns the PHP code.
712
  *
 
 
713
  * @return string The compiled PHP source code
714
  */
715
  public function compile(Twig_NodeInterface $node)
724
  /**
725
  * Compiles a template source code.
726
  *
727
+ * @param string|Twig_Source $source The template source code
728
+ * @param string $name The template name (deprecated)
729
  *
730
  * @return string The compiled PHP source code
731
  *
733
  */
734
  public function compileSource($source, $name = null)
735
  {
736
+ if (!$source instanceof Twig_Source) {
737
+ @trigger_error(sprintf('Passing a string as the $source argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
738
+ $source = new Twig_Source($source, $name);
739
+ }
740
+
741
  try {
742
+ return $this->compile($this->parse($this->tokenize($source)));
743
  } catch (Twig_Error $e) {
744
+ $e->setTemplateName($source->getName());
745
  throw $e;
746
  } catch (Exception $e) {
747
+ throw new Twig_Error_Syntax(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source->getName(), $e);
748
  }
749
  }
750
 
 
 
 
 
 
751
  public function setLoader(Twig_LoaderInterface $loader)
752
  {
753
+ if (!$loader instanceof Twig_SourceContextLoaderInterface && 0 !== strpos(get_class($loader), 'Mock_Twig_LoaderInterface')) {
754
+ @trigger_error(sprintf('Twig loader "%s" should implement Twig_SourceContextLoaderInterface since version 1.27.', get_class($loader)), E_USER_DEPRECATED);
755
+ }
756
+
757
  $this->loader = $loader;
758
  }
759
 
760
  /**
761
  * Gets the Loader instance.
762
  *
763
+ * @return Twig_LoaderInterface
764
  */
765
  public function getLoader()
766
  {
816
  /**
817
  * Returns true if the given extension is registered.
818
  *
819
+ * @param string $class The extension class name
820
  *
821
  * @return bool Whether the extension is registered or not
822
  */
823
+ public function hasExtension($class)
824
  {
825
+ $class = ltrim($class, '\\');
826
+ if (isset($this->extensions[$class])) {
827
+ if ($class !== get_class($this->extensions[$class])) {
828
+ @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.', $class), E_USER_DEPRECATED);
829
+ }
830
+
831
+ return true;
832
+ }
833
+
834
+ return isset($this->extensionsByClass[ltrim($class, '\\')]);
835
+ }
836
+
837
+ /**
838
+ * Adds a runtime loader.
839
+ */
840
+ public function addRuntimeLoader(Twig_RuntimeLoaderInterface $loader)
841
+ {
842
+ $this->runtimeLoaders[] = $loader;
843
  }
844
 
845
  /**
846
+ * Gets an extension by class name.
847
  *
848
+ * @param string $class The extension class name
849
  *
850
+ * @return Twig_ExtensionInterface
851
  */
852
+ public function getExtension($class)
853
  {
854
+ $class = ltrim($class, '\\');
855
+
856
+ if (isset($this->extensions[$class])) {
857
+ if ($class !== get_class($this->extensions[$class])) {
858
+ @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.', $class), E_USER_DEPRECATED);
859
+ }
860
+
861
+ return $this->extensions[$class];
862
+ }
863
+
864
+ if (!isset($this->extensionsByClass[$class])) {
865
+ throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $class));
866
  }
867
 
868
+ return $this->extensionsByClass[$class];
869
  }
870
 
871
  /**
872
+ * Returns the runtime implementation of a Twig element (filter/function/test).
873
  *
874
+ * @param string $class A runtime class name
875
+ *
876
+ * @return object The runtime implementation
877
+ *
878
+ * @throws Twig_Error_Runtime When the template cannot be found
879
  */
880
+ public function getRuntime($class)
881
  {
882
+ if (isset($this->runtimes[$class])) {
883
+ return $this->runtimes[$class];
884
+ }
885
+
886
+ foreach ($this->runtimeLoaders as $loader) {
887
+ if (null !== $runtime = $loader->load($class)) {
888
+ return $this->runtimes[$class] = $runtime;
889
+ }
890
+ }
891
 
892
+ throw new Twig_Error_Runtime(sprintf('Unable to load the "%s" runtime.', $class));
893
+ }
894
+
895
+ public function addExtension(Twig_ExtensionInterface $extension)
896
+ {
897
  if ($this->extensionInitialized) {
898
+ throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $extension->getName()));
899
  }
900
 
901
+ $class = get_class($extension);
902
+ if ($class !== $extension->getName()) {
903
+ if (isset($this->extensions[$extension->getName()])) {
904
+ unset($this->extensions[$extension->getName()], $this->extensionsByClass[$class]);
905
+ @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.', $extension->getName()), E_USER_DEPRECATED);
906
+ }
907
  }
908
 
909
  $this->lastModifiedExtension = 0;
910
+ $this->extensionsByClass[$class] = $extension;
911
+ $this->extensions[$extension->getName()] = $extension;
912
+ $this->updateOptionsHash();
913
  }
914
 
915
  /**
929
  throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
930
  }
931
 
932
+ $class = ltrim($name, '\\');
933
+ if (isset($this->extensions[$class])) {
934
+ if ($class !== get_class($this->extensions[$class])) {
935
+ @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.', $class), E_USER_DEPRECATED);
936
+ }
937
+
938
+ unset($this->extensions[$class]);
939
+ }
940
+
941
+ unset($this->extensions[$class]);
942
+ $this->updateOptionsHash();
943
  }
944
 
945
  /**
957
  /**
958
  * Returns all registered extensions.
959
  *
960
+ * @return Twig_ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on)
961
  */
962
  public function getExtensions()
963
  {
964
  return $this->extensions;
965
  }
966
 
 
 
 
 
 
967
  public function addTokenParser(Twig_TokenParserInterface $parser)
968
  {
969
  if ($this->extensionInitialized) {
976
  /**
977
  * Gets the registered Token Parsers.
978
  *
979
+ * @return Twig_TokenParserBrokerInterface
980
  *
981
  * @internal
982
  */
994
  *
995
  * Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes.
996
  *
997
+ * @return Twig_TokenParserInterface[]
998
  *
999
  * @internal
1000
  */
1010
  return $tags;
1011
  }
1012
 
 
 
 
 
 
1013
  public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
1014
  {
1015
  if ($this->extensionInitialized) {
1022
  /**
1023
  * Gets the registered Node Visitors.
1024
  *
1025
+ * @return Twig_NodeVisitorInterface[]
1026
  *
1027
  * @internal
1028
  */
1039
  * Registers a Filter.
1040
  *
1041
  * @param string|Twig_SimpleFilter $name The filter name or a Twig_SimpleFilter instance
1042
+ * @param Twig_FilterInterface|Twig_SimpleFilter $filter
1043
  */
1044
  public function addFilter($name, $filter = null)
1045
  {
1046
  if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) {
1047
+ throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter.');
1048
  }
1049
 
1050
  if ($name instanceof Twig_SimpleFilter) {
1115
  *
1116
  * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback.
1117
  *
1118
+ * @return Twig_FilterInterface[]
1119
  *
1120
  * @see registerUndefinedFilterCallback
1121
  *
1139
  public function addTest($name, $test = null)
1140
  {
1141
  if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) {
1142
+ throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest.');
1143
  }
1144
 
1145
  if ($name instanceof Twig_SimpleTest) {
1159
  /**
1160
  * Gets the registered Tests.
1161
  *
1162
+ * @return Twig_TestInterface[]
1163
  *
1164
  * @internal
1165
  */
1198
  * Registers a Function.
1199
  *
1200
  * @param string|Twig_SimpleFunction $name The function name or a Twig_SimpleFunction instance
1201
+ * @param Twig_FunctionInterface|Twig_SimpleFunction $function
1202
  */
1203
  public function addFunction($name, $function = null)
1204
  {
1205
  if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) {
1206
+ throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction.');
1207
  }
1208
 
1209
  if ($name instanceof Twig_SimpleFunction) {
1274
  *
1275
  * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
1276
  *
1277
+ * @return Twig_FunctionInterface[]
1278
  *
1279
  * @see registerUndefinedFunctionCallback
1280
  *
1501
 
1502
  $this->parsers->addTokenParserBroker($parser);
1503
  } else {
1504
+ throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances.');
1505
  }
1506
  }
1507
 
1528
  {
1529
  $this->cache->write($file, $content);
1530
  }
1531
+
1532
+ private function updateOptionsHash()
1533
+ {
1534
+ $hashParts = array_merge(
1535
+ array_keys($this->extensions),
1536
+ array(
1537
+ (int) function_exists('twig_template_get_attributes'),
1538
+ PHP_MAJOR_VERSION,
1539
+ PHP_MINOR_VERSION,
1540
+ self::VERSION,
1541
+ (int) $this->debug,
1542
+ $this->baseTemplateClass,
1543
+ (int) $this->strictVariables,
1544
+ )
1545
+ );
1546
+ $this->optionsHash = implode(':', $hashParts);
1547
+ }
1548
  }
library/twig/twig/lib/Twig/Error.php CHANGED
@@ -25,8 +25,8 @@
25
  * and line number) yourself by passing them to the constructor. If some or all
26
  * these information are not available from where you throw the exception, then
27
  * this class will guess them automatically (when the line number is set to -1
28
- * and/or the filename is set to null). As this is a costly operation, this
29
- * can be disabled by passing false for both the filename and the line number
30
  * when creating a new instance of this class.
31
  *
32
  * @author Fabien Potencier <fabien@symfony.com>
@@ -34,6 +34,7 @@
34
  class Twig_Error extends Exception
35
  {
36
  protected $lineno;
 
37
  protected $filename;
38
  protected $rawMessage;
39
  protected $previous;
@@ -41,21 +42,21 @@ class Twig_Error extends Exception
41
  /**
42
  * Constructor.
43
  *
44
- * Set both the line number and the filename to false to
45
  * disable automatic guessing of the original template name
46
  * and line number.
47
  *
48
  * Set the line number to -1 to enable its automatic guessing.
49
- * Set the filename to null to enable its automatic guessing.
50
  *
51
  * By default, automatic guessing is enabled.
52
  *
53
  * @param string $message The error message
54
  * @param int $lineno The template line where the error occurred
55
- * @param string $filename The template file name where the error occurred
56
  * @param Exception $previous The previous exception
57
  */
58
- public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
59
  {
60
  if (PHP_VERSION_ID < 50300) {
61
  $this->previous = $previous;
@@ -65,9 +66,9 @@ class Twig_Error extends Exception
65
  }
66
 
67
  $this->lineno = $lineno;
68
- $this->filename = $filename;
69
 
70
- if (-1 === $this->lineno || null === $this->filename) {
71
  $this->guessTemplateInfo();
72
  }
73
 
@@ -87,23 +88,53 @@ class Twig_Error extends Exception
87
  }
88
 
89
  /**
90
- * Gets the filename where the error occurred.
91
  *
92
- * @return string The filename
 
 
93
  */
94
  public function getTemplateFile()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  {
96
  return $this->filename;
97
  }
98
 
99
  /**
100
- * Sets the filename where the error occurred.
101
  *
102
- * @param string $filename The filename
103
  */
104
- public function setTemplateFile($filename)
105
  {
106
- $this->filename = $filename;
107
 
108
  $this->updateRepr();
109
  }
@@ -182,11 +213,11 @@ class Twig_Error extends Exception
182
 
183
  if ($this->filename) {
184
  if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) {
185
- $filename = sprintf('"%s"', $this->filename);
186
  } else {
187
- $filename = json_encode($this->filename);
188
  }
189
- $this->message .= sprintf(' in %s', $filename);
190
  }
191
 
192
  if ($this->lineno && $this->lineno >= 0) {
@@ -227,7 +258,7 @@ class Twig_Error extends Exception
227
  }
228
  }
229
 
230
- // update template filename
231
  if (null !== $template && null === $this->filename) {
232
  $this->filename = $template->getTemplateName();
233
  }
25
  * and line number) yourself by passing them to the constructor. If some or all
26
  * these information are not available from where you throw the exception, then
27
  * this class will guess them automatically (when the line number is set to -1
28
+ * and/or the name is set to null). As this is a costly operation, this
29
+ * can be disabled by passing false for both the name and the line number
30
  * when creating a new instance of this class.
31
  *
32
  * @author Fabien Potencier <fabien@symfony.com>
34
  class Twig_Error extends Exception
35
  {
36
  protected $lineno;
37
+ // to be renamed to name in 2.0
38
  protected $filename;
39
  protected $rawMessage;
40
  protected $previous;
42
  /**
43
  * Constructor.
44
  *
45
+ * Set both the line number and the name to false to
46
  * disable automatic guessing of the original template name
47
  * and line number.
48
  *
49
  * Set the line number to -1 to enable its automatic guessing.
50
+ * Set the name to null to enable its automatic guessing.
51
  *
52
  * By default, automatic guessing is enabled.
53
  *
54
  * @param string $message The error message
55
  * @param int $lineno The template line where the error occurred
56
+ * @param string $name The template logical name where the error occurred
57
  * @param Exception $previous The previous exception
58
  */
59
+ public function __construct($message, $lineno = -1, $name = null, Exception $previous = null)
60
  {
61
  if (PHP_VERSION_ID < 50300) {
62
  $this->previous = $previous;
66
  }
67
 
68
  $this->lineno = $lineno;
69
+ $this->filename = $name;
70
 
71
+ if (-1 === $lineno || null === $name) {
72
  $this->guessTemplateInfo();
73
  }
74
 
88
  }
89
 
90
  /**
91
+ * Gets the logical name where the error occurred.
92
  *
93
+ * @return string The name
94
+ *
95
+ * @deprecated since 1.27 (to be removed in 2.0). Use getTemplateName() instead.
96
  */
97
  public function getTemplateFile()
98
+ {
99
+ @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateName() instead.', __METHOD__), E_USER_DEPRECATED);
100
+
101
+ return $this->filename;
102
+ }
103
+
104
+ /**
105
+ * Sets the logical name where the error occurred.
106
+ *
107
+ * @param string $name The name
108
+ *
109
+ * @deprecated since 1.27 (to be removed in 2.0). Use setTemplateName() instead.
110
+ */
111
+ public function setTemplateFile($name)
112
+ {
113
+ @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use setTemplateName() instead.', __METHOD__), E_USER_DEPRECATED);
114
+
115
+ $this->filename = $name;
116
+
117
+ $this->updateRepr();
118
+ }
119
+
120
+ /**
121
+ * Gets the logical name where the error occurred.
122
+ *
123
+ * @return string The name
124
+ */
125
+ public function getTemplateName()
126
  {
127
  return $this->filename;
128
  }
129
 
130
  /**
131
+ * Sets the logical name where the error occurred.
132
  *
133
+ * @param string $name The name
134
  */
135
+ public function setTemplateName($name)
136
  {
137
+ $this->filename = $name;
138
 
139
  $this->updateRepr();
140
  }
213
 
214
  if ($this->filename) {
215
  if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) {
216
+ $name = sprintf('"%s"', $this->filename);
217
  } else {
218
+ $name = json_encode($this->filename);
219
  }
220
+ $this->message .= sprintf(' in %s', $name);
221
  }
222
 
223
  if ($this->lineno && $this->lineno >= 0) {
258
  }
259
  }
260
 
261
+ // update template name
262
  if (null !== $template && null === $this->filename) {
263
  $this->filename = $template->getTemplateName();
264
  }
library/twig/twig/lib/Twig/ExpressionParser.php CHANGED
@@ -19,6 +19,8 @@
19
  * @see http://en.wikipedia.org/wiki/Operator-precedence_parser
20
  *
21
  * @author Fabien Potencier <fabien@symfony.com>
 
 
22
  */
23
  class Twig_ExpressionParser
24
  {
@@ -29,11 +31,23 @@ class Twig_ExpressionParser
29
  protected $unaryOperators;
30
  protected $binaryOperators;
31
 
32
- public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators)
 
 
33
  {
34
  $this->parser = $parser;
35
- $this->unaryOperators = $unaryOperators;
36
- $this->binaryOperators = $binaryOperators;
 
 
 
 
 
 
 
 
 
 
37
  }
38
 
39
  public function parseExpression($precedence = 0)
@@ -44,7 +58,11 @@ class Twig_ExpressionParser
44
  $op = $this->binaryOperators[$token->getValue()];
45
  $this->parser->getStream()->next();
46
 
47
- if (isset($op['callable'])) {
 
 
 
 
48
  $expr = call_user_func($op['callable'], $this->parser, $expr);
49
  } else {
50
  $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
@@ -171,7 +189,7 @@ class Twig_ExpressionParser
171
  $negClass = 'Twig_Node_Expression_Unary_Neg';
172
  $posClass = 'Twig_Node_Expression_Unary_Pos';
173
  if (!(in_array($ref->getName(), array($negClass, $posClass)) || $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass))) {
174
- throw new Twig_Error_Syntax(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getFilename());
175
  }
176
 
177
  $this->parser->getStream()->next();
@@ -187,7 +205,7 @@ class Twig_ExpressionParser
187
  } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
188
  $node = $this->parseHashExpression();
189
  } else {
190
- throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
191
  }
192
  }
193
 
@@ -216,7 +234,7 @@ class Twig_ExpressionParser
216
 
217
  $expr = array_shift($nodes);
218
  foreach ($nodes as $node) {
219
- $expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getLine());
220
  }
221
 
222
  return $expr;
@@ -278,7 +296,7 @@ class Twig_ExpressionParser
278
  } else {
279
  $current = $stream->getCurrent();
280
 
281
- throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
282
  }
283
 
284
  $stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
@@ -317,20 +335,25 @@ class Twig_ExpressionParser
317
  case 'parent':
318
  $this->parseArguments();
319
  if (!count($this->parser->getBlockStack())) {
320
- throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden.', $line, $this->parser->getFilename());
321
  }
322
 
323
  if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
324
- throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getFilename());
325
  }
326
 
327
  return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
328
  case 'block':
329
- return new Twig_Node_Expression_BlockReference($this->parseArguments()->getNode(0), false, $line);
 
 
 
 
 
330
  case 'attribute':
331
  $args = $this->parseArguments();
332
  if (count($args) < 2) {
333
- throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getFilename());
334
  }
335
 
336
  return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : null, Twig_Template::ANY_CALL, $line);
@@ -379,18 +402,18 @@ class Twig_ExpressionParser
379
  }
380
  }
381
  } else {
382
- throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename());
383
  }
384
 
385
  if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
386
  if (!$arg instanceof Twig_Node_Expression_Constant) {
387
- throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename());
388
  }
389
 
390
  $name = $arg->getAttribute('value');
391
 
392
  if ($this->parser->isReservedMacroName($name)) {
393
- throw new Twig_Error_Syntax(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $this->parser->getFilename());
394
  }
395
 
396
  $node = new Twig_Node_Expression_MethodCall($node, 'get'.$name, $arguments, $lineno);
@@ -500,7 +523,7 @@ class Twig_ExpressionParser
500
  $name = null;
501
  if ($namedArguments && $token = $stream->nextIf(Twig_Token::OPERATOR_TYPE, '=')) {
502
  if (!$value instanceof Twig_Node_Expression_Name) {
503
- throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given.', get_class($value)), $token->getLine(), $this->parser->getFilename());
504
  }
505
  $name = $value->getAttribute('name');
506
 
@@ -508,7 +531,7 @@ class Twig_ExpressionParser
508
  $value = $this->parsePrimaryExpression();
509
 
510
  if (!$this->checkConstantExpression($value)) {
511
- throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $this->parser->getFilename());
512
  }
513
  } else {
514
  $value = $this->parseExpression();
@@ -536,16 +559,17 @@ class Twig_ExpressionParser
536
 
537
  public function parseAssignmentExpression()
538
  {
 
539
  $targets = array();
540
  while (true) {
541
- $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to');
542
  $value = $token->getValue();
543
  if (in_array(strtolower($value), array('true', 'false', 'none', 'null'))) {
544
- throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $value), $token->getLine(), $this->parser->getFilename());
545
  }
546
  $targets[] = new Twig_Node_Expression_AssignName($value, $token->getLine());
547
 
548
- if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
549
  break;
550
  }
551
  }
@@ -566,13 +590,79 @@ class Twig_ExpressionParser
566
  return new Twig_Node($targets);
567
  }
568
 
569
- protected function getFunctionNodeClass($name, $line)
570
  {
571
- $env = $this->parser->getEnvironment();
 
 
 
 
 
 
 
 
 
 
 
 
572
 
573
- if (false === $function = $env->getFunction($name)) {
574
- $e = new Twig_Error_Syntax(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getFilename());
575
- $e->addSuggestions($name, array_keys($env->getFunctions()));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
 
577
  throw $e;
578
  }
@@ -585,7 +675,7 @@ class Twig_ExpressionParser
585
  if ($function->getAlternative()) {
586
  $message .= sprintf('. Use "%s" instead', $function->getAlternative());
587
  }
588
- $message .= sprintf(' in %s at line %d.', $this->parser->getFilename(), $line);
589
 
590
  @trigger_error($message, E_USER_DEPRECATED);
591
  }
@@ -599,11 +689,9 @@ class Twig_ExpressionParser
599
 
600
  protected function getFilterNodeClass($name, $line)
601
  {
602
- $env = $this->parser->getEnvironment();
603
-
604
- if (false === $filter = $env->getFilter($name)) {
605
- $e = new Twig_Error_Syntax(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getFilename());
606
- $e->addSuggestions($name, array_keys($env->getFilters()));
607
 
608
  throw $e;
609
  }
@@ -616,7 +704,7 @@ class Twig_ExpressionParser
616
  if ($filter->getAlternative()) {
617
  $message .= sprintf('. Use "%s" instead', $filter->getAlternative());
618
  }
619
- $message .= sprintf(' in %s at line %d.', $this->parser->getFilename(), $line);
620
 
621
  @trigger_error($message, E_USER_DEPRECATED);
622
  }
19
  * @see http://en.wikipedia.org/wiki/Operator-precedence_parser
20
  *
21
  * @author Fabien Potencier <fabien@symfony.com>
22
+ *
23
+ * @internal
24
  */
25
  class Twig_ExpressionParser
26
  {
31
  protected $unaryOperators;
32
  protected $binaryOperators;
33
 
34
+ private $env;
35
+
36
+ public function __construct(Twig_Parser $parser, Twig_Environment $env = null)
37
  {
38
  $this->parser = $parser;
39
+
40
+ if ($env instanceof Twig_Environment) {
41
+ $this->env = $env;
42
+ $this->unaryOperators = $env->getUnaryOperators();
43
+ $this->binaryOperators = $env->getBinaryOperators();
44
+ } else {
45
+ @trigger_error('Passing the operators as constructor arguments to '.__METHOD__.' is deprecated since version 1.27. Pass the environment instead.', E_USER_DEPRECATED);
46
+
47
+ $this->env = $parser->getEnvironment();
48
+ $this->unaryOperators = func_get_arg(1);
49
+ $this->binaryOperators = func_get_arg(2);
50
+ }
51
  }
52
 
53
  public function parseExpression($precedence = 0)
58
  $op = $this->binaryOperators[$token->getValue()];
59
  $this->parser->getStream()->next();
60
 
61
+ if ('is not' === $token->getValue()) {
62
+ $expr = $this->parseNotTestExpression($expr);
63
+ } elseif ('is' === $token->getValue()) {
64
+ $expr = $this->parseTestExpression($expr);
65
+ } elseif (isset($op['callable'])) {
66
  $expr = call_user_func($op['callable'], $this->parser, $expr);
67
  } else {
68
  $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
189
  $negClass = 'Twig_Node_Expression_Unary_Neg';
190
  $posClass = 'Twig_Node_Expression_Unary_Pos';
191
  if (!(in_array($ref->getName(), array($negClass, $posClass)) || $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass))) {
192
+ throw new Twig_Error_Syntax(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()->getName());
193
  }
194
 
195
  $this->parser->getStream()->next();
205
  } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
206
  $node = $this->parseHashExpression();
207
  } else {
208
+ throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()->getName());
209
  }
210
  }
211
 
234
 
235
  $expr = array_shift($nodes);
236
  foreach ($nodes as $node) {
237
+ $expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getTemplateLine());
238
  }
239
 
240
  return $expr;
296
  } else {
297
  $current = $stream->getCurrent();
298
 
299
+ throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext()->getName());
300
  }
301
 
302
  $stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
335
  case 'parent':
336
  $this->parseArguments();
337
  if (!count($this->parser->getBlockStack())) {
338
+ throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden.', $line, $this->parser->getStream()->getSourceContext()->getName());
339
  }
340
 
341
  if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
342
+ throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getStream()->getSourceContext()->getName());
343
  }
344
 
345
  return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
346
  case 'block':
347
+ $args = $this->parseArguments();
348
+ if (count($args) < 1) {
349
+ throw new Twig_Error_Syntax('The "block" function takes one argument (the block name).', $line, $this->parser->getStream()->getSourceContext()->getName());
350
+ }
351
+
352
+ return new Twig_Node_Expression_BlockReference($args->getNode(0), count($args) > 1 ? $args->getNode(1) : null, $line);
353
  case 'attribute':
354
  $args = $this->parseArguments();
355
  if (count($args) < 2) {
356
+ throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getStream()->getSourceContext()->getName());
357
  }
358
 
359
  return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : null, Twig_Template::ANY_CALL, $line);
402
  }
403
  }
404
  } else {
405
+ throw new Twig_Error_Syntax('Expected name or number.', $lineno, $stream->getSourceContext()->getName());
406
  }
407
 
408
  if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
409
  if (!$arg instanceof Twig_Node_Expression_Constant) {
410
+ throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $stream->getSourceContext()->getName());
411
  }
412
 
413
  $name = $arg->getAttribute('value');
414
 
415
  if ($this->parser->isReservedMacroName($name)) {
416
+ throw new Twig_Error_Syntax(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getSourceContext()->getName());
417
  }
418
 
419
  $node = new Twig_Node_Expression_MethodCall($node, 'get'.$name, $arguments, $lineno);
523
  $name = null;
524
  if ($namedArguments && $token = $stream->nextIf(Twig_Token::OPERATOR_TYPE, '=')) {
525
  if (!$value instanceof Twig_Node_Expression_Name) {
526
+ throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given.', get_class($value)), $token->getLine(), $stream->getSourceContext()->getName());
527
  }
528
  $name = $value->getAttribute('name');
529
 
531
  $value = $this->parsePrimaryExpression();
532
 
533
  if (!$this->checkConstantExpression($value)) {
534
+ throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $stream->getSourceContext()->getName());
535
  }
536
  } else {
537
  $value = $this->parseExpression();
559
 
560
  public function parseAssignmentExpression()
561
  {
562
+ $stream = $this->parser->getStream();
563
  $targets = array();
564
  while (true) {
565
+ $token = $stream->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to');
566
  $value = $token->getValue();
567
  if (in_array(strtolower($value), array('true', 'false', 'none', 'null'))) {
568
+ throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext()->getName());
569
  }
570
  $targets[] = new Twig_Node_Expression_AssignName($value, $token->getLine());
571
 
572
+ if (!$stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
573
  break;
574
  }
575
  }
590
  return new Twig_Node($targets);
591
  }
592
 
593
+ private function parseNotTestExpression(Twig_NodeInterface $node)
594
  {
595
+ return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine());
596
+ }
597
+
598
+ private function parseTestExpression(Twig_NodeInterface $node)
599
+ {
600
+ $stream = $this->parser->getStream();
601
+ list($name, $test) = $this->getTest($node->getTemplateLine());
602
+
603
+ $class = $this->getTestNodeClass($test);
604
+ $arguments = null;
605
+ if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
606
+ $arguments = $this->parser->getExpressionParser()->parseArguments(true);
607
+ }
608
 
609
+ return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine());
610
+ }
611
+
612
+ private function getTest($line)
613
+ {
614
+ $stream = $this->parser->getStream();
615
+ $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
616
+
617
+ if ($test = $this->env->getTest($name)) {
618
+ return array($name, $test);
619
+ }
620
+
621
+ if ($stream->test(Twig_Token::NAME_TYPE)) {
622
+ // try 2-words tests
623
+ $name = $name.' '.$this->parser->getCurrentToken()->getValue();
624
+
625
+ if ($test = $this->env->getTest($name)) {
626
+ $stream->next();
627
+
628
+ return array($name, $test);
629
+ }
630
+ }
631
+
632
+ $e = new Twig_Error_Syntax(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext()->getName());
633
+ $e->addSuggestions($name, array_keys($this->env->getTests()));
634
+
635
+ throw $e;
636
+ }
637
+
638
+ private function getTestNodeClass($test)
639
+ {
640
+ if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) {
641
+ $stream = $this->parser->getStream();
642
+ $message = sprintf('Twig Test "%s" is deprecated', $test->getName());
643
+ if (!is_bool($test->getDeprecatedVersion())) {
644
+ $message .= sprintf(' since version %s', $test->getDeprecatedVersion());
645
+ }
646
+ if ($test->getAlternative()) {
647
+ $message .= sprintf('. Use "%s" instead', $test->getAlternative());
648
+ }
649
+ $message .= sprintf(' in %s at line %d.', $stream->getSourceContext()->getName(), $stream->getCurrent()->getLine());
650
+
651
+ @trigger_error($message, E_USER_DEPRECATED);
652
+ }
653
+
654
+ if ($test instanceof Twig_SimpleTest) {
655
+ return $test->getNodeClass();
656
+ }
657
+
658
+ return $test instanceof Twig_Test_Node ? $test->getClass() : 'Twig_Node_Expression_Test';
659
+ }
660
+
661
+ protected function getFunctionNodeClass($name, $line)
662
+ {
663
+ if (false === $function = $this->env->getFunction($name)) {
664
+ $e = new Twig_Error_Syntax(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext()->getName());
665
+ $e->addSuggestions($name, array_keys($this->env->getFunctions()));
666
 
667
  throw $e;
668
  }
675
  if ($function->getAlternative()) {
676
  $message .= sprintf('. Use "%s" instead', $function->getAlternative());
677
  }
678
+ $message .= sprintf(' in %s at line %d.', $this->parser->getStream()->getSourceContext()->getName(), $line);
679
 
680
  @trigger_error($message, E_USER_DEPRECATED);
681
  }
689
 
690
  protected function getFilterNodeClass($name, $line)
691
  {
692
+ if (false === $filter = $this->env->getFilter($name)) {
693
+ $e = new Twig_Error_Syntax(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext()->getName());
694
+ $e->addSuggestions($name, array_keys($this->env->getFilters()));
 
 
695
 
696
  throw $e;
697
  }
704
  if ($filter->getAlternative()) {
705
  $message .= sprintf('. Use "%s" instead', $filter->getAlternative());
706
  }
707
+ $message .= sprintf(' in %s at line %d.', $this->parser->getStream()->getSourceContext()->getName(), $line);
708
 
709
  @trigger_error($message, E_USER_DEPRECATED);
710
  }
library/twig/twig/lib/Twig/Extension.php CHANGED
@@ -76,4 +76,14 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
76
  {
77
  return array();
78
  }
 
 
 
 
 
 
 
 
 
 
79
  }
76
  {
77
  return array();
78
  }
79
+
80
+ /**
81
+ * {@inheritdoc}
82
+ *
83
+ * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
84
+ */
85
+ public function getName()
86
+ {
87
+ return get_class($this);
88
+ }
89
  }
library/twig/twig/lib/Twig/Extension/Core.php CHANGED
@@ -132,6 +132,7 @@ class Twig_Extension_Core extends Twig_Extension
132
  new Twig_TokenParser_Flush(),
133
  new Twig_TokenParser_Do(),
134
  new Twig_TokenParser_Embed(),
 
135
  );
136
  }
137
 
@@ -258,82 +259,14 @@ class Twig_Extension_Core extends Twig_Extension
258
  '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
259
  '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
260
  '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
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
  }
268
 
269
- public function parseNotTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
270
- {
271
- return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine());
272
- }
273
-
274
- public function parseTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
275
- {
276
- $stream = $parser->getStream();
277
- list($name, $test) = $this->getTest($parser, $node->getLine());
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
- }
287
- $message .= sprintf(' in %s at line %d.', $stream->getFilename(), $stream->getCurrent()->getLine());
288
-
289
- @trigger_error($message, E_USER_DEPRECATED);
290
- }
291
-
292
- $class = $this->getTestNodeClass($parser, $test);
293
- $arguments = null;
294
- if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
295
- $arguments = $parser->getExpressionParser()->parseArguments(true);
296
- }
297
-
298
- return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine());
299
- }
300
-
301
- protected function getTest(Twig_Parser $parser, $line)
302
- {
303
- $stream = $parser->getStream();
304
- $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
305
- $env = $parser->getEnvironment();
306
-
307
- if ($test = $env->getTest($name)) {
308
- return array($name, $test);
309
- }
310
-
311
- if ($stream->test(Twig_Token::NAME_TYPE)) {
312
- // try 2-words tests
313
- $name = $name.' '.$parser->getCurrentToken()->getValue();
314
-
315
- if ($test = $env->getTest($name)) {
316
- $parser->getStream()->next();
317
-
318
- return array($name, $test);
319
- }
320
- }
321
-
322
- $e = new Twig_Error_Syntax(sprintf('Unknown "%s" test.', $name), $line, $parser->getFilename());
323
- $e->addSuggestions($name, array_keys($env->getTests()));
324
-
325
- throw $e;
326
- }
327
-
328
- protected function getTestNodeClass(Twig_Parser $parser, $test)
329
- {
330
- if ($test instanceof Twig_SimpleTest) {
331
- return $test->getNodeClass();
332
- }
333
-
334
- return $test instanceof Twig_Test_Node ? $test->getClass() : 'Twig_Node_Expression_Test';
335
- }
336
-
337
  public function getName()
338
  {
339
  return 'core';
@@ -343,7 +276,7 @@ class Twig_Extension_Core extends Twig_Extension
343
  /**
344
  * Cycles over a value.
345
  *
346
- * @param ArrayAccess|array $values An array or an ArrayAccess instance
347
  * @param int $position The cycle position
348
  *
349
  * @return string The next value in the cycle
@@ -363,8 +296,8 @@ function twig_cycle($values, $position)
363
  * - a random character from a string
364
  * - a random integer between 0 and the integer parameter.
365
  *
366
- * @param Twig_Environment $env A Twig_Environment instance
367
- * @param Traversable|array|int|string $values The values to pick a random item from
368
  *
369
  * @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is).
370
  *
@@ -423,7 +356,7 @@ function twig_random(Twig_Environment $env, $values = null)
423
  * {{ post.published_at|date("m/d/Y") }}
424
  * </pre>
425
  *
426
- * @param Twig_Environment $env A Twig_Environment instance
427
  * @param DateTime|DateTimeInterface|DateInterval|string $date A date
428
  * @param string|null $format The target format, null to use the default
429
  * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
@@ -433,7 +366,7 @@ function twig_random(Twig_Environment $env, $values = null)
433
  function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null)
434
  {
435
  if (null === $format) {
436
- $formats = $env->getExtension('core')->getDateFormat();
437
  $format = $date instanceof DateInterval ? $formats[1] : $formats[0];
438
  }
439
 
@@ -451,7 +384,7 @@ function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $
451
  * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
452
  * </pre>
453
  *
454
- * @param Twig_Environment $env A Twig_Environment instance
455
  * @param DateTime|string $date A date
456
  * @param string $modifier A modifier string
457
  *
@@ -477,7 +410,7 @@ function twig_date_modify_filter(Twig_Environment $env, $date, $modifier)
477
  * {% endif %}
478
  * </pre>
479
  *
480
- * @param Twig_Environment $env A Twig_Environment instance
481
  * @param DateTime|DateTimeInterface|string|null $date A date
482
  * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
483
  *
@@ -488,7 +421,7 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu
488
  // determine the timezone
489
  if (false !== $timezone) {
490
  if (null === $timezone) {
491
- $timezone = $env->getExtension('core')->getTimezone();
492
  } elseif (!$timezone instanceof DateTimeZone) {
493
  $timezone = new DateTimeZone($timezone);
494
  }
@@ -509,14 +442,14 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu
509
  }
510
 
511
  if (null === $date || 'now' === $date) {
512
- return new DateTime($date, false !== $timezone ? $timezone : $env->getExtension('core')->getTimezone());
513
  }
514
 
515
  $asString = (string) $date;
516
  if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
517
  $date = new DateTime('@'.$date);
518
  } else {
519
- $date = new DateTime($date, $env->getExtension('core')->getTimezone());
520
  }
521
 
522
  if (false !== $timezone) {
@@ -544,7 +477,7 @@ function twig_replace_filter($str, $from, $to = null)
544
 
545
  return strtr($str, $from, $to);
546
  } elseif (!is_array($from)) {
547
- throw new Twig_Error_Runtime(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".',is_object($from) ? get_class($from) : gettype($from)));
548
  }
549
 
550
  return strtr($str, $from);
@@ -579,7 +512,7 @@ function twig_round($value, $precision = 0, $method = 'common')
579
  * be used. Supplying any of the parameters will override the defaults set in the
580
  * environment object.
581
  *
582
- * @param Twig_Environment $env A Twig_Environment instance
583
  * @param mixed $number A float/int/string of the number to format
584
  * @param int $decimal The number of decimal points to display.
585
  * @param string $decimalPoint The character(s) to use for the decimal point.
@@ -589,7 +522,7 @@ function twig_round($value, $precision = 0, $method = 'common')
589
  */
590
  function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
591
  {
592
- $defaults = $env->getExtension('core')->getNumberFormat();
593
  if (null === $decimal) {
594
  $decimal = $defaults[0];
595
  }
@@ -708,7 +641,7 @@ function twig_array_merge($arr1, $arr2)
708
  /**
709
  * Slices a variable.
710
  *
711
- * @param Twig_Environment $env A Twig_Environment instance
712
  * @param mixed $item A variable
713
  * @param int $start Start of the slice
714
  * @param int $length Size of the slice
@@ -750,7 +683,7 @@ function twig_slice(Twig_Environment $env, $item, $start, $length = null, $prese
750
  /**
751
  * Returns the first element of the item.
752
  *
753
- * @param Twig_Environment $env A Twig_Environment instance
754
  * @param mixed $item A variable
755
  *
756
  * @return mixed The first element of the item
@@ -765,7 +698,7 @@ function twig_first(Twig_Environment $env, $item)
765
  /**
766
  * Returns the last element of the item.
767
  *
768
- * @param Twig_Environment $env A Twig_Environment instance
769
  * @param mixed $item A variable
770
  *
771
  * @return mixed The last element of the item
@@ -821,7 +754,7 @@ function twig_join_filter($value, $glue = '')
821
  * {# returns [aa, bb, cc] #}
822
  * </pre>
823
  *
824
- * @param Twig_Environment $env A Twig_Environment instance
825
  * @param string $value A string
826
  * @param string $delimiter The delimiter
827
  * @param int $limit The limit
@@ -901,7 +834,7 @@ function twig_get_array_keys_filter($array)
901
  /**
902
  * Reverses a variable.
903
  *
904
- * @param Twig_Environment $env A Twig_Environment instance
905
  * @param array|Traversable|string $item An array, a Traversable instance, or a string
906
  * @param bool $preserveKeys Whether to preserve key or not
907
  *
@@ -977,7 +910,7 @@ function twig_in_filter($value, $compare)
977
  /**
978
  * Escapes a string.
979
  *
980
- * @param Twig_Environment $env A Twig_Environment instance
981
  * @param mixed $string The value to be escaped
982
  * @param string $strategy The escaping strategy
983
  * @param string $charset The charset
@@ -1058,7 +991,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
1058
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1059
  }
1060
 
1061
- if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
1062
  throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1063
  }
1064
 
@@ -1075,7 +1008,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
1075
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1076
  }
1077
 
1078
- if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
1079
  throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1080
  }
1081
 
@@ -1092,7 +1025,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
1092
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1093
  }
1094
 
1095
- if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
1096
  throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1097
  }
1098
 
@@ -1115,7 +1048,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
1115
  static $escapers;
1116
 
1117
  if (null === $escapers) {
1118
- $escapers = $env->getExtension('core')->getEscapers();
1119
  }
1120
 
1121
  if (isset($escapers[$strategy])) {
@@ -1256,7 +1189,7 @@ if (function_exists('mb_get_info')) {
1256
  /**
1257
  * Returns the length of a variable.
1258
  *
1259
- * @param Twig_Environment $env A Twig_Environment instance
1260
  * @param mixed $thing A variable
1261
  *
1262
  * @return int The length of the value
@@ -1269,7 +1202,7 @@ if (function_exists('mb_get_info')) {
1269
  /**
1270
  * Converts a string to uppercase.
1271
  *
1272
- * @param Twig_Environment $env A Twig_Environment instance
1273
  * @param string $string A string
1274
  *
1275
  * @return string The uppercased string
@@ -1286,7 +1219,7 @@ if (function_exists('mb_get_info')) {
1286
  /**
1287
  * Converts a string to lowercase.
1288
  *
1289
- * @param Twig_Environment $env A Twig_Environment instance
1290
  * @param string $string A string
1291
  *
1292
  * @return string The lowercased string
@@ -1303,7 +1236,7 @@ if (function_exists('mb_get_info')) {
1303
  /**
1304
  * Returns a titlecased string.
1305
  *
1306
- * @param Twig_Environment $env A Twig_Environment instance
1307
  * @param string $string A string
1308
  *
1309
  * @return string The titlecased string
@@ -1320,7 +1253,7 @@ if (function_exists('mb_get_info')) {
1320
  /**
1321
  * Returns a capitalized string.
1322
  *
1323
- * @param Twig_Environment $env A Twig_Environment instance
1324
  * @param string $string A string
1325
  *
1326
  * @return string The capitalized string
@@ -1339,7 +1272,7 @@ else {
1339
  /**
1340
  * Returns the length of a variable.
1341
  *
1342
- * @param Twig_Environment $env A Twig_Environment instance
1343
  * @param mixed $thing A variable
1344
  *
1345
  * @return int The length of the value
@@ -1352,7 +1285,7 @@ else {
1352
  /**
1353
  * Returns a titlecased string.
1354
  *
1355
- * @param Twig_Environment $env A Twig_Environment instance
1356
  * @param string $string A string
1357
  *
1358
  * @return string The titlecased string
@@ -1365,7 +1298,7 @@ else {
1365
  /**
1366
  * Returns a capitalized string.
1367
  *
1368
- * @param Twig_Environment $env A Twig_Environment instance
1369
  * @param string $string A string
1370
  *
1371
  * @return string The capitalized string
@@ -1451,8 +1384,8 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
1451
  $variables = array_merge($context, $variables);
1452
  }
1453
 
1454
- if ($isSandboxed = $sandboxed && $env->hasExtension('sandbox')) {
1455
- $sandbox = $env->getExtension('sandbox');
1456
  if (!$alreadySandboxed = $sandbox->isSandboxed()) {
1457
  $sandbox->enableSandbox();
1458
  }
@@ -1489,8 +1422,13 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
1489
  */
1490
  function twig_source(Twig_Environment $env, $name, $ignoreMissing = false)
1491
  {
 
1492
  try {
1493
- return $env->getLoader()->getSource($name);
 
 
 
 
1494
  } catch (Twig_Error_Loader $e) {
1495
  if (!$ignoreMissing) {
1496
  throw $e;
@@ -1515,6 +1453,23 @@ function twig_constant($constant, $object = null)
1515
  return constant($constant);
1516
  }
1517
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1518
  /**
1519
  * Batches item.
1520
  *
132
  new Twig_TokenParser_Flush(),
133
  new Twig_TokenParser_Do(),
134
  new Twig_TokenParser_Embed(),
135
+ new Twig_TokenParser_With(),
136
  );
137
  }
138
 
259
  '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
260
  '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
261
  '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
262
+ 'is' => array('precedence' => 100, 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
263
+ 'is not' => array('precedence' => 100, 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
264
  '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
265
  '??' => array('precedence' => 300, 'class' => 'Twig_Node_Expression_NullCoalesce', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
266
  ),
267
  );
268
  }
269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  public function getName()
271
  {
272
  return 'core';
276
  /**
277
  * Cycles over a value.
278
  *
279
+ * @param ArrayAccess|array $values
280
  * @param int $position The cycle position
281
  *
282
  * @return string The next value in the cycle
296
  * - a random character from a string
297
  * - a random integer between 0 and the integer parameter.
298
  *
299
+ * @param Twig_Environment $env
300
+ * @param Traversable|array|int|float|string $values The values to pick a random item from
301
  *
302
  * @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is).
303
  *
356
  * {{ post.published_at|date("m/d/Y") }}
357
  * </pre>
358
  *
359
+ * @param Twig_Environment $env
360
  * @param DateTime|DateTimeInterface|DateInterval|string $date A date
361
  * @param string|null $format The target format, null to use the default
362
  * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
366
  function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null)
367
  {
368
  if (null === $format) {
369
+ $formats = $env->getExtension('Twig_Extension_Core')->getDateFormat();
370
  $format = $date instanceof DateInterval ? $formats[1] : $formats[0];
371
  }
372
 
384
  * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
385
  * </pre>
386
  *
387
+ * @param Twig_Environment $env
388
  * @param DateTime|string $date A date
389
  * @param string $modifier A modifier string
390
  *
410
  * {% endif %}
411
  * </pre>
412
  *
413
+ * @param Twig_Environment $env
414
  * @param DateTime|DateTimeInterface|string|null $date A date
415
  * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
416
  *
421
  // determine the timezone
422
  if (false !== $timezone) {
423
  if (null === $timezone) {
424
+ $timezone = $env->getExtension('Twig_Extension_Core')->getTimezone();
425
  } elseif (!$timezone instanceof DateTimeZone) {
426
  $timezone = new DateTimeZone($timezone);
427
  }
442
  }
443
 
444
  if (null === $date || 'now' === $date) {
445
+ return new DateTime($date, false !== $timezone ? $timezone : $env->getExtension('Twig_Extension_Core')->getTimezone());
446
  }
447
 
448
  $asString = (string) $date;
449
  if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
450
  $date = new DateTime('@'.$date);
451
  } else {
452
+ $date = new DateTime($date, $env->getExtension('Twig_Extension_Core')->getTimezone());
453
  }
454
 
455
  if (false !== $timezone) {
477
 
478
  return strtr($str, $from, $to);
479
  } elseif (!is_array($from)) {
480
+ throw new Twig_Error_Runtime(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".', is_object($from) ? get_class($from) : gettype($from)));
481
  }
482
 
483
  return strtr($str, $from);
512
  * be used. Supplying any of the parameters will override the defaults set in the
513
  * environment object.
514
  *
515
+ * @param Twig_Environment $env
516
  * @param mixed $number A float/int/string of the number to format
517
  * @param int $decimal The number of decimal points to display.
518
  * @param string $decimalPoint The character(s) to use for the decimal point.
522
  */
523
  function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
524
  {
525
+ $defaults = $env->getExtension('Twig_Extension_Core')->getNumberFormat();
526
  if (null === $decimal) {
527
  $decimal = $defaults[0];
528
  }
641
  /**
642
  * Slices a variable.
643
  *
644
+ * @param Twig_Environment $env
645
  * @param mixed $item A variable
646
  * @param int $start Start of the slice
647
  * @param int $length Size of the slice
683
  /**
684
  * Returns the first element of the item.
685
  *
686
+ * @param Twig_Environment $env
687
  * @param mixed $item A variable
688
  *
689
  * @return mixed The first element of the item
698
  /**
699
  * Returns the last element of the item.
700
  *
701
+ * @param Twig_Environment $env
702
  * @param mixed $item A variable
703
  *
704
  * @return mixed The last element of the item
754
  * {# returns [aa, bb, cc] #}
755
  * </pre>
756
  *
757
+ * @param Twig_Environment $env
758
  * @param string $value A string
759
  * @param string $delimiter The delimiter
760
  * @param int $limit The limit
834
  /**
835
  * Reverses a variable.
836
  *
837
+ * @param Twig_Environment $env
838
  * @param array|Traversable|string $item An array, a Traversable instance, or a string
839
  * @param bool $preserveKeys Whether to preserve key or not
840
  *
910
  /**
911
  * Escapes a string.
912
  *
913
+ * @param Twig_Environment $env
914
  * @param mixed $string The value to be escaped
915
  * @param string $strategy The escaping strategy
916
  * @param string $charset The charset
991
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
992
  }
993
 
994
+ if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) {
995
  throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
996
  }
997
 
1008
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1009
  }
1010
 
1011
+ if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) {
1012
  throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1013
  }
1014
 
1025
  $string = twig_convert_encoding($string, 'UTF-8', $charset);
1026
  }
1027
 
1028
+ if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) {
1029
  throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1030
  }
1031
 
1048
  static $escapers;
1049
 
1050
  if (null === $escapers) {
1051
+ $escapers = $env->getExtension('Twig_Extension_Core')->getEscapers();
1052
  }
1053
 
1054
  if (isset($escapers[$strategy])) {
1189
  /**
1190
  * Returns the length of a variable.
1191
  *
1192
+ * @param Twig_Environment $env
1193
  * @param mixed $thing A variable
1194
  *
1195
  * @return int The length of the value
1202
  /**
1203
  * Converts a string to uppercase.
1204
  *
1205
+ * @param Twig_Environment $env
1206
  * @param string $string A string
1207
  *
1208
  * @return string The uppercased string
1219
  /**
1220
  * Converts a string to lowercase.
1221
  *
1222
+ * @param Twig_Environment $env
1223
  * @param string $string A string
1224
  *
1225
  * @return string The lowercased string
1236
  /**
1237
  * Returns a titlecased string.
1238
  *
1239
+ * @param Twig_Environment $env
1240
  * @param string $string A string
1241
  *
1242
  * @return string The titlecased string
1253
  /**
1254
  * Returns a capitalized string.
1255
  *
1256
+ * @param Twig_Environment $env
1257
  * @param string $string A string
1258
  *
1259
  * @return string The capitalized string
1272
  /**
1273
  * Returns the length of a variable.
1274
  *
1275
+ * @param Twig_Environment $env
1276
  * @param mixed $thing A variable
1277
  *
1278
  * @return int The length of the value
1285
  /**
1286
  * Returns a titlecased string.
1287
  *
1288
+ * @param Twig_Environment $env
1289
  * @param string $string A string
1290
  *
1291
  * @return string The titlecased string
1298
  /**
1299
  * Returns a capitalized string.
1300
  *
1301
+ * @param Twig_Environment $env
1302
  * @param string $string A string
1303
  *
1304
  * @return string The capitalized string
1384
  $variables = array_merge($context, $variables);
1385
  }
1386
 
1387
+ if ($isSandboxed = $sandboxed && $env->hasExtension('Twig_Extension_Sandbox')) {
1388
+ $sandbox = $env->getExtension('Twig_Extension_Sandbox');
1389
  if (!$alreadySandboxed = $sandbox->isSandboxed()) {
1390
  $sandbox->enableSandbox();
1391
  }
1422
  */
1423
  function twig_source(Twig_Environment $env, $name, $ignoreMissing = false)
1424
  {
1425
+ $loader = $env->getLoader();
1426
  try {
1427
+ if (!$loader instanceof Twig_SourceContextLoaderInterface) {
1428
+ return $loader->getSource($name);
1429
+ } else {
1430
+ return $loader->getSourceContext($name)->getCode();
1431
+ }
1432
  } catch (Twig_Error_Loader $e) {
1433
  if (!$ignoreMissing) {
1434
  throw $e;
1453
  return constant($constant);
1454
  }
1455
 
1456
+ /**
1457
+ * Checks if a constant exists.
1458
+ *
1459
+ * @param string $constant The name of the constant
1460
+ * @param null|object $object The object to get the constant from
1461
+ *
1462
+ * @return bool
1463
+ */
1464
+ function twig_constant_is_defined($constant, $object = null)
1465
+ {
1466
+ if (null !== $object) {
1467
+ $constant = get_class($object).'::'.$constant;
1468
+ }
1469
+
1470
+ return defined($constant);
1471
+ }
1472
+
1473
  /**
1474
  * Batches item.
1475
  *
library/twig/twig/lib/Twig/Extension/Escaper.php CHANGED
@@ -13,8 +13,6 @@ class Twig_Extension_Escaper extends Twig_Extension
13
  protected $defaultStrategy;
14
 
15
  /**
16
- * Constructor.
17
- *
18
  * @param string|false|callable $defaultStrategy An escaping strategy
19
  *
20
  * @see setDefaultStrategy()
@@ -45,7 +43,7 @@ class Twig_Extension_Escaper extends Twig_Extension
45
  * Sets the default strategy to use when not defined by the user.
46
  *
47
  * The strategy can be a valid PHP callback that takes the template
48
- * "filename" as an argument and returns the strategy to use.
49
  *
50
  * @param string|false|callable $defaultStrategy An escaping strategy
51
  */
@@ -59,6 +57,12 @@ class Twig_Extension_Escaper extends Twig_Extension
59
  }
60
 
61
  if ('filename' === $defaultStrategy) {
 
 
 
 
 
 
62
  $defaultStrategy = array('Twig_FileExtensionEscapingStrategy', 'guess');
63
  }
64
 
@@ -68,16 +72,16 @@ class Twig_Extension_Escaper extends Twig_Extension
68
  /**
69
  * Gets the default strategy to use when not defined by the user.
70
  *
71
- * @param string $filename The template "filename"
72
  *
73
  * @return string|false The default strategy to use for the template
74
  */
75
- public function getDefaultStrategy($filename)
76
  {
77
  // disable string callables to avoid calling a function named html or js,
78
  // or any other upcoming escaping strategy
79
  if (!is_string($this->defaultStrategy) && false !== $this->defaultStrategy) {
80
- return call_user_func($this->defaultStrategy, $filename);
81
  }
82
 
83
  return $this->defaultStrategy;
13
  protected $defaultStrategy;
14
 
15
  /**
 
 
16
  * @param string|false|callable $defaultStrategy An escaping strategy
17
  *
18
  * @see setDefaultStrategy()
43
  * Sets the default strategy to use when not defined by the user.
44
  *
45
  * The strategy can be a valid PHP callback that takes the template
46
+ * name as an argument and returns the strategy to use.
47
  *
48
  * @param string|false|callable $defaultStrategy An escaping strategy
49
  */
57
  }
58
 
59
  if ('filename' === $defaultStrategy) {
60
+ @trigger_error('Using "filename" as the default strategy is deprecated since version 1.27. Use "name" instead.', E_USER_DEPRECATED);
61
+
62
+ $defaultStrategy = 'name';
63
+ }
64
+
65
+ if ('name' === $defaultStrategy) {
66
  $defaultStrategy = array('Twig_FileExtensionEscapingStrategy', 'guess');
67
  }
68
 
72
  /**
73
  * Gets the default strategy to use when not defined by the user.
74
  *
75
+ * @param string $name The template name
76
  *
77
  * @return string|false The default strategy to use for the template
78
  */
79
+ public function getDefaultStrategy($name)
80
  {
81
  // disable string callables to avoid calling a function named html or js,
82
  // or any other upcoming escaping strategy
83
  if (!is_string($this->defaultStrategy) && false !== $this->defaultStrategy) {
84
+ return call_user_func($this->defaultStrategy, $name);
85
  }
86
 
87
  return $this->defaultStrategy;
library/twig/twig/lib/Twig/Extension/Profiler.php CHANGED
@@ -36,7 +36,7 @@ class Twig_Extension_Profiler extends Twig_Extension
36
 
37
  public function getNodeVisitors()
38
  {
39
- return array(new Twig_Profiler_NodeVisitor_Profiler($this->getName()));
40
  }
41
 
42
  public function getName()
36
 
37
  public function getNodeVisitors()
38
  {
39
+ return array(new Twig_Profiler_NodeVisitor_Profiler(get_class($this)));
40
  }
41
 
42
  public function getName()
library/twig/twig/lib/Twig/Extension/StringLoader.php CHANGED
@@ -33,7 +33,7 @@ class Twig_Extension_StringLoader extends Twig_Extension
33
  * @param Twig_Environment $env A Twig_Environment instance
34
  * @param string $template A template as a string or object implementing __toString()
35
  *
36
- * @return Twig_Template A Twig_Template instance
37
  */
38
  function twig_template_from_string(Twig_Environment $env, $template)
39
  {
33
  * @param Twig_Environment $env A Twig_Environment instance
34
  * @param string $template A template as a string or object implementing __toString()
35
  *
36
+ * @return Twig_Template
37
  */
38
  function twig_template_from_string(Twig_Environment $env, $template)
39
  {
library/twig/twig/lib/Twig/ExtensionInterface.php CHANGED
@@ -21,8 +21,6 @@ interface Twig_ExtensionInterface
21
  *
22
  * This is where you can load some file that contains filter functions for instance.
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);
@@ -37,7 +35,7 @@ interface Twig_ExtensionInterface
37
  /**
38
  * Returns the node visitor instances to add to the existing list.
39
  *
40
- * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
41
  */
42
  public function getNodeVisitors();
43
 
@@ -82,6 +80,8 @@ interface Twig_ExtensionInterface
82
  * Returns the name of the extension.
83
  *
84
  * @return string The extension name
 
 
85
  */
86
  public function getName();
87
  }
21
  *
22
  * This is where you can load some file that contains filter functions for instance.
23
  *
 
 
24
  * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead
25
  */
26
  public function initRuntime(Twig_Environment $environment);
35
  /**
36
  * Returns the node visitor instances to add to the existing list.
37
  *
38
+ * @return Twig_NodeVisitorInterface[]
39
  */
40
  public function getNodeVisitors();
41
 
80
  * Returns the name of the extension.
81
  *
82
  * @return string The extension name
83
+ *
84
+ * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
85
  */
86
  public function getName();
87
  }
library/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php CHANGED
@@ -13,7 +13,7 @@
13
  * Default autoescaping strategy based on file names.
14
  *
15
  * This strategy sets the HTML as the default autoescaping strategy,
16
- * but changes it based on the filename.
17
  *
18
  * Note that there is no runtime performance impact as the
19
  * default autoescaping strategy is set at compilation time.
@@ -25,21 +25,21 @@ class Twig_FileExtensionEscapingStrategy
25
  /**
26
  * Guesses the best autoescaping strategy based on the file name.
27
  *
28
- * @param string $filename The template file name
29
  *
30
  * @return string|false The escaping strategy name to use or false to disable
31
  */
32
- public static function guess($filename)
33
  {
34
- if (in_array(substr($filename, -1), array('/', '\\'))) {
35
  return 'html'; // return html for directories
36
  }
37
 
38
- if ('.twig' === substr($filename, -5)) {
39
- $filename = substr($filename, 0, -5);
40
  }
41
 
42
- $extension = pathinfo($filename, PATHINFO_EXTENSION);
43
 
44
  switch ($extension) {
45
  case 'js':
13
  * Default autoescaping strategy based on file names.
14
  *
15
  * This strategy sets the HTML as the default autoescaping strategy,
16
+ * but changes it based on the template name.
17
  *
18
  * Note that there is no runtime performance impact as the
19
  * default autoescaping strategy is set at compilation time.
25
  /**
26
  * Guesses the best autoescaping strategy based on the file name.
27
  *
28
+ * @param string $name The template name
29
  *
30
  * @return string|false The escaping strategy name to use or false to disable
31
  */
32
+ public static function guess($name)
33
  {
34
+ if (in_array(substr($name, -1), array('/', '\\'))) {
35
  return 'html'; // return html for directories
36
  }
37
 
38
+ if ('.twig' === substr($name, -5)) {
39
+ $name = substr($name, 0, -5);
40
  }
41
 
42
+ $extension = pathinfo($name, PATHINFO_EXTENSION);
43
 
44
  switch ($extension) {
45
  case 'js':
library/twig/twig/lib/Twig/Filter/Method.php CHANGED
@@ -37,6 +37,6 @@ class Twig_Filter_Method extends Twig_Filter
37
 
38
  public function compile()
39
  {
40
- return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
41
  }
42
  }
37
 
38
  public function compile()
39
  {
40
+ return sprintf('$this->env->getExtension(\'%s\')->%s', get_class($this->extension), $this->method);
41
  }
42
  }
library/twig/twig/lib/Twig/Function/Method.php CHANGED
@@ -38,6 +38,6 @@ class Twig_Function_Method extends Twig_Function
38
 
39
  public function compile()
40
  {
41
- return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
42
  }
43
  }
38
 
39
  public function compile()
40
  {
41
+ return sprintf('$this->env->getExtension(\'%s\')->%s', get_class($this->extension), $this->method);
42
  }
43
  }
library/twig/twig/lib/Twig/Lexer.php CHANGED
@@ -26,6 +26,7 @@ class Twig_Lexer implements Twig_LexerInterface
26
  protected $states;
27
  protected $brackets;
28
  protected $env;
 
29
  protected $filename;
30
  protected $options;
31
  protected $regexes;
@@ -75,8 +76,15 @@ class Twig_Lexer implements Twig_LexerInterface
75
  /**
76
  * {@inheritdoc}
77
  */
78
- public function tokenize($code, $filename = null)
79
  {
 
 
 
 
 
 
 
80
  if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
81
  $mbEncoding = mb_internal_encoding();
82
  mb_internal_encoding('ASCII');
@@ -84,8 +92,8 @@ class Twig_Lexer implements Twig_LexerInterface
84
  $mbEncoding = null;
85
  }
86
 
87
- $this->code = str_replace(array("\r\n", "\r"), "\n", $code);
88
- $this->filename = $filename;
89
  $this->cursor = 0;
90
  $this->lineno = 1;
91
  $this->end = strlen($this->code);
@@ -136,7 +144,7 @@ class Twig_Lexer implements Twig_LexerInterface
136
  mb_internal_encoding($mbEncoding);
137
  }
138
 
139
- return new Twig_TokenStream($this->tokens, $this->filename, $code);
140
  }
141
 
142
  protected function lexData()
@@ -403,7 +411,7 @@ class Twig_Lexer implements Twig_LexerInterface
403
  protected function popState()
404
  {
405
  if (0 === count($this->states)) {
406
- throw new Exception('Cannot pop state without a previous state');
407
  }
408
 
409
  $this->state = array_pop($this->states);
26
  protected $states;
27
  protected $brackets;
28
  protected $env;
29
+ // to be renamed to $name in 2.0 (where it is private)
30
  protected $filename;
31
  protected $options;
32
  protected $regexes;
76
  /**
77
  * {@inheritdoc}
78
  */
79
+ public function tokenize($code, $name = null)
80
  {
81
+ if (!$code instanceof Twig_Source) {
82
+ @trigger_error(sprintf('Passing a string as the $code argument of %s() is deprecated since version 1.27 and will be removed in 2.0. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
83
+ $source = new Twig_Source($code, $name);
84
+ } else {
85
+ $source = $code;
86
+ }
87
+
88
  if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
89
  $mbEncoding = mb_internal_encoding();
90
  mb_internal_encoding('ASCII');
92
  $mbEncoding = null;
93
  }
94
 
95
+ $this->code = str_replace(array("\r\n", "\r"), "\n", $source->getCode());
96
+ $this->filename = $source->getName();
97
  $this->cursor = 0;
98
  $this->lineno = 1;
99
  $this->end = strlen($this->code);
144
  mb_internal_encoding($mbEncoding);
145
  }
146
 
147
+ return new Twig_TokenStream($this->tokens, $source);
148
  }
149
 
150
  protected function lexData()
411
  protected function popState()
412
  {
413
  if (0 === count($this->states)) {
414
+ throw new Exception('Cannot pop state without a previous state.');
415
  }
416
 
417
  $this->state = array_pop($this->states);
library/twig/twig/lib/Twig/LexerInterface.php CHANGED
@@ -21,12 +21,12 @@ interface Twig_LexerInterface
21
  /**
22
  * Tokenizes a source code.
23
  *
24
- * @param string $code The source code
25
- * @param string $filename A unique identifier for the source code
26
  *
27
- * @return Twig_TokenStream A token stream instance
28
  *
29
  * @throws Twig_Error_Syntax When the code is syntactically wrong
30
  */
31
- public function tokenize($code, $filename = null);
32
  }
21
  /**
22
  * Tokenizes a source code.
23
  *
24
+ * @param string|Twig_Source $code The source code
25
+ * @param string $name A unique identifier for the source code
26
  *
27
+ * @return Twig_TokenStream
28
  *
29
  * @throws Twig_Error_Syntax When the code is syntactically wrong
30
  */
31
+ public function tokenize($code, $name = null);
32
  }
library/twig/twig/lib/Twig/Loader/Array.php CHANGED
@@ -21,16 +21,14 @@
21
  *
22
  * @author Fabien Potencier <fabien@symfony.com>
23
  */
24
- class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
25
  {
26
  protected $templates = array();
27
 
28
  /**
29
- * Constructor.
30
- *
31
  * @param array $templates An array of templates (keys are the names, and values are the source code)
32
  */
33
- public function __construct(array $templates)
34
  {
35
  $this->templates = $templates;
36
  }
@@ -51,6 +49,8 @@ class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
51
  */
52
  public function getSource($name)
53
  {
 
 
54
  $name = (string) $name;
55
  if (!isset($this->templates[$name])) {
56
  throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
@@ -59,6 +59,19 @@ class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
59
  return $this->templates[$name];
60
  }
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  /**
63
  * {@inheritdoc}
64
  */
21
  *
22
  * @author Fabien Potencier <fabien@symfony.com>
23
  */
24
+ class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
25
  {
26
  protected $templates = array();
27
 
28
  /**
 
 
29
  * @param array $templates An array of templates (keys are the names, and values are the source code)
30
  */
31
+ public function __construct(array $templates = array())
32
  {
33
  $this->templates = $templates;
34
  }
49
  */
50
  public function getSource($name)
51
  {
52
+ @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);
53
+
54
  $name = (string) $name;
55
  if (!isset($this->templates[$name])) {
56
  throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
59
  return $this->templates[$name];
60
  }
61
 
62
+ /**
63
+ * {@inheritdoc}
64
+ */
65
+ public function getSourceContext($name)
66
+ {
67
+ $name = (string) $name;
68
+ if (!isset($this->templates[$name])) {
69
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
70
+ }
71
+
72
+ return new Twig_Source($this->templates[$name], $name);
73
+ }
74
+
75
  /**
76
  * {@inheritdoc}
77
  */
library/twig/twig/lib/Twig/Loader/Chain.php CHANGED
@@ -14,15 +14,13 @@
14
  *
15
  * @author Fabien Potencier <fabien@symfony.com>
16
  */
17
- class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
18
  {
19
  private $hasSourceCache = array();
20
  protected $loaders = array();
21
 
22
  /**
23
- * Constructor.
24
- *
25
- * @param Twig_LoaderInterface[] $loaders An array of loader instances
26
  */
27
  public function __construct(array $loaders = array())
28
  {
@@ -31,11 +29,6 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
31
  }
32
  }
33
 
34
- /**
35
- * Adds a loader instance.
36
- *
37
- * @param Twig_LoaderInterface $loader A Loader instance
38
- */
39
  public function addLoader(Twig_LoaderInterface $loader)
40
  {
41
  $this->loaders[] = $loader;
@@ -47,6 +40,8 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
47
  */
48
  public function getSource($name)
49
  {
 
 
50
  $exceptions = array();
51
  foreach ($this->loaders as $loader) {
52
  if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
@@ -63,6 +58,31 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
63
  throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
64
  }
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  /**
67
  * {@inheritdoc}
68
  */
@@ -84,7 +104,11 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
84
  }
85
 
86
  try {
87
- $loader->getSource($name);
 
 
 
 
88
 
89
  return $this->hasSourceCache[$name] = true;
90
  } catch (Twig_Error_Loader $e) {
14
  *
15
  * @author Fabien Potencier <fabien@symfony.com>
16
  */
17
+ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
18
  {
19
  private $hasSourceCache = array();
20
  protected $loaders = array();
21
 
22
  /**
23
+ * @param Twig_LoaderInterface[] $loaders
 
 
24
  */
25
  public function __construct(array $loaders = array())
26
  {
29
  }
30
  }
31
 
 
 
 
 
 
32
  public function addLoader(Twig_LoaderInterface $loader)
33
  {
34
  $this->loaders[] = $loader;
40
  */
41
  public function getSource($name)
42
  {
43
+ @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);
44
+
45
  $exceptions = array();
46
  foreach ($this->loaders as $loader) {
47
  if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
58
  throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
59
  }
60
 
61
+ /**
62
+ * {@inheritdoc}
63
+ */
64
+ public function getSourceContext($name)
65
+ {
66
+ $exceptions = array();
67
+ foreach ($this->loaders as $loader) {
68
+ if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
69
+ continue;
70
+ }
71
+
72
+ try {
73
+ if ($loader instanceof Twig_SourceContextLoaderInterface) {
74
+ return $loader->getSourceContext($name);
75
+ }
76
+
77
+ return new Twig_Source($loader->getSource($name), $name);
78
+ } catch (Twig_Error_Loader $e) {
79
+ $exceptions[] = $e->getMessage();
80
+ }
81
+ }
82
+
83
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
84
+ }
85
+
86
  /**
87
  * {@inheritdoc}
88
  */
104
  }
105
 
106
  try {
107
+ if ($loader instanceof Twig_SourceContextLoaderInterface) {
108
+ $loader->getSourceContext($name);
109
+ } else {
110
+ $loader->getSource($name);
111
+ }
112
 
113
  return $this->hasSourceCache[$name] = true;
114
  } catch (Twig_Error_Loader $e) {
library/twig/twig/lib/Twig/Loader/Filesystem.php CHANGED
@@ -14,7 +14,7 @@
14
  *
15
  * @author Fabien Potencier <fabien@symfony.com>
16
  */
17
- class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
18
  {
19
  /** Identifier of the main namespace. */
20
  const MAIN_NAMESPACE = '__main__';
@@ -23,13 +23,19 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
23
  protected $cache = array();
24
  protected $errorCache = array();
25
 
 
 
26
  /**
27
- * Constructor.
28
- *
29
- * @param string|array $paths A path or an array of paths where to look for templates
30
  */
31
- public function __construct($paths = array())
32
  {
 
 
 
 
 
33
  if ($paths) {
34
  $this->setPaths($paths);
35
  }
@@ -81,7 +87,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
81
  * Adds a path where templates are stored.
82
  *
83
  * @param string $path A path where to look for templates
84
- * @param string $namespace A path name
85
  *
86
  * @throws Twig_Error_Loader
87
  */
@@ -90,8 +96,9 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
90
  // invalidate the cache
91
  $this->cache = $this->errorCache = array();
92
 
93
- if (!is_dir($path)) {
94
- throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
 
95
  }
96
 
97
  $this->paths[$namespace][] = rtrim($path, '/\\');
@@ -101,7 +108,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
101
  * Prepends a path where templates are stored.
102
  *
103
  * @param string $path A path where to look for templates
104
- * @param string $namespace A path name
105
  *
106
  * @throws Twig_Error_Loader
107
  */
@@ -110,8 +117,9 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
110
  // invalidate the cache
111
  $this->cache = $this->errorCache = array();
112
 
113
- if (!is_dir($path)) {
114
- throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
 
115
  }
116
 
117
  $path = rtrim($path, '/\\');
@@ -128,15 +136,33 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
128
  */
129
  public function getSource($name)
130
  {
 
 
131
  return file_get_contents($this->findTemplate($name));
132
  }
133
 
 
 
 
 
 
 
 
 
 
 
134
  /**
135
  * {@inheritdoc}
136
  */
137
  public function getCacheKey($name)
138
  {
139
- return $this->findTemplate($name);
 
 
 
 
 
 
140
  }
141
 
142
  /**
@@ -153,6 +179,8 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
153
  try {
154
  return false !== $this->findTemplate($name, false);
155
  } catch (Twig_Error_Loader $exception) {
 
 
156
  return false;
157
  }
158
  }
@@ -197,8 +225,16 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
197
  }
198
 
199
  foreach ($this->paths[$namespace] as $path) {
 
 
 
 
200
  if (is_file($path.'/'.$shortname)) {
201
- return $this->cache[$name] = $this->normalizePath($path.'/'.$shortname);
 
 
 
 
202
  }
203
  }
204
 
@@ -254,18 +290,14 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
254
  }
255
  }
256
 
257
- private function normalizePath($path)
258
  {
259
- $parts = explode('/', str_replace('\\', '/', $path));
260
- $new = array();
261
- foreach ($parts as $i => $part) {
262
- if ('..' === $part) {
263
- array_pop($new);
264
- } elseif ('.' !== $part && ('' !== $part || 0 === $i)) {
265
- $new[] = $part;
266
- }
267
- }
268
-
269
- return implode('/', $new);
270
  }
271
  }
14
  *
15
  * @author Fabien Potencier <fabien@symfony.com>
16
  */
17
+ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
18
  {
19
  /** Identifier of the main namespace. */
20
  const MAIN_NAMESPACE = '__main__';
23
  protected $cache = array();
24
  protected $errorCache = array();
25
 
26
+ private $rootPath;
27
+
28
  /**
29
+ * @param string|array $paths A path or an array of paths where to look for templates
30
+ * @param string|null $rootPath The root path common to all relative paths (null for getcwd())
 
31
  */
32
+ public function __construct($paths = array(), $rootPath = null)
33
  {
34
+ $this->rootPath = (null === $rootPath ? getcwd() : $rootPath).DIRECTORY_SEPARATOR;
35
+ if (false !== $realPath = realpath($rootPath)) {
36
+ $this->rootPath = $realPath.DIRECTORY_SEPARATOR;
37
+ }
38
+
39
  if ($paths) {
40
  $this->setPaths($paths);
41
  }
87
  * Adds a path where templates are stored.
88
  *
89
  * @param string $path A path where to look for templates
90
+ * @param string $namespace A path namespace
91
  *
92
  * @throws Twig_Error_Loader
93
  */
96
  // invalidate the cache
97
  $this->cache = $this->errorCache = array();
98
 
99
+ $checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
100
+ if (!is_dir($checkPath)) {
101
+ throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
102
  }
103
 
104
  $this->paths[$namespace][] = rtrim($path, '/\\');
108
  * Prepends a path where templates are stored.
109
  *
110
  * @param string $path A path where to look for templates
111
+ * @param string $namespace A path namespace
112
  *
113
  * @throws Twig_Error_Loader
114
  */
117
  // invalidate the cache
118
  $this->cache = $this->errorCache = array();
119
 
120
+ $checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
121
+ if (!is_dir($checkPath)) {
122
+ throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
123
  }
124
 
125
  $path = rtrim($path, '/\\');
136
  */
137
  public function getSource($name)
138
  {
139
+ @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);
140
+
141
  return file_get_contents($this->findTemplate($name));
142
  }
143
 
144
+ /**
145
+ * {@inheritdoc}
146
+ */
147
+ public function getSourceContext($name)
148
+ {
149
+ $path = $this->findTemplate($name);
150
+
151
+ return new Twig_Source(file_get_contents($path), $name, $path);
152
+ }
153
+
154
  /**
155
  * {@inheritdoc}
156
  */
157
  public function getCacheKey($name)
158
  {
159
+ $path = $this->findTemplate($name);
160
+ $len = strlen($this->rootPath);
161
+ if (0 === strncmp($this->rootPath, $path, $len)) {
162
+ return substr($path, $len);
163
+ }
164
+
165
+ return $path;
166
  }
167
 
168
  /**
179
  try {
180
  return false !== $this->findTemplate($name, false);
181
  } catch (Twig_Error_Loader $exception) {
182
+ @trigger_error(sprintf('In %s::findTemplate(), you must accept a second argument that when set to "false" returns "false" instead of throwing an exception. Not supporting this argument is deprecated since version 1.27.', get_class($this)), E_USER_DEPRECATED);
183
+
184
  return false;
185
  }
186
  }
225
  }
226
 
227
  foreach ($this->paths[$namespace] as $path) {
228
+ if (!$this->isAbsolutePath($path)) {
229
+ $path = $this->rootPath.'/'.$path;
230
+ }
231
+
232
  if (is_file($path.'/'.$shortname)) {
233
+ if (false !== $realpath = realpath($path.'/'.$shortname)) {
234
+ return $this->cache[$name] = $realpath;
235
+ }
236
+
237
+ return $this->cache[$name] = $path.'/'.$shortname;
238
  }
239
  }
240
 
290
  }
291
  }
292
 
293
+ private function isAbsolutePath($file)
294
  {
295
+ return strspn($file, '/\\', 0, 1)
296
+ || (strlen($file) > 3 && ctype_alpha($file[0])
297
+ && substr($file, 1, 1) === ':'
298
+ && strspn($file, '/\\', 2, 1)
299
+ )
300
+ || null !== parse_url($file, PHP_URL_SCHEME)
301
+ ;
 
 
 
 
302
  }
303
  }
library/twig/twig/lib/Twig/Loader/String.php CHANGED
@@ -27,16 +27,26 @@
27
  *
28
  * @author Fabien Potencier <fabien@symfony.com>
29
  */
30
- class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
31
  {
32
  /**
33
  * {@inheritdoc}
34
  */
35
  public function getSource($name)
36
  {
 
 
37
  return $name;
38
  }
39
 
 
 
 
 
 
 
 
 
40
  /**
41
  * {@inheritdoc}
42
  */
27
  *
28
  * @author Fabien Potencier <fabien@symfony.com>
29
  */
30
+ class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
31
  {
32
  /**
33
  * {@inheritdoc}
34
  */
35
  public function getSource($name)
36
  {
37
+ @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);
38
+
39
  return $name;
40
  }
41
 
42
+ /**
43
+ * {@inheritdoc}
44
+ */
45
+ public function getSourceContext($name)
46
+ {
47
+ return new Twig_Source($name, $name);
48
+ }
49
+
50
  /**
51
  * {@inheritdoc}
52
  */
library/twig/twig/lib/Twig/LoaderInterface.php CHANGED
@@ -24,6 +24,8 @@ interface Twig_LoaderInterface
24
  * @return string The template source code
25
  *
26
  * @throws Twig_Error_Loader When $name is not found
 
 
27
  */
28
  public function getSource($name);
29
 
24
  * @return string The template source code
25
  *
26
  * @throws Twig_Error_Loader When $name is not found
27
+ *
28
+ * @deprecated since 1.27 (to be removed in 2.0), implement Twig_SourceContextLoaderInterface
29
  */
30
  public function getSource($name);
31
 
library/twig/twig/lib/Twig/Node.php CHANGED
@@ -22,7 +22,7 @@ class Twig_Node implements Twig_NodeInterface
22
  protected $lineno;
23
  protected $tag;
24
 
25
- private $filename;
26
 
27
  /**
28
  * Constructor.
@@ -118,8 +118,18 @@ class Twig_Node implements Twig_NodeInterface
118
  }
119
  }
120
 
 
 
 
 
 
 
 
 
121
  public function getLine()
122
  {
 
 
123
  return $this->lineno;
124
  }
125
 
@@ -129,11 +139,7 @@ class Twig_Node implements Twig_NodeInterface
129
  }
130
 
131
  /**
132
- * Returns true if the attribute is defined.
133
- *
134
- * @param string $name The attribute name
135
- *
136
- * @return bool true if the attribute is defined, false otherwise
137
  */
138
  public function hasAttribute($name)
139
  {
@@ -141,10 +147,6 @@ class Twig_Node implements Twig_NodeInterface
141
  }
142
 
143
  /**
144
- * Gets an attribute value by name.
145
- *
146
- * @param string $name
147
- *
148
  * @return mixed
149
  */
150
  public function getAttribute($name)
@@ -157,8 +159,6 @@ class Twig_Node implements Twig_NodeInterface
157
  }
158
 
159
  /**
160
- * Sets an attribute by name to a value.
161
- *
162
  * @param string $name
163
  * @param mixed $value
164
  */
@@ -167,21 +167,12 @@ class Twig_Node implements Twig_NodeInterface
167
  $this->attributes[$name] = $value;
168
  }
169
 
170
- /**
171
- * Removes an attribute by name.
172
- *
173
- * @param string $name
174
- */
175
  public function removeAttribute($name)
176
  {
177
  unset($this->attributes[$name]);
178
  }
179
 
180
  /**
181
- * Returns true if the node with the given name exists.
182
- *
183
- * @param string $name
184
- *
185
  * @return bool
186
  */
187
  public function hasNode($name)
@@ -190,10 +181,6 @@ class Twig_Node implements Twig_NodeInterface
190
  }
191
 
192
  /**
193
- * Gets a node by name.
194
- *
195
- * @param string $name
196
- *
197
  * @return Twig_Node
198
  */
199
  public function getNode($name)
@@ -205,22 +192,15 @@ class Twig_Node implements Twig_NodeInterface
205
  return $this->nodes[$name];
206
  }
207
 
208
- /**
209
- * Sets a node.
210
- *
211
- * @param string $name
212
- * @param Twig_Node $node
213
- */
214
  public function setNode($name, $node = null)
215
  {
 
 
 
 
216
  $this->nodes[$name] = $node;
217
  }
218
 
219
- /**
220
- * Removes a node by name.
221
- *
222
- * @param string $name
223
- */
224
  public function removeNode($name)
225
  {
226
  unset($this->nodes[$name]);
@@ -236,18 +216,38 @@ class Twig_Node implements Twig_NodeInterface
236
  return new ArrayIterator($this->nodes);
237
  }
238
 
239
- public function setFilename($filename)
240
  {
241
- $this->filename = $filename;
242
  foreach ($this->nodes as $node) {
243
  if (null !== $node) {
244
- $node->setFilename($filename);
245
  }
246
  }
247
  }
248
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  public function getFilename()
250
  {
251
- return $this->filename;
 
 
252
  }
253
  }
22
  protected $lineno;
23
  protected $tag;
24
 
25
+ private $name;
26
 
27
  /**
28
  * Constructor.
118
  }
119
  }
120
 
121
+ public function getTemplateLine()
122
+ {
123
+ return $this->lineno;
124
+ }
125
+
126
+ /**
127
+ * @deprecated since 1.27 (to be removed in 2.0)
128
+ */
129
  public function getLine()
130
  {
131
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateLine() instead.', E_USER_DEPRECATED);
132
+
133
  return $this->lineno;
134
  }
135
 
139
  }
140
 
141
  /**
142
+ * @return bool
 
 
 
 
143
  */
144
  public function hasAttribute($name)
145
  {
147
  }
148
 
149
  /**
 
 
 
 
150
  * @return mixed
151
  */
152
  public function getAttribute($name)
159
  }
160
 
161
  /**
 
 
162
  * @param string $name
163
  * @param mixed $value
164
  */
167
  $this->attributes[$name] = $value;
168
  }
169
 
 
 
 
 
 
170
  public function removeAttribute($name)
171
  {
172
  unset($this->attributes[$name]);
173
  }
174
 
175
  /**
 
 
 
 
176
  * @return bool
177
  */
178
  public function hasNode($name)
181
  }
182
 
183
  /**
 
 
 
 
184
  * @return Twig_Node
185
  */
186
  public function getNode($name)
192
  return $this->nodes[$name];
193
  }
194
 
 
 
 
 
 
 
195
  public function setNode($name, $node = null)
196
  {
197
+ if (!$node instanceof Twig_NodeInterface) {
198
+ @trigger_error(sprintf('Using "%s" for the value of node "%s" of "%s" is deprecated since version 1.25 and will be removed in 2.0.', is_object($node) ? get_class($node) : null === $node ? 'null' : gettype($node), $name, get_class($this)), E_USER_DEPRECATED);
199
+ }
200
+
201
  $this->nodes[$name] = $node;
202
  }
203
 
 
 
 
 
 
204
  public function removeNode($name)
205
  {
206
  unset($this->nodes[$name]);
216
  return new ArrayIterator($this->nodes);
217
  }
218
 
219
+ public function setTemplateName($name)
220
  {
221
+ $this->name = $name;
222
  foreach ($this->nodes as $node) {
223
  if (null !== $node) {
224
+ $node->setTemplateName($name);
225
  }
226
  }
227
  }
228
 
229
+ public function getTemplateName()
230
+ {
231
+ return $this->name;
232
+ }
233
+
234
+ /**
235
+ * @deprecated since 1.27 (to be removed in 2.0)
236
+ */
237
+ public function setFilename($name)
238
+ {
239
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use setTemplateName() instead.', E_USER_DEPRECATED);
240
+
241
+ $this->setTemplateName($name);
242
+ }
243
+
244
+ /**
245
+ * @deprecated since 1.27 (to be removed in 2.0)
246
+ */
247
  public function getFilename()
248
  {
249
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateName() instead.', E_USER_DEPRECATED);
250
+
251
+ return $this->name;
252
  }
253
  }
library/twig/twig/lib/Twig/Node/CheckSecurity.php CHANGED
@@ -33,7 +33,7 @@ class Twig_Node_CheckSecurity extends Twig_Node
33
  foreach (array('tags', 'filters', 'functions') as $type) {
34
  foreach ($this->{'used'.ucfirst($type)} as $name => $node) {
35
  if ($node instanceof Twig_Node) {
36
- ${$type}[$name] = $node->getLine();
37
  } else {
38
  ${$type}[$node] = null;
39
  }
@@ -46,7 +46,7 @@ class Twig_Node_CheckSecurity extends Twig_Node
46
  ->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n")
47
  ->write("try {\n")
48
  ->indent()
49
- ->write("\$this->env->getExtension('sandbox')->checkSecurity(\n")
50
  ->indent()
51
  ->write(!$tags ? "array(),\n" : "array('".implode("', '", array_keys($tags))."'),\n")
52
  ->write(!$filters ? "array(),\n" : "array('".implode("', '", array_keys($filters))."'),\n")
@@ -56,7 +56,7 @@ class Twig_Node_CheckSecurity extends Twig_Node
56
  ->outdent()
57
  ->write("} catch (Twig_Sandbox_SecurityError \$e) {\n")
58
  ->indent()
59
- ->write("\$e->setTemplateFile(\$this->getTemplateName());\n\n")
60
  ->write("if (\$e instanceof Twig_Sandbox_SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n")
61
  ->indent()
62
  ->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
33
  foreach (array('tags', 'filters', 'functions') as $type) {
34
  foreach ($this->{'used'.ucfirst($type)} as $name => $node) {
35
  if ($node instanceof Twig_Node) {
36
+ ${$type}[$name] = $node->getTemplateLine();
37
  } else {
38
  ${$type}[$node] = null;
39
  }
46
  ->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n")
47
  ->write("try {\n")
48
  ->indent()
49
+ ->write("\$this->env->getExtension('Twig_Extension_Sandbox')->checkSecurity(\n")
50
  ->indent()
51
  ->write(!$tags ? "array(),\n" : "array('".implode("', '", array_keys($tags))."'),\n")
52
  ->write(!$filters ? "array(),\n" : "array('".implode("', '", array_keys($filters))."'),\n")
56
  ->outdent()
57
  ->write("} catch (Twig_Sandbox_SecurityError \$e) {\n")
58
  ->indent()
59
+ ->write("\$e->setTemplateName(\$this->getTemplateName());\n\n")
60
  ->write("if (\$e instanceof Twig_Sandbox_SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n")
61
  ->indent()
62
  ->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
library/twig/twig/lib/Twig/Node/Embed.php CHANGED
@@ -17,11 +17,13 @@
17
  class Twig_Node_Embed extends Twig_Node_Include
18
  {
19
  // we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
20
- public function __construct($filename, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
21
  {
22
  parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
23
 
24
- $this->setAttribute('filename', $filename);
 
 
25
  $this->setAttribute('index', $index);
26
  }
27
 
@@ -29,11 +31,11 @@ class Twig_Node_Embed extends Twig_Node_Include
29
  {
30
  $compiler
31
  ->write('$this->loadTemplate(')
32
- ->string($this->getAttribute('filename'))
33
  ->raw(', ')
34
- ->repr($this->getFilename())
35
  ->raw(', ')
36
- ->repr($this->getLine())
37
  ->raw(', ')
38
  ->string($this->getAttribute('index'))
39
  ->raw(')')
17
  class Twig_Node_Embed extends Twig_Node_Include
18
  {
19
  // we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
20
+ public function __construct($name, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
21
  {
22
  parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
23
 
24
+ $this->setAttribute('name', $name);
25
+ // to be removed in 2.0, used name instead
26
+ $this->setAttribute('filename', $name);
27
  $this->setAttribute('index', $index);
28
  }
29
 
31
  {
32
  $compiler
33
  ->write('$this->loadTemplate(')
34
+ ->string($this->getAttribute('name'))
35
  ->raw(', ')
36
+ ->repr($this->getTemplateName())
37
  ->raw(', ')
38
+ ->repr($this->getTemplateLine())
39
  ->raw(', ')
40
  ->string($this->getAttribute('index'))
41
  ->raw(')')
library/twig/twig/lib/Twig/Node/Expression/Array.php CHANGED
@@ -43,7 +43,7 @@ class Twig_Node_Expression_Array extends Twig_Node_Expression
43
  foreach ($this->getKeyValuePairs() as $pair) {
44
  // we compare the string representation of the keys
45
  // to avoid comparing the line numbers which are not relevant here.
46
- if ((string) $key == (string) $pair['key']) {
47
  return true;
48
  }
49
  }
@@ -54,7 +54,7 @@ class Twig_Node_Expression_Array extends Twig_Node_Expression
54
  public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null)
55
  {
56
  if (null === $key) {
57
- $key = new Twig_Node_Expression_Constant(++$this->index, $value->getLine());
58
  }
59
 
60
  array_push($this->nodes, $key, $value);
43
  foreach ($this->getKeyValuePairs() as $pair) {
44
  // we compare the string representation of the keys
45
  // to avoid comparing the line numbers which are not relevant here.
46
+ if ((string) $key === (string) $pair['key']) {
47
  return true;
48
  }
49
  }
54
  public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null)
55
  {
56
  if (null === $key) {
57
+ $key = new Twig_Node_Expression_Constant(++$this->index, $value->getTemplateLine());
58
  }
59
 
60
  array_push($this->nodes, $key, $value);
library/twig/twig/lib/Twig/Node/Expression/Binary/Power.php CHANGED
@@ -12,6 +12,10 @@ class Twig_Node_Expression_Binary_Power extends Twig_Node_Expression_Binary
12
  {
13
  public function compile(Twig_Compiler $compiler)
14
  {
 
 
 
 
15
  $compiler
16
  ->raw('pow(')
17
  ->subcompile($this->getNode('left'))
12
  {
13
  public function compile(Twig_Compiler $compiler)
14
  {
15
+ if (PHP_VERSION_ID >= 50600) {
16
+ return parent::compile($compiler);
17
+ }
18
+
19
  $compiler
20
  ->raw('pow(')
21
  ->subcompile($this->getNode('left'))
library/twig/twig/lib/Twig/Node/Expression/BlockReference.php CHANGED
@@ -17,30 +17,75 @@
17
  */
18
  class Twig_Node_Expression_BlockReference extends Twig_Node_Expression
19
  {
20
- public function __construct(Twig_NodeInterface $name, $asString = false, $lineno, $tag = null)
 
 
 
21
  {
22
- parent::__construct(array('name' => $name), array('as_string' => $asString, 'output' => false), $lineno, $tag);
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
 
25
  public function compile(Twig_Compiler $compiler)
26
  {
27
- if ($this->getAttribute('as_string')) {
28
- $compiler->raw('(string) ');
 
 
 
 
 
 
 
 
 
 
29
  }
 
30
 
31
- if ($this->getAttribute('output')) {
32
- $compiler
33
- ->addDebugInfo($this)
34
- ->write('$this->displayBlock(')
35
- ->subcompile($this->getNode('name'))
36
- ->raw(", \$context, \$blocks);\n")
37
- ;
38
  } else {
39
  $compiler
40
- ->raw('$this->renderBlock(')
41
- ->subcompile($this->getNode('name'))
42
- ->raw(', $context, $blocks)')
 
 
 
 
43
  ;
44
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
  }
17
  */
18
  class Twig_Node_Expression_BlockReference extends Twig_Node_Expression
19
  {
20
+ /**
21
+ * @param Twig_Node|null $template
22
+ */
23
+ public function __construct(Twig_NodeInterface $name, $template = null, $lineno, $tag = null)
24
  {
25
+ if (is_bool($template)) {
26
+ @trigger_error(sprintf('The %s method "$asString" argument is deprecated since version 1.28 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED);
27
+
28
+ $template = null;
29
+ }
30
+
31
+ $nodes = array('name' => $name);
32
+ if (null !== $template) {
33
+ $nodes['template'] = $template;
34
+ }
35
+
36
+ parent::__construct($nodes, array('is_defined_test' => false, 'output' => false), $lineno, $tag);
37
  }
38
 
39
  public function compile(Twig_Compiler $compiler)
40
  {
41
+ if ($this->getAttribute('is_defined_test')) {
42
+ $this->compileTemplateCall($compiler, 'hasBlock');
43
+ } else {
44
+ if ($this->getAttribute('output')) {
45
+ $compiler->addDebugInfo($this);
46
+
47
+ $this
48
+ ->compileTemplateCall($compiler, 'displayBlock')
49
+ ->raw(";\n");
50
+ } else {
51
+ $this->compileTemplateCall($compiler, 'renderBlock');
52
+ }
53
  }
54
+ }
55
 
56
+ private function compileTemplateCall(Twig_Compiler $compiler, $method)
57
+ {
58
+ if (!$this->hasNode('template')) {
59
+ $compiler->write('$this');
 
 
 
60
  } else {
61
  $compiler
62
+ ->write('$this->loadTemplate(')
63
+ ->subcompile($this->getNode('template'))
64
+ ->raw(', ')
65
+ ->repr($this->getTemplateName())
66
+ ->raw(', ')
67
+ ->repr($this->getTemplateLine())
68
+ ->raw(')')
69
  ;
70
  }
71
+
72
+ $compiler->raw(sprintf('->%s', $method));
73
+ $this->compileBlockArguments($compiler);
74
+
75
+ return $compiler;
76
+ }
77
+
78
+ private function compileBlockArguments(Twig_Compiler $compiler)
79
+ {
80
+ $compiler
81
+ ->raw('(')
82
+ ->subcompile($this->getNode('name'))
83
+ ->raw(', $context');
84
+
85
+ if (!$this->hasNode('template')) {
86
+ $compiler->raw(', $blocks');
87
+ }
88
+
89
+ return $compiler->raw(')');
90
  }
91
  }
library/twig/twig/lib/Twig/Node/Expression/Call.php CHANGED
@@ -10,18 +10,29 @@
10
  */
11
  abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
12
  {
 
 
13
  protected function compileCallable(Twig_Compiler $compiler)
14
  {
15
  $closingParenthesis = false;
16
  if ($this->hasAttribute('callable') && $callable = $this->getAttribute('callable')) {
17
- if (is_string($callable)) {
18
  $compiler->raw($callable);
19
- } elseif (is_array($callable) && $callable[0] instanceof Twig_ExtensionInterface) {
20
- $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', $callable[0]->getName(), $callable[1]));
21
  } else {
22
- $type = ucfirst($this->getAttribute('type'));
23
- $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name')));
24
- $closingParenthesis = true;
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
  } else {
27
  $compiler->raw($this->getAttribute('thing')->compile());
@@ -121,7 +132,6 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
121
  throw new LogicException($message);
122
  }
123
 
124
- // manage named arguments
125
  $callableParameters = $this->getCallableParameters($callable, $isVariadic);
126
  $arguments = array();
127
  $names = array();
@@ -136,7 +146,7 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
136
  throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName));
137
  }
138
 
139
- if (!empty($missingArguments)) {
140
  throw new Twig_Error_Syntax(sprintf(
141
  'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".',
142
  $name, $callType, $callName, implode(', ', $names), count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments))
@@ -195,7 +205,7 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
195
  throw new Twig_Error_Syntax(sprintf(
196
  'Unknown argument%s "%s" for %s "%s(%s)".',
197
  count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
198
- ), $unknownParameter ? $unknownParameter->getLine() : -1);
199
  }
200
 
201
  return $arguments;
@@ -208,15 +218,9 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
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();
@@ -250,4 +254,36 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
250
 
251
  return $parameters;
252
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  }
10
  */
11
  abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
12
  {
13
+ private $reflector;
14
+
15
  protected function compileCallable(Twig_Compiler $compiler)
16
  {
17
  $closingParenthesis = false;
18
  if ($this->hasAttribute('callable') && $callable = $this->getAttribute('callable')) {
19
+ if (is_string($callable) && false === strpos($callable, '::')) {
20
  $compiler->raw($callable);
 
 
21
  } else {
22
+ list($r, $callable) = $this->reflectCallable($callable);
23
+ if ($r instanceof ReflectionMethod && is_string($callable[0])) {
24
+ if ($r->isStatic()) {
25
+ $compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
26
+ } else {
27
+ $compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
28
+ }
29
+ } elseif ($r instanceof ReflectionMethod && $callable[0] instanceof Twig_ExtensionInterface) {
30
+ $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', get_class($callable[0]), $callable[1]));
31
+ } else {
32
+ $type = ucfirst($this->getAttribute('type'));
33
+ $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name')));
34
+ $closingParenthesis = true;
35
+ }
36
  }
37
  } else {
38
  $compiler->raw($this->getAttribute('thing')->compile());
132
  throw new LogicException($message);
133
  }
134
 
 
135
  $callableParameters = $this->getCallableParameters($callable, $isVariadic);
136
  $arguments = array();
137
  $names = array();
146
  throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName));
147
  }
148
 
149
+ if (count($missingArguments)) {
150
  throw new Twig_Error_Syntax(sprintf(
151
  'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".',
152
  $name, $callType, $callName, implode(', ', $names), count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments))
205
  throw new Twig_Error_Syntax(sprintf(
206
  'Unknown argument%s "%s" for %s "%s(%s)".',
207
  count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
208
+ ), $unknownParameter ? $unknownParameter->getTemplateLine() : -1);
209
  }
210
 
211
  return $arguments;
218
 
219
  private function getCallableParameters($callable, $isVariadic)
220
  {
221
+ list($r) = $this->reflectCallable($callable);
222
+ if (null === $r) {
223
+ return array();
 
 
 
 
 
 
224
  }
225
 
226
  $parameters = $r->getParameters();
254
 
255
  return $parameters;
256
  }
257
+
258
+ private function reflectCallable($callable)
259
+ {
260
+ if (null !== $this->reflector) {
261
+ return $this->reflector;
262
+ }
263
+
264
+ if (is_array($callable)) {
265
+ if (!method_exists($callable[0], $callable[1])) {
266
+ // __call()
267
+ return array(null, array());
268
+ }
269
+ $r = new ReflectionMethod($callable[0], $callable[1]);
270
+ } elseif (is_object($callable) && !$callable instanceof Closure) {
271
+ $r = new ReflectionObject($callable);
272
+ $r = $r->getMethod('__invoke');
273
+ $callable = array($callable, '__invoke');
274
+ } elseif (is_string($callable) && false !== $pos = strpos($callable, '::')) {
275
+ $class = substr($callable, 0, $pos);
276
+ $method = substr($callable, $pos + 2);
277
+ if (!method_exists($class, $method)) {
278
+ // __staticCall()
279
+ return array(null, array());
280
+ }
281
+ $r = new ReflectionMethod($callable);
282
+ $callable = array($class, $method);
283
+ } else {
284
+ $r = new ReflectionFunction($callable);
285
+ }
286
+
287
+ return $this->reflector = array($r, $callable);
288
+ }
289
  }
library/twig/twig/lib/Twig/Node/Expression/Filter/Default.php CHANGED
@@ -22,13 +22,13 @@ class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter
22
  {
23
  public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
24
  {
25
- $default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('default', $node->getLine()), $arguments, $node->getLine());
26
 
27
  if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) {
28
- $test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getLine());
29
- $false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getLine());
30
 
31
- $node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getLine());
32
  } else {
33
  $node = $default;
34
  }
22
  {
23
  public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
24
  {
25
+ $default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('default', $node->getTemplateLine()), $arguments, $node->getTemplateLine());
26
 
27
  if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) {
28
+ $test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getTemplateLine());
29
+ $false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getTemplateLine());
30
 
31
+ $node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getTemplateLine());
32
  } else {
33
  $node = $default;
34
  }
library/twig/twig/lib/Twig/Node/Expression/Function.php CHANGED
@@ -12,7 +12,7 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call
12
  {
13
  public function __construct($name, Twig_NodeInterface $arguments, $lineno)
14
  {
15
- parent::__construct(array('arguments' => $arguments), array('name' => $name), $lineno);
16
  }
17
 
18
  public function compile(Twig_Compiler $compiler)
@@ -27,7 +27,12 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call
27
  $this->setAttribute('needs_context', $function->needsContext());
28
  $this->setAttribute('arguments', $function->getArguments());
29
  if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) {
30
- $this->setAttribute('callable', $function->getCallable());
 
 
 
 
 
31
  }
32
  if ($function instanceof Twig_SimpleFunction) {
33
  $this->setAttribute('is_variadic', $function->isVariadic());
12
  {
13
  public function __construct($name, Twig_NodeInterface $arguments, $lineno)
14
  {
15
+ parent::__construct(array('arguments' => $arguments), array('name' => $name, 'is_defined_test' => false), $lineno);
16
  }
17
 
18
  public function compile(Twig_Compiler $compiler)
27
  $this->setAttribute('needs_context', $function->needsContext());
28
  $this->setAttribute('arguments', $function->getArguments());
29
  if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) {
30
+ $callable = $function->getCallable();
31
+ if ('constant' === $name && $this->getAttribute('is_defined_test')) {
32
+ $callable = 'twig_constant_is_defined';
33
+ }
34
+
35
+ $this->setAttribute('callable', $callable);
36
  }
37
  if ($function instanceof Twig_SimpleFunction) {
38
  $this->setAttribute('is_variadic', $function->isVariadic());
library/twig/twig/lib/Twig/Node/Expression/Name.php CHANGED
@@ -43,10 +43,20 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression
43
  ->raw(']')
44
  ;
45
  } else {
46
- // remove the non-PHP 5.4 version when PHP 5.3 support is dropped
47
- // as the non-optimized version is just a workaround for slow ternary operator
48
- // when the context has a lot of variables
49
- if (PHP_VERSION_ID >= 50400) {
 
 
 
 
 
 
 
 
 
 
50
  // PHP 5.4 ternary operator performance was optimized
51
  $compiler
52
  ->raw('(isset($context[')
43
  ->raw(']')
44
  ;
45
  } else {
46
+ if (PHP_VERSION_ID >= 70000) {
47
+ // use PHP 7 null coalescing operator
48
+ $compiler
49
+ ->raw('($context[')
50
+ ->string($name)
51
+ ->raw('] ?? ')
52
+ ;
53
+
54
+ if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
55
+ $compiler->raw('null)');
56
+ } else {
57
+ $compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
58
+ }
59
+ } elseif (PHP_VERSION_ID >= 50400) {
60
  // PHP 5.4 ternary operator performance was optimized
61
  $compiler
62
  ->raw('(isset($context[')
library/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php CHANGED
@@ -13,11 +13,34 @@ class Twig_Node_Expression_NullCoalesce extends Twig_Node_Expression_Conditional
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
  }
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->getTemplateLine()),
17
+ new Twig_Node_Expression_Unary_Not(new Twig_Node_Expression_Test_Null($left, 'null', new Twig_Node(), $left->getTemplateLine()), $left->getTemplateLine()),
18
+ $left->getTemplateLine()
19
  );
20
 
21
  parent::__construct($test, $left, $right, $lineno);
22
  }
23
+
24
+ public function compile(Twig_Compiler $compiler)
25
+ {
26
+ /*
27
+ * This optimizes only one case. PHP 7 also supports more complex expressions
28
+ * that can return null. So, for instance, if log is defined, log("foo") ?? "..." works,
29
+ * but log($a["foo"]) ?? "..." does not if $a["foo"] is not defined. More advanced
30
+ * cases might be implemented as an optimizer node visitor, but has not been done
31
+ * as benefits are probably not worth the added complexity.
32
+ */
33
+ if (PHP_VERSION_ID >= 70000 && $this->getNode('expr2') instanceof Twig_Node_Expression_Name) {
34
+ $this->getNode('expr2')->setAttribute('always_defined', true);
35
+ $compiler
36
+ ->raw('((')
37
+ ->subcompile($this->getNode('expr2'))
38
+ ->raw(') ?? (')
39
+ ->subcompile($this->getNode('expr3'))
40
+ ->raw('))')
41
+ ;
42
+ } else {
43
+ parent::compile($compiler);
44
+ }
45
+ }
46
  }
library/twig/twig/lib/Twig/Node/Expression/Test/Defined.php CHANGED
@@ -29,12 +29,15 @@ class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test
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);
29
  $node->setAttribute('is_defined_test', true);
30
  } elseif ($node instanceof Twig_Node_Expression_GetAttr) {
31
  $node->setAttribute('is_defined_test', true);
 
32
  $this->changeIgnoreStrictCheck($node);
33
+ } elseif ($node instanceof Twig_Node_Expression_BlockReference) {
34
+ $node->setAttribute('is_defined_test', true);
35
+ } elseif ($node instanceof Twig_Node_Expression_Function && 'constant' === $node->getAttribute('name')) {
36
+ $node->setAttribute('is_defined_test', true);
37
  } elseif ($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array) {
38
+ $node = new Twig_Node_Expression_Constant(true, $node->getTemplateLine());
39
  } else {
40
+ throw new Twig_Error_Syntax('The "defined" test only works with simple variables.', $this->getTemplateLine());
41
  }
42
 
43
  parent::__construct($node, $name, $arguments, $lineno);
library/twig/twig/lib/Twig/Node/Import.php CHANGED
@@ -37,9 +37,9 @@ class Twig_Node_Import extends Twig_Node
37
  ->raw('$this->loadTemplate(')
38
  ->subcompile($this->getNode('expr'))
39
  ->raw(', ')
40
- ->repr($this->getFilename())
41
  ->raw(', ')
42
- ->repr($this->getLine())
43
  ->raw(')')
44
  ;
45
  }
37
  ->raw('$this->loadTemplate(')
38
  ->subcompile($this->getNode('expr'))
39
  ->raw(', ')
40
+ ->repr($this->getTemplateName())
41
  ->raw(', ')
42
+ ->repr($this->getTemplateLine())
43
  ->raw(')')
44
  ;
45
  }
library/twig/twig/lib/Twig/Node/Include.php CHANGED
@@ -64,9 +64,9 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
64
  ->write('$this->loadTemplate(')
65
  ->subcompile($this->getNode('expr'))
66
  ->raw(', ')
67
- ->repr($this->getFilename())
68
  ->raw(', ')
69
- ->repr($this->getLine())
70
  ->raw(')')
71
  ;
72
  }
64
  ->write('$this->loadTemplate(')
65
  ->subcompile($this->getNode('expr'))
66
  ->raw(', ')
67
+ ->repr($this->getTemplateName())
68
  ->raw(', ')
69
+ ->repr($this->getTemplateLine())
70
  ->raw(')')
71
  ;
72
  }
library/twig/twig/lib/Twig/Node/Macro.php CHANGED
@@ -22,7 +22,7 @@ class Twig_Node_Macro extends Twig_Node
22
  {
23
  foreach ($arguments as $argumentName => $argument) {
24
  if (self::VARARGS_NAME === $argumentName) {
25
- throw new Twig_Error_Syntax(sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getLine());
26
  }
27
  }
28
 
@@ -70,7 +70,7 @@ class Twig_Node_Macro extends Twig_Node
70
 
71
  foreach ($this->getNode('arguments') as $name => $default) {
72
  $compiler
73
- ->addIndentation()
74
  ->string($name)
75
  ->raw(' => $__'.$name.'__')
76
  ->raw(",\n")
@@ -78,7 +78,7 @@ class Twig_Node_Macro extends Twig_Node
78
  }
79
 
80
  $compiler
81
- ->addIndentation()
82
  ->string(self::VARARGS_NAME)
83
  ->raw(' => ')
84
  ;
22
  {
23
  foreach ($arguments as $argumentName => $argument) {
24
  if (self::VARARGS_NAME === $argumentName) {
25
+ throw new Twig_Error_Syntax(sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getTemplateLine());
26
  }
27
  }
28
 
70
 
71
  foreach ($this->getNode('arguments') as $name => $default) {
72
  $compiler
73
+ ->write('')
74
  ->string($name)
75
  ->raw(' => $__'.$name.'__')
76
  ->raw(",\n")
78
  }
79
 
80
  $compiler
81
+ ->write('')
82
  ->string(self::VARARGS_NAME)
83
  ->raw(' => ')
84
  ;
library/twig/twig/lib/Twig/Node/Module.php CHANGED
@@ -21,8 +21,17 @@
21
  */
22
  class Twig_Node_Module extends Twig_Node
23
  {
24
- public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename, $source = '')
 
 
25
  {
 
 
 
 
 
 
 
26
  $nodes = array(
27
  'body' => $body,
28
  'blocks' => $blocks,
@@ -40,11 +49,16 @@ class Twig_Node_Module extends Twig_Node
40
 
41
  // embedded templates are set as attributes so that they are only visited once by the visitors
42
  parent::__construct($nodes, array(
43
- 'source' => $source,
44
- 'filename' => $filename,
 
 
45
  'index' => null,
46
  'embedded_templates' => $embeddedTemplates,
47
  ), 1);
 
 
 
48
  }
49
 
50
  public function setIndex($index)
@@ -96,6 +110,8 @@ class Twig_Node_Module extends Twig_Node
96
 
97
  $this->compileGetSource($compiler);
98
 
 
 
99
  $this->compileClassFooter($compiler);
100
  }
101
 
@@ -120,9 +136,9 @@ class Twig_Node_Module extends Twig_Node
120
  ->raw('$this->loadTemplate(')
121
  ->subcompile($parent)
122
  ->raw(', ')
123
- ->repr($this->getAttribute('filename'))
124
  ->raw(', ')
125
- ->repr($parent->getLine())
126
  ->raw(')')
127
  ;
128
  }
@@ -138,9 +154,9 @@ class Twig_Node_Module extends Twig_Node
138
  {
139
  $compiler
140
  ->write("\n\n")
141
- // if the filename contains */, add a blank to avoid a PHP parse error
142
- ->write('/* '.str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
143
- ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index')))
144
  ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
145
  ->write("{\n")
146
  ->indent()
@@ -165,9 +181,9 @@ class Twig_Node_Module extends Twig_Node
165
  ->write('$this->parent = $this->loadTemplate(')
166
  ->subcompile($parent)
167
  ->raw(', ')
168
- ->repr($this->getAttribute('filename'))
169
  ->raw(', ')
170
- ->repr($parent->getLine())
171
  ->raw(");\n")
172
  ;
173
  }
@@ -323,7 +339,7 @@ class Twig_Node_Module extends Twig_Node
323
  ->write("public function getTemplateName()\n", "{\n")
324
  ->indent()
325
  ->write('return ')
326
- ->repr($this->getAttribute('filename'))
327
  ->raw(";\n")
328
  ->outdent()
329
  ->write("}\n\n")
@@ -396,11 +412,29 @@ class Twig_Node_Module extends Twig_Node
396
  protected function compileGetSource(Twig_Compiler $compiler)
397
  {
398
  $compiler
 
399
  ->write("public function getSource()\n", "{\n")
400
  ->indent()
401
- ->write('return ')
402
- ->string($this->getAttribute('source'))
403
- ->raw(";\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  ->outdent()
405
  ->write("}\n")
406
  ;
@@ -413,13 +447,13 @@ class Twig_Node_Module extends Twig_Node
413
  ->write(sprintf('%s = $this->loadTemplate(', $var))
414
  ->subcompile($node)
415
  ->raw(', ')
416
- ->repr($this->getAttribute('filename'))
417
  ->raw(', ')
418
- ->repr($node->getLine())
419
  ->raw(");\n")
420
  ;
421
  } else {
422
- throw new LogicException('Trait templates can only be constant nodes');
423
  }
424
  }
425
  }
21
  */
22
  class Twig_Node_Module extends Twig_Node
23
  {
24
+ private $source;
25
+
26
+ public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $name, $source = '')
27
  {
28
+ if (!$name instanceof Twig_Source) {
29
+ @trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
30
+ $this->source = new Twig_Source($source, $name);
31
+ } else {
32
+ $this->source = $name;
33
+ }
34
+
35
  $nodes = array(
36
  'body' => $body,
37
  'blocks' => $blocks,
49
 
50
  // embedded templates are set as attributes so that they are only visited once by the visitors
51
  parent::__construct($nodes, array(
52
+ // source to be remove in 2.0
53
+ 'source' => $this->source->getCode(),
54
+ // filename to be remove in 2.0 (use getTemplateName() instead)
55
+ 'filename' => $this->source->getName(),
56
  'index' => null,
57
  'embedded_templates' => $embeddedTemplates,
58
  ), 1);
59
+
60
+ // populate the template name of all node children
61
+ $this->setTemplateName($this->source->getName());
62
  }
63
 
64
  public function setIndex($index)
110
 
111
  $this->compileGetSource($compiler);
112
 
113
+ $this->compileGetSourceContext($compiler);
114
+
115
  $this->compileClassFooter($compiler);
116
  }
117
 
136
  ->raw('$this->loadTemplate(')
137
  ->subcompile($parent)
138
  ->raw(', ')
139
+ ->repr($this->source->getName())
140
  ->raw(', ')
141
+ ->repr($parent->getTemplateLine())
142
  ->raw(')')
143
  ;
144
  }
154
  {
155
  $compiler
156
  ->write("\n\n")
157
+ // if the template name contains */, add a blank to avoid a PHP parse error
158
+ ->write('/* '.str_replace('*/', '* /', $this->source->getName())." */\n")
159
+ ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->source->getName(), $this->getAttribute('index')))
160
  ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
161
  ->write("{\n")
162
  ->indent()
181
  ->write('$this->parent = $this->loadTemplate(')
182
  ->subcompile($parent)
183
  ->raw(', ')
184
+ ->repr($this->source->getName())
185
  ->raw(', ')
186
+ ->repr($parent->getTemplateLine())
187
  ->raw(");\n")
188
  ;
189
  }
339
  ->write("public function getTemplateName()\n", "{\n")
340
  ->indent()
341
  ->write('return ')
342
+ ->repr($this->source->getName())
343
  ->raw(";\n")
344
  ->outdent()
345
  ->write("}\n\n")
412
  protected function compileGetSource(Twig_Compiler $compiler)
413
  {
414
  $compiler
415
+ ->write("/** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */\n")
416
  ->write("public function getSource()\n", "{\n")
417
  ->indent()
418
+ ->write("@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);\n\n")
419
+ ->write('return $this->getSourceContext()->getCode();')
420
+ ->raw("\n")
421
+ ->outdent()
422
+ ->write("}\n\n")
423
+ ;
424
+ }
425
+
426
+ protected function compileGetSourceContext(Twig_Compiler $compiler)
427
+ {
428
+ $compiler
429
+ ->write("public function getSourceContext()\n", "{\n")
430
+ ->indent()
431
+ ->write('return new Twig_Source(')
432
+ ->string($compiler->getEnvironment()->isDebug() ? $this->source->getCode() : '')
433
+ ->raw(', ')
434
+ ->string($this->source->getName())
435
+ ->raw(', ')
436
+ ->string($this->source->getPath())
437
+ ->raw(");\n")
438
  ->outdent()
439
  ->write("}\n")
440
  ;
447
  ->write(sprintf('%s = $this->loadTemplate(', $var))
448
  ->subcompile($node)
449
  ->raw(', ')
450
+ ->repr($node->getTemplateName())
451
  ->raw(', ')
452
+ ->repr($node->getTemplateLine())
453
  ->raw(");\n")
454
  ;
455
  } else {
456
+ throw new LogicException('Trait templates can only be constant nodes.');
457
  }
458
  }
459
  }
library/twig/twig/lib/Twig/Node/Sandbox.php CHANGED
@@ -25,7 +25,7 @@ class Twig_Node_Sandbox extends Twig_Node
25
  {
26
  $compiler
27
  ->addDebugInfo($this)
28
- ->write("\$sandbox = \$this->env->getExtension('sandbox');\n")
29
  ->write("if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {\n")
30
  ->indent()
31
  ->write("\$sandbox->enableSandbox();\n")
25
  {
26
  $compiler
27
  ->addDebugInfo($this)
28
+ ->write("\$sandbox = \$this->env->getExtension('Twig_Extension_Sandbox');\n")
29
  ->write("if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {\n")
30
  ->indent()
31
  ->write("\$sandbox->enableSandbox();\n")
library/twig/twig/lib/Twig/Node/SandboxedPrint.php CHANGED
@@ -25,7 +25,7 @@ class Twig_Node_SandboxedPrint extends Twig_Node_Print
25
  {
26
  $compiler
27
  ->addDebugInfo($this)
28
- ->write('echo $this->env->getExtension(\'sandbox\')->ensureToStringAllowed(')
29
  ->subcompile($this->getNode('expr'))
30
  ->raw(");\n")
31
  ;
@@ -36,11 +36,9 @@ class Twig_Node_SandboxedPrint extends Twig_Node_Print
36
  *
37
  * This is mostly needed when another visitor adds filters (like the escaper one).
38
  *
39
- * @param Twig_Node $node A Node
40
- *
41
  * @return Twig_Node
42
  */
43
- protected function removeNodeFilter($node)
44
  {
45
  if ($node instanceof Twig_Node_Expression_Filter) {
46
  return $this->removeNodeFilter($node->getNode('node'));
25
  {
26
  $compiler
27
  ->addDebugInfo($this)
28
+ ->write('echo $this->env->getExtension(\'Twig_Extension_Sandbox\')->ensureToStringAllowed(')
29
  ->subcompile($this->getNode('expr'))
30
  ->raw(");\n")
31
  ;
36
  *
37
  * This is mostly needed when another visitor adds filters (like the escaper one).
38
  *
 
 
39
  * @return Twig_Node
40
  */
41
+ protected function removeNodeFilter(Twig_Node $node)
42
  {
43
  if ($node instanceof Twig_Node_Expression_Filter) {
44
  return $this->removeNodeFilter($node->getNode('node'));
library/twig/twig/lib/Twig/Node/Set.php CHANGED
@@ -30,7 +30,7 @@ class Twig_Node_Set extends Twig_Node
30
 
31
  $values = $this->getNode('values');
32
  if ($values instanceof Twig_Node_Text) {
33
- $this->setNode('values', new Twig_Node_Expression_Constant($values->getAttribute('data'), $values->getLine()));
34
  $this->setAttribute('capture', false);
35
  }
36
  }
30
 
31
  $values = $this->getNode('values');
32
  if ($values instanceof Twig_Node_Text) {
33
+ $this->setNode('values', new Twig_Node_Expression_Constant($values->getAttribute('data'), $values->getTemplateLine()));
34
  $this->setAttribute('capture', false);
35
  }
36
  }
library/twig/twig/lib/Twig/Node/With.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) 2016 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
+ /**
13
+ * Represents a nested "with" scope.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_With extends Twig_Node
18
+ {
19
+ public function __construct(Twig_Node $body, Twig_Node $variables = null, $only = false, $lineno, $tag = null)
20
+ {
21
+ $nodes = array('body' => $body);
22
+ if (null !== $variables) {
23
+ $nodes['variables'] = $variables;
24
+ }
25
+
26
+ parent::__construct($nodes, array('only' => (bool) $only), $lineno, $tag);
27
+ }
28
+
29
+ public function compile(Twig_Compiler $compiler)
30
+ {
31
+ $compiler->addDebugInfo($this);
32
+
33
+ if ($this->hasNode('variables')) {
34
+ $varsName = $compiler->getVarName();
35
+ $compiler
36
+ ->write(sprintf('$%s = ', $varsName))
37
+ ->subcompile($this->getNode('variables'))
38
+ ->raw(";\n")
39
+ ->write(sprintf("if (!is_array(\$%s)) {\n", $varsName))
40
+ ->indent()
41
+ ->write("throw new Twig_Error_Runtime('Variables passed to the \"with\" tag must be a hash.');\n")
42
+ ->outdent()
43
+ ->write("}\n")
44
+ ;
45
+
46
+ if ($this->getAttribute('only')) {
47
+ $compiler->write("\$context = array('_parent' => \$context);\n");
48
+ } else {
49
+ $compiler->write("\$context['_parent'] = \$context;\n");
50
+ }
51
+
52
+ $compiler->write(sprintf("\$context = array_merge(\$context, \$%s);\n", $varsName));
53
+ } else {
54
+ $compiler->write("\$context['_parent'] = \$context;\n");
55
+ }
56
+
57
+ $compiler
58
+ ->subcompile($this->getNode('body'))
59
+ ->write("\$context = \$context['_parent'];\n")
60
+ ;
61
+ }
62
+ }
library/twig/twig/lib/Twig/NodeInterface.php CHANGED
@@ -20,11 +20,12 @@ interface Twig_NodeInterface extends Countable, IteratorAggregate
20
  {
21
  /**
22
  * Compiles the node to PHP.
23
- *
24
- * @param Twig_Compiler $compiler A Twig_Compiler instance
25
  */
26
  public function compile(Twig_Compiler $compiler);
27
 
 
 
 
28
  public function getLine();
29
 
30
  public function getNodeTag();
20
  {
21
  /**
22
  * Compiles the node to PHP.
 
 
23
  */
24
  public function compile(Twig_Compiler $compiler);
25
 
26
+ /**
27
+ * @deprecated since 1.27 (to be removed in 2.0)
28
+ */
29
  public function getLine();
30
 
31
  public function getNodeTag();
library/twig/twig/lib/Twig/NodeTraverser.php CHANGED
@@ -22,10 +22,8 @@ class Twig_NodeTraverser
22
  protected $visitors = array();
23
 
24
  /**
25
- * Constructor.
26
- *
27
- * @param Twig_Environment $env A Twig_Environment instance
28
- * @param Twig_NodeVisitorInterface[] $visitors An array of Twig_NodeVisitorInterface instances
29
  */
30
  public function __construct(Twig_Environment $env, array $visitors = array())
31
  {
@@ -35,11 +33,6 @@ class Twig_NodeTraverser
35
  }
36
  }
37
 
38
- /**
39
- * Adds a visitor.
40
- *
41
- * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
42
- */
43
  public function addVisitor(Twig_NodeVisitorInterface $visitor)
44
  {
45
  if (!isset($this->visitors[$visitor->getPriority()])) {
@@ -52,8 +45,6 @@ class Twig_NodeTraverser
52
  /**
53
  * Traverses a node and calls the registered visitors.
54
  *
55
- * @param Twig_NodeInterface $node A Twig_NodeInterface instance
56
- *
57
  * @return Twig_NodeInterface
58
  */
59
  public function traverse(Twig_NodeInterface $node)
22
  protected $visitors = array();
23
 
24
  /**
25
+ * @param Twig_Environment $env
26
+ * @param Twig_NodeVisitorInterface[] $visitors
 
 
27
  */
28
  public function __construct(Twig_Environment $env, array $visitors = array())
29
  {
33
  }
34
  }
35
 
 
 
 
 
 
36
  public function addVisitor(Twig_NodeVisitorInterface $visitor)
37
  {
38
  if (!isset($this->visitors[$visitor->getPriority()])) {
45
  /**
46
  * Traverses a node and calls the registered visitors.
47
  *
 
 
48
  * @return Twig_NodeInterface
49
  */
50
  public function traverse(Twig_NodeInterface $node)
library/twig/twig/lib/Twig/NodeVisitor/Escaper.php CHANGED
@@ -34,7 +34,7 @@ class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor
34
  protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
35
  {
36
  if ($node instanceof Twig_Node_Module) {
37
- if ($env->hasExtension('escaper') && $defaultStrategy = $env->getExtension('escaper')->getDefaultStrategy($node->getAttribute('filename'))) {
38
  $this->defaultStrategy = $defaultStrategy;
39
  }
40
  $this->safeVars = array();
@@ -90,7 +90,7 @@ class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor
90
 
91
  return new $class(
92
  $this->getEscaperFilter($type, $expression),
93
- $node->getLine()
94
  );
95
  }
96
 
@@ -142,7 +142,7 @@ class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor
142
 
143
  protected function getEscaperFilter($type, Twig_NodeInterface $node)
144
  {
145
- $line = $node->getLine();
146
  $name = new Twig_Node_Expression_Constant('escape', $line);
147
  $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line)));
148
 
34
  protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
35
  {
36
  if ($node instanceof Twig_Node_Module) {
37
+ if ($env->hasExtension('Twig_Extension_Escaper') && $defaultStrategy = $env->getExtension('Twig_Extension_Escaper')->getDefaultStrategy($node->getTemplateName())) {
38
  $this->defaultStrategy = $defaultStrategy;
39
  }
40
  $this->safeVars = array();
90
 
91
  return new $class(
92
  $this->getEscaperFilter($type, $expression),
93
+ $node->getTemplateLine()
94
  );
95
  }
96
 
142
 
143
  protected function getEscaperFilter($type, Twig_NodeInterface $node)
144
  {
145
+ $line = $node->getTemplateLine();
146
  $name = new Twig_Node_Expression_Constant('escape', $line);
147
  $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line)));
148
 
library/twig/twig/lib/Twig/NodeVisitor/Optimizer.php CHANGED
@@ -34,8 +34,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
34
  protected $inABody = false;
35
 
36
  /**
37
- * Constructor.
38
- *
39
  * @param int $optimizers The optimizer mode
40
  */
41
  public function __construct($optimizers = -1)
@@ -56,7 +54,7 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
56
  $this->enterOptimizeFor($node, $env);
57
  }
58
 
59
- if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
60
  if ($this->inABody) {
61
  if (!$node instanceof Twig_Node_Expression) {
62
  if (get_class($node) !== 'Twig_Node') {
@@ -90,14 +88,14 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
90
 
91
  $node = $this->optimizePrintNode($node, $env);
92
 
93
- if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
94
  if ($node instanceof Twig_Node_Body) {
95
  $this->inABody = false;
96
  } elseif ($this->inABody) {
97
  if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
98
  $nodes = array();
99
  foreach (array_unique($prependedNodes) as $name) {
100
- $nodes[] = new Twig_Node_SetTemp($name, $node->getLine());
101
  }
102
 
103
  $nodes[] = $node;
@@ -114,7 +112,7 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
114
  if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
115
  $this->prependedNodes[0][] = $node->getAttribute('name');
116
 
117
- return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine());
118
  }
119
 
120
  return $node;
@@ -127,9 +125,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
127
  *
128
  * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
129
  *
130
- * @param Twig_NodeInterface $node A Node
131
- * @param Twig_Environment $env The current Twig environment
132
- *
133
  * @return Twig_NodeInterface
134
  */
135
  protected function optimizePrintNode(Twig_NodeInterface $node, Twig_Environment $env)
@@ -138,13 +133,14 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
138
  return $node;
139
  }
140
 
 
141
  if (
142
- $node->getNode('expr') instanceof Twig_Node_Expression_BlockReference ||
143
- $node->getNode('expr') instanceof Twig_Node_Expression_Parent
144
  ) {
145
- $node->getNode('expr')->setAttribute('output', true);
146
 
147
- return $node->getNode('expr');
148
  }
149
 
150
  return $node;
@@ -153,9 +149,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
153
  /**
154
  * Removes "raw" filters.
155
  *
156
- * @param Twig_NodeInterface $node A Node
157
- * @param Twig_Environment $env The current Twig environment
158
- *
159
  * @return Twig_NodeInterface
160
  */
161
  protected function optimizeRawFilter(Twig_NodeInterface $node, Twig_Environment $env)
@@ -169,9 +162,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
169
 
170
  /**
171
  * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
172
- *
173
- * @param Twig_NodeInterface $node A Node
174
- * @param Twig_Environment $env The current Twig environment
175
  */
176
  protected function enterOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
177
  {
@@ -236,9 +226,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
236
 
237
  /**
238
  * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
239
- *
240
- * @param Twig_NodeInterface $node A Node
241
- * @param Twig_Environment $env The current Twig environment
242
  */
243
  protected function leaveOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
244
  {
34
  protected $inABody = false;
35
 
36
  /**
 
 
37
  * @param int $optimizers The optimizer mode
38
  */
39
  public function __construct($optimizers = -1)
54
  $this->enterOptimizeFor($node, $env);
55
  }
56
 
57
+ if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) {
58
  if ($this->inABody) {
59
  if (!$node instanceof Twig_Node_Expression) {
60
  if (get_class($node) !== 'Twig_Node') {
88
 
89
  $node = $this->optimizePrintNode($node, $env);
90
 
91
+ if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) {
92
  if ($node instanceof Twig_Node_Body) {
93
  $this->inABody = false;
94
  } elseif ($this->inABody) {
95
  if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
96
  $nodes = array();
97
  foreach (array_unique($prependedNodes) as $name) {
98
+ $nodes[] = new Twig_Node_SetTemp($name, $node->getTemplateLine());
99
  }
100
 
101
  $nodes[] = $node;
112
  if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
113
  $this->prependedNodes[0][] = $node->getAttribute('name');
114
 
115
+ return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getTemplateLine());
116
  }
117
 
118
  return $node;
125
  *
126
  * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
127
  *
 
 
 
128
  * @return Twig_NodeInterface
129
  */
130
  protected function optimizePrintNode(Twig_NodeInterface $node, Twig_Environment $env)
133
  return $node;
134
  }
135
 
136
+ $exprNode = $node->getNode('expr');
137
  if (
138
+ $exprNode instanceof Twig_Node_Expression_BlockReference ||
139
+ $exprNode instanceof Twig_Node_Expression_Parent
140
  ) {
141
+ $exprNode->setAttribute('output', true);
142
 
143
+ return $exprNode;
144
  }
145
 
146
  return $node;
149
  /**
150
  * Removes "raw" filters.
151
  *
 
 
 
152
  * @return Twig_NodeInterface
153
  */
154
  protected function optimizeRawFilter(Twig_NodeInterface $node, Twig_Environment $env)
162
 
163
  /**
164
  * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
 
 
 
165
  */
166
  protected function enterOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
167
  {
226
 
227
  /**
228
  * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
 
 
 
229
  */
230
  protected function leaveOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
231
  {
library/twig/twig/lib/Twig/NodeVisitor/Sandbox.php CHANGED
@@ -51,7 +51,7 @@ class Twig_NodeVisitor_Sandbox extends Twig_BaseNodeVisitor
51
 
52
  // wrap print to check __toString() calls
53
  if ($node instanceof Twig_Node_Print) {
54
- return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getLine(), $node->getNodeTag());
55
  }
56
  }
57
 
51
 
52
  // wrap print to check __toString() calls
53
  if ($node instanceof Twig_Node_Print) {
54
+ return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getTemplateLine(), $node->getNodeTag());
55
  }
56
  }
57
 
library/twig/twig/lib/Twig/NodeVisitorInterface.php CHANGED
@@ -19,9 +19,6 @@ interface Twig_NodeVisitorInterface
19
  /**
20
  * Called before child nodes are visited.
21
  *
22
- * @param Twig_NodeInterface $node The node to visit
23
- * @param Twig_Environment $env The Twig environment instance
24
- *
25
  * @return Twig_NodeInterface The modified node
26
  */
27
  public function enterNode(Twig_NodeInterface $node, Twig_Environment $env);
@@ -29,9 +26,6 @@ interface Twig_NodeVisitorInterface
29
  /**
30
  * Called after child nodes are visited.
31
  *
32
- * @param Twig_NodeInterface $node The node to visit
33
- * @param Twig_Environment $env The Twig environment instance
34
- *
35
  * @return Twig_NodeInterface|false The modified node or false if the node must be removed
36
  */
37
  public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env);
19
  /**
20
  * Called before child nodes are visited.
21
  *
 
 
 
22
  * @return Twig_NodeInterface The modified node
23
  */
24
  public function enterNode(Twig_NodeInterface $node, Twig_Environment $env);
26
  /**
27
  * Called after child nodes are visited.
28
  *
 
 
 
29
  * @return Twig_NodeInterface|false The modified node or false if the node must be removed
30
  */
31
  public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env);
library/twig/twig/lib/Twig/Parser.php CHANGED
@@ -32,18 +32,18 @@ class Twig_Parser implements Twig_ParserInterface
32
  protected $traits;
33
  protected $embeddedTemplates = array();
34
 
35
- /**
36
- * Constructor.
37
- *
38
- * @param Twig_Environment $env A Twig_Environment instance
39
- */
40
  public function __construct(Twig_Environment $env)
41
  {
42
  $this->env = $env;
43
  }
44
 
 
 
 
45
  public function getEnvironment()
46
  {
 
 
47
  return $this->env;
48
  }
49
 
@@ -52,9 +52,14 @@ class Twig_Parser implements Twig_ParserInterface
52
  return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
53
  }
54
 
 
 
 
55
  public function getFilename()
56
  {
57
- return $this->stream->getFilename();
 
 
58
  }
59
 
60
  /**
@@ -64,6 +69,7 @@ class Twig_Parser implements Twig_ParserInterface
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;
@@ -84,7 +90,7 @@ class Twig_Parser implements Twig_ParserInterface
84
  }
85
 
86
  if (null === $this->expressionParser) {
87
- $this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators());
88
  }
89
 
90
  $this->stream = $stream;
@@ -103,8 +109,8 @@ class Twig_Parser implements Twig_ParserInterface
103
  $body = new Twig_Node();
104
  }
105
  } catch (Twig_Error_Syntax $e) {
106
- if (!$e->getTemplateFile()) {
107
- $e->setTemplateFile($this->getFilename());
108
  }
109
 
110
  if (!$e->getTemplateLine()) {
@@ -114,7 +120,7 @@ class Twig_Parser implements Twig_ParserInterface
114
  throw $e;
115
  }
116
 
117
- $node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $this->getFilename(), $stream->getSource());
118
 
119
  $traverser = new Twig_NodeTraverser($this->env, $this->visitors);
120
 
@@ -151,7 +157,7 @@ class Twig_Parser implements Twig_ParserInterface
151
  $token = $this->getCurrentToken();
152
 
153
  if ($token->getType() !== Twig_Token::NAME_TYPE) {
154
- throw new Twig_Error_Syntax('A block must start with a tag name.', $token->getLine(), $this->getFilename());
155
  }
156
 
157
  if (null !== $test && call_user_func($test, $token)) {
@@ -169,13 +175,13 @@ class Twig_Parser implements Twig_ParserInterface
169
  $subparser = $this->handlers->getTokenParser($token->getValue());
170
  if (null === $subparser) {
171
  if (null !== $test) {
172
- $e = new Twig_Error_Syntax(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->getFilename());
173
 
174
  if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) {
175
  $e->appendMessage(sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $test[0]->getTag(), $lineno));
176
  }
177
  } else {
178
- $e = new Twig_Error_Syntax(sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->getFilename());
179
  $e->addSuggestions($token->getValue(), array_keys($this->env->getTags()));
180
  }
181
 
@@ -191,7 +197,7 @@ class Twig_Parser implements Twig_ParserInterface
191
  break;
192
 
193
  default:
194
- throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', 0, $this->getFilename());
195
  }
196
  }
197
 
@@ -202,13 +208,23 @@ class Twig_Parser implements Twig_ParserInterface
202
  return new Twig_Node($rv, array(), $lineno);
203
  }
204
 
 
 
 
205
  public function addHandler($name, $class)
206
  {
 
 
207
  $this->handlers[$name] = $class;
208
  }
209
 
 
 
 
210
  public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
211
  {
 
 
212
  $this->visitors[] = $visitor;
213
  }
214
 
@@ -244,7 +260,7 @@ class Twig_Parser implements Twig_ParserInterface
244
 
245
  public function setBlock($name, Twig_Node_Block $value)
246
  {
247
- $this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getLine());
248
  }
249
 
250
  public function hasMacro($name)
@@ -255,7 +271,7 @@ class Twig_Parser implements Twig_ParserInterface
255
  public function setMacro($name, Twig_Node_Macro $node)
256
  {
257
  if ($this->isReservedMacroName($name)) {
258
- throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword.', $name), $node->getLine(), $this->getFilename());
259
  }
260
 
261
  $this->macros[$name] = $node;
@@ -325,9 +341,7 @@ class Twig_Parser implements Twig_ParserInterface
325
  }
326
 
327
  /**
328
- * Gets the expression parser.
329
- *
330
- * @return Twig_ExpressionParser The expression parser
331
  */
332
  public function getExpressionParser()
333
  {
@@ -345,9 +359,7 @@ class Twig_Parser implements Twig_ParserInterface
345
  }
346
 
347
  /**
348
- * Gets the token stream.
349
- *
350
- * @return Twig_TokenStream The token stream
351
  */
352
  public function getStream()
353
  {
@@ -355,9 +367,7 @@ class Twig_Parser implements Twig_ParserInterface
355
  }
356
 
357
  /**
358
- * Gets the current token.
359
- *
360
- * @return Twig_Token The current token
361
  */
362
  public function getCurrentToken()
363
  {
@@ -373,10 +383,10 @@ class Twig_Parser implements Twig_ParserInterface
373
  (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface)
374
  ) {
375
  if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) {
376
- throw new Twig_Error_Syntax('A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed.', $node->getLine(), $this->getFilename());
377
  }
378
 
379
- throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->getFilename());
380
  }
381
 
382
  // bypass "set" nodes as they "capture" the output
32
  protected $traits;
33
  protected $embeddedTemplates = array();
34
 
 
 
 
 
 
35
  public function __construct(Twig_Environment $env)
36
  {
37
  $this->env = $env;
38
  }
39
 
40
+ /**
41
+ * @deprecated since 1.27 (to be removed in 2.0)
42
+ */
43
  public function getEnvironment()
44
  {
45
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED);
46
+
47
  return $this->env;
48
  }
49
 
52
  return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
53
  }
54
 
55
+ /**
56
+ * @deprecated since 1.27 (to be removed in 2.0). Use $parser->getStream()->getSourceContext()->getPath() instead.
57
+ */
58
  public function getFilename()
59
  {
60
+ @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use $parser->getStream()->getSourceContext()->getPath() instead.', __METHOD__), E_USER_DEPRECATED);
61
+
62
+ return $this->stream->getSourceContext()->getName();
63
  }
64
 
65
  /**
69
  {
70
  // push all variables into the stack to keep the current state of the parser
71
  // using get_object_vars() instead of foreach would lead to https://bugs.php.net/71336
72
+ // This hack can be removed when min version if PHP 7.0
73
  $vars = array();
74
  foreach ($this as $k => $v) {
75
  $vars[$k] = $v;
90
  }
91
 
92
  if (null === $this->expressionParser) {
93
+ $this->expressionParser = new Twig_ExpressionParser($this, $this->env);
94
  }
95
 
96
  $this->stream = $stream;
109
  $body = new Twig_Node();
110
  }
111
  } catch (Twig_Error_Syntax $e) {
112
+ if (!$e->getTemplateName()) {
113
+ $e->setTemplateName($this->stream->getSourceContext()->getName());
114
  }
115
 
116
  if (!$e->getTemplateLine()) {
120
  throw $e;
121
  }
122
 
123
+ $node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $stream->getSourceContext());
124
 
125
  $traverser = new Twig_NodeTraverser($this->env, $this->visitors);
126
 
157
  $token = $this->getCurrentToken();
158
 
159
  if ($token->getType() !== Twig_Token::NAME_TYPE) {
160
+ throw new Twig_Error_Syntax('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext()->getName());
161
  }
162
 
163
  if (null !== $test && call_user_func($test, $token)) {
175
  $subparser = $this->handlers->getTokenParser($token->getValue());
176
  if (null === $subparser) {
177
  if (null !== $test) {
178
+ $e = new Twig_Error_Syntax(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()->getName());
179
 
180
  if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) {
181
  $e->appendMessage(sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $test[0]->getTag(), $lineno));
182
  }
183
  } else {
184
+ $e = new Twig_Error_Syntax(sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()->getName());
185
  $e->addSuggestions($token->getValue(), array_keys($this->env->getTags()));
186
  }
187
 
197
  break;
198
 
199
  default:
200
+ throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', 0, $this->stream->getSourceContext()->getName());
201
  }
202
  }
203
 
208
  return new Twig_Node($rv, array(), $lineno);
209
  }
210
 
211
+ /**
212
+ * @deprecated since 1.27 (to be removed in 2.0)
213
+ */
214
  public function addHandler($name, $class)
215
  {
216
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED);
217
+
218
  $this->handlers[$name] = $class;
219
  }
220
 
221
+ /**
222
+ * @deprecated since 1.27 (to be removed in 2.0)
223
+ */
224
  public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
225
  {
226
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED);
227
+
228
  $this->visitors[] = $visitor;
229
  }
230
 
260
 
261
  public function setBlock($name, Twig_Node_Block $value)
262
  {
263
+ $this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getTemplateLine());
264
  }
265
 
266
  public function hasMacro($name)
271
  public function setMacro($name, Twig_Node_Macro $node)
272
  {
273
  if ($this->isReservedMacroName($name)) {
274
+ throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword.', $name), $node->getTemplateLine(), $this->stream->getSourceContext()->getName());
275
  }
276
 
277
  $this->macros[$name] = $node;
341
  }
342
 
343
  /**
344
+ * @return Twig_ExpressionParser
 
 
345
  */
346
  public function getExpressionParser()
347
  {
359
  }
360
 
361
  /**
362
+ * @return Twig_TokenStream
 
 
363
  */
364
  public function getStream()
365
  {
367
  }
368
 
369
  /**
370
+ * @return Twig_Token
 
 
371
  */
372
  public function getCurrentToken()
373
  {
383
  (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface)
384
  ) {
385
  if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) {
386
+ throw new Twig_Error_Syntax('A template that extends another one cannot start with a byte order mark (BOM); it must be removed.', $node->getTemplateLine(), $this->stream->getSourceContext()->getName());
387
  }
388
 
389
+ throw new Twig_Error_Syntax('A template that extends another one cannot include contents outside Twig blocks. Did you forget to put the contents inside a {% block %} tag?', $node->getTemplateLine(), $this->stream->getSourceContext()->getName());
390
  }
391
 
392
  // bypass "set" nodes as they "capture" the output
library/twig/twig/lib/Twig/ParserInterface.php CHANGED
@@ -21,9 +21,7 @@ interface Twig_ParserInterface
21
  /**
22
  * Converts a token stream to a node tree.
23
  *
24
- * @param Twig_TokenStream $stream A token stream instance
25
- *
26
- * @return Twig_Node_Module A node tree
27
  *
28
  * @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
29
  */
21
  /**
22
  * Converts a token stream to a node tree.
23
  *
24
+ * @return Twig_Node_Module
 
 
25
  *
26
  * @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
27
  */
library/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php CHANGED
@@ -36,7 +36,7 @@ class Twig_Profiler_NodeVisitor_Profiler extends Twig_BaseNodeVisitor
36
  {
37
  if ($node instanceof Twig_Node_Module) {
38
  $varName = $this->getVarName();
39
- $node->setNode('display_start', new Twig_Node(array(new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::TEMPLATE, $node->getAttribute('filename'), $varName), $node->getNode('display_start'))));
40
  $node->setNode('display_end', new Twig_Node(array(new Twig_Profiler_Node_LeaveProfile($varName), $node->getNode('display_end'))));
41
  } elseif ($node instanceof Twig_Node_Block) {
42
  $varName = $this->getVarName();
36
  {
37
  if ($node instanceof Twig_Node_Module) {
38
  $varName = $this->getVarName();
39
+ $node->setNode('display_start', new Twig_Node(array(new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::TEMPLATE, $node->getTemplateName(), $varName), $node->getNode('display_start'))));
40
  $node->setNode('display_end', new Twig_Node(array(new Twig_Profiler_Node_LeaveProfile($varName), $node->getNode('display_end'))));
41
  } elseif ($node instanceof Twig_Node_Block) {
42
  $varName = $this->getVarName();
library/twig/twig/lib/Twig/RuntimeLoaderInterface.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /**
13
+ * Creates runtime implementations for Twig elements (filters/functions/tests).
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ interface Twig_RuntimeLoaderInterface
18
+ {
19
+ /**
20
+ * Creates the runtime implementation of a Twig element (filter/function/test).
21
+ *
22
+ * @param string $class A runtime class
23
+ *
24
+ * @return object|null The runtime instance or null if the loader does not know how to create the runtime for this class
25
+ */
26
+ public function load($class);
27
+ }
library/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) 2009 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
+ /**
13
+ * Exception thrown when a not allowed class method is used in a template.
14
+ *
15
+ * @author Kit Burton-Senior <mail@kitbs.com>
16
+ */
17
+ class Twig_Sandbox_SecurityNotAllowedMethodError extends Twig_Sandbox_SecurityError
18
+ {
19
+ private $className;
20
+ private $methodName;
21
+
22
+ public function __construct($message, $className, $methodName, $lineno = -1, $filename = null, Exception $previous = null)
23
+ {
24
+ parent::__construct($message, $lineno, $filename, $previous);
25
+ $this->className = $className;
26
+ $this->methodName = $methodName;
27
+ }
28
+
29
+ public function getClassName()
30
+ {
31
+ return $this->className;
32
+ }
33
+
34
+ public function getMethodName()
35
+ {
36
+ return $this->methodName;
37
+ }
38
+ }
library/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) 2009 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
+ /**
13
+ * Exception thrown when a not allowed class property is used in a template.
14
+ *
15
+ * @author Kit Burton-Senior <mail@kitbs.com>
16
+ */
17
+ class Twig_Sandbox_SecurityNotAllowedPropertyError extends Twig_Sandbox_SecurityError
18
+ {
19
+ private $className;
20
+ private $propertyName;
21
+
22
+ public function __construct($message, $className, $propertyName, $lineno = -1, $filename = null, Exception $previous = null)
23
+ {
24
+ parent::__construct($message, $lineno, $filename, $previous);
25
+ $this->className = $className;
26
+ $this->propertyName = $propertyName;
27
+ }
28
+
29
+ public function getClassName()
30
+ {
31
+ return $this->className;
32
+ }
33
+
34
+ public function getPropertyName()
35
+ {
36
+ return $this->propertyName;
37
+ }
38
+ }
library/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php CHANGED
@@ -97,7 +97,8 @@ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterfac
97
  }
98
 
99
  if (!$allowed) {
100
- throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, get_class($obj)));
 
101
  }
102
  }
103
 
@@ -113,7 +114,8 @@ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterfac
113
  }
114
 
115
  if (!$allowed) {
116
- throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, get_class($obj)));
 
117
  }
118
  }
119
  }
97
  }
98
 
99
  if (!$allowed) {
100
+ $class = get_class($obj);
101
+ throw new Twig_Sandbox_SecurityNotAllowedMethodError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, $class), $class, $method);
102
  }
103
  }
104
 
114
  }
115
 
116
  if (!$allowed) {
117
+ $class = get_class($obj);
118
+ throw new Twig_Sandbox_SecurityNotAllowedPropertyError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, $class), $class, $property);
119
  }
120
  }
121
  }
library/twig/twig/lib/Twig/Source.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) 2016 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
+ /**
13
+ * Holds information about a non-compiled Twig template.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Source
18
+ {
19
+ private $code;
20
+ private $name;
21
+ private $path;
22
+
23
+ /**
24
+ * @param string $code The template source code
25
+ * @param string $name The template logical name
26
+ * @param string $path The filesystem path of the template if any
27
+ */
28
+ public function __construct($code, $name, $path = '')
29
+ {
30
+ $this->code = $code;
31
+ $this->name = $name;
32
+ $this->path = $path;
33
+ }
34
+
35
+ public function getCode()
36
+ {
37
+ return $this->code;
38
+ }
39
+
40
+ public function getName()
41
+ {
42
+ return $this->name;
43
+ }
44
+
45
+ public function getPath()
46
+ {
47
+ return $this->path;
48
+ }
49
+ }
library/twig/twig/lib/Twig/SourceContextLoaderInterface.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) 2016 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
+ /**
13
+ * Adds a getSourceContext() method for loaders.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ *
17
+ * @deprecated since 1.27 (to be removed in 3.0)
18
+ */
19
+ interface Twig_SourceContextLoaderInterface
20
+ {
21
+ /**
22
+ * Returns the source context for a given template logical name.
23
+ *
24
+ * @param string $name The template logical name
25
+ *
26
+ * @return Twig_Source
27
+ *
28
+ * @throws Twig_Error_Loader When $name is not found
29
+ */
30
+ public function getSourceContext($name);
31
+ }
library/twig/twig/lib/Twig/Template.php CHANGED
@@ -13,10 +13,19 @@
13
  /**
14
  * Default base class for compiled templates.
15
  *
 
 
 
 
16
  * @author Fabien Potencier <fabien@symfony.com>
 
 
17
  */
18
  abstract class Twig_Template implements Twig_TemplateInterface
19
  {
 
 
 
20
  protected static $cache = array();
21
 
22
  protected $parent;
@@ -25,11 +34,6 @@ abstract class Twig_Template implements Twig_TemplateInterface
25
  protected $blocks = array();
26
  protected $traits = array();
27
 
28
- /**
29
- * Constructor.
30
- *
31
- * @param Twig_Environment $env A Twig_Environment instance
32
- */
33
  public function __construct(Twig_Environment $env)
34
  {
35
  $this->env = $env;
@@ -49,18 +53,35 @@ abstract class Twig_Template implements Twig_TemplateInterface
49
  *
50
  * @internal
51
  */
52
- abstract public function getDebugInfo();
 
 
 
53
 
54
  /**
55
  * Returns the template source code.
56
  *
57
  * @return string The template source code
 
 
58
  */
59
  public function getSource()
60
  {
 
 
61
  return '';
62
  }
63
 
 
 
 
 
 
 
 
 
 
 
64
  /**
65
  * @deprecated since 1.20 (to be removed in 2.0)
66
  */
@@ -104,7 +125,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
104
  $this->parents[$parent] = $this->loadTemplate($parent);
105
  }
106
  } catch (Twig_Error_Loader $e) {
107
- $e->setTemplateFile(null);
108
  $e->guess();
109
 
110
  throw $e;
@@ -144,7 +165,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
144
  } elseif (false !== $parent = $this->getParent($context)) {
145
  $parent->displayBlock($name, $context, $blocks, false);
146
  } else {
147
- throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
148
  }
149
  }
150
 
@@ -185,8 +206,8 @@ abstract class Twig_Template implements Twig_TemplateInterface
185
  try {
186
  $template->$block($context, $blocks);
187
  } catch (Twig_Error $e) {
188
- if (!$e->getTemplateFile()) {
189
- $e->setTemplateFile($template->getTemplateName());
190
  }
191
 
192
  // this is mostly useful for Twig_Error_Loader exceptions
@@ -251,44 +272,70 @@ abstract class Twig_Template implements Twig_TemplateInterface
251
  }
252
 
253
  /**
254
- * Returns whether a block exists or not.
255
- *
256
- * This method is for internal use only and should never be called
257
- * directly.
258
  *
259
- * This method does only return blocks defined in the current template
260
- * or defined in "used" traits.
261
  *
262
- * It does not return blocks from parent templates as the parent
263
- * template name can be dynamic, which is only known based on the
264
- * current context.
265
- *
266
- * @param string $name The block name
267
  *
268
  * @return bool true if the block exists, false otherwise
269
  *
270
  * @internal
271
  */
272
- public function hasBlock($name)
273
  {
274
- return isset($this->blocks[(string) $name]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  }
276
 
277
  /**
278
- * Returns all block names.
279
  *
280
- * This method is for internal use only and should never be called
281
- * directly.
282
  *
283
- * @return array An array of block names
 
284
  *
285
- * @see hasBlock
286
  *
287
  * @internal
288
  */
289
- public function getBlockNames()
290
  {
291
- return array_keys($this->blocks);
 
 
 
 
 
 
 
 
 
 
 
 
292
  }
293
 
294
  protected function loadTemplate($template, $templateName = null, $line = null, $index = null)
@@ -302,10 +349,14 @@ abstract class Twig_Template implements Twig_TemplateInterface
302
  return $template;
303
  }
304
 
 
 
 
 
305
  return $this->env->loadTemplate($template, $index);
306
  } catch (Twig_Error $e) {
307
- if (!$e->getTemplateFile()) {
308
- $e->setTemplateFile($templateName ? $templateName : $this->getTemplateName());
309
  }
310
 
311
  if ($e->getTemplateLine()) {
@@ -330,8 +381,6 @@ abstract class Twig_Template implements Twig_TemplateInterface
330
  *
331
  * @return array An array of blocks
332
  *
333
- * @see hasBlock
334
- *
335
  * @internal
336
  */
337
  public function getBlocks()
@@ -378,8 +427,8 @@ abstract class Twig_Template implements Twig_TemplateInterface
378
  try {
379
  $this->doDisplay($context, $blocks);
380
  } catch (Twig_Error $e) {
381
- if (!$e->getTemplateFile()) {
382
- $e->setTemplateFile($this->getTemplateName());
383
  }
384
 
385
  // this is mostly useful for Twig_Error_Loader exceptions
@@ -431,7 +480,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
431
  return;
432
  }
433
 
434
- throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName());
435
  }
436
 
437
  return $context[$item];
@@ -450,6 +499,8 @@ abstract class Twig_Template implements Twig_TemplateInterface
450
  * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
451
  *
452
  * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
 
 
453
  */
454
  protected function getAttribute($object, $item, array $arguments = array(), $type = self::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
455
  {
@@ -457,7 +508,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
457
  if (self::METHOD_CALL !== $type) {
458
  $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
459
 
460
- if ((is_array($object) && array_key_exists($arrayItem, $object))
461
  || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
462
  ) {
463
  if ($isDefinedTest) {
@@ -477,25 +528,25 @@ abstract class Twig_Template implements Twig_TemplateInterface
477
  }
478
 
479
  if ($object instanceof ArrayAccess) {
480
- $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist', $arrayItem, get_class($object));
481
  } elseif (is_object($object)) {
482
- $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object));
483
  } elseif (is_array($object)) {
484
  if (empty($object)) {
485
- $message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
486
  } else {
487
- $message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
488
  }
489
  } elseif (self::ARRAY_CALL === $type) {
490
  if (null === $object) {
491
- $message = sprintf('Impossible to access a key ("%s") on a null variable', $item);
492
  } else {
493
- $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
494
  }
495
  } elseif (null === $object) {
496
- $message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item);
497
  } else {
498
- $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
499
  }
500
 
501
  throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
@@ -512,9 +563,9 @@ abstract class Twig_Template implements Twig_TemplateInterface
512
  }
513
 
514
  if (null === $object) {
515
- $message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item);
516
  } else {
517
- $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
518
  }
519
 
520
  throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
@@ -527,8 +578,8 @@ abstract class Twig_Template implements Twig_TemplateInterface
527
  return true;
528
  }
529
 
530
- if ($this->env->hasExtension('sandbox')) {
531
- $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
532
  }
533
 
534
  return $object->$item;
@@ -538,37 +589,57 @@ abstract class Twig_Template implements Twig_TemplateInterface
538
  $class = get_class($object);
539
 
540
  // object method
541
- if (!isset(self::$cache[$class]['methods'])) {
542
  // get_class_methods returns all methods accessible in the scope, but we only want public ones to be accessible in templates
543
  if ($object instanceof self) {
544
  $ref = new ReflectionClass($class);
545
  $methods = array();
546
 
547
  foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) {
548
- $methodName = strtolower($refMethod->name);
549
-
550
  // Accessing the environment from templates is forbidden to prevent untrusted changes to the environment
551
- if ('getenvironment' !== $methodName) {
552
- $methods[$methodName] = true;
553
  }
554
  }
555
-
556
- self::$cache[$class]['methods'] = $methods;
557
  } else {
558
- self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
559
  }
 
560
  }
561
 
562
  $call = false;
563
- $lcItem = strtolower($item);
564
- if (isset(self::$cache[$class]['methods'][$lcItem])) {
565
- $method = (string) $item;
566
- } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
567
- $method = 'get'.$item;
568
- } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
569
- $method = 'is'.$item;
570
- } elseif (isset(self::$cache[$class]['methods']['__call'])) {
571
- $method = (string) $item;
572
  $call = true;
573
  } else {
574
  if ($isDefinedTest) {
@@ -579,21 +650,25 @@ abstract class Twig_Template implements Twig_TemplateInterface
579
  return;
580
  }
581
 
582
- throw new Twig_Error_Runtime(sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()" or "__call()" exist and have public access in class "%2$s"', $item, get_class($object)), -1, $this->getTemplateName());
583
  }
584
 
585
  if ($isDefinedTest) {
586
  return true;
587
  }
588
 
589
- if ($this->env->hasExtension('sandbox')) {
590
- $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
591
  }
592
 
593
  // Some objects throw exceptions when they have __call, and the method we try
594
  // to call is not supported. If ignoreStrictCheck is true, we should return null.
595
  try {
596
- $ret = call_user_func_array(array($object, $method), $arguments);
 
 
 
 
597
  } catch (BadMethodCallException $e) {
598
  if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
599
  return;
@@ -601,9 +676,19 @@ abstract class Twig_Template implements Twig_TemplateInterface
601
  throw $e;
602
  }
603
 
604
- // useful when calling a template method from a template
605
- // this is not supported but unfortunately heavily used in the Symfony profiler
606
  if ($object instanceof Twig_TemplateInterface) {
 
 
 
 
 
 
 
 
 
 
 
607
  return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
608
  }
609
 
13
  /**
14
  * Default base class for compiled templates.
15
  *
16
+ * This class is an implementation detail of how template compilation currently
17
+ * works, which might change. It should never be used directly. Use $twig->load()
18
+ * instead, which returns an instance of Twig_TemplateWrapper.
19
+ *
20
  * @author Fabien Potencier <fabien@symfony.com>
21
+ *
22
+ * @internal
23
  */
24
  abstract class Twig_Template implements Twig_TemplateInterface
25
  {
26
+ /**
27
+ * @internal
28
+ */
29
  protected static $cache = array();
30
 
31
  protected $parent;
34
  protected $blocks = array();
35
  protected $traits = array();
36
 
 
 
 
 
 
37
  public function __construct(Twig_Environment $env)
38
  {
39
  $this->env = $env;
53
  *
54
  * @internal
55
  */
56
+ public function getDebugInfo()
57
+ {
58
+ return array();
59
+ }
60
 
61
  /**
62
  * Returns the template source code.
63
  *
64
  * @return string The template source code
65
+ *
66
+ * @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead
67
  */
68
  public function getSource()
69
  {
70
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);
71
+
72
  return '';
73
  }
74
 
75
+ /**
76
+ * Returns information about the original template source code.
77
+ *
78
+ * @return Twig_Source
79
+ */
80
+ public function getSourceContext()
81
+ {
82
+ return new Twig_Source('', $this->getTemplateName());
83
+ }
84
+
85
  /**
86
  * @deprecated since 1.20 (to be removed in 2.0)
87
  */
125
  $this->parents[$parent] = $this->loadTemplate($parent);
126
  }
127
  } catch (Twig_Error_Loader $e) {
128
+ $e->setTemplateName(null);
129
  $e->guess();
130
 
131
  throw $e;
165
  } elseif (false !== $parent = $this->getParent($context)) {
166
  $parent->displayBlock($name, $context, $blocks, false);
167
  } else {
168
+ throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block.', $name), -1, $this->getTemplateName());
169
  }
170
  }
171
 
206
  try {
207
  $template->$block($context, $blocks);
208
  } catch (Twig_Error $e) {
209
+ if (!$e->getTemplateName()) {
210
+ $e->setTemplateName($template->getTemplateName());
211
  }
212
 
213
  // this is mostly useful for Twig_Error_Loader exceptions
272
  }
273
 
274
  /**
275
+ * Returns whether a block exists or not in the current context of the template.
 
 
 
276
  *
277
+ * This method checks blocks defined in the current template
278
+ * or defined in "used" traits or defined in parent templates.
279
  *
280
+ * @param string $name The block name
281
+ * @param array $context The context
282
+ * @param array $blocks The current set of blocks
 
 
283
  *
284
  * @return bool true if the block exists, false otherwise
285
  *
286
  * @internal
287
  */
288
+ public function hasBlock($name, array $context = null, array $blocks = array())
289
  {
290
+ if (null === $context) {
291
+ @trigger_error('The '.__METHOD__.' method is internal and should never be called; calling it directly is deprecated since version 1.28 and won\'t be possible anymore in 2.0.', E_USER_DEPRECATED);
292
+
293
+ return isset($this->blocks[(string) $name]);
294
+ }
295
+
296
+ if (isset($blocks[$name])) {
297
+ return $blocks[$name][0] instanceof self;
298
+ }
299
+
300
+ if (isset($this->blocks[$name])) {
301
+ return true;
302
+ }
303
+
304
+ if (false !== $parent = $this->getParent($context)) {
305
+ return $parent->hasBlock($name, $context);
306
+ }
307
+
308
+ return false;
309
  }
310
 
311
  /**
312
+ * Returns all block names in the current context of the template.
313
  *
314
+ * This method checks blocks defined in the current template
315
+ * or defined in "used" traits or defined in parent templates.
316
  *
317
+ * @param array $context The context
318
+ * @param array $blocks The current set of blocks
319
  *
320
+ * @return array An array of block names
321
  *
322
  * @internal
323
  */
324
+ public function getBlockNames(array $context = null, array $blocks = array())
325
  {
326
+ if (null === $context) {
327
+ @trigger_error('The '.__METHOD__.' method is internal and should never be called; calling it directly is deprecated since version 1.28 and won\'t be possible anymore in 2.0.', E_USER_DEPRECATED);
328
+
329
+ return array_keys($this->blocks);
330
+ }
331
+
332
+ $names = array_merge(array_keys($blocks), array_keys($this->blocks));
333
+
334
+ if (false !== $parent = $this->getParent($context)) {
335
+ $names = array_merge($names, $parent->getBlockNames($context));
336
+ }
337
+
338
+ return array_unique($names);
339
  }
340
 
341
  protected function loadTemplate($template, $templateName = null, $line = null, $index = null)
349
  return $template;
350
  }
351
 
352
+ if ($template instanceof Twig_TemplateWrapper) {
353
+ return $template;
354
+ }
355
+
356
  return $this->env->loadTemplate($template, $index);
357
  } catch (Twig_Error $e) {
358
+ if (!$e->getTemplateName()) {
359
+ $e->setTemplateName($templateName ? $templateName : $this->getTemplateName());
360
  }
361
 
362
  if ($e->getTemplateLine()) {
381
  *
382
  * @return array An array of blocks
383
  *
 
 
384
  * @internal
385
  */
386
  public function getBlocks()
427
  try {
428
  $this->doDisplay($context, $blocks);
429
  } catch (Twig_Error $e) {
430
+ if (!$e->getTemplateName()) {
431
+ $e->setTemplateName($this->getTemplateName());
432
  }
433
 
434
  // this is mostly useful for Twig_Error_Loader exceptions
480
  return;
481
  }
482
 
483
+ throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist.', $item), -1, $this->getTemplateName());
484
  }
485
 
486
  return $context[$item];
499
  * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
500
  *
501
  * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
502
+ *
503
+ * @internal
504
  */
505
  protected function getAttribute($object, $item, array $arguments = array(), $type = self::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
506
  {
508
  if (self::METHOD_CALL !== $type) {
509
  $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
510
 
511
+ if ((is_array($object) && (isset($object[$arrayItem]) || array_key_exists($arrayItem, $object)))
512
  || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
513
  ) {
514
  if ($isDefinedTest) {
528
  }
529
 
530
  if ($object instanceof ArrayAccess) {
531
+ $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, get_class($object));
532
  } elseif (is_object($object)) {
533
+ $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, get_class($object));
534
  } elseif (is_array($object)) {
535
  if (empty($object)) {
536
+ $message = sprintf('Key "%s" does not exist as the array is empty.', $arrayItem);
537
  } else {
538
+ $message = sprintf('Key "%s" for array with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object)));
539
  }
540
  } elseif (self::ARRAY_CALL === $type) {
541
  if (null === $object) {
542
+ $message = sprintf('Impossible to access a key ("%s") on a null variable.', $item);
543
  } else {
544
+ $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, gettype($object), $object);
545
  }
546
  } elseif (null === $object) {
547
+ $message = sprintf('Impossible to access an attribute ("%s") on a null variable.', $item);
548
  } else {
549
+ $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, gettype($object), $object);
550
  }
551
 
552
  throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
563
  }
564
 
565
  if (null === $object) {
566
+ $message = sprintf('Impossible to invoke a method ("%s") on a null variable.', $item);
567
  } else {
568
+ $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, gettype($object), $object);
569
  }
570
 
571
  throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
578
  return true;
579
  }
580
 
581
+ if ($this->env->hasExtension('Twig_Extension_Sandbox')) {
582
+ $this->env->getExtension('Twig_Extension_Sandbox')->checkPropertyAllowed($object, $item);
583
  }
584
 
585
  return $object->$item;
589
  $class = get_class($object);
590
 
591
  // object method
592
+ if (!isset(self::$cache[$class])) {
593
  // get_class_methods returns all methods accessible in the scope, but we only want public ones to be accessible in templates
594
  if ($object instanceof self) {
595
  $ref = new ReflectionClass($class);
596
  $methods = array();
597
 
598
  foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) {
 
 
599
  // Accessing the environment from templates is forbidden to prevent untrusted changes to the environment
600
+ if ('getenvironment' !== strtolower($refMethod->name)) {
601
+ $methods[] = $refMethod->name;
602
  }
603
  }
 
 
604
  } else {
605
+ $methods = get_class_methods($object);
606
+ }
607
+ // sort values to have consistent behavior, so that "get" methods win precedence over "is" methods
608
+ sort($methods);
609
+
610
+ $cache = array();
611
+
612
+ foreach ($methods as $method) {
613
+ $cache[$method] = $method;
614
+ $cache[$lcName = strtolower($method)] = $method;
615
+
616
+ if ('g' === $lcName[0] && 0 === strpos($lcName, 'get')) {
617
+ $name = substr($method, 3);
618
+ $lcName = substr($lcName, 3);
619
+ } elseif ('i' === $lcName[0] && 0 === strpos($lcName, 'is')) {
620
+ $name = substr($method, 2);
621
+ $lcName = substr($lcName, 2);
622
+ } else {
623
+ continue;
624
+ }
625
+
626
+ if (!isset($cache[$name])) {
627
+ $cache[$name] = $method;
628
+ }
629
+ if (!isset($cache[$lcName])) {
630
+ $cache[$lcName] = $method;
631
+ }
632
  }
633
+ self::$cache[$class] = $cache;
634
  }
635
 
636
  $call = false;
637
+ if (isset(self::$cache[$class][$item])) {
638
+ $method = self::$cache[$class][$item];
639
+ } elseif (isset(self::$cache[$class][$lcItem = strtolower($item)])) {
640
+ $method = self::$cache[$class][$lcItem];
641
+ } elseif (isset(self::$cache[$class]['__call'])) {
642
+ $method = $item;
 
 
 
643
  $call = true;
644
  } else {
645
  if ($isDefinedTest) {
650
  return;
651
  }
652
 
653
+ throw new Twig_Error_Runtime(sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()" or "__call()" exist and have public access in class "%2$s".', $item, $class), -1, $this->getTemplateName());
654
  }
655
 
656
  if ($isDefinedTest) {
657
  return true;
658
  }
659
 
660
+ if ($this->env->hasExtension('Twig_Extension_Sandbox')) {
661
+ $this->env->getExtension('Twig_Extension_Sandbox')->checkMethodAllowed($object, $method);
662
  }
663
 
664
  // Some objects throw exceptions when they have __call, and the method we try
665
  // to call is not supported. If ignoreStrictCheck is true, we should return null.
666
  try {
667
+ if (!$arguments) {
668
+ $ret = $object->$method();
669
+ } else {
670
+ $ret = call_user_func_array(array($object, $method), $arguments);
671
+ }
672
  } catch (BadMethodCallException $e) {
673
  if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
674
  return;
676
  throw $e;
677
  }
678
 
679
+ // @deprecated in 1.28
 
680
  if ($object instanceof Twig_TemplateInterface) {
681
+ $self = $object->getTemplateName() === $this->getTemplateName();
682
+ $message = sprintf('Calling "%s" on template "%s" from template "%s" is deprecated since version 1.28 and won\'t be supported anymore in 2.0.', $method, $object->getTemplateName(), $this->getTemplateName());
683
+ if ('renderBlock' === $method || 'displayBlock' === $method) {
684
+ $message .= sprintf(' Use block("%s"%s) instead).', $arguments[0], $self ? '' : ', template');
685
+ } elseif ('hasBlock' === $method) {
686
+ $message .= sprintf(' Use "block("%s"%s) is defined" instead).', $arguments[0], $self ? '' : ', template');
687
+ } elseif ('render' === $method || 'display' === $method) {
688
+ $message .= sprintf(' Use include("%s") instead).', $object->getTemplateName());
689
+ }
690
+ @trigger_error($message, E_USER_DEPRECATED);
691
+
692
  return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
693
  }
694
 
library/twig/twig/lib/Twig/TemplateInterface.php CHANGED
@@ -42,7 +42,7 @@ interface Twig_TemplateInterface
42
  /**
43
  * Returns the bound environment for this template.
44
  *
45
- * @return Twig_Environment The current environment
46
  */
47
  public function getEnvironment();
48
  }
42
  /**
43
  * Returns the bound environment for this template.
44
  *
45
+ * @return Twig_Environment
46
  */
47
  public function getEnvironment();
48
  }
library/twig/twig/lib/Twig/TemplateWrapper.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) 2016 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
+ /**
13
+ * Exposes a template to userland.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ final class Twig_TemplateWrapper
18
+ {
19
+ private $env;
20
+ private $template;
21
+
22
+ /**
23
+ * This method is for internal use only and should never be called
24
+ * directly (use Twig_Environment::load() instead).
25
+ *
26
+ * @internal
27
+ */
28
+ public function __construct(Twig_Environment $env, Twig_Template $template)
29
+ {
30
+ $this->env = $env;
31
+ $this->template = $template;
32
+ }
33
+
34
+ /**
35
+ * Renders the template.
36
+ *
37
+ * @param array $context An array of parameters to pass to the template
38
+ *
39
+ * @return string The rendered template
40
+ */
41
+ public function render($context = array())
42
+ {
43
+ return $this->template->render($context);
44
+ }
45
+
46
+ /**
47
+ * Displays the template.
48
+ *
49
+ * @param array $context An array of parameters to pass to the template
50
+ */
51
+ public function display($context = array())
52
+ {
53
+ $this->template->display($context);
54
+ }
55
+
56
+ /**
57
+ * Checks if a block is defined.
58
+ *
59
+ * @param string $name The block name
60
+ * @param array $context An array of parameters to pass to the template
61
+ *
62
+ * @return bool
63
+ */
64
+ public function hasBlock($name, $context = array())
65
+ {
66
+ return $this->template->hasBlock($name, $context);
67
+ }
68
+
69
+ /**
70
+ * Returns defined block names in the template.
71
+ *
72
+ * @param array $context An array of parameters to pass to the template
73
+ *
74
+ * @return string[] An array of defined template block names
75
+ */
76
+ public function getBlockNames($context = array())
77
+ {
78
+ return $this->template->getBlockNames($context);
79
+ }
80
+
81
+ /**
82
+ * Renders a template block.
83
+ *
84
+ * @param string $name The block name to render
85
+ * @param array $context An array of parameters to pass to the template
86
+ *
87
+ * @return string The rendered block
88
+ */
89
+ public function renderBlock($name, $context = array())
90
+ {
91
+ $context = $this->env->mergeGlobals($context);
92
+ $level = ob_get_level();
93
+ ob_start();
94
+ try {
95
+ $this->template->displayBlock($name, $context);
96
+ } catch (Exception $e) {
97
+ while (ob_get_level() > $level) {
98
+ ob_end_clean();
99
+ }
100
+
101
+ throw $e;
102
+ } catch (Throwable $e) {
103
+ while (ob_get_level() > $level) {
104
+ ob_end_clean();
105
+ }
106
+
107
+ throw $e;
108
+ }
109
+
110
+ return ob_get_clean();
111
+ }
112
+
113
+ /**
114
+ * Displays a template block.
115
+ *
116
+ * @param string $name The block name to render
117
+ * @param array $context An array of parameters to pass to the template
118
+ */
119
+ public function displayBlock($name, $context = array())
120
+ {
121
+ $this->template->displayBlock($name, $this->env->mergeGlobals($context));
122
+ }
123
+
124
+ /**
125
+ * @return Twig_Source
126
+ */
127
+ public function getSourceContext()
128
+ {
129
+ return $this->template->getSourceContext();
130
+ }
131
+ }
library/twig/twig/lib/Twig/Test/IntegrationTestCase.php CHANGED
@@ -167,13 +167,14 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase
167
  if (false !== $exception) {
168
  $message = $e->getMessage();
169
  $this->assertSame(trim($exception), trim(sprintf('%s: %s', get_class($e), $message)));
170
- $this->assertSame('.', substr($message, strlen($message) - 1), $message, 'Exception message must end with a dot.');
 
171
 
172
  return;
173
  }
174
 
175
  if ($e instanceof Twig_Error_Syntax) {
176
- $e->setTemplateFile($file);
177
 
178
  throw $e;
179
  }
@@ -191,7 +192,7 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase
191
  }
192
 
193
  if ($e instanceof Twig_Error_Syntax) {
194
- $e->setTemplateFile($file);
195
  } else {
196
  $e = new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e);
197
  }
@@ -211,8 +212,13 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase
211
 
212
  foreach (array_keys($templates) as $name) {
213
  echo "Template: $name\n";
214
- $source = $loader->getSource($name);
215
- echo $twig->compile($twig->parse($twig->tokenize($source, $name)));
 
 
 
 
 
216
  }
217
  }
218
  $this->assertEquals($expected, $output, $message.' (in '.$file.')');
167
  if (false !== $exception) {
168
  $message = $e->getMessage();
169
  $this->assertSame(trim($exception), trim(sprintf('%s: %s', get_class($e), $message)));
170
+ $last = substr($message, strlen($message) - 1);
171
+ $this->assertTrue('.' === $last || '?' === $last, $message, 'Exception message must end with a dot or a question mark.');
172
 
173
  return;
174
  }
175
 
176
  if ($e instanceof Twig_Error_Syntax) {
177
+ $e->setTemplateName($file);
178
 
179
  throw $e;
180
  }
192
  }
193
 
194
  if ($e instanceof Twig_Error_Syntax) {
195
+ $e->setTemplateName($file);
196
  } else {
197
  $e = new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e);
198
  }
212
 
213
  foreach (array_keys($templates) as $name) {
214
  echo "Template: $name\n";
215
+ $loader = $twig->getLoader();
216
+ if (!$loader instanceof Twig_SourceContextLoaderInterface) {
217
+ $source = new Twig_Source($loader->getSource($name), $name);
218
+ } else {
219
+ $source = $loader->getSourceContext($name);
220
+ }
221
+ echo $twig->compile($twig->parse($twig->tokenize($source)));
222
  }
223
  }
224
  $this->assertEquals($expected, $output, $message.' (in '.$file.')');
library/twig/twig/lib/Twig/Test/Method.php CHANGED
@@ -35,6 +35,6 @@ class Twig_Test_Method extends Twig_Test
35
 
36
  public function compile()
37
  {
38
- return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
39
  }
40
  }
35
 
36
  public function compile()
37
  {
38
+ return sprintf('$this->env->getExtension(\'%s\')->%s', get_class($this->extension), $this->method);
39
  }
40
  }
library/twig/twig/lib/Twig/Test/NodeTestCase.php CHANGED
@@ -46,6 +46,10 @@ abstract class Twig_Test_NodeTestCase extends PHPUnit_Framework_TestCase
46
  {
47
  $line = $line > 0 ? "// line {$line}\n" : '';
48
 
 
 
 
 
49
  if (PHP_VERSION_ID >= 50400) {
50
  return sprintf('%s(isset($context["%s"]) ? $context["%s"] : null)', $line, $name, $name);
51
  }
46
  {
47
  $line = $line > 0 ? "// line {$line}\n" : '';
48
 
49
+ if (PHP_VERSION_ID >= 70000) {
50
+ return sprintf('%s($context["%s"] ?? null)', $line, $name, $name);
51
+ }
52
+
53
  if (PHP_VERSION_ID >= 50400) {
54
  return sprintf('%s(isset($context["%s"]) ? $context["%s"] : null)', $line, $name, $name);
55
  }
library/twig/twig/lib/Twig/Token.php CHANGED
@@ -36,8 +36,6 @@ class Twig_Token
36
  const INTERPOLATION_END_TYPE = 11;
37
 
38
  /**
39
- * Constructor.
40
- *
41
  * @param int $type The type of the token
42
  * @param string $value The token value
43
  * @param int $lineno The line position in the source
@@ -49,11 +47,6 @@ class Twig_Token
49
  $this->lineno = $lineno;
50
  }
51
 
52
- /**
53
- * Returns a string representation of the token.
54
- *
55
- * @return string A string representation of the token
56
- */
57
  public function __toString()
58
  {
59
  return sprintf('%s(%s)', self::typeToString($this->type, true), $this->value);
@@ -63,9 +56,9 @@ class Twig_Token
63
  * Tests the current token for a type and/or a value.
64
  *
65
  * Parameters may be:
66
- * * just type
67
- * * type and value (or array of possible values)
68
- * * just value (or array of possible values) (NAME_TYPE is used as type)
69
  *
70
  * @param array|int $type The type to test
71
  * @param array|string|null $values The token value
@@ -87,9 +80,7 @@ class Twig_Token
87
  }
88
 
89
  /**
90
- * Gets the line.
91
- *
92
- * @return int The source line
93
  */
94
  public function getLine()
95
  {
@@ -97,9 +88,7 @@ class Twig_Token
97
  }
98
 
99
  /**
100
- * Gets the token type.
101
- *
102
- * @return int The token type
103
  */
104
  public function getType()
105
  {
@@ -107,9 +96,7 @@ class Twig_Token
107
  }
108
 
109
  /**
110
- * Gets the token value.
111
- *
112
- * @return string The token value
113
  */
114
  public function getValue()
115
  {
@@ -174,7 +161,7 @@ class Twig_Token
174
  }
175
 
176
  /**
177
- * Returns the english representation of a given type.
178
  *
179
  * @param int $type The type as an integer
180
  *
36
  const INTERPOLATION_END_TYPE = 11;
37
 
38
  /**
 
 
39
  * @param int $type The type of the token
40
  * @param string $value The token value
41
  * @param int $lineno The line position in the source
47
  $this->lineno = $lineno;
48
  }
49
 
 
 
 
 
 
50
  public function __toString()
51
  {
52
  return sprintf('%s(%s)', self::typeToString($this->type, true), $this->value);
56
  * Tests the current token for a type and/or a value.
57
  *
58
  * Parameters may be:
59
+ * * just type
60
+ * * type and value (or array of possible values)
61
+ * * just value (or array of possible values) (NAME_TYPE is used as type)
62
  *
63
  * @param array|int $type The type to test
64
  * @param array|string|null $values The token value
80
  }
81
 
82
  /**
83
+ * @return int
 
 
84
  */
85
  public function getLine()
86
  {
88
  }
89
 
90
  /**
91
+ * @return int
 
 
92
  */
93
  public function getType()
94
  {
96
  }
97
 
98
  /**
99
+ * @return string
 
 
100
  */
101
  public function getValue()
102
  {
161
  }
162
 
163
  /**
164
+ * Returns the English representation of a given type.
165
  *
166
  * @param int $type The type as an integer
167
  *
library/twig/twig/lib/Twig/TokenParser.php CHANGED
@@ -23,8 +23,6 @@ abstract class Twig_TokenParser implements Twig_TokenParserInterface
23
 
24
  /**
25
  * Sets the parser associated with this token parser.
26
- *
27
- * @param Twig_Parser $parser A Twig_Parser instance
28
  */
29
  public function setParser(Twig_Parser $parser)
30
  {
23
 
24
  /**
25
  * Sets the parser associated with this token parser.
 
 
26
  */
27
  public function setParser(Twig_Parser $parser)
28
  {
library/twig/twig/lib/Twig/TokenParser/AutoEscape.php CHANGED
@@ -39,7 +39,7 @@ class Twig_TokenParser_AutoEscape extends Twig_TokenParser
39
  } else {
40
  $expr = $this->parser->getExpressionParser()->parseExpression();
41
  if (!$expr instanceof Twig_Node_Expression_Constant) {
42
- throw new Twig_Error_Syntax('An escaping strategy must be a string or a bool.', $stream->getCurrent()->getLine(), $stream->getFilename());
43
  }
44
  $value = $expr->getAttribute('value');
45
 
@@ -53,7 +53,7 @@ class Twig_TokenParser_AutoEscape extends Twig_TokenParser
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());
57
  }
58
 
59
  $value = $stream->next()->getValue();
39
  } else {
40
  $expr = $this->parser->getExpressionParser()->parseExpression();
41
  if (!$expr instanceof Twig_Node_Expression_Constant) {
42
+ throw new Twig_Error_Syntax('An escaping strategy must be a string or a bool.', $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName());
43
  }
44
  $value = $expr->getAttribute('value');
45
 
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->getSourceContext()->getName());
57
  }
58
 
59
  $value = $stream->next()->getValue();
library/twig/twig/lib/Twig/TokenParser/Block.php CHANGED
@@ -28,7 +28,7 @@ class Twig_TokenParser_Block extends Twig_TokenParser
28
  $stream = $this->parser->getStream();
29
  $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
30
  if ($this->parser->hasBlock($name)) {
31
- throw new Twig_Error_Syntax(sprintf("The block '%s' has already been defined line %d.", $name, $this->parser->getBlock($name)->getLine()), $stream->getCurrent()->getLine(), $stream->getFilename());
32
  }
33
  $this->parser->setBlock($name, $block = new Twig_Node_Block($name, new Twig_Node(array()), $lineno));
34
  $this->parser->pushLocalScope();
@@ -40,7 +40,7 @@ class Twig_TokenParser_Block extends Twig_TokenParser
40
  $value = $token->getValue();
41
 
42
  if ($value != $name) {
43
- throw new Twig_Error_Syntax(sprintf('Expected endblock for block "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getFilename());
44
  }
45
  }
46
  } else {
28
  $stream = $this->parser->getStream();
29
  $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
30
  if ($this->parser->hasBlock($name)) {
31
+ throw new Twig_Error_Syntax(sprintf("The block '%s' has already been defined line %d.", $name, $this->parser->getBlock($name)->getTemplateLine()), $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName());
32
  }
33
  $this->parser->setBlock($name, $block = new Twig_Node_Block($name, new Twig_Node(array()), $lineno));
34
  $this->parser->pushLocalScope();
40
  $value = $token->getValue();
41
 
42
  if ($value != $name) {
43
+ throw new Twig_Error_Syntax(sprintf('Expected endblock for block "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName());
44
  }
45
  }
46
  } else {
library/twig/twig/lib/Twig/TokenParser/Embed.php CHANGED
@@ -48,7 +48,7 @@ class Twig_TokenParser_Embed extends Twig_TokenParser_Include
48
 
49
  $stream->expect(Twig_Token::BLOCK_END_TYPE);
50
 
51
- return new Twig_Node_Embed($module->getAttribute('filename'), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
52
  }
53
 
54
  public function decideBlockEnd(Twig_Token $token)
48
 
49
  $stream->expect(Twig_Token::BLOCK_END_TYPE);
50
 
51
+ return new Twig_Node_Embed($module->getTemplateName(), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
52
  }
53
 
54
  public function decideBlockEnd(Twig_Token $token)
library/twig/twig/lib/Twig/TokenParser/Extends.php CHANGED
@@ -21,16 +21,18 @@ class Twig_TokenParser_Extends extends Twig_TokenParser
21
  {
22
  public function parse(Twig_Token $token)
23
  {
 
 
24
  if (!$this->parser->isMainScope()) {
25
- throw new Twig_Error_Syntax('Cannot extend from a block.', $token->getLine(), $this->parser->getFilename());
26
  }
27
 
28
  if (null !== $this->parser->getParent()) {
29
- throw new Twig_Error_Syntax('Multiple extends tags are forbidden.', $token->getLine(), $this->parser->getFilename());
30
  }
31
  $this->parser->setParent($this->parser->getExpressionParser()->parseExpression());
32
 
33
- $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
34
  }
35
 
36
  public function getTag()
21
  {
22
  public function parse(Twig_Token $token)
23
  {
24
+ $stream = $this->parser->getStream();
25
+
26
  if (!$this->parser->isMainScope()) {
27
+ throw new Twig_Error_Syntax('Cannot extend from a block.', $token->getLine(), $stream->getSourceContext()->getName());
28
  }
29
 
30
  if (null !== $this->parser->getParent()) {
31
+ throw new Twig_Error_Syntax('Multiple extends tags are forbidden.', $token->getLine(), $stream->getSourceContext()->getName());
32
  }
33
  $this->parser->setParent($this->parser->getExpressionParser()->parseExpression());
34
 
35
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
36
  }
37
 
38
  public function getTag()
library/twig/twig/lib/Twig/TokenParser/Filter.php CHANGED
@@ -23,7 +23,7 @@ class Twig_TokenParser_Filter extends Twig_TokenParser
23
  public function parse(Twig_Token $token)
24
  {
25
  $name = $this->parser->getVarName();
26
- $ref = new Twig_Node_Expression_BlockReference(new Twig_Node_Expression_Constant($name, $token->getLine()), true, $token->getLine(), $this->getTag());
27
 
28
  $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag());
29
  $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
23
  public function parse(Twig_Token $token)
24
  {
25
  $name = $this->parser->getVarName();
26
+ $ref = new Twig_Node_Expression_BlockReference(new Twig_Node_Expression_Constant($name, $token->getLine()), null, $token->getLine(), $this->getTag());
27
 
28
  $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag());
29
  $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
library/twig/twig/lib/Twig/TokenParser/For.php CHANGED
@@ -48,13 +48,13 @@ class Twig_TokenParser_For extends Twig_TokenParser
48
 
49
  if (count($targets) > 1) {
50
  $keyTarget = $targets->getNode(0);
51
- $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine());
52
  $valueTarget = $targets->getNode(1);
53
- $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
54
  } else {
55
  $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno);
56
  $valueTarget = $targets->getNode(0);
57
- $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
58
  }
59
 
60
  if ($ifexpr) {
@@ -79,7 +79,7 @@ class Twig_TokenParser_For extends Twig_TokenParser
79
  protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node)
80
  {
81
  if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
82
- throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition.', $node->getLine(), $stream->getFilename());
83
  }
84
 
85
  foreach ($node as $n) {
@@ -98,7 +98,7 @@ class Twig_TokenParser_For extends Twig_TokenParser
98
  if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
99
  $attribute = $node->getNode('attribute');
100
  if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) {
101
- throw new Twig_Error_Syntax(sprintf('The "loop.%s" variable is not defined when looping with a condition.', $attribute->getAttribute('value')), $node->getLine(), $stream->getFilename());
102
  }
103
  }
104
 
48
 
49
  if (count($targets) > 1) {
50
  $keyTarget = $targets->getNode(0);
51
+ $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getTemplateLine());
52
  $valueTarget = $targets->getNode(1);
53
+ $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine());
54
  } else {
55
  $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno);
56
  $valueTarget = $targets->getNode(0);
57
+ $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine());
58
  }
59
 
60
  if ($ifexpr) {
79
  protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node)
80
  {
81
  if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
82
+ throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition.', $node->getTemplateLine(), $stream->getSourceContext()->getName());
83
  }
84
 
85
  foreach ($node as $n) {
98
  if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
99
  $attribute = $node->getNode('attribute');
100
  if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) {
101
+ throw new Twig_Error_Syntax(sprintf('The "loop.%s" variable is not defined when looping with a condition.', $attribute->getAttribute('value')), $node->getTemplateLine(), $stream->getSourceContext()->getName());
102
  }
103
  }
104
 
library/twig/twig/lib/Twig/TokenParser/From.php CHANGED
@@ -46,7 +46,7 @@ class Twig_TokenParser_From extends Twig_TokenParser
46
 
47
  foreach ($targets as $name => $alias) {
48
  if ($this->parser->isReservedMacroName($name)) {
49
- throw new Twig_Error_Syntax(sprintf('"%s" cannot be an imported macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getFilename());
50
  }
51
 
52
  $this->parser->addImportedSymbol('function', $alias, 'get'.$name, $node->getNode('var'));
46
 
47
  foreach ($targets as $name => $alias) {
48
  if ($this->parser->isReservedMacroName($name)) {
49
+ throw new Twig_Error_Syntax(sprintf('"%s" cannot be an imported macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getSourceContext()->getName());
50
  }
51
 
52
  $this->parser->addImportedSymbol('function', $alias, 'get'.$name, $node->getNode('var'));
library/twig/twig/lib/Twig/TokenParser/If.php CHANGED
@@ -56,7 +56,7 @@ class Twig_TokenParser_If extends Twig_TokenParser
56
  break;
57
 
58
  default:
59
- throw new Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "else", "elseif", or "endif" to close the "if" block started at line %d).', $lineno), $stream->getCurrent()->getLine(), $stream->getFilename());
60
  }
61
  }
62
 
56
  break;
57
 
58
  default:
59
+ throw new Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "else", "elseif", or "endif" to close the "if" block started at line %d).', $lineno), $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName());
60
  }
61
  }
62
 
library/twig/twig/lib/Twig/TokenParser/Macro.php CHANGED
@@ -35,7 +35,7 @@ class Twig_TokenParser_Macro extends Twig_TokenParser
35
  $value = $token->getValue();
36
 
37
  if ($value != $name) {
38
- throw new Twig_Error_Syntax(sprintf('Expected endmacro for macro "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getFilename());
39
  }
40
  }
41
  $this->parser->popLocalScope();
35
  $value = $token->getValue();
36
 
37
  if ($value != $name) {
38
+ throw new Twig_Error_Syntax(sprintf('Expected endmacro for macro "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName());
39
  }
40
  }
41
  $this->parser->popLocalScope();
library/twig/twig/lib/Twig/TokenParser/Sandbox.php CHANGED
@@ -24,9 +24,10 @@ class Twig_TokenParser_Sandbox extends Twig_TokenParser
24
  {
25
  public function parse(Twig_Token $token)
26
  {
27
- $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
 
28
  $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
29
- $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
30
 
31
  // in a sandbox tag, only include tags are allowed
32
  if (!$body instanceof Twig_Node_Include) {
@@ -36,7 +37,7 @@ class Twig_TokenParser_Sandbox extends Twig_TokenParser
36
  }
37
 
38
  if (!$node instanceof Twig_Node_Include) {
39
- throw new Twig_Error_Syntax('Only "include" tags are allowed within a "sandbox" section.', $node->getLine(), $this->parser->getFilename());
40
  }
41
  }
42
  }
24
  {
25
  public function parse(Twig_Token $token)
26
  {
27
+ $stream = $this->parser->getStream();
28
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
29
  $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
30
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
31
 
32
  // in a sandbox tag, only include tags are allowed
33
  if (!$body instanceof Twig_Node_Include) {
37
  }
38
 
39
  if (!$node instanceof Twig_Node_Include) {
40
+ throw new Twig_Error_Syntax('Only "include" tags are allowed within a "sandbox" section.', $node->getTemplateLine(), $stream->getSourceContext()->getName());
41
  }
42
  }
43
  }
library/twig/twig/lib/Twig/TokenParser/Set.php CHANGED
@@ -41,13 +41,13 @@ class Twig_TokenParser_Set extends Twig_TokenParser
41
  $stream->expect(Twig_Token::BLOCK_END_TYPE);
42
 
43
  if (count($names) !== count($values)) {
44
- throw new Twig_Error_Syntax('When using set, you must have the same number of variables and assignments.', $stream->getCurrent()->getLine(), $stream->getFilename());
45
  }
46
  } else {
47
  $capture = true;
48
 
49
  if (count($names) > 1) {
50
- throw new Twig_Error_Syntax('When using set with a block, you cannot have a multi-target.', $stream->getCurrent()->getLine(), $stream->getFilename());
51
  }
52
 
53
  $stream->expect(Twig_Token::BLOCK_END_TYPE);
41
  $stream->expect(Twig_Token::BLOCK_END_TYPE);
42
 
43
  if (count($names) !== count($values)) {
44
+ throw new Twig_Error_Syntax('When using set, you must have the same number of variables and assignments.', $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName());
45
  }
46
  } else {
47
  $capture = true;
48
 
49
  if (count($names) > 1) {
50
+ throw new Twig_Error_Syntax('When using set with a block, you cannot have a multi-target.', $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName());
51
  }
52
 
53
  $stream->expect(Twig_Token::BLOCK_END_TYPE);
library/twig/twig/lib/Twig/TokenParser/Use.php CHANGED
@@ -31,7 +31,7 @@ class Twig_TokenParser_Use extends Twig_TokenParser
31
  $stream = $this->parser->getStream();
32
 
33
  if (!$template instanceof Twig_Node_Expression_Constant) {
34
- throw new Twig_Error_Syntax('The template references in a "use" statement must be a string.', $stream->getCurrent()->getLine(), $stream->getFilename());
35
  }
36
 
37
  $targets = array();
31
  $stream = $this->parser->getStream();
32
 
33
  if (!$template instanceof Twig_Node_Expression_Constant) {
34
+ throw new Twig_Error_Syntax('The template references in a "use" statement must be a string.', $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName());
35
  }
36
 
37
  $targets = array();
library/twig/twig/lib/Twig/TokenParser/With.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) 2016 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
+ /**
13
+ * Creates a nested scope.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_TokenParser_With extends Twig_TokenParser
18
+ {
19
+ public function parse(Twig_Token $token)
20
+ {
21
+ $stream = $this->parser->getStream();
22
+
23
+ $variables = null;
24
+ $only = false;
25
+ if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) {
26
+ $variables = $this->parser->getExpressionParser()->parseExpression();
27
+ $only = $stream->nextIf(Twig_Token::NAME_TYPE, 'only');
28
+ }
29
+
30
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
31
+
32
+ $body = $this->parser->subparse(array($this, 'decideWithEnd'), true);
33
+
34
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
35
+
36
+ return new Twig_Node_With($body, $variables, $only, $token->getLine(), $this->getTag());
37
+ }
38
+
39
+ public function decideWithEnd(Twig_Token $token)
40
+ {
41
+ return $token->test('endwith');
42
+ }
43
+
44
+ public function getTag()
45
+ {
46
+ return 'with';
47
+ }
48
+ }
library/twig/twig/lib/Twig/TokenParserBroker.php CHANGED
@@ -24,8 +24,6 @@ class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface
24
  protected $brokers = array();
25
 
26
  /**
27
- * Constructor.
28
- *
29
  * @param array|Traversable $parsers A Traversable of Twig_TokenParserInterface instances
30
  * @param array|Traversable $brokers A Traversable of Twig_TokenParserBrokerInterface instances
31
  * @param bool $triggerDeprecationError
@@ -50,21 +48,11 @@ class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface
50
  }
51
  }
52
 
53
- /**
54
- * Adds a TokenParser.
55
- *
56
- * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
57
- */
58
  public function addTokenParser(Twig_TokenParserInterface $parser)
59
  {
60
  $this->parsers[$parser->getTag()] = $parser;
61
  }
62
 
63
- /**
64
- * Removes a TokenParser.
65
- *
66
- * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
67
- */
68
  public function removeTokenParser(Twig_TokenParserInterface $parser)
69
  {
70
  $name = $parser->getTag();
@@ -73,21 +61,11 @@ class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface
73
  }
74
  }
75
 
76
- /**
77
- * Adds a TokenParserBroker.
78
- *
79
- * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance
80
- */
81
  public function addTokenParserBroker(Twig_TokenParserBroker $broker)
82
  {
83
  $this->brokers[] = $broker;
84
  }
85
 
86
- /**
87
- * Removes a TokenParserBroker.
88
- *
89
- * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance
90
- */
91
  public function removeTokenParserBroker(Twig_TokenParserBroker $broker)
92
  {
93
  if (false !== $pos = array_search($broker, $this->brokers)) {
24
  protected $brokers = array();
25
 
26
  /**
 
 
27
  * @param array|Traversable $parsers A Traversable of Twig_TokenParserInterface instances
28
  * @param array|Traversable $brokers A Traversable of Twig_TokenParserBrokerInterface instances
29
  * @param bool $triggerDeprecationError
48
  }
49
  }
50
 
 
 
 
 
 
51
  public function addTokenParser(Twig_TokenParserInterface $parser)
52
  {
53
  $this->parsers[$parser->getTag()] = $parser;
54
  }
55
 
 
 
 
 
 
56
  public function removeTokenParser(Twig_TokenParserInterface $parser)
57
  {
58
  $name = $parser->getTag();
61
  }
62
  }
63
 
 
 
 
 
 
64
  public function addTokenParserBroker(Twig_TokenParserBroker $broker)
65
  {
66
  $this->brokers[] = $broker;
67
  }
68
 
 
 
 
 
 
69
  public function removeTokenParserBroker(Twig_TokenParserBroker $broker)
70
  {
71
  if (false !== $pos = array_search($broker, $this->brokers)) {
library/twig/twig/lib/Twig/TokenParserBrokerInterface.php CHANGED
@@ -26,14 +26,12 @@ interface Twig_TokenParserBrokerInterface
26
  *
27
  * @param string $tag A tag name
28
  *
29
- * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found
30
  */
31
  public function getTokenParser($tag);
32
 
33
  /**
34
  * Calls Twig_TokenParserInterface::setParser on all parsers the implementation knows of.
35
- *
36
- * @param Twig_ParserInterface $parser A Twig_ParserInterface interface
37
  */
38
  public function setParser(Twig_ParserInterface $parser);
39
 
26
  *
27
  * @param string $tag A tag name
28
  *
29
+ * @return Twig_TokenParserInterface|null A Twig_TokenParserInterface or null if no suitable TokenParser was found
30
  */
31
  public function getTokenParser($tag);
32
 
33
  /**
34
  * Calls Twig_TokenParserInterface::setParser on all parsers the implementation knows of.
 
 
35
  */
36
  public function setParser(Twig_ParserInterface $parser);
37
 
library/twig/twig/lib/Twig/TokenParserInterface.php CHANGED
@@ -18,17 +18,13 @@ interface Twig_TokenParserInterface
18
  {
19
  /**
20
  * Sets the parser associated with this token parser.
21
- *
22
- * @param Twig_Parser $parser A Twig_Parser instance
23
  */
24
  public function setParser(Twig_Parser $parser);
25
 
26
  /**
27
  * Parses a token and returns a node.
28
  *
29
- * @param Twig_Token $token A Twig_Token instance
30
- *
31
- * @return Twig_NodeInterface A Twig_NodeInterface instance
32
  *
33
  * @throws Twig_Error_Syntax
34
  */
18
  {
19
  /**
20
  * Sets the parser associated with this token parser.
 
 
21
  */
22
  public function setParser(Twig_Parser $parser);
23
 
24
  /**
25
  * Parses a token and returns a node.
26
  *
27
+ * @return Twig_NodeInterface
 
 
28
  *
29
  * @throws Twig_Error_Syntax
30
  */
library/twig/twig/lib/Twig/TokenStream.php CHANGED
@@ -24,24 +24,27 @@ class Twig_TokenStream
24
  private $source;
25
 
26
  /**
27
- * Constructor.
28
- *
29
- * @param array $tokens An array of tokens
30
- * @param string $filename|null The name of the filename which tokens are associated with
31
- * @param string $source|null The source code associated with the tokens
32
  */
33
- public function __construct(array $tokens, $filename = null, $source = null)
34
  {
 
 
 
 
 
 
 
 
 
35
  $this->tokens = $tokens;
36
- $this->filename = $filename;
37
- $this->source = $source ? $source : '';
 
38
  }
39
 
40
- /**
41
- * Returns a string representation of the token stream.
42
- *
43
- * @return string
44
- */
45
  public function __toString()
46
  {
47
  return implode("\n", $this->tokens);
@@ -60,7 +63,7 @@ class Twig_TokenStream
60
  public function next()
61
  {
62
  if (!isset($this->tokens[++$this->current])) {
63
- throw new Twig_Error_Syntax('Unexpected end of template.', $this->tokens[$this->current - 1]->getLine(), $this->filename);
64
  }
65
 
66
  return $this->tokens[$this->current - 1];
@@ -93,7 +96,7 @@ class Twig_TokenStream
93
  Twig_Token::typeToEnglish($token->getType()), $token->getValue(),
94
  Twig_Token::typeToEnglish($type), $value ? sprintf(' with value "%s"', $value) : ''),
95
  $line,
96
- $this->filename
97
  );
98
  }
99
  $this->next();
@@ -111,7 +114,7 @@ class Twig_TokenStream
111
  public function look($number = 1)
112
  {
113
  if (!isset($this->tokens[$this->current + $number])) {
114
- throw new Twig_Error_Syntax('Unexpected end of template.', $this->tokens[$this->current + $number - 1]->getLine(), $this->filename);
115
  }
116
 
117
  return $this->tokens[$this->current + $number];
@@ -138,8 +141,6 @@ class Twig_TokenStream
138
  }
139
 
140
  /**
141
- * Gets the current token.
142
- *
143
  * @return Twig_Token
144
  */
145
  public function getCurrent()
@@ -148,21 +149,43 @@ class Twig_TokenStream
148
  }
149
 
150
  /**
151
- * Gets the filename associated with this stream (null if not defined).
152
  *
153
  * @return string|null
 
 
154
  */
155
  public function getFilename()
156
  {
157
- return $this->filename;
 
 
158
  }
159
 
160
  /**
161
  * Gets the source code associated with this stream.
162
  *
163
  * @return string
 
 
 
 
164
  */
165
  public function getSource()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  {
167
  return $this->source;
168
  }
24
  private $source;
25
 
26
  /**
27
+ * @param array $tokens An array of tokens
28
+ * @param string|null $name The name of the template which tokens are associated with
29
+ * @param string|null $source The source code associated with the tokens
 
 
30
  */
31
+ public function __construct(array $tokens, $name = null, $source = null)
32
  {
33
+ if (!$name instanceof Twig_Source) {
34
+ if (null !== $name || null !== $source) {
35
+ @trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
36
+ }
37
+ $this->source = new Twig_Source($source, $name);
38
+ } else {
39
+ $this->source = $name;
40
+ }
41
+
42
  $this->tokens = $tokens;
43
+
44
+ // deprecated, not used anymore, to be removed in 2.0
45
+ $this->filename = $this->source->getName();
46
  }
47
 
 
 
 
 
 
48
  public function __toString()
49
  {
50
  return implode("\n", $this->tokens);
63
  public function next()
64
  {
65
  if (!isset($this->tokens[++$this->current])) {
66
+ throw new Twig_Error_Syntax('Unexpected end of template.', $this->tokens[$this->current - 1]->getLine(), $this->source->getName());
67
  }
68
 
69
  return $this->tokens[$this->current - 1];
96
  Twig_Token::typeToEnglish($token->getType()), $token->getValue(),
97
  Twig_Token::typeToEnglish($type), $value ? sprintf(' with value "%s"', $value) : ''),
98
  $line,
99
+ $this->source->getName()
100
  );
101
  }
102
  $this->next();
114
  public function look($number = 1)
115
  {
116
  if (!isset($this->tokens[$this->current + $number])) {
117
+ throw new Twig_Error_Syntax('Unexpected end of template.', $this->tokens[$this->current + $number - 1]->getLine(), $this->source->getName());
118
  }
119
 
120
  return $this->tokens[$this->current + $number];
141
  }
142
 
143
  /**
 
 
144
  * @return Twig_Token
145
  */
146
  public function getCurrent()
149
  }
150
 
151
  /**
152
+ * Gets the name associated with this stream (null if not defined).
153
  *
154
  * @return string|null
155
+ *
156
+ * @deprecated since 1.27 (to be removed in 2.0)
157
  */
158
  public function getFilename()
159
  {
160
+ @trigger_error(sprintf('The %s() method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
161
+
162
+ return $this->source->getName();
163
  }
164
 
165
  /**
166
  * Gets the source code associated with this stream.
167
  *
168
  * @return string
169
+ *
170
+ * @internal Don't use this as it might be empty depending on the environment configuration
171
+ *
172
+ * @deprecated since 1.27 (to be removed in 2.0)
173
  */
174
  public function getSource()
175
+ {
176
+ @trigger_error(sprintf('The %s() method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
177
+
178
+ return $this->source->getCode();
179
+ }
180
+
181
+ /**
182
+ * Gets the source associated with this stream.
183
+ *
184
+ * @return Twig_Source
185
+ *
186
+ * @internal
187
+ */
188
+ public function getSourceContext()
189
  {
190
  return $this->source;
191
  }
library/twig/twig/lib/Twig/Util/DeprecationCollector.php CHANGED
@@ -28,7 +28,7 @@ class Twig_Util_DeprecationCollector
28
  * @param string $dir A directory where templates are stored
29
  * @param string $ext Limit the loaded templates by extension
30
  *
31
- * @return array() An array of deprecations
32
  */
33
  public function collectDir($dir, $ext = '.twig')
34
  {
@@ -46,7 +46,7 @@ class Twig_Util_DeprecationCollector
46
  *
47
  * @param Iterator $iterator An iterator of templates (where keys are template names and values the contents of the template)
48
  *
49
- * @return array() An array of deprecations
50
  */
51
  public function collect(Iterator $iterator)
52
  {
28
  * @param string $dir A directory where templates are stored
29
  * @param string $ext Limit the loaded templates by extension
30
  *
31
+ * @return array An array of deprecations
32
  */
33
  public function collectDir($dir, $ext = '.twig')
34
  {
46
  *
47
  * @param Iterator $iterator An iterator of templates (where keys are template names and values the contents of the template)
48
  *
49
+ * @return array An array of deprecations
50
  */
51
  public function collect(Iterator $iterator)
52
  {
library/twig/twig/phpunit.xml.dist CHANGED
@@ -17,6 +17,14 @@
17
  </testsuite>
18
  </testsuites>
19
 
 
 
 
 
 
 
 
 
20
  <filter>
21
  <whitelist>
22
  <directory suffix=".php">./lib/Twig/</directory>
17
  </testsuite>
18
  </testsuites>
19
 
20
+ <php>
21
+ <ini name="error_reporting" value="-1" />
22
+ </php>
23
+
24
+ <listeners>
25
+ <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
26
+ </listeners>
27
+
28
  <filter>
29
  <whitelist>
30
  <directory suffix=".php">./lib/Twig/</directory>
library/twig/twig/test/Twig/Tests/Cache/FilesystemTest.php CHANGED
@@ -115,7 +115,7 @@ class Twig_Tests_Cache_FilesystemTest extends PHPUnit_Framework_TestCase
115
  // Create root directory.
116
  @mkdir($this->directory, 0777, true);
117
  // Create read-only subdirectory.
118
- @mkdir($this->directory.'/cache' , 0555);
119
  $this->assertTrue(is_dir($this->directory.'/cache'));
120
 
121
  $this->cache->write($key, $content);
115
  // Create root directory.
116
  @mkdir($this->directory, 0777, true);
117
  // Create read-only subdirectory.
118
+ @mkdir($this->directory.'/cache', 0555);
119
  $this->assertTrue(is_dir($this->directory.'/cache'));
120
 
121
  $this->cache->write($key, $content);
library/twig/twig/test/Twig/Tests/EnvironmentTest.php CHANGED
@@ -15,6 +15,27 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
15
  {
16
  private $deprecations = array();
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  /**
19
  * @expectedException LogicException
20
  * @expectedExceptionMessage You must set a loader first.
@@ -43,16 +64,21 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
43
  $this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo<br/ >')));
44
  }
45
 
46
- public function escapingStrategyCallback($filename)
47
  {
48
- return $filename;
49
  }
50
 
51
  public function testGlobals()
52
  {
 
 
 
 
 
53
  // globals can be added after calling getGlobals
54
 
55
- $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
56
  $twig->addGlobal('foo', 'foo');
57
  $twig->getGlobals();
58
  $twig->addGlobal('foo', 'bar');
@@ -60,7 +86,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
60
  $this->assertEquals('bar', $globals['foo']);
61
 
62
  // globals can be modified after a template has been loaded
63
- $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
64
  $twig->addGlobal('foo', 'foo');
65
  $twig->getGlobals();
66
  $twig->loadTemplate('index');
@@ -69,7 +95,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
69
  $this->assertEquals('bar', $globals['foo']);
70
 
71
  // globals can be modified after extensions init
72
- $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
73
  $twig->addGlobal('foo', 'foo');
74
  $twig->getGlobals();
75
  $twig->getFunctions();
@@ -78,7 +104,8 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
78
  $this->assertEquals('bar', $globals['foo']);
79
 
80
  // globals can be modified after extensions and a template has been loaded
81
- $twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{foo}}')));
 
82
  $twig->addGlobal('foo', 'foo');
83
  $twig->getGlobals();
84
  $twig->getFunctions();
@@ -87,7 +114,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
87
  $globals = $twig->getGlobals();
88
  $this->assertEquals('bar', $globals['foo']);
89
 
90
- $twig = new Twig_Environment($loader);
91
  $twig->getGlobals();
92
  $twig->addGlobal('foo', 'bar');
93
  $template = $twig->loadTemplate('index');
@@ -95,7 +122,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
95
 
96
  /* to be uncomment in Twig 2.0
97
  // globals cannot be added after a template has been loaded
98
- $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
99
  $twig->addGlobal('foo', 'foo');
100
  $twig->getGlobals();
101
  $twig->loadTemplate('index');
@@ -107,7 +134,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
107
  }
108
 
109
  // globals cannot be added after extensions init
110
- $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
111
  $twig->addGlobal('foo', 'foo');
112
  $twig->getGlobals();
113
  $twig->getFunctions();
@@ -119,7 +146,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
119
  }
120
 
121
  // globals cannot be added after extensions and a template has been loaded
122
- $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
123
  $twig->addGlobal('foo', 'foo');
124
  $twig->getGlobals();
125
  $twig->getFunctions();
@@ -132,7 +159,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
132
  }
133
 
134
  // test adding globals after a template has been loaded without call to getGlobals
135
- $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
136
  $twig->loadTemplate('index');
137
  try {
138
  $twig->addGlobal('bar', 'bar');
@@ -152,7 +179,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
152
  $twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{ foo }}')), $options);
153
 
154
  $key = $cache->generateKey('index', $twig->getTemplateClass('index'));
155
- $cache->write($key, $twig->compileSource('{{ foo }}', 'index'));
156
 
157
  // check that extensions won't be initialized when rendering a template that is already in the cache
158
  $twig = $this
@@ -190,7 +217,9 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
190
  ->will($this->returnValue(0));
191
  $loader->expects($this->never())
192
  ->method('isFresh');
193
- $cache->expects($this->never())
 
 
194
  ->method('load');
195
 
196
  $twig->loadTemplate($templateName);
@@ -218,7 +247,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
218
  $loader->expects($this->once())
219
  ->method('isFresh')
220
  ->will($this->returnValue(true));
221
- $cache->expects($this->once())
222
  ->method('load');
223
 
224
  $twig->loadTemplate($templateName);
@@ -244,12 +273,46 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
244
  $loader->expects($this->once())
245
  ->method('isFresh')
246
  ->will($this->returnValue(false));
247
- $cache->expects($this->never())
 
 
248
  ->method('load');
249
 
250
  $twig->loadTemplate($templateName);
251
  }
252
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  public function testAddExtension()
254
  {
255
  $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
@@ -286,7 +349,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
286
  $this->assertArrayHasKey('foo_global', $twig->getGlobals());
287
 
288
  $this->assertCount(1, $this->deprecations);
289
- $this->assertContains('Defining the getGlobals() method in the "environment_test" extension ', $this->deprecations[0]);
290
 
291
  restore_error_handler();
292
  }
@@ -297,7 +360,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
297
  public function testRemoveExtension()
298
  {
299
  $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
300
- $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
301
  $twig->removeExtension('environment_test');
302
 
303
  $this->assertFalse(array_key_exists('test', $twig->getTags()));
@@ -312,17 +375,22 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
312
 
313
  public function testAddMockExtension()
314
  {
315
- $extension = $this->getMockBuilder('Twig_ExtensionInterface')->getMock();
316
- $extension->expects($this->once())
317
- ->method('getName')
318
- ->will($this->returnValue('mock'));
 
 
 
 
 
319
 
320
  $loader = new Twig_Loader_Array(array('page' => 'hey'));
321
 
322
  $twig = new Twig_Environment($loader);
323
  $twig->addExtension($extension);
324
 
325
- $this->assertInstanceOf('Twig_ExtensionInterface', $twig->getExtension('mock'));
326
  $this->assertTrue($twig->isTemplateFresh('page', time()));
327
  }
328
 
@@ -348,7 +416,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
348
  $twig->initRuntime();
349
 
350
  $this->assertCount(1, $this->deprecations);
351
- $this->assertContains('Defining the initRuntime() method in the "with_deprecation" extension is deprecated since version 1.23.', $this->deprecations[0]);
352
 
353
  restore_error_handler();
354
  }
@@ -371,8 +439,8 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
371
  $this->deprecations = array();
372
  set_error_handler(array($this, 'handleError'));
373
 
374
- $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
375
- $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
376
 
377
  $this->assertCount(1, $this->deprecations);
378
  $this->assertContains('The possibility to register the same extension twice', $this->deprecations[0]);
@@ -380,13 +448,41 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
380
  restore_error_handler();
381
  }
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  protected function getMockLoader($templateName, $templateContent)
384
  {
385
- $loader = $this->getMockBuilder('Twig_LoaderInterface')->getMock();
 
 
386
  $loader->expects($this->any())
387
- ->method('getSource')
388
  ->with($templateName)
389
- ->will($this->returnValue($templateContent));
390
  $loader->expects($this->any())
391
  ->method('getCacheKey')
392
  ->with($templateName)
@@ -404,11 +500,6 @@ class Twig_Tests_EnvironmentTest_Extension_WithGlobals extends Twig_Extension
404
  'foo_global' => 'foo_global',
405
  );
406
  }
407
-
408
- public function getName()
409
- {
410
- return 'environment_test';
411
- }
412
  }
413
 
414
  class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface
@@ -462,13 +553,31 @@ class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension implements Twi
462
  'foo_global' => 'foo_global',
463
  );
464
  }
 
465
 
 
 
466
  public function getName()
467
  {
468
  return 'environment_test';
469
  }
470
  }
471
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  class Twig_Tests_EnvironmentTest_TokenParser extends Twig_TokenParser
473
  {
474
  public function parse(Twig_Token $token)
@@ -504,21 +613,40 @@ class Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime extends Twi
504
  public function initRuntime(Twig_Environment $env)
505
  {
506
  }
 
507
 
508
- public function getName()
 
 
509
  {
510
- return 'with_deprecation';
511
  }
512
  }
513
 
514
- class Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime extends Twig_Extension implements Twig_Extension_InitRuntimeInterface
515
  {
516
- public function initRuntime(Twig_Environment $env)
517
  {
 
 
 
 
518
  }
519
 
520
  public function getName()
521
  {
522
- return 'without_deprecation';
 
 
 
 
 
 
 
 
523
  }
524
  }
 
 
 
 
 
15
  {
16
  private $deprecations = array();
17
 
18
+ /**
19
+ * @group legacy
20
+ */
21
+ public function testLegacyTokenizeSignature()
22
+ {
23
+ $env = new Twig_Environment();
24
+ $stream = $env->tokenize('{{ foo }}', 'foo');
25
+ $this->assertEquals('{{ foo }}', $stream->getSource());
26
+ $this->assertEquals('foo', $stream->getFilename());
27
+ }
28
+
29
+ /**
30
+ * @group legacy
31
+ */
32
+ public function testLegacyCompileSourceSignature()
33
+ {
34
+ $loader = new Twig_Loader_Array(array('foo' => '{{ foo }}'));
35
+ $env = new Twig_Environment($loader);
36
+ $this->assertContains('getTemplateName', $env->compileSource('{{ foo }}', 'foo'));
37
+ }
38
+
39
  /**
40
  * @expectedException LogicException
41
  * @expectedExceptionMessage You must set a loader first.
64
  $this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo<br/ >')));
65
  }
66
 
67
+ public function escapingStrategyCallback($name)
68
  {
69
+ return $name;
70
  }
71
 
72
  public function testGlobals()
73
  {
74
+ // to be removed in 2.0
75
+ $loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock();
76
+ //$loader = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock();
77
+ $loader->expects($this->any())->method('getSourceContext')->will($this->returnValue(new Twig_Source('', '')));
78
+
79
  // globals can be added after calling getGlobals
80
 
81
+ $twig = new Twig_Environment($loader);
82
  $twig->addGlobal('foo', 'foo');
83
  $twig->getGlobals();
84
  $twig->addGlobal('foo', 'bar');
86
  $this->assertEquals('bar', $globals['foo']);
87
 
88
  // globals can be modified after a template has been loaded
89
+ $twig = new Twig_Environment($loader);
90
  $twig->addGlobal('foo', 'foo');
91
  $twig->getGlobals();
92
  $twig->loadTemplate('index');
95
  $this->assertEquals('bar', $globals['foo']);
96
 
97
  // globals can be modified after extensions init
98
+ $twig = new Twig_Environment($loader);
99
  $twig->addGlobal('foo', 'foo');
100
  $twig->getGlobals();
101
  $twig->getFunctions();
104
  $this->assertEquals('bar', $globals['foo']);
105
 
106
  // globals can be modified after extensions and a template has been loaded
107
+ $arrayLoader = new Twig_Loader_Array(array('index' => '{{foo}}'));
108
+ $twig = new Twig_Environment($arrayLoader);
109
  $twig->addGlobal('foo', 'foo');
110
  $twig->getGlobals();
111
  $twig->getFunctions();
114
  $globals = $twig->getGlobals();
115
  $this->assertEquals('bar', $globals['foo']);
116
 
117
+ $twig = new Twig_Environment($arrayLoader);
118
  $twig->getGlobals();
119
  $twig->addGlobal('foo', 'bar');
120
  $template = $twig->loadTemplate('index');
122
 
123
  /* to be uncomment in Twig 2.0
124
  // globals cannot be added after a template has been loaded
125
+ $twig = new Twig_Environment($loader);
126
  $twig->addGlobal('foo', 'foo');
127
  $twig->getGlobals();
128
  $twig->loadTemplate('index');
134
  }
135
 
136
  // globals cannot be added after extensions init
137
+ $twig = new Twig_Environment($loader);
138
  $twig->addGlobal('foo', 'foo');
139
  $twig->getGlobals();
140
  $twig->getFunctions();
146
  }
147
 
148
  // globals cannot be added after extensions and a template has been loaded
149
+ $twig = new Twig_Environment($loader);
150
  $twig->addGlobal('foo', 'foo');
151
  $twig->getGlobals();
152
  $twig->getFunctions();
159
  }
160
 
161
  // test adding globals after a template has been loaded without call to getGlobals
162
+ $twig = new Twig_Environment($loader);
163
  $twig->loadTemplate('index');
164
  try {
165
  $twig->addGlobal('bar', 'bar');
179
  $twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{ foo }}')), $options);
180
 
181
  $key = $cache->generateKey('index', $twig->getTemplateClass('index'));
182
+ $cache->write($key, $twig->compileSource(new Twig_Source('{{ foo }}', 'index')));
183
 
184
  // check that extensions won't be initialized when rendering a template that is already in the cache
185
  $twig = $this
217
  ->will($this->returnValue(0));
218
  $loader->expects($this->never())
219
  ->method('isFresh');
220
+ $cache->expects($this->once())
221
+ ->method('write');
222
+ $cache->expects($this->once())
223
  ->method('load');
224
 
225
  $twig->loadTemplate($templateName);
247
  $loader->expects($this->once())
248
  ->method('isFresh')
249
  ->will($this->returnValue(true));
250
+ $cache->expects($this->atLeastOnce())
251
  ->method('load');
252
 
253
  $twig->loadTemplate($templateName);
273
  $loader->expects($this->once())
274
  ->method('isFresh')
275
  ->will($this->returnValue(false));
276
+ $cache->expects($this->once())
277
+ ->method('write');
278
+ $cache->expects($this->once())
279
  ->method('load');
280
 
281
  $twig->loadTemplate($templateName);
282
  }
283
 
284
+ /**
285
+ * @group legacy
286
+ */
287
+ public function testHasGetExtensionWithDynamicName()
288
+ {
289
+ $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
290
+
291
+ $ext1 = new Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName('ext1');
292
+ $ext2 = new Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName('ext2');
293
+ $twig->addExtension($ext1);
294
+ $twig->addExtension($ext2);
295
+
296
+ $this->assertTrue($twig->hasExtension('ext1'));
297
+ $this->assertTrue($twig->hasExtension('ext2'));
298
+
299
+ $this->assertTrue($twig->hasExtension('Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName'));
300
+
301
+ $this->assertSame($ext1, $twig->getExtension('ext1'));
302
+ $this->assertSame($ext2, $twig->getExtension('ext2'));
303
+ }
304
+
305
+ public function testHasGetExtensionByClassName()
306
+ {
307
+ $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
308
+ $twig->addExtension($ext = new Twig_Tests_EnvironmentTest_Extension());
309
+ $this->assertTrue($twig->hasExtension('Twig_Tests_EnvironmentTest_Extension'));
310
+ $this->assertTrue($twig->hasExtension('\Twig_Tests_EnvironmentTest_Extension'));
311
+
312
+ $this->assertSame($ext, $twig->getExtension('Twig_Tests_EnvironmentTest_Extension'));
313
+ $this->assertSame($ext, $twig->getExtension('\Twig_Tests_EnvironmentTest_Extension'));
314
+ }
315
+
316
  public function testAddExtension()
317
  {
318
  $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
349
  $this->assertArrayHasKey('foo_global', $twig->getGlobals());
350
 
351
  $this->assertCount(1, $this->deprecations);
352
+ $this->assertContains('Defining the getGlobals() method in the "Twig_Tests_EnvironmentTest_Extension_WithGlobals" extension ', $this->deprecations[0]);
353
 
354
  restore_error_handler();
355
  }
360
  public function testRemoveExtension()
361
  {
362
  $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
363
+ $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName());
364
  $twig->removeExtension('environment_test');
365
 
366
  $this->assertFalse(array_key_exists('test', $twig->getTags()));
375
 
376
  public function testAddMockExtension()
377
  {
378
+ // should be replaced by the following in 2.0 (this current code is just to avoid a dep notice)
379
+ // $extension = $this->getMockBuilder('Twig_Extension')->getMock();
380
+ $extension = eval(<<<EOF
381
+ class Twig_Tests_EnvironmentTest_ExtensionInEval extends Twig_Extension
382
+ {
383
+ }
384
+ EOF
385
+ );
386
+ $extension = new Twig_Tests_EnvironmentTest_ExtensionInEval();
387
 
388
  $loader = new Twig_Loader_Array(array('page' => 'hey'));
389
 
390
  $twig = new Twig_Environment($loader);
391
  $twig->addExtension($extension);
392
 
393
+ $this->assertInstanceOf('Twig_ExtensionInterface', $twig->getExtension(get_class($extension)));
394
  $this->assertTrue($twig->isTemplateFresh('page', time()));
395
  }
396
 
416
  $twig->initRuntime();
417
 
418
  $this->assertCount(1, $this->deprecations);
419
+ $this->assertContains('Defining the initRuntime() method in the "Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime" extension is deprecated since version 1.23.', $this->deprecations[0]);
420
 
421
  restore_error_handler();
422
  }
439
  $this->deprecations = array();
440
  set_error_handler(array($this, 'handleError'));
441
 
442
+ $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName());
443
+ $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName());
444
 
445
  $this->assertCount(1, $this->deprecations);
446
  $this->assertContains('The possibility to register the same extension twice', $this->deprecations[0]);
448
  restore_error_handler();
449
  }
450
 
451
+ public function testAddRuntimeLoader()
452
+ {
453
+ $runtimeLoader = $this->getMockBuilder('Twig_RuntimeLoaderInterface')->getMock();
454
+ $runtimeLoader->expects($this->any())->method('load')->will($this->returnValue(new Twig_Tests_EnvironmentTest_Runtime()));
455
+
456
+ $loader = new Twig_Loader_Array(array(
457
+ 'func_array' => '{{ from_runtime_array("foo") }}',
458
+ 'func_array_default' => '{{ from_runtime_array() }}',
459
+ 'func_array_named_args' => '{{ from_runtime_array(name="foo") }}',
460
+ 'func_string' => '{{ from_runtime_string("foo") }}',
461
+ 'func_string_default' => '{{ from_runtime_string() }}',
462
+ 'func_string_named_args' => '{{ from_runtime_string(name="foo") }}',
463
+ ));
464
+
465
+ $twig = new Twig_Environment($loader);
466
+ $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithoutRuntime());
467
+ $twig->addRuntimeLoader($runtimeLoader);
468
+
469
+ $this->assertEquals('foo', $twig->render('func_array'));
470
+ $this->assertEquals('bar', $twig->render('func_array_default'));
471
+ $this->assertEquals('foo', $twig->render('func_array_named_args'));
472
+ $this->assertEquals('foo', $twig->render('func_string'));
473
+ $this->assertEquals('bar', $twig->render('func_string_default'));
474
+ $this->assertEquals('foo', $twig->render('func_string_named_args'));
475
+ }
476
+
477
  protected function getMockLoader($templateName, $templateContent)
478
  {
479
+ // to be removed in 2.0
480
+ $loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock();
481
+ //$loader = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock();
482
  $loader->expects($this->any())
483
+ ->method('getSourceContext')
484
  ->with($templateName)
485
+ ->will($this->returnValue(new Twig_Source($templateContent, $templateName)));
486
  $loader->expects($this->any())
487
  ->method('getCacheKey')
488
  ->with($templateName)
500
  'foo_global' => 'foo_global',
501
  );
502
  }
 
 
 
 
 
503
  }
504
 
505
  class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface
553
  'foo_global' => 'foo_global',
554
  );
555
  }
556
+ }
557
 
558
+ class Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName extends Twig_Extension
559
+ {
560
  public function getName()
561
  {
562
  return 'environment_test';
563
  }
564
  }
565
 
566
+ class Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName extends Twig_Extension
567
+ {
568
+ private $name;
569
+
570
+ public function __construct($name)
571
+ {
572
+ $this->name = $name;
573
+ }
574
+
575
+ public function getName()
576
+ {
577
+ return $this->name;
578
+ }
579
+ }
580
+
581
  class Twig_Tests_EnvironmentTest_TokenParser extends Twig_TokenParser
582
  {
583
  public function parse(Twig_Token $token)
613
  public function initRuntime(Twig_Environment $env)
614
  {
615
  }
616
+ }
617
 
618
+ class Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime extends Twig_Extension implements Twig_Extension_InitRuntimeInterface
619
+ {
620
+ public function initRuntime(Twig_Environment $env)
621
  {
 
622
  }
623
  }
624
 
625
+ class Twig_Tests_EnvironmentTest_ExtensionWithoutRuntime extends Twig_Extension
626
  {
627
+ public function getFunctions()
628
  {
629
+ return array(
630
+ new Twig_SimpleFunction('from_runtime_array', array('Twig_Tests_EnvironmentTest_Runtime', 'fromRuntime')),
631
+ new Twig_SimpleFunction('from_runtime_string', 'Twig_Tests_EnvironmentTest_Runtime::fromRuntime'),
632
+ );
633
  }
634
 
635
  public function getName()
636
  {
637
+ return 'from_runtime';
638
+ }
639
+ }
640
+
641
+ class Twig_Tests_EnvironmentTest_Runtime
642
+ {
643
+ public function fromRuntime($name = 'bar')
644
+ {
645
+ return $name;
646
  }
647
  }
648
+
649
+ // to be removed in 2.0
650
+ interface Twig_EnvironmentTestLoaderInterface extends Twig_LoaderInterface, Twig_SourceContextLoaderInterface
651
+ {
652
+ }
library/twig/twig/test/Twig/Tests/ErrorTest.php CHANGED
@@ -14,7 +14,7 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase
14
  public function testErrorWithObjectFilename()
15
  {
16
  $error = new Twig_Error('foo');
17
- $error->setTemplateFile(new SplFileInfo(__FILE__));
18
 
19
  $this->assertContains('test'.DIRECTORY_SEPARATOR.'Twig'.DIRECTORY_SEPARATOR.'Tests'.DIRECTORY_SEPARATOR.'ErrorTest.php', $error->getMessage());
20
  }
@@ -22,7 +22,7 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase
22
  public function testErrorWithArrayFilename()
23
  {
24
  $error = new Twig_Error('foo');
25
- $error->setTemplateFile(array('foo' => 'bar'));
26
 
27
  $this->assertEquals('foo in {"foo":"bar"}', $error->getMessage());
28
  }
@@ -38,9 +38,9 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase
38
 
39
  $this->fail();
40
  } catch (Twig_Error_Runtime $e) {
41
- $this->assertEquals('Variable "foo" does not exist in "index.html" at line 3', $e->getMessage());
42
  $this->assertEquals(3, $e->getTemplateLine());
43
- $this->assertEquals('index.html', $e->getTemplateFile());
44
  }
45
 
46
  try {
@@ -50,7 +50,7 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase
50
  } catch (Twig_Error_Runtime $e) {
51
  $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index.html" at line 3.', $e->getMessage());
52
  $this->assertEquals(3, $e->getTemplateLine());
53
- $this->assertEquals('index.html', $e->getTemplateFile());
54
  }
55
  }
56
 
@@ -69,9 +69,9 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase
69
 
70
  $this->fail();
71
  } catch (Twig_Error_Runtime $e) {
72
- $this->assertEquals(sprintf('Variable "foo" does not exist in "%s" at line %d', $name, $line), $e->getMessage());
73
  $this->assertEquals($line, $e->getTemplateLine());
74
- $this->assertEquals($name, $e->getTemplateFile());
75
  }
76
 
77
  try {
@@ -81,7 +81,7 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase
81
  } catch (Twig_Error_Runtime $e) {
82
  $this->assertEquals(sprintf('An exception has been thrown during the rendering of a template ("Runtime error...") in "%s" at line %d.', $name, $line), $e->getMessage());
83
  $this->assertEquals($line, $e->getTemplateLine());
84
- $this->assertEquals($name, $e->getTemplateFile());
85
  }
86
  }
87
 
14
  public function testErrorWithObjectFilename()
15
  {
16
  $error = new Twig_Error('foo');
17
+ $error->setTemplateName(new SplFileInfo(__FILE__));
18
 
19
  $this->assertContains('test'.DIRECTORY_SEPARATOR.'Twig'.DIRECTORY_SEPARATOR.'Tests'.DIRECTORY_SEPARATOR.'ErrorTest.php', $error->getMessage());
20
  }
22
  public function testErrorWithArrayFilename()
23
  {
24
  $error = new Twig_Error('foo');
25
+ $error->setTemplateName(array('foo' => 'bar'));
26
 
27
  $this->assertEquals('foo in {"foo":"bar"}', $error->getMessage());
28
  }
38
 
39
  $this->fail();
40
  } catch (Twig_Error_Runtime $e) {
41
+ $this->assertEquals('Variable "foo" does not exist in "index.html" at line 3.', $e->getMessage());
42
  $this->assertEquals(3, $e->getTemplateLine());
43
+ $this->assertEquals('index.html', $e->getTemplateName());
44
  }
45
 
46
  try {
50
  } catch (Twig_Error_Runtime $e) {
51
  $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index.html" at line 3.', $e->getMessage());
52
  $this->assertEquals(3, $e->getTemplateLine());
53
+ $this->assertEquals('index.html', $e->getTemplateName());
54
  }
55
  }
56
 
69
 
70
  $this->fail();
71
  } catch (Twig_Error_Runtime $e) {
72
+ $this->assertEquals(sprintf('Variable "foo" does not exist in "%s" at line %d.', $name, $line), $e->getMessage());
73
  $this->assertEquals($line, $e->getTemplateLine());
74
+ $this->assertEquals($name, $e->getTemplateName());
75
  }
76
 
77
  try {
81
  } catch (Twig_Error_Runtime $e) {
82
  $this->assertEquals(sprintf('An exception has been thrown during the rendering of a template ("Runtime error...") in "%s" at line %d.', $name, $line), $e->getMessage());
83
  $this->assertEquals($line, $e->getTemplateLine());
84
+ $this->assertEquals($name, $e->getTemplateName());
85
  }
86
  }
87
 
library/twig/twig/test/Twig/Tests/ExpressionParserTest.php CHANGED
@@ -20,7 +20,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
20
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
21
  $parser = new Twig_Parser($env);
22
 
23
- $parser->parse($env->tokenize($template, 'index'));
24
  }
25
 
26
  public function getFailingTestsForAssignment()
@@ -47,7 +47,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
47
  public function testArrayExpression($template, $expected)
48
  {
49
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
50
- $stream = $env->tokenize($template, 'index');
51
  $parser = new Twig_Parser($env);
52
 
53
  $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
@@ -62,7 +62,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
62
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
63
  $parser = new Twig_Parser($env);
64
 
65
- $parser->parse($env->tokenize($template, 'index'));
66
  }
67
 
68
  public function getFailingTestsForArray()
@@ -155,7 +155,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
155
  public function testStringExpressionDoesNotConcatenateTwoConsecutiveStrings()
156
  {
157
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
158
- $stream = $env->tokenize('{{ "a" "b" }}', 'index');
159
  $parser = new Twig_Parser($env);
160
 
161
  $parser->parse($stream);
@@ -167,7 +167,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
167
  public function testStringExpression($template, $expected)
168
  {
169
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
170
- $stream = $env->tokenize($template, 'index');
171
  $parser = new Twig_Parser($env);
172
 
173
  $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
@@ -228,7 +228,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
228
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
229
  $parser = new Twig_Parser($env);
230
 
231
- $parser->parse($env->tokenize('{{ foo.bar(name="Foo") }}', 'index'));
232
  }
233
 
234
  /**
@@ -239,7 +239,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
239
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
240
  $parser = new Twig_Parser($env);
241
 
242
- $parser->parse($env->tokenize('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index'));
243
  }
244
 
245
  /**
@@ -251,7 +251,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
251
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
252
  $parser = new Twig_Parser($env);
253
 
254
- $parser->parse($env->tokenize('{% macro foo("a") %}{% endmacro %}', 'index'));
255
  }
256
 
257
  /**
@@ -264,7 +264,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
264
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
265
  $parser = new Twig_Parser($env);
266
 
267
- $parser->parse($env->tokenize($template, 'index'));
268
  }
269
 
270
  public function getMacroDefinitionDoesNotSupportNonConstantDefaultValues()
@@ -283,7 +283,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
283
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
284
  $parser = new Twig_Parser($env);
285
 
286
- $parser->parse($env->tokenize($template, 'index'));
287
  }
288
 
289
  public function getMacroDefinitionSupportsConstantDefaultValues()
@@ -308,7 +308,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
308
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
309
  $parser = new Twig_Parser($env);
310
 
311
- $parser->parse($env->tokenize('{{ cycl() }}', 'index'));
312
  }
313
 
314
  /**
@@ -320,7 +320,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
320
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
321
  $parser = new Twig_Parser($env);
322
 
323
- $parser->parse($env->tokenize('{{ foobar() }}', 'index'));
324
  }
325
 
326
  /**
@@ -332,7 +332,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
332
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
333
  $parser = new Twig_Parser($env);
334
 
335
- $parser->parse($env->tokenize('{{ 1|lowe }}', 'index'));
336
  }
337
 
338
  /**
@@ -344,7 +344,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
344
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
345
  $parser = new Twig_Parser($env);
346
 
347
- $parser->parse($env->tokenize('{{ 1|foobar }}', 'index'));
348
  }
349
 
350
  /**
@@ -355,8 +355,8 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
355
  {
356
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
357
  $parser = new Twig_Parser($env);
358
-
359
- $parser->parse($env->tokenize('{{ 1 is nul }}', 'index'));
360
  }
361
 
362
  /**
@@ -368,6 +368,6 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
368
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
369
  $parser = new Twig_Parser($env);
370
 
371
- $parser->parse($env->tokenize('{{ 1 is foobar }}', 'index'));
372
  }
373
  }
20
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
21
  $parser = new Twig_Parser($env);
22
 
23
+ $parser->parse($env->tokenize(new Twig_Source($template, 'index')));
24
  }
25
 
26
  public function getFailingTestsForAssignment()
47
  public function testArrayExpression($template, $expected)
48
  {
49
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
50
+ $stream = $env->tokenize(new Twig_Source($template, ''));
51
  $parser = new Twig_Parser($env);
52
 
53
  $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
62
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
63
  $parser = new Twig_Parser($env);
64
 
65
+ $parser->parse($env->tokenize(new Twig_Source($template, 'index')));
66
  }
67
 
68
  public function getFailingTestsForArray()
155
  public function testStringExpressionDoesNotConcatenateTwoConsecutiveStrings()
156
  {
157
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
158
+ $stream = $env->tokenize(new Twig_Source('{{ "a" "b" }}', 'index'));
159
  $parser = new Twig_Parser($env);
160
 
161
  $parser->parse($stream);
167
  public function testStringExpression($template, $expected)
168
  {
169
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
170
+ $stream = $env->tokenize(new Twig_Source($template, ''));
171
  $parser = new Twig_Parser($env);
172
 
173
  $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
228
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
229
  $parser = new Twig_Parser($env);
230
 
231
+ $parser->parse($env->tokenize(new Twig_Source('{{ foo.bar(name="Foo") }}', 'index')));
232
  }
233
 
234
  /**
239
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
240
  $parser = new Twig_Parser($env);
241
 
242
+ $parser->parse($env->tokenize(new Twig_Source('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index')));
243
  }
244
 
245
  /**
251
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
252
  $parser = new Twig_Parser($env);
253
 
254
+ $parser->parse($env->tokenize(new Twig_Source('{% macro foo("a") %}{% endmacro %}', 'index')));
255
  }
256
 
257
  /**
264
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
265
  $parser = new Twig_Parser($env);
266
 
267
+ $parser->parse($env->tokenize(new Twig_Source($template, 'index')));
268
  }
269
 
270
  public function getMacroDefinitionDoesNotSupportNonConstantDefaultValues()
283
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
284
  $parser = new Twig_Parser($env);
285
 
286
+ $parser->parse($env->tokenize(new Twig_Source($template, 'index')));
287
  }
288
 
289
  public function getMacroDefinitionSupportsConstantDefaultValues()
308
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
309
  $parser = new Twig_Parser($env);
310
 
311
+ $parser->parse($env->tokenize(new Twig_Source('{{ cycl() }}', 'index')));
312
  }
313
 
314
  /**
320
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
321
  $parser = new Twig_Parser($env);
322
 
323
+ $parser->parse($env->tokenize(new Twig_Source('{{ foobar() }}', 'index')));
324
  }
325
 
326
  /**
332
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
333
  $parser = new Twig_Parser($env);
334
 
335
+ $parser->parse($env->tokenize(new Twig_Source('{{ 1|lowe }}', 'index')));
336
  }
337
 
338
  /**
344
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
345
  $parser = new Twig_Parser($env);
346
 
347
+ $parser->parse($env->tokenize(new Twig_Source('{{ 1|foobar }}', 'index')));
348
  }
349
 
350
  /**
355
  {
356
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
357
  $parser = new Twig_Parser($env);
358
+ $stream = $env->tokenize(new Twig_Source('{{ 1 is nul }}', 'index'));
359
+ $parser->parse($stream);
360
  }
361
 
362
  /**
368
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
369
  $parser = new Twig_Parser($env);
370
 
371
+ $parser->parse($env->tokenize(new Twig_Source('{{ 1 is foobar }}', 'index')));
372
  }
373
  }
library/twig/twig/test/Twig/Tests/Extension/CoreTest.php CHANGED
@@ -118,7 +118,7 @@ class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase
118
  public function testCustomEscaper()
119
  {
120
  $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
121
- $twig->getExtension('core')->setEscaper('foo', 'foo_escaper_for_test');
122
 
123
  $this->assertEquals('fooUTF-8', twig_escape_filter($twig, 'foo', 'foo'));
124
  $this->assertEquals('UTF-8', twig_escape_filter($twig, null, 'foo'));
118
  public function testCustomEscaper()
119
  {
120
  $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
121
+ $twig->getExtension('Twig_Extension_Core')->setEscaper('foo', 'foo_escaper_for_test');
122
 
123
  $this->assertEquals('fooUTF-8', twig_escape_filter($twig, 'foo', 'foo'));
124
  $this->assertEquals('UTF-8', twig_escape_filter($twig, null, 'foo'));
library/twig/twig/test/Twig/Tests/Extension/SandboxTest.php CHANGED
@@ -51,83 +51,146 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
51
  {
52
  $twig = $this->getEnvironment(false, array(), self::$templates);
53
  $this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
 
54
 
 
 
55
  $twig = $this->getEnvironment(true, array(), self::$templates);
56
  try {
57
  $twig->loadTemplate('1_basic1')->render(self::$params);
58
  $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
59
  } catch (Twig_Sandbox_SecurityError $e) {
 
 
 
60
  }
 
61
 
 
 
62
  $twig = $this->getEnvironment(true, array(), self::$templates);
63
  try {
64
  $twig->loadTemplate('1_basic2')->render(self::$params);
65
  $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
66
  } catch (Twig_Sandbox_SecurityError $e) {
 
 
67
  }
 
68
 
 
 
69
  $twig = $this->getEnvironment(true, array(), self::$templates);
70
  try {
71
  $twig->loadTemplate('1_basic3')->render(self::$params);
72
  $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
73
  } catch (Twig_Sandbox_SecurityError $e) {
 
 
74
  }
 
75
 
 
 
76
  $twig = $this->getEnvironment(true, array(), self::$templates);
77
  try {
78
  $twig->loadTemplate('1_basic4')->render(self::$params);
79
  $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
80
  } catch (Twig_Sandbox_SecurityError $e) {
 
 
 
81
  }
 
82
 
 
 
83
  $twig = $this->getEnvironment(true, array(), self::$templates);
84
  try {
85
  $twig->loadTemplate('1_basic5')->render(self::$params);
86
  $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
87
  } catch (Twig_Sandbox_SecurityError $e) {
 
 
 
88
  }
 
89
 
 
 
90
  $twig = $this->getEnvironment(true, array(), self::$templates);
91
  try {
92
  $twig->loadTemplate('1_basic6')->render(self::$params);
93
  $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
94
  } catch (Twig_Sandbox_SecurityError $e) {
 
 
 
95
  }
 
96
 
 
 
97
  $twig = $this->getEnvironment(true, array(), self::$templates);
98
  try {
99
  $twig->loadTemplate('1_basic7')->render(self::$params);
100
  $this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template');
101
  } catch (Twig_Sandbox_SecurityError $e) {
 
 
102
  }
 
103
 
 
 
104
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
105
  FooObject::reset();
106
  $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
107
  $this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once');
 
108
 
 
 
109
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
110
  FooObject::reset();
111
  $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
112
  $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
 
113
 
 
 
114
  $twig = $this->getEnvironment(false, array(), self::$templates);
115
  FooObject::reset();
116
  $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allows __toString when sandbox disabled');
117
  $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
 
118
 
 
 
119
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
120
  $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
 
121
 
 
 
122
  $twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
123
  $this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
 
124
 
 
 
125
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar'));
126
  $this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
 
127
 
 
 
128
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle'));
129
  $this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
 
130
 
 
 
131
  foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
132
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
133
  FooObject::reset();
@@ -158,6 +221,8 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
158
  $twig->loadTemplate('3_basic')->render(self::$params);
159
  $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
160
  } catch (Twig_Sandbox_SecurityError $e) {
 
 
161
  }
162
  }
163
 
51
  {
52
  $twig = $this->getEnvironment(false, array(), self::$templates);
53
  $this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
54
+ }
55
 
56
+ public function testSandboxUnallowedMethodAccessor()
57
+ {
58
  $twig = $this->getEnvironment(true, array(), self::$templates);
59
  try {
60
  $twig->loadTemplate('1_basic1')->render(self::$params);
61
  $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
62
  } catch (Twig_Sandbox_SecurityError $e) {
63
+ $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError');
64
+ $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
65
+ $this->assertEquals('foo', $e->getMethodName(), 'Exception should be raised on the "foo" method');
66
  }
67
+ }
68
 
69
+ public function testSandboxUnallowedFilter()
70
+ {
71
  $twig = $this->getEnvironment(true, array(), self::$templates);
72
  try {
73
  $twig->loadTemplate('1_basic2')->render(self::$params);
74
  $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
75
  } catch (Twig_Sandbox_SecurityError $e) {
76
+ $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFilterError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFilterError');
77
+ $this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter');
78
  }
79
+ }
80
 
81
+ public function testSandboxUnallowedTag()
82
+ {
83
  $twig = $this->getEnvironment(true, array(), self::$templates);
84
  try {
85
  $twig->loadTemplate('1_basic3')->render(self::$params);
86
  $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
87
  } catch (Twig_Sandbox_SecurityError $e) {
88
+ $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedTagError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedTagError');
89
+ $this->assertEquals('if', $e->getTagName(), 'Exception should be raised on the "if" tag');
90
  }
91
+ }
92
 
93
+ public function testSandboxUnallowedProperty()
94
+ {
95
  $twig = $this->getEnvironment(true, array(), self::$templates);
96
  try {
97
  $twig->loadTemplate('1_basic4')->render(self::$params);
98
  $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
99
  } catch (Twig_Sandbox_SecurityError $e) {
100
+ $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedPropertyError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedPropertyError');
101
+ $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
102
+ $this->assertEquals('bar', $e->getPropertyName(), 'Exception should be raised on the "bar" property');
103
  }
104
+ }
105
 
106
+ public function testSandboxUnallowedToString()
107
+ {
108
  $twig = $this->getEnvironment(true, array(), self::$templates);
109
  try {
110
  $twig->loadTemplate('1_basic5')->render(self::$params);
111
  $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
112
  } catch (Twig_Sandbox_SecurityError $e) {
113
+ $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError');
114
+ $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
115
+ $this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method');
116
  }
117
+ }
118
 
119
+ public function testSandboxUnallowedToStringArray()
120
+ {
121
  $twig = $this->getEnvironment(true, array(), self::$templates);
122
  try {
123
  $twig->loadTemplate('1_basic6')->render(self::$params);
124
  $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
125
  } catch (Twig_Sandbox_SecurityError $e) {
126
+ $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError');
127
+ $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class');
128
+ $this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method');
129
  }
130
+ }
131
 
132
+ public function testSandboxUnallowedFunction()
133
+ {
134
  $twig = $this->getEnvironment(true, array(), self::$templates);
135
  try {
136
  $twig->loadTemplate('1_basic7')->render(self::$params);
137
  $this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template');
138
  } catch (Twig_Sandbox_SecurityError $e) {
139
+ $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFunctionError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFunctionError');
140
+ $this->assertEquals('cycle', $e->getFunctionName(), 'Exception should be raised on the "cycle" function');
141
  }
142
+ }
143
 
144
+ public function testSandboxAllowMethodFoo()
145
+ {
146
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
147
  FooObject::reset();
148
  $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
149
  $this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once');
150
+ }
151
 
152
+ public function testSandboxAllowMethodToString()
153
+ {
154
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
155
  FooObject::reset();
156
  $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
157
  $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
158
+ }
159
 
160
+ public function testSandboxAllowMethodToStringDisabled()
161
+ {
162
  $twig = $this->getEnvironment(false, array(), self::$templates);
163
  FooObject::reset();
164
  $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allows __toString when sandbox disabled');
165
  $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
166
+ }
167
 
168
+ public function testSandboxAllowFilter()
169
+ {
170
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
171
  $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
172
+ }
173
 
174
+ public function testSandboxAllowTag()
175
+ {
176
  $twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
177
  $this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
178
+ }
179
 
180
+ public function testSandboxAllowProperty()
181
+ {
182
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar'));
183
  $this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
184
+ }
185
 
186
+ public function testSandboxAllowFunction()
187
+ {
188
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle'));
189
  $this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
190
+ }
191
 
192
+ public function testSandboxAllowFunctionsCaseInsensitive()
193
+ {
194
  foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
195
  $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
196
  FooObject::reset();
221
  $twig->loadTemplate('3_basic')->render(self::$params);
222
  $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
223
  } catch (Twig_Sandbox_SecurityError $e) {
224
+ $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedTagError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedTagError');
225
+ $this->assertEquals('sandbox', $e->getTagName());
226
  }
227
  }
228
 
library/twig/twig/test/Twig/Tests/Fixtures/autoescape/block.test CHANGED
@@ -16,6 +16,6 @@ blocks and autoescape
16
  --DATA--
17
  return array('br' => '<br />')
18
  --CONFIG--
19
- return array('autoescape' => 'filename')
20
  --EXPECT--
21
  &lt;br /&gt;
16
  --DATA--
17
  return array('br' => '<br />')
18
  --CONFIG--
19
+ return array('autoescape' => 'name')
20
  --EXPECT--
21
  &lt;br /&gt;
library/twig/twig/test/Twig/Tests/Fixtures/autoescape/name.test ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "name" autoescape strategy
3
+ --TEMPLATE--
4
+ {{ br -}}
5
+ {{ include('index.html.twig') -}}
6
+ {{ include('index.txt.twig') -}}
7
+ --TEMPLATE(index.html.twig)--
8
+ {{ br -}}
9
+ --TEMPLATE(index.txt.twig)--
10
+ {{ br -}}
11
+ --DATA--
12
+ return array('br' => '<br />')
13
+ --CONFIG--
14
+ return array('autoescape' => 'name')
15
+ --EXPECT--
16
+ &lt;br /&gt;
17
+ &lt;br /&gt;
18
+ <br />
library/twig/twig/test/Twig/Tests/Fixtures/exceptions/child_contents_outside_blocks.test ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ Exception for child templates defining contents outside blocks defined by parent
3
+ --TEMPLATE--
4
+ {% extends 'base.twig' %}
5
+
6
+ Content outside a block.
7
+
8
+ {% block sidebar %}
9
+ Content inside a block.
10
+ {% endblock %}
11
+ --TEMPLATE(base.twig)--
12
+ {% block sidebar %}
13
+ {% endblock %}
14
+ --EXCEPTION--
15
+ Twig_Error_Syntax: A template that extends another one cannot include contents outside Twig blocks. Did you forget to put the contents inside a {% block %} tag in "index.twig" at line 3?
library/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test CHANGED
@@ -15,4 +15,4 @@ Exception for multiline array with undefined variable
15
  --DATA--
16
  return array('foobar' => 'foobar')
17
  --EXCEPTION--
18
- Twig_Error_Runtime: Variable "foo2" does not exist in "index.twig" at line 11
15
  --DATA--
16
  return array('foobar' => 'foobar')
17
  --EXCEPTION--
18
+ Twig_Error_Runtime: Variable "foo2" does not exist in "index.twig" at line 11.
library/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test CHANGED
@@ -15,4 +15,4 @@ Exception for multiline array with undefined variable
15
  --DATA--
16
  return array()
17
  --EXCEPTION--
18
- Twig_Error_Runtime: Variable "foobar" does not exist in "index.twig" at line 7
15
  --DATA--
16
  return array()
17
  --EXCEPTION--
18
+ Twig_Error_Runtime: Variable "foobar" does not exist in "index.twig" at line 7.
library/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test CHANGED
@@ -9,4 +9,4 @@ Foo
9
  --DATA--
10
  return array()
11
  --EXCEPTION--
12
- Twig_Error_Runtime: Variable "with_context" does not exist in "index.twig" at line 3
9
  --DATA--
10
  return array()
11
  --EXCEPTION--
12
+ Twig_Error_Runtime: Variable "with_context" does not exist in "index.twig" at line 3.
library/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test CHANGED
@@ -9,4 +9,4 @@ Foo
9
  --DATA--
10
  return array()
11
  --EXCEPTION--
12
- Twig_Error_Runtime: Variable "vars" does not exist in "index.twig" at line 3
9
  --DATA--
10
  return array()
11
  --EXCEPTION--
12
+ Twig_Error_Runtime: Variable "vars" does not exist in "index.twig" at line 3.
library/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test CHANGED
@@ -6,21 +6,21 @@ Twig supports __call() for attributes
6
  --DATA--
7
  class TestClassForMagicCallAttributes
8
  {
9
- public function getBar()
10
- {
11
- return 'bar_from_getbar';
12
- }
13
-
14
- public function __call($method, $arguments)
15
- {
16
- if ('foo' === $method)
17
  {
18
- return 'foo_from_call';
19
  }
20
 
21
- return false;
22
- }
 
 
 
 
 
 
23
  }
 
24
  return array('foo' => new TestClassForMagicCallAttributes())
25
  --EXPECT--
26
  foo_from_call
6
  --DATA--
7
  class TestClassForMagicCallAttributes
8
  {
9
+ public function getBar()
 
 
 
 
 
 
 
10
  {
11
+ return 'bar_from_getbar';
12
  }
13
 
14
+ public function __call($method, $arguments)
15
+ {
16
+ if ('foo' === $method) {
17
+ return 'foo_from_call';
18
+ }
19
+
20
+ return false;
21
+ }
22
  }
23
+
24
  return array('foo' => new TestClassForMagicCallAttributes())
25
  --EXPECT--
26
  foo_from_call
library/twig/twig/test/Twig/Tests/Fixtures/expressions/power.test ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ Twig parses power expressions
3
+ --TEMPLATE--
4
+ {{ 2**3 }}
5
+ {{ (-2)**3 }}
6
+ {{ (-2)**(-3) }}
7
+ {{ a ** a }}
8
+ {{ a ** b }}
9
+ {{ b ** a }}
10
+ {{ b ** b }}
11
+ --DATA--
12
+ return array('a' => 4, 'b' => -2);
13
+ --EXPECT--
14
+ 8
15
+ -8
16
+ -0.125
17
+ 256
18
+ 0.0625
19
+ 16
20
+ 0.25
library/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test CHANGED
@@ -5,7 +5,7 @@
5
  {{ date1|date('d/m/Y') }}
6
  --DATA--
7
  date_default_timezone_set('UTC');
8
- $twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
9
  return array(
10
  'date1' => mktime(13, 45, 0, 10, 4, 2010),
11
  )
5
  {{ date1|date('d/m/Y') }}
6
  --DATA--
7
  date_default_timezone_set('UTC');
8
+ $twig->getExtension('Twig_Extension_Core')->setDateFormat('Y-m-d', '%d days %h hours');
9
  return array(
10
  'date1' => mktime(13, 45, 0, 10, 4, 2010),
11
  )
library/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test CHANGED
@@ -7,7 +7,7 @@ version_compare(phpversion(), '5.3.0', '>=')
7
  {{ date2|date('%d days') }}
8
  --DATA--
9
  date_default_timezone_set('UTC');
10
- $twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
11
  return array(
12
  'date2' => new DateInterval('P2D'),
13
  )
7
  {{ date2|date('%d days') }}
8
  --DATA--
9
  date_default_timezone_set('UTC');
10
+ $twig->getExtension('Twig_Extension_Core')->setDateFormat('Y-m-d', '%d days %h hours');
11
  return array(
12
  'date2' => new DateInterval('P2D'),
13
  )
library/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test CHANGED
@@ -9,7 +9,7 @@
9
  {{ 1020.25|number_format(2, ',') }}
10
  {{ 1020.25|number_format(2, ',', '.') }}
11
  --DATA--
12
- $twig->getExtension('core')->setNumberFormat(2, '!', '=');
13
  return array();
14
  --EXPECT--
15
  20!00
9
  {{ 1020.25|number_format(2, ',') }}
10
  {{ 1020.25|number_format(2, ',', '.') }}
11
  --DATA--
12
+ $twig->getExtension('Twig_Extension_Core')->setNumberFormat(2, '!', '=');
13
  return array();
14
  --EXPECT--
15
  20!00
library/twig/twig/test/Twig/Tests/Fixtures/filters/static_calls.test ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ Filters as static method calls
3
+ --TEMPLATE--
4
+ {{ 'foo'|static_call_string }}
5
+ {{ 'foo'|static_call_array }}
6
+ --DATA--
7
+ return array('foo' => 'foo')
8
+ --EXPECT--
9
+ *foo*
10
+ *foo*
library/twig/twig/test/Twig/Tests/Fixtures/functions/block_with_template.test ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "block" function with a template argument
3
+ --TEMPLATE--
4
+ {{ block('foo', 'included.twig') }}
5
+ {{ block('foo', included_loaded) }}
6
+ {{ block('foo', included_loaded_internal) }}
7
+ {% set output = block('foo', 'included.twig') %}
8
+ {{ output }}
9
+ {% block foo %}NOT FOO{% endblock %}
10
+ --TEMPLATE(included.twig)--
11
+ {% block foo %}FOO{% endblock %}
12
+ --DATA--
13
+ return array(
14
+ 'included_loaded' => $twig->load('included.twig'),
15
+ 'included_loaded_internal' => $twig->loadTemplate('included.twig'),
16
+ )
17
+ --EXPECT--
18
+ FOO
19
+ FOO
20
+ FOO
21
+ FOO
22
+ NOT FOO
library/twig/twig/test/Twig/Tests/Fixtures/functions/block_without_name.test ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "block" function without arguments
3
+ --TEMPLATE--
4
+ {% extends 'base.twig' %}
5
+ {% block bar %}BAR{% endblock %}
6
+ --TEMPLATE(base.twig)--
7
+ {% block foo %}{{ block() }}{% endblock %}
8
+ {% block bar %}BAR_BASE{% endblock %}
9
+ --DATA--
10
+ return array()
11
+ --EXCEPTION--
12
+ Twig_Error_Syntax: The "block" function takes one argument (the block name) in "base.twig" at line 2.
library/twig/twig/test/Twig/Tests/Fixtures/functions/date.test CHANGED
@@ -1,7 +1,7 @@
1
  --TEST--
2
  "date" function
3
  --TEMPLATE--
4
- {{ date() == date('now') ? 'OK' : 'KO' }}
5
  {{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
6
  {{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
7
  {{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
1
  --TEST--
2
  "date" function
3
  --TEMPLATE--
4
+ {{ date().format('r') == date('now').format('r') ? 'OK' : 'KO' }}
5
  {{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
6
  {{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
7
  {{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
library/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call.test ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ __call calls
3
+ --TEMPLATE--
4
+ {{ 'foo'|magic_call }}
5
+ --DATA--
6
+ return array()
7
+ --EXPECT--
8
+ magic_foo
library/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call53.test ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ __staticCall calls
3
+ --CONDITION--
4
+ version_compare(phpversion(), '5.3.0', '>=')
5
+ --TEMPLATE--
6
+ {{ 'foo'|magic_call_string }}
7
+ {{ 'foo'|magic_call_array }}
8
+ --DATA--
9
+ return array()
10
+ --EXPECT--
11
+ static_magic_foo
12
+ static_magic_foo
library/twig/twig/test/Twig/Tests/Fixtures/functions/static_calls.test ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ Functions as static method calls
3
+ --TEMPLATE--
4
+ {{ static_call_string('foo') }}
5
+ {{ static_call_array('foo') }}
6
+ --DATA--
7
+ return array('foo' => 'foo')
8
+ --EXPECT--
9
+ *foo*
10
+ *foo*
library/twig/twig/test/Twig/Tests/Fixtures/macros/varargs_argument.test CHANGED
@@ -5,4 +5,3 @@ macro with varargs argument
5
  {% endmacro %}
6
  --EXCEPTION--
7
  Twig_Error_Syntax: The argument "varargs" in macro "test" cannot be defined because the variable "varargs" is reserved for arbitrary arguments in "index.twig" at line 2.
8
-
5
  {% endmacro %}
6
  --EXCEPTION--
7
  Twig_Error_Syntax: The argument "varargs" in macro "test" cannot be defined because the variable "varargs" is reserved for arbitrary arguments in "index.twig" at line 2.
 
library/twig/twig/test/Twig/Tests/Fixtures/regression/combined_debug_info.test CHANGED
@@ -12,4 +12,4 @@ foo
12
  --DATA--
13
  return array('foo' => 'foo');
14
  --EXCEPTION--
15
- Twig_Error_Runtime: Impossible to access an attribute ("bar") on a string variable ("foo") in "foo" at line 3
12
  --DATA--
13
  return array('foo' => 'foo');
14
  --EXCEPTION--
15
+ Twig_Error_Runtime: Impossible to access an attribute ("bar") on a string variable ("foo") in "foo" at line 3.
library/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test CHANGED
@@ -13,4 +13,4 @@ BAR
13
  --DATA--
14
  return array()
15
  --EXCEPTION--
16
- Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5
13
  --DATA--
14
  return array()
15
  --EXCEPTION--
16
+ Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5.
library/twig/twig/test/Twig/Tests/Fixtures/tags/with/basic.test ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "with" tag
3
+ --TEMPLATE--
4
+ {% with %}
5
+ {% set bar = 'BAZ' %}
6
+ {{ foo }}{{ bar }}
7
+ {% endwith %}
8
+ {{ foo }}{{ bar }}
9
+ --DATA--
10
+ return array('foo' => 'foo', 'bar' => 'bar')
11
+ --EXPECT--
12
+ fooBAZ
13
+ foobar
library/twig/twig/test/Twig/Tests/Fixtures/tags/with/expression.test ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "with" tag with expression
3
+ --TEMPLATE--
4
+ {% with {foo: 'foo', bar: 'BAZ'} %}
5
+ {{ foo }}{{ bar }}
6
+ {% endwith %}
7
+ --DATA--
8
+ return array('foo' => 'baz')
9
+ --EXPECT--
10
+ fooBAZ
library/twig/twig/test/Twig/Tests/Fixtures/tags/with/nested.test ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ nested "with" tags
3
+ --TEMPLATE--
4
+ {% set foo, bar = 'foo', 'bar' %}
5
+ {% with {bar: 'BAZ'} %}
6
+ {% with {foo: 'FOO'} %}
7
+ {{ foo }}{{ bar }}
8
+ {% endwith %}
9
+ {% endwith %}
10
+ {{ foo }}{{ bar }}
11
+ --DATA--
12
+ return array()
13
+ --EXPECT--
14
+ FOOBAZ
15
+ foobar
library/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_no_hash.test ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "with" tag with an expression that is not a hash
3
+ --TEMPLATE--
4
+ {% with vars %}
5
+ {{ foo }}{{ bar }}
6
+ {% endwith %}
7
+ --DATA--
8
+ return array('vars' => 'no-hash')
9
+ --EXCEPTION--
10
+ Twig_Error_Runtime: Variables passed to the "with" tag must be a hash in "index.twig" at line 2.
library/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_only.test ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "with" tag with expression and only
3
+ --TEMPLATE--
4
+ {% with {foo: 'foo', bar: 'BAZ'} only %}
5
+ {{ foo }}{{ bar }}{{ baz }}
6
+ {% endwith %}
7
+ --DATA--
8
+ return array('foo' => 'baz', 'baz' => 'baz')
9
+ --EXCEPTION--
10
+ Twig_Error_Runtime: Variable "baz" does not exist in "index.twig" at line 3.
library/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_attribute.test ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "defined" support for attribute
3
+ --TEMPLATE--
4
+ {{ attribute(nested, "definedVar") is defined ? 'ok' : 'ko' }}
5
+ {{ attribute(nested, "undefinedVar") is not defined ? 'ok' : 'ko' }}
6
+ {{ attribute(nested, definedVarName) is defined ? 'ok' : 'ko' }}
7
+ {{ attribute(nested, undefinedVarName) is not defined ? 'ok' : 'ko' }}
8
+ --DATA--
9
+ return array(
10
+ 'nested' => array(
11
+ 'definedVar' => 'defined',
12
+ ),
13
+ 'definedVarName' => 'definedVar',
14
+ 'undefinedVarName' => 'undefinedVar',
15
+ );
16
+ --EXPECT--
17
+ ok
18
+ ok
19
+ ok
20
+ ok
21
+ --DATA--
22
+ return array(
23
+ 'nested' => array(
24
+ 'definedVar' => 'defined',
25
+ ),
26
+ 'definedVarName' => 'definedVar',
27
+ 'undefinedVarName' => 'undefinedVar',
28
+ );
29
+ --CONFIG--
30
+ return array('strict_variables' => false)
31
+ --EXPECT--
32
+ ok
33
+ ok
34
+ ok
35
+ ok
library/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks.test ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "defined" support for blocks
3
+ --TEMPLATE--
4
+ {% extends 'parent' %}
5
+ {% block icon %}icon{% endblock %}
6
+ {% block body %}
7
+ {{ parent() }}
8
+ {{ block('foo') is defined ? 'ok' : 'ko' }}
9
+ {{ block('footer') is defined ? 'ok' : 'ko' }}
10
+ {{ block('icon') is defined ? 'ok' : 'ko' }}
11
+ {{ block('block1') is defined ? 'ok' : 'ko' }}
12
+ {%- embed 'embed' %}
13
+ {% block content %}content{% endblock %}
14
+ {% endembed %}
15
+ {% endblock %}
16
+ {% use 'blocks' %}
17
+ --TEMPLATE(parent)--
18
+ {% block body %}
19
+ {{ block('icon') is defined ? 'ok' : 'ko' -}}
20
+ {% endblock %}
21
+ {% block footer %}{% endblock %}
22
+ --TEMPLATE(embed)--
23
+ {{ block('icon') is defined ? 'ok' : 'ko' }}
24
+ {{ block('content') is defined ? 'ok' : 'ko' }}
25
+ {{ block('block1') is defined ? 'ok' : 'ko' }}
26
+ --TEMPLATE(blocks)--
27
+ {% block block1 %}{%endblock %}
28
+ --DATA--
29
+ return array()
30
+ --EXPECT--
31
+ ok
32
+ ko
33
+ ok
34
+ ok
35
+ ok
36
+ ko
37
+ ok
38
+ ko
library/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks_with_template.test ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "defined" support for blocks with a template argument
3
+ --TEMPLATE--
4
+ {{ block('foo', 'included.twig') is defined ? 'ok' : 'ko' }}
5
+ {{ block('foo', included_loaded) is defined ? 'ok' : 'ko' }}
6
+ {{ block('foo', included_loaded_internal) is defined ? 'ok' : 'ko' }}
7
+ --TEMPLATE(included.twig)--
8
+ {% block foo %}FOO{% endblock %}
9
+ --DATA--
10
+ return array(
11
+ 'included_loaded' => $twig->load('included.twig'),
12
+ 'included_loaded_internal' => $twig->loadTemplate('included.twig'),
13
+ )
14
+ --EXPECT--
15
+ ok
16
+ ok
17
+ ok
library/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_constants.test ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "defined" support for constants
3
+ --TEMPLATE--
4
+ {{ constant('DATE_W3C') is defined ? 'ok' : 'ko' }}
5
+ {{ constant('ARRAY_AS_PROPS', object) is defined ? 'ok' : 'ko' }}
6
+ {{ constant('FOOBAR') is not defined ? 'ok' : 'ko' }}
7
+ {{ constant('FOOBAR', object) is not defined ? 'ok' : 'ko' }}
8
+ --DATA--
9
+ return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi')));
10
+ --EXPECT--
11
+ ok
12
+ ok
13
+ ok
14
+ ok
library/twig/twig/test/Twig/Tests/IntegrationTest.php CHANGED
@@ -141,6 +141,11 @@ class TwigTestExtension extends Twig_Extension
141
  new Twig_SimpleFilter('nl2br', array($this, 'nl2br'), array('pre_escape' => 'html', 'is_safe' => array('html'))),
142
  new Twig_SimpleFilter('escape_something', array($this, 'escape_something'), array('is_safe' => array('something'))),
143
  new Twig_SimpleFilter('preserves_safety', array($this, 'preserves_safety'), array('preserves_safety' => array('html'))),
 
 
 
 
 
144
  new Twig_SimpleFilter('*_path', array($this, 'dynamic_path')),
145
  new Twig_SimpleFilter('*_foo_*_bar', array($this, 'dynamic_foo')),
146
  );
@@ -152,6 +157,8 @@ class TwigTestExtension extends Twig_Extension
152
  new Twig_SimpleFunction('§', array($this, '§Function')),
153
  new Twig_SimpleFunction('safe_br', array($this, 'br'), array('is_safe' => array('html'))),
154
  new Twig_SimpleFunction('unsafe_br', array($this, 'br')),
 
 
155
  new Twig_SimpleFunction('*_path', array($this, 'dynamic_path')),
156
  new Twig_SimpleFunction('*_foo_*_bar', array($this, 'dynamic_foo')),
157
  );
@@ -212,6 +219,11 @@ class TwigTestExtension extends Twig_Extension
212
  return strtoupper($value);
213
  }
214
 
 
 
 
 
 
215
  public function br()
216
  {
217
  return '<br />';
@@ -222,8 +234,21 @@ class TwigTestExtension extends Twig_Extension
222
  return false !== strpos($value, ' ');
223
  }
224
 
225
- public function getName()
 
 
 
 
 
 
 
 
 
226
  {
227
- return 'integration_test';
 
 
 
 
228
  }
229
  }
141
  new Twig_SimpleFilter('nl2br', array($this, 'nl2br'), array('pre_escape' => 'html', 'is_safe' => array('html'))),
142
  new Twig_SimpleFilter('escape_something', array($this, 'escape_something'), array('is_safe' => array('something'))),
143
  new Twig_SimpleFilter('preserves_safety', array($this, 'preserves_safety'), array('preserves_safety' => array('html'))),
144
+ new Twig_SimpleFilter('static_call_string', 'TwigTestExtension::staticCall'),
145
+ new Twig_SimpleFilter('static_call_array', array('TwigTestExtension', 'staticCall')),
146
+ new Twig_SimpleFilter('magic_call', array($this, 'magicCall')),
147
+ new Twig_SimpleFilter('magic_call_string', 'TwigTestExtension::magicStaticCall'),
148
+ new Twig_SimpleFilter('magic_call_array', array('TwigTestExtension', 'magicStaticCall')),
149
  new Twig_SimpleFilter('*_path', array($this, 'dynamic_path')),
150
  new Twig_SimpleFilter('*_foo_*_bar', array($this, 'dynamic_foo')),
151
  );
157
  new Twig_SimpleFunction('§', array($this, '§Function')),
158
  new Twig_SimpleFunction('safe_br', array($this, 'br'), array('is_safe' => array('html'))),
159
  new Twig_SimpleFunction('unsafe_br', array($this, 'br')),
160
+ new Twig_SimpleFunction('static_call_string', 'TwigTestExtension::staticCall'),
161
+ new Twig_SimpleFunction('static_call_array', array('TwigTestExtension', 'staticCall')),
162
  new Twig_SimpleFunction('*_path', array($this, 'dynamic_path')),
163
  new Twig_SimpleFunction('*_foo_*_bar', array($this, 'dynamic_foo')),
164
  );
219
  return strtoupper($value);
220
  }
221
 
222
+ public static function staticCall($value)
223
+ {
224
+ return "*$value*";
225
+ }
226
+
227
  public function br()
228
  {
229
  return '<br />';
234
  return false !== strpos($value, ' ');
235
  }
236
 
237
+ public function __call($method, $arguments)
238
+ {
239
+ if ('magicCall' !== $method) {
240
+ throw new BadMethodCallException('Unexpected call to __call');
241
+ }
242
+
243
+ return 'magic_'.$arguments[0];
244
+ }
245
+
246
+ public static function __callStatic($method, $arguments)
247
  {
248
+ if ('magicStaticCall' !== $method) {
249
+ throw new BadMethodCallException('Unexpected call to __callStatic');
250
+ }
251
+
252
+ return 'static_magic_'.$arguments[0];
253
  }
254
  }
library/twig/twig/test/Twig/Tests/LegacyFixtures/autoescape/filename.legacy.test ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ "filename" autoescape strategy
3
+ --TEMPLATE--
4
+ {{ br -}}
5
+ {{ include('index.html.twig') -}}
6
+ {{ include('index.txt.twig') -}}
7
+ --TEMPLATE(index.html.twig)--
8
+ {{ br -}}
9
+ --TEMPLATE(index.txt.twig)--
10
+ {{ br -}}
11
+ --DATA--
12
+ return array('br' => '<br />')
13
+ --CONFIG--
14
+ return array('autoescape' => 'filename')
15
+ --EXPECT--
16
+ &lt;br /&gt;
17
+ &lt;br /&gt;
18
+ <br />
library/twig/twig/test/Twig/Tests/LexerTest.php CHANGED
@@ -10,12 +10,23 @@
10
  */
11
  class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
12
  {
 
 
 
 
 
 
 
 
 
 
 
13
  public function testNameLabelForTag()
14
  {
15
  $template = '{% § %}';
16
 
17
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
18
- $stream = $lexer->tokenize($template);
19
 
20
  $stream->expect(Twig_Token::BLOCK_START_TYPE);
21
  $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
@@ -26,7 +37,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
26
  $template = '{{ §() }}';
27
 
28
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
29
- $stream = $lexer->tokenize($template);
30
 
31
  $stream->expect(Twig_Token::VAR_START_TYPE);
32
  $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
@@ -43,7 +54,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
43
  protected function countToken($template, $type, $value = null)
44
  {
45
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
46
- $stream = $lexer->tokenize($template);
47
 
48
  $count = 0;
49
  while (!$stream->isEOF()) {
@@ -68,7 +79,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
68
  ."}}\n";
69
 
70
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
71
- $stream = $lexer->tokenize($template);
72
 
73
  // foo\nbar\n
74
  $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
@@ -88,7 +99,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
88
  ."}}\n";
89
 
90
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
91
- $stream = $lexer->tokenize($template);
92
 
93
  // foo\nbar
94
  $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
@@ -103,7 +114,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
103
  $template = '{# '.str_repeat('*', 100000).' #}';
104
 
105
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
106
- $lexer->tokenize($template);
107
 
108
  // should not throw an exception
109
  }
@@ -113,7 +124,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
113
  $template = '{% verbatim %}'.str_repeat('*', 100000).'{% endverbatim %}';
114
 
115
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
116
- $lexer->tokenize($template);
117
 
118
  // should not throw an exception
119
  }
@@ -123,7 +134,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
123
  $template = '{{ '.str_repeat('x', 100000).' }}';
124
 
125
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
126
- $lexer->tokenize($template);
127
 
128
  // should not throw an exception
129
  }
@@ -133,7 +144,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
133
  $template = '{% '.str_repeat('x', 100000).' %}';
134
 
135
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
136
- $lexer->tokenize($template);
137
 
138
  // should not throw an exception
139
  }
@@ -143,7 +154,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
143
  $template = '{{ 922337203685477580700 }}';
144
 
145
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
146
- $stream = $lexer->tokenize($template);
147
  $stream->next();
148
  $node = $stream->next();
149
  $this->assertEquals('922337203685477580700', $node->getValue());
@@ -157,7 +168,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
157
  );
158
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
159
  foreach ($tests as $template => $expected) {
160
- $stream = $lexer->tokenize($template);
161
  $stream->expect(Twig_Token::VAR_START_TYPE);
162
  $stream->expect(Twig_Token::STRING_TYPE, $expected);
163
  }
@@ -168,7 +179,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
168
  $template = 'foo {{ "bar #{ baz + 1 }" }}';
169
 
170
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
171
- $stream = $lexer->tokenize($template);
172
  $stream->expect(Twig_Token::TEXT_TYPE, 'foo ');
173
  $stream->expect(Twig_Token::VAR_START_TYPE);
174
  $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
@@ -185,7 +196,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
185
  $template = '{{ "bar \#{baz+1}" }}';
186
 
187
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
188
- $stream = $lexer->tokenize($template);
189
  $stream->expect(Twig_Token::VAR_START_TYPE);
190
  $stream->expect(Twig_Token::STRING_TYPE, 'bar #{baz+1}');
191
  $stream->expect(Twig_Token::VAR_END_TYPE);
@@ -196,7 +207,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
196
  $template = '{{ "bar # baz" }}';
197
 
198
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
199
- $stream = $lexer->tokenize($template);
200
  $stream->expect(Twig_Token::VAR_START_TYPE);
201
  $stream->expect(Twig_Token::STRING_TYPE, 'bar # baz');
202
  $stream->expect(Twig_Token::VAR_END_TYPE);
@@ -211,7 +222,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
211
  $template = '{{ "bar #{x" }}';
212
 
213
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
214
- $lexer->tokenize($template);
215
  }
216
 
217
  public function testStringWithNestedInterpolations()
@@ -219,7 +230,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
219
  $template = '{{ "bar #{ "foo#{bar}" }" }}';
220
 
221
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
222
- $stream = $lexer->tokenize($template);
223
  $stream->expect(Twig_Token::VAR_START_TYPE);
224
  $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
225
  $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
@@ -236,7 +247,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
236
  $template = '{% foo "bar #{ "foo#{bar}" }" %}';
237
 
238
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
239
- $stream = $lexer->tokenize($template);
240
  $stream->expect(Twig_Token::BLOCK_START_TYPE);
241
  $stream->expect(Twig_Token::NAME_TYPE, 'foo');
242
  $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
@@ -254,7 +265,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
254
  $template = "{{ 1 and\n0}}";
255
 
256
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
257
- $stream = $lexer->tokenize($template);
258
  $stream->expect(Twig_Token::VAR_START_TYPE);
259
  $stream->expect(Twig_Token::NUMBER_TYPE, 1);
260
  $stream->expect(Twig_Token::OPERATOR_TYPE, 'and');
@@ -262,7 +273,7 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
262
 
263
  /**
264
  * @expectedException Twig_Error_Syntax
265
- * @expectedExceptionMessage Unclosed "variable" at line 3
266
  */
267
  public function testUnterminatedVariable()
268
  {
@@ -276,12 +287,12 @@ bar
276
  ';
277
 
278
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
279
- $lexer->tokenize($template);
280
  }
281
 
282
  /**
283
  * @expectedException Twig_Error_Syntax
284
- * @expectedExceptionMessage Unclosed "block" at line 3
285
  */
286
  public function testUnterminatedBlock()
287
  {
@@ -295,6 +306,6 @@ bar
295
  ';
296
 
297
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
298
- $lexer->tokenize($template);
299
  }
300
  }
10
  */
11
  class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
12
  {
13
+ /**
14
+ * @group legacy
15
+ */
16
+ public function testLegacyConstructorSignature()
17
+ {
18
+ $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
19
+ $stream = $lexer->tokenize('{{ foo }}', 'foo');
20
+ $this->assertEquals('foo', $stream->getFilename());
21
+ $this->assertEquals('{{ foo }}', $stream->getSource());
22
+ }
23
+
24
  public function testNameLabelForTag()
25
  {
26
  $template = '{% § %}';
27
 
28
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
29
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
30
 
31
  $stream->expect(Twig_Token::BLOCK_START_TYPE);
32
  $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
37
  $template = '{{ §() }}';
38
 
39
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
40
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
41
 
42
  $stream->expect(Twig_Token::VAR_START_TYPE);
43
  $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
54
  protected function countToken($template, $type, $value = null)
55
  {
56
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
57
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
58
 
59
  $count = 0;
60
  while (!$stream->isEOF()) {
79
  ."}}\n";
80
 
81
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
82
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
83
 
84
  // foo\nbar\n
85
  $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
99
  ."}}\n";
100
 
101
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
102
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
103
 
104
  // foo\nbar
105
  $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
114
  $template = '{# '.str_repeat('*', 100000).' #}';
115
 
116
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
117
+ $lexer->tokenize(new Twig_Source($template, 'index'));
118
 
119
  // should not throw an exception
120
  }
124
  $template = '{% verbatim %}'.str_repeat('*', 100000).'{% endverbatim %}';
125
 
126
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
127
+ $lexer->tokenize(new Twig_Source($template, 'index'));
128
 
129
  // should not throw an exception
130
  }
134
  $template = '{{ '.str_repeat('x', 100000).' }}';
135
 
136
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
137
+ $lexer->tokenize(new Twig_Source($template, 'index'));
138
 
139
  // should not throw an exception
140
  }
144
  $template = '{% '.str_repeat('x', 100000).' %}';
145
 
146
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
147
+ $lexer->tokenize(new Twig_Source($template, 'index'));
148
 
149
  // should not throw an exception
150
  }
154
  $template = '{{ 922337203685477580700 }}';
155
 
156
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
157
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
158
  $stream->next();
159
  $node = $stream->next();
160
  $this->assertEquals('922337203685477580700', $node->getValue());
168
  );
169
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
170
  foreach ($tests as $template => $expected) {
171
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
172
  $stream->expect(Twig_Token::VAR_START_TYPE);
173
  $stream->expect(Twig_Token::STRING_TYPE, $expected);
174
  }
179
  $template = 'foo {{ "bar #{ baz + 1 }" }}';
180
 
181
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
182
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
183
  $stream->expect(Twig_Token::TEXT_TYPE, 'foo ');
184
  $stream->expect(Twig_Token::VAR_START_TYPE);
185
  $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
196
  $template = '{{ "bar \#{baz+1}" }}';
197
 
198
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
199
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
200
  $stream->expect(Twig_Token::VAR_START_TYPE);
201
  $stream->expect(Twig_Token::STRING_TYPE, 'bar #{baz+1}');
202
  $stream->expect(Twig_Token::VAR_END_TYPE);
207
  $template = '{{ "bar # baz" }}';
208
 
209
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
210
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
211
  $stream->expect(Twig_Token::VAR_START_TYPE);
212
  $stream->expect(Twig_Token::STRING_TYPE, 'bar # baz');
213
  $stream->expect(Twig_Token::VAR_END_TYPE);
222
  $template = '{{ "bar #{x" }}';
223
 
224
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
225
+ $lexer->tokenize(new Twig_Source($template, 'index'));
226
  }
227
 
228
  public function testStringWithNestedInterpolations()
230
  $template = '{{ "bar #{ "foo#{bar}" }" }}';
231
 
232
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
233
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
234
  $stream->expect(Twig_Token::VAR_START_TYPE);
235
  $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
236
  $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
247
  $template = '{% foo "bar #{ "foo#{bar}" }" %}';
248
 
249
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
250
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
251
  $stream->expect(Twig_Token::BLOCK_START_TYPE);
252
  $stream->expect(Twig_Token::NAME_TYPE, 'foo');
253
  $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
265
  $template = "{{ 1 and\n0}}";
266
 
267
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
268
+ $stream = $lexer->tokenize(new Twig_Source($template, 'index'));
269
  $stream->expect(Twig_Token::VAR_START_TYPE);
270
  $stream->expect(Twig_Token::NUMBER_TYPE, 1);
271
  $stream->expect(Twig_Token::OPERATOR_TYPE, 'and');
273
 
274
  /**
275
  * @expectedException Twig_Error_Syntax
276
+ * @expectedExceptionMessage Unclosed "variable" in "index" at line 3
277
  */
278
  public function testUnterminatedVariable()
279
  {
287
  ';
288
 
289
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
290
+ $lexer->tokenize(new Twig_Source($template, 'index'));
291
  }
292
 
293
  /**
294
  * @expectedException Twig_Error_Syntax
295
+ * @expectedExceptionMessage Unclosed "block" in "index" at line 3
296
  */
297
  public function testUnterminatedBlock()
298
  {
306
  ';
307
 
308
  $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
309
+ $lexer->tokenize(new Twig_Source($template, 'index'));
310
  }
311
  }
library/twig/twig/test/Twig/Tests/Loader/ArrayTest.php CHANGED
@@ -11,6 +11,9 @@
11
 
12
  class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase
13
  {
 
 
 
14
  public function testGetSource()
15
  {
16
  $loader = new Twig_Loader_Array(array('foo' => 'bar'));
@@ -19,6 +22,7 @@ class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase
19
  }
20
 
21
  /**
 
22
  * @expectedException Twig_Error_Loader
23
  */
24
  public function testGetSourceWhenTemplateDoesNotExist()
@@ -28,6 +32,16 @@ class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase
28
  $loader->getSource('foo');
29
  }
30
 
 
 
 
 
 
 
 
 
 
 
31
  public function testGetCacheKey()
32
  {
33
  $loader = new Twig_Loader_Array(array('foo' => 'bar'));
@@ -50,7 +64,7 @@ class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase
50
  $loader = new Twig_Loader_Array(array());
51
  $loader->setTemplate('foo', 'bar');
52
 
53
- $this->assertEquals('bar', $loader->getSource('foo'));
54
  }
55
 
56
  public function testIsFresh()
@@ -75,7 +89,7 @@ class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase
75
  $loader = new Twig_Loader_Array(array('foo' => 'bar'));
76
 
77
  $loader->getCacheKey($name);
78
- $loader->getSource($name);
79
  $loader->isFresh($name, time());
80
  $loader->setTemplate($name, 'foobar');
81
  }
11
 
12
  class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase
13
  {
14
+ /**
15
+ * @group legacy
16
+ */
17
  public function testGetSource()
18
  {
19
  $loader = new Twig_Loader_Array(array('foo' => 'bar'));
22
  }
23
 
24
  /**
25
+ * @group legacy
26
  * @expectedException Twig_Error_Loader
27
  */
28
  public function testGetSourceWhenTemplateDoesNotExist()
32
  $loader->getSource('foo');
33
  }
34
 
35
+ /**
36
+ * @expectedException Twig_Error_Loader
37
+ */
38
+ public function testGetSourceContextWhenTemplateDoesNotExist()
39
+ {
40
+ $loader = new Twig_Loader_Array(array());
41
+
42
+ $loader->getSourceContext('foo');
43
+ }
44
+
45
  public function testGetCacheKey()
46
  {
47
  $loader = new Twig_Loader_Array(array('foo' => 'bar'));
64
  $loader = new Twig_Loader_Array(array());
65
  $loader->setTemplate('foo', 'bar');
66
 
67
+ $this->assertEquals('bar', $loader->getSourceContext('foo')->getCode());
68
  }
69
 
70
  public function testIsFresh()
89
  $loader = new Twig_Loader_Array(array('foo' => 'bar'));
90
 
91
  $loader->getCacheKey($name);
92
+ $loader->getSourceContext($name);
93
  $loader->isFresh($name, time());
94
  $loader->setTemplate($name, 'foobar');
95
  }
library/twig/twig/test/Twig/Tests/Loader/ChainTest.php CHANGED
@@ -11,6 +11,9 @@
11
 
12
  class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase
13
  {
 
 
 
14
  public function testGetSource()
15
  {
16
  $loader = new Twig_Loader_Chain(array(
@@ -22,9 +25,41 @@ class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase
22
  $this->assertEquals('foo', $loader->getSource('bar'));
23
  }
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  * @expectedException Twig_Error_Loader
27
  */
 
 
 
 
 
 
 
 
 
 
 
28
  public function testGetSourceWhenTemplateDoesNotExist()
29
  {
30
  $loader = new Twig_Loader_Chain(array());
@@ -58,17 +93,19 @@ class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase
58
  $loader = new Twig_Loader_Chain();
59
  $loader->addLoader(new Twig_Loader_Array(array('foo' => 'bar')));
60
 
61
- $this->assertEquals('bar', $loader->getSource('foo'));
62
  }
63
 
64
  public function testExists()
65
  {
66
- $loader1 = $this->getMockBuilder('Twig_Loader_Array')->setMethods(array('exists', 'getSource'))->disableOriginalConstructor()->getMock();
67
  $loader1->expects($this->once())->method('exists')->will($this->returnValue(false));
68
- $loader1->expects($this->never())->method('getSource');
69
 
70
- $loader2 = $this->getMockBuilder('Twig_LoaderInterface')->getMock();
71
- $loader2->expects($this->once())->method('getSource')->will($this->returnValue('content'));
 
 
72
 
73
  $loader = new Twig_Loader_Chain();
74
  $loader->addLoader($loader1);
@@ -77,3 +114,7 @@ class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase
77
  $this->assertTrue($loader->exists('foo'));
78
  }
79
  }
 
 
 
 
11
 
12
  class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase
13
  {
14
+ /**
15
+ * @group legacy
16
+ */
17
  public function testGetSource()
18
  {
19
  $loader = new Twig_Loader_Chain(array(
25
  $this->assertEquals('foo', $loader->getSource('bar'));
26
  }
27
 
28
+ public function testGetSourceContext()
29
+ {
30
+ $path = dirname(__FILE__).'/../Fixtures';
31
+ $loader = new Twig_Loader_Chain(array(
32
+ new Twig_Loader_Array(array('foo' => 'bar')),
33
+ new Twig_Loader_Array(array('errors/index.html' => 'baz')),
34
+ new Twig_Loader_Filesystem(array($path)),
35
+ ));
36
+
37
+ $this->assertEquals('foo', $loader->getSourceContext('foo')->getName());
38
+ $this->assertSame('', $loader->getSourceContext('foo')->getPath());
39
+
40
+ $this->assertEquals('errors/index.html', $loader->getSourceContext('errors/index.html')->getName());
41
+ $this->assertSame('', $loader->getSourceContext('errors/index.html')->getPath());
42
+ $this->assertEquals('baz', $loader->getSourceContext('errors/index.html')->getCode());
43
+
44
+ $this->assertEquals('errors/base.html', $loader->getSourceContext('errors/base.html')->getName());
45
+ $this->assertEquals(realpath($path.'/errors/base.html'), realpath($loader->getSourceContext('errors/base.html')->getPath()));
46
+ $this->assertNotEquals('baz', $loader->getSourceContext('errors/base.html')->getCode());
47
+ }
48
+
49
  /**
50
  * @expectedException Twig_Error_Loader
51
  */
52
+ public function testGetSourceContextWhenTemplateDoesNotExist()
53
+ {
54
+ $loader = new Twig_Loader_Chain(array());
55
+
56
+ $loader->getSourceContext('foo');
57
+ }
58
+
59
+ /**
60
+ * @group legacy
61
+ * @expectedException Twig_Error_Loader
62
+ */
63
  public function testGetSourceWhenTemplateDoesNotExist()
64
  {
65
  $loader = new Twig_Loader_Chain(array());
93
  $loader = new Twig_Loader_Chain();
94
  $loader->addLoader(new Twig_Loader_Array(array('foo' => 'bar')));
95
 
96
+ $this->assertEquals('bar', $loader->getSourceContext('foo')->getCode());
97
  }
98
 
99
  public function testExists()
100
  {
101
+ $loader1 = $this->getMockBuilder('Twig_Loader_Array')->setMethods(array('exists', 'getSourceContext'))->disableOriginalConstructor()->getMock();
102
  $loader1->expects($this->once())->method('exists')->will($this->returnValue(false));
103
+ $loader1->expects($this->never())->method('getSourceContext');
104
 
105
+ // can be removed in 2.0
106
+ $loader2 = $this->getMockBuilder('Twig_ChainTestLoaderInterface')->getMock();
107
+ //$loader2 = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock();
108
+ $loader2->expects($this->once())->method('getSourceContext')->will($this->returnValue(new Twig_Source('content', 'index')));
109
 
110
  $loader = new Twig_Loader_Chain();
111
  $loader->addLoader($loader1);
114
  $this->assertTrue($loader->exists('foo'));
115
  }
116
  }
117
+
118
+ interface Twig_ChainTestLoaderInterface extends Twig_LoaderInterface, Twig_SourceContextLoaderInterface
119
+ {
120
+ }
library/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php CHANGED
@@ -11,6 +11,14 @@
11
 
12
  class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase
13
  {
 
 
 
 
 
 
 
 
14
  /**
15
  * @dataProvider getSecurityTests
16
  */
@@ -54,9 +62,9 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase
54
  /**
55
  * @dataProvider getBasePaths
56
  */
57
- public function testPaths($basePath)
58
  {
59
- $loader = new Twig_Loader_Filesystem(array($basePath.'/normal', $basePath.'/normal_bis'));
60
  $loader->setPaths(array($basePath.'/named', $basePath.'/named_bis'), 'named');
61
  $loader->addPath($basePath.'/named_ter', 'named');
62
  $loader->addPath($basePath.'/normal_ter');
@@ -79,17 +87,40 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase
79
  ), $loader->getPaths('named'));
80
 
81
  // do not use realpath here as it would make the test unuseful
82
- $this->assertEquals(str_replace('\\', '/', $basePath.'/named_quater/named_absolute.html'), str_replace('\\', '/', $loader->getCacheKey('@named/named_absolute.html')));
83
- $this->assertEquals("path (final)\n", $loader->getSource('index.html'));
84
- $this->assertEquals("path (final)\n", $loader->getSource('@__main__/index.html'));
85
- $this->assertEquals("named path (final)\n", $loader->getSource('@named/index.html'));
86
  }
87
 
88
  public function getBasePaths()
89
  {
90
  return array(
91
- array(dirname(__FILE__).'/Fixtures'),
92
- array('test/Twig/Tests/Loader/Fixtures'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  );
94
  }
95
 
@@ -116,7 +147,7 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase
116
  $loader->addPath($basePath.'/named', 'named');
117
 
118
  try {
119
- $loader->getSource('@named/nowhere.html');
120
  } catch (Exception $e) {
121
  $this->assertInstanceof('Twig_Error_Loader', $e);
122
  $this->assertContains('Unable to find template "@named/nowhere.html"', $e->getMessage());
@@ -131,11 +162,11 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase
131
  $loader->addPath($basePath.'/named', 'named');
132
 
133
  // prime the cache for index.html in the named namespace
134
- $namedSource = $loader->getSource('@named/index.html');
135
  $this->assertEquals("named path\n", $namedSource);
136
 
137
  // get index.html from the main namespace
138
- $this->assertEquals("path\n", $loader->getSource('index.html'));
139
  }
140
 
141
  public function testLoadTemplateAndRenderBlockWithCache()
@@ -179,4 +210,17 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase
179
  $template = $twig->loadTemplate($templateName);
180
  $this->assertSame('VALID Child', $template->renderBlock('body', array()));
181
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  }
11
 
12
  class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase
13
  {
14
+ public function testGetSourceContext()
15
+ {
16
+ $path = dirname(__FILE__).'/../Fixtures';
17
+ $loader = new Twig_Loader_Filesystem(array($path));
18
+ $this->assertEquals('errors/index.html', $loader->getSourceContext('errors/index.html')->getName());
19
+ $this->assertEquals(realpath($path.'/errors/index.html'), realpath($loader->getSourceContext('errors/index.html')->getPath()));
20
+ }
21
+
22
  /**
23
  * @dataProvider getSecurityTests
24
  */
62
  /**
63
  * @dataProvider getBasePaths
64
  */
65
+ public function testPaths($basePath, $cacheKey, $rootPath)
66
  {
67
+ $loader = new Twig_Loader_Filesystem(array($basePath.'/normal', $basePath.'/normal_bis'), $rootPath);
68
  $loader->setPaths(array($basePath.'/named', $basePath.'/named_bis'), 'named');
69
  $loader->addPath($basePath.'/named_ter', 'named');
70
  $loader->addPath($basePath.'/normal_ter');
87
  ), $loader->getPaths('named'));
88
 
89
  // do not use realpath here as it would make the test unuseful
90
+ $this->assertEquals($cacheKey, str_replace('\\', '/', $loader->getCacheKey('@named/named_absolute.html')));
91
+ $this->assertEquals("path (final)\n", $loader->getSourceContext('index.html')->getCode());
92
+ $this->assertEquals("path (final)\n", $loader->getSourceContext('@__main__/index.html')->getCode());
93
+ $this->assertEquals("named path (final)\n", $loader->getSourceContext('@named/index.html')->getCode());
94
  }
95
 
96
  public function getBasePaths()
97
  {
98
  return array(
99
+ array(
100
+ dirname(__FILE__).'/Fixtures',
101
+ 'test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html',
102
+ null,
103
+ ),
104
+ array(
105
+ dirname(__FILE__).'/Fixtures/../Fixtures',
106
+ 'test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html',
107
+ null,
108
+ ),
109
+ array(
110
+ 'test/Twig/Tests/Loader/Fixtures',
111
+ 'test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html',
112
+ getcwd(),
113
+ ),
114
+ array(
115
+ 'Fixtures',
116
+ 'Fixtures/named_quater/named_absolute.html',
117
+ getcwd().'/test/Twig/Tests/Loader',
118
+ ),
119
+ array(
120
+ 'Fixtures',
121
+ 'Fixtures/named_quater/named_absolute.html',
122
+ getcwd().'/test/../test/Twig/Tests/Loader',
123
+ ),
124
  );
125
  }
126
 
147
  $loader->addPath($basePath.'/named', 'named');
148
 
149
  try {
150
+ $loader->getSourceContext('@named/nowhere.html');
151
  } catch (Exception $e) {
152
  $this->assertInstanceof('Twig_Error_Loader', $e);
153
  $this->assertContains('Unable to find template "@named/nowhere.html"', $e->getMessage());
162
  $loader->addPath($basePath.'/named', 'named');
163
 
164
  // prime the cache for index.html in the named namespace
165
+ $namedSource = $loader->getSourceContext('@named/index.html')->getCode();
166
  $this->assertEquals("named path\n", $namedSource);
167
 
168
  // get index.html from the main namespace
169
+ $this->assertEquals("path\n", $loader->getSourceContext('index.html')->getCode());
170
  }
171
 
172
  public function testLoadTemplateAndRenderBlockWithCache()
210
  $template = $twig->loadTemplate($templateName);
211
  $this->assertSame('VALID Child', $template->renderBlock('body', array()));
212
  }
213
+
214
+ /**
215
+ * @requires PHP 5.3
216
+ */
217
+ public function testLoadTemplateFromPhar()
218
+ {
219
+ $loader = new Twig_Loader_Filesystem(array());
220
+ // phar-sample.phar was created with the following script:
221
+ // $f = new Phar('phar-test.phar');
222
+ // $f->addFromString('hello.twig', 'hello from phar');
223
+ $loader->addPath('phar://'.dirname(__FILE__).'/Fixtures/phar/phar-sample.phar');
224
+ $this->assertSame('hello from phar', $loader->getSourceContext('hello.twig')->getCode());
225
+ }
226
  }
library/twig/twig/test/Twig/Tests/Loader/Fixtures/phar/phar-sample.phar ADDED
Binary file
library/twig/twig/test/Twig/Tests/NativeExtensionTest.php CHANGED
@@ -11,6 +11,9 @@
11
 
12
  class Twig_Tests_NativeExtensionTest extends PHPUnit_Framework_TestCase
13
  {
 
 
 
14
  public function testGetProperties()
15
  {
16
  if (defined('HHVM_VERSION')) {
11
 
12
  class Twig_Tests_NativeExtensionTest extends PHPUnit_Framework_TestCase
13
  {
14
+ /**
15
+ * @requires PHP 5.3
16
+ */
17
  public function testGetProperties()
18
  {
19
  if (defined('HHVM_VERSION')) {
library/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php CHANGED
@@ -26,8 +26,16 @@ class Twig_Tests_Node_Expression_NameTest extends Twig_Test_NodeTestCase
26
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true));
27
  $env1 = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => false));
28
 
 
 
 
 
 
 
 
 
29
  return array(
30
- array($node, "// line 1\n".(PHP_VERSION_ID >= 50400 ? '(isset($context["foo"]) ? $context["foo"] : $this->getContext($context, "foo"))' : '$this->getContext($context, "foo")'), $env),
31
  array($node, $this->getVariableGetter('foo', 1), $env1),
32
  array($context, "// line 1\n\$context"),
33
  );
26
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true));
27
  $env1 = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => false));
28
 
29
+ if (PHP_VERSION_ID >= 70000) {
30
+ $output = '($context["foo"] ?? $this->getContext($context, "foo"))';
31
+ } elseif (PHP_VERSION_ID >= 50400) {
32
+ $output = '(isset($context["foo"]) ? $context["foo"] : $this->getContext($context, "foo"))';
33
+ } else {
34
+ $output = '$this->getContext($context, "foo")';
35
+ }
36
+
37
  return array(
38
+ array($node, "// line 1\n".$output, $env),
39
  array($node, $this->getVariableGetter('foo', 1), $env1),
40
  array($context, "// line 1\n\$context"),
41
  );
library/twig/twig/test/Twig/Tests/Node/Expression/NullCoalesceTest.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Node_Expression_NullCoalesceTest extends Twig_Test_NodeTestCase
13
+ {
14
+ public function getTests()
15
+ {
16
+ $tests = array();
17
+
18
+ $left = new Twig_Node_Expression_Name('foo', 1);
19
+ $right = new Twig_Node_Expression_Constant(2, 1);
20
+ $node = new Twig_Node_Expression_NullCoalesce($left, $right, 1);
21
+ if (PHP_VERSION_ID >= 70000) {
22
+ $tests[] = array($node, "((// line 1\n\$context[\"foo\"]) ?? (2))");
23
+ } elseif (PHP_VERSION_ID >= 50400) {
24
+ $tests[] = array($node, "(((// line 1\narray_key_exists(\"foo\", \$context) && !(null === (isset(\$context[\"foo\"]) ? \$context[\"foo\"] : null)))) ? ((isset(\$context[\"foo\"]) ? \$context[\"foo\"] : null)) : (2))");
25
+ } else {
26
+ $tests[] = array($node, "(((// line 1\narray_key_exists(\"foo\", \$context) && !(null === \$this->getContext(\$context, \"foo\")))) ? (\$this->getContext(\$context, \"foo\")) : (2))");
27
+ }
28
+
29
+ return $tests;
30
+ }
31
+ }
library/twig/twig/test/Twig/Tests/Node/ModuleTest.php CHANGED
@@ -18,14 +18,14 @@ class Twig_Tests_Node_ModuleTest extends Twig_Test_NodeTestCase
18
  $blocks = new Twig_Node();
19
  $macros = new Twig_Node();
20
  $traits = new Twig_Node();
21
- $filename = 'foo.twig';
22
- $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
23
 
24
  $this->assertEquals($body, $node->getNode('body'));
25
  $this->assertEquals($blocks, $node->getNode('blocks'));
26
  $this->assertEquals($macros, $node->getNode('macros'));
27
  $this->assertEquals($parent, $node->getNode('parent'));
28
- $this->assertEquals($filename, $node->getAttribute('filename'));
29
  }
30
 
31
  public function getTests()
@@ -39,9 +39,9 @@ class Twig_Tests_Node_ModuleTest extends Twig_Test_NodeTestCase
39
  $blocks = new Twig_Node();
40
  $macros = new Twig_Node();
41
  $traits = new Twig_Node();
42
- $filename = 'foo.twig';
43
 
44
- $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
45
  $tests[] = array($node, <<<EOF
46
  <?php
47
 
@@ -74,9 +74,17 @@ class __TwigTemplate_%x extends Twig_Template
74
  return array ( 19 => 1,);
75
  }
76
 
 
77
  public function getSource()
78
  {
79
- return "";
 
 
 
 
 
 
 
80
  }
81
  }
82
  EOF
@@ -87,7 +95,7 @@ EOF
87
  $body = new Twig_Node(array($import));
88
  $extends = new Twig_Node_Expression_Constant('layout.twig', 1);
89
 
90
- $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
91
  $tests[] = array($node, <<<EOF
92
  <?php
93
 
@@ -132,9 +140,17 @@ class __TwigTemplate_%x extends Twig_Template
132
  return array ( 26 => 1, 24 => 2, 11 => 1,);
133
  }
134
 
 
135
  public function getSource()
136
  {
137
- return "";
 
 
 
 
 
 
 
138
  }
139
  }
140
  EOF
@@ -149,7 +165,8 @@ EOF
149
  2
150
  );
151
 
152
- $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename, '{{ foo }}');
 
153
  $tests[] = array($node, <<<EOF
154
  <?php
155
 
@@ -185,9 +202,17 @@ class __TwigTemplate_%x extends Twig_Template
185
  return array ( 17 => 2, 15 => 4, 9 => 2,);
186
  }
187
 
 
188
  public function getSource()
189
  {
190
- return "{{ foo }}";
 
 
 
 
 
 
 
191
  }
192
  }
193
  EOF
18
  $blocks = new Twig_Node();
19
  $macros = new Twig_Node();
20
  $traits = new Twig_Node();
21
+ $source = new Twig_Source('{{ foo }}', 'foo.twig');
22
+ $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, new Twig_Node(array()), $source);
23
 
24
  $this->assertEquals($body, $node->getNode('body'));
25
  $this->assertEquals($blocks, $node->getNode('blocks'));
26
  $this->assertEquals($macros, $node->getNode('macros'));
27
  $this->assertEquals($parent, $node->getNode('parent'));
28
+ $this->assertEquals($source->getName(), $node->getTemplateName());
29
  }
30
 
31
  public function getTests()
39
  $blocks = new Twig_Node();
40
  $macros = new Twig_Node();
41
  $traits = new Twig_Node();
42
+ $source = new Twig_Source('{{ foo }}', 'foo.twig');
43
 
44
+ $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $source);
45
  $tests[] = array($node, <<<EOF
46
  <?php
47
 
74
  return array ( 19 => 1,);
75
  }
76
 
77
+ /** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */
78
  public function getSource()
79
  {
80
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);
81
+
82
+ return \$this->getSourceContext()->getCode();
83
+ }
84
+
85
+ public function getSourceContext()
86
+ {
87
+ return new Twig_Source("", "foo.twig", "");
88
  }
89
  }
90
  EOF
95
  $body = new Twig_Node(array($import));
96
  $extends = new Twig_Node_Expression_Constant('layout.twig', 1);
97
 
98
+ $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $source);
99
  $tests[] = array($node, <<<EOF
100
  <?php
101
 
140
  return array ( 26 => 1, 24 => 2, 11 => 1,);
141
  }
142
 
143
+ /** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */
144
  public function getSource()
145
  {
146
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);
147
+
148
+ return \$this->getSourceContext()->getCode();
149
+ }
150
+
151
+ public function getSourceContext()
152
+ {
153
+ return new Twig_Source("", "foo.twig", "");
154
  }
155
  }
156
  EOF
165
  2
166
  );
167
 
168
+ $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('debug' => true));
169
+ $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $source);
170
  $tests[] = array($node, <<<EOF
171
  <?php
172
 
202
  return array ( 17 => 2, 15 => 4, 9 => 2,);
203
  }
204
 
205
+ /** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */
206
  public function getSource()
207
  {
208
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);
209
+
210
+ return \$this->getSourceContext()->getCode();
211
+ }
212
+
213
+ public function getSourceContext()
214
+ {
215
+ return new Twig_Source("{{ foo }}", "foo.twig", "");
216
  }
217
  }
218
  EOF
library/twig/twig/test/Twig/Tests/Node/SandboxTest.php CHANGED
@@ -28,7 +28,7 @@ class Twig_Tests_Node_SandboxTest extends Twig_Test_NodeTestCase
28
 
29
  $tests[] = array($node, <<<EOF
30
  // line 1
31
- \$sandbox = \$this->env->getExtension('sandbox');
32
  if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {
33
  \$sandbox->enableSandbox();
34
  }
28
 
29
  $tests[] = array($node, <<<EOF
30
  // line 1
31
+ \$sandbox = \$this->env->getExtension('Twig_Extension_Sandbox');
32
  if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {
33
  \$sandbox->enableSandbox();
34
  }
library/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php CHANGED
@@ -24,7 +24,7 @@ class Twig_Tests_Node_SandboxedPrintTest extends Twig_Test_NodeTestCase
24
 
25
  $tests[] = array(new Twig_Node_SandboxedPrint(new Twig_Node_Expression_Constant('foo', 1), 1), <<<EOF
26
  // line 1
27
- echo \$this->env->getExtension('sandbox')->ensureToStringAllowed("foo");
28
  EOF
29
  );
30
 
24
 
25
  $tests[] = array(new Twig_Node_SandboxedPrint(new Twig_Node_Expression_Constant('foo', 1), 1), <<<EOF
26
  // line 1
27
+ echo \$this->env->getExtension('Twig_Extension_Sandbox')->ensureToStringAllowed("foo");
28
  EOF
29
  );
30
 
library/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php CHANGED
@@ -14,7 +14,7 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
14
  {
15
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
16
 
17
- $stream = $env->parse($env->tokenize('{{ block("foo") }}', 'index'));
18
 
19
  $node = $stream->getNode('body')->getNode(0);
20
 
@@ -26,7 +26,7 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
26
  {
27
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
28
 
29
- $stream = $env->parse($env->tokenize('{% extends "foo" %}{% block content %}{{ parent() }}{% endblock %}', 'index'));
30
 
31
  $node = $stream->getNode('blocks')->getNode('content')->getNode(0)->getNode('body');
32
 
@@ -41,7 +41,7 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
41
  }
42
 
43
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
44
- $stream = $env->parse($env->tokenize('{{ block(name|lower) }}', 'index'));
45
 
46
  $node = $stream->getNode('body')->getNode(0)->getNode(1);
47
 
@@ -56,7 +56,7 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
56
  {
57
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false));
58
 
59
- $stream = $env->parse($env->tokenize($template, 'index'));
60
 
61
  foreach ($expected as $target => $withLoop) {
62
  $this->assertTrue($this->checkForConfiguration($stream, $target, $withLoop), sprintf('variable %s is %soptimized', $target, $withLoop ? 'not ' : ''));
14
  {
15
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
16
 
17
+ $stream = $env->parse($env->tokenize(new Twig_Source('{{ block("foo") }}', 'index')));
18
 
19
  $node = $stream->getNode('body')->getNode(0);
20
 
26
  {
27
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
28
 
29
+ $stream = $env->parse($env->tokenize(new Twig_Source('{% extends "foo" %}{% block content %}{{ parent() }}{% endblock %}', 'index')));
30
 
31
  $node = $stream->getNode('blocks')->getNode('content')->getNode(0)->getNode('body');
32
 
41
  }
42
 
43
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false));
44
+ $stream = $env->parse($env->tokenize(new Twig_Source('{{ block(name|lower) }}', 'index')));
45
 
46
  $node = $stream->getNode('body')->getNode(0)->getNode(1);
47
 
56
  {
57
  $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false));
58
 
59
+ $stream = $env->parse($env->tokenize(new Twig_Source($template, 'index')));
60
 
61
  foreach ($expected as $target => $withLoop) {
62
  $this->assertTrue($this->checkForConfiguration($stream, $target, $withLoop), sprintf('variable %s is %soptimized', $target, $withLoop ? 'not ' : ''));
library/twig/twig/test/Twig/Tests/ParserTest.php CHANGED
@@ -100,7 +100,7 @@ class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase
100
 
101
  /**
102
  * @expectedException Twig_Error_Syntax
103
- * @expectedExceptionMessage A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed at line 1.
104
  */
105
  public function testFilterBodyNodesWithBOM()
106
  {
@@ -141,14 +141,14 @@ class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase
141
  'optimizations' => 0,
142
  ));
143
 
144
- $twig->parse($twig->tokenize(<<<EOF
145
  {% from _self import foo %}
146
 
147
  {% macro foo() %}
148
  {{ foo }}
149
  {% endmacro %}
150
  EOF
151
- ));
152
  }
153
 
154
  protected function getParser()
@@ -156,6 +156,7 @@ EOF
156
  $parser = new TestParser(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
157
  $parser->setParent(new Twig_Node());
158
  $parser->stream = $this->getMockBuilder('Twig_TokenStream')->disableOriginalConstructor()->getMock();
 
159
 
160
  return $parser;
161
  }
100
 
101
  /**
102
  * @expectedException Twig_Error_Syntax
103
+ * @expectedExceptionMessage A template that extends another one cannot start with a byte order mark (BOM); it must be removed at line 1
104
  */
105
  public function testFilterBodyNodesWithBOM()
106
  {
141
  'optimizations' => 0,
142
  ));
143
 
144
+ $twig->parse($twig->tokenize(new Twig_Source(<<<EOF
145
  {% from _self import foo %}
146
 
147
  {% macro foo() %}
148
  {{ foo }}
149
  {% endmacro %}
150
  EOF
151
+ , 'index')));
152
  }
153
 
154
  protected function getParser()
156
  $parser = new TestParser(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()));
157
  $parser->setParent(new Twig_Node());
158
  $parser->stream = $this->getMockBuilder('Twig_TokenStream')->disableOriginalConstructor()->getMock();
159
+ $parser->stream->expects($this->any())->method('getSourceContext')->will($this->returnValue(new Twig_Source('', '')));
160
 
161
  return $parser;
162
  }
library/twig/twig/test/Twig/Tests/TemplateTest.php CHANGED
@@ -56,22 +56,22 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
56
  public function getAttributeExceptions()
57
  {
58
  $tests = array(
59
- array('{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1', false),
60
- array('{{ null["a"] }}', 'Impossible to access a key ("a") on a null variable in "%s" at line 1', false),
61
- array('{{ empty_array["a"] }}', 'Key "a" does not exist as the array is empty in "%s" at line 1', false),
62
- array('{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false),
63
- array('{{ array_access["a"] }}', 'Key "a" in object with ArrayAccess of class "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
64
- array('{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1', false),
65
- array('{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1', false),
66
- array('{{ null.a }}', 'Impossible to access an attribute ("a") on a null variable in "%s" at line 1', false),
67
- array('{{ null.a() }}', 'Impossible to invoke a method ("a") on a null variable in "%s" at line 1', false),
68
- array('{{ empty_array.a }}', 'Key "a" does not exist as the array is empty in "%s" at line 1', false),
69
- array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false),
70
- array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1', false),
71
- array('{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1', false),
72
- array('{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1', false),
73
  array('{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.', false),
74
- array('{{ object["a"] }}', 'Impossible to access a key "a" on an object of class "stdClass" that does not implement ArrayAccess interface in "%s" at line 1', false),
75
  );
76
 
77
  if (function_exists('twig_template_get_attributes')) {
@@ -130,11 +130,16 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
130
 
131
  /**
132
  * @dataProvider getGetAttributeWithTemplateAsObject
 
133
  */
134
  public function testGetAttributeWithTemplateAsObject($useExt)
135
  {
136
- $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()), $useExt);
137
- $template1 = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()), false);
 
 
 
 
138
 
139
  $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'string'));
140
  $this->assertEquals('some_string', $template->getAttribute($template1, 'string'));
@@ -167,6 +172,67 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
167
  return $bools;
168
  }
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  /**
171
  * @dataProvider getTestsDependingOnExtensionAvailability
172
  */
@@ -279,6 +345,7 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
279
  'null' => null,
280
  '1' => 1,
281
  'bar' => true,
 
282
  '09' => '09',
283
  '+4' => '+4',
284
  );
@@ -307,6 +374,7 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
307
  array(true, 1, 1.0),
308
  array(true, null, 'null'),
309
  array(true, true, 'bar'),
 
310
  array(true, '09', '09'),
311
  array(true, '+4', '+4'),
312
  );
@@ -387,9 +455,9 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
387
 
388
  // tests when input is not an array or object
389
  $tests = array_merge($tests, array(
390
- array(false, null, 42, 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a integer variable ("42")'),
391
- array(false, null, 'string', 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a string variable ("string")'),
392
- array(false, null, array(), 'a', array(), $anyType, false, 'Key "a" does not exist as the array is empty'),
393
  ));
394
 
395
  // add twig_template_get_attributes tests
@@ -409,12 +477,14 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
409
  class Twig_TemplateTest extends Twig_Template
410
  {
411
  protected $useExtGetAttribute = false;
 
412
 
413
- public function __construct(Twig_Environment $env, $useExtGetAttribute = false)
414
  {
415
  parent::__construct($env);
416
  $this->useExtGetAttribute = $useExtGetAttribute;
417
  self::$cache = array();
 
418
  }
419
 
420
  public function getZero()
@@ -439,6 +509,7 @@ class Twig_TemplateTest extends Twig_Template
439
 
440
  public function getTemplateName()
441
  {
 
442
  }
443
 
444
  public function getDebugInfo()
@@ -446,13 +517,9 @@ class Twig_TemplateTest extends Twig_Template
446
  return array();
447
  }
448
 
449
- public function getSource()
450
- {
451
- return '';
452
- }
453
-
454
  protected function doGetParent(array $context)
455
  {
 
456
  }
457
 
458
  protected function doDisplay(array $context, array $blocks = array())
@@ -479,6 +546,7 @@ class Twig_TemplateArrayAccessObject implements ArrayAccess
479
  'null' => null,
480
  '1' => 1,
481
  'bar' => true,
 
482
  '09' => '09',
483
  '+4' => '+4',
484
  );
@@ -511,6 +579,7 @@ class Twig_TemplateMagicPropertyObject
511
  'null' => null,
512
  '1' => 1,
513
  'bar' => true,
 
514
  '09' => '09',
515
  '+4' => '+4',
516
  );
@@ -542,6 +611,7 @@ class Twig_TemplatePropertyObject
542
  public $zero = 0;
543
  public $null = null;
544
  public $bar = true;
 
545
 
546
  protected $protected = 'protected';
547
  }
@@ -618,6 +688,16 @@ class Twig_TemplateMethodObject
618
  return true;
619
  }
620
 
 
 
 
 
 
 
 
 
 
 
621
  protected function getProtected()
622
  {
623
  return 'protected';
@@ -687,3 +767,8 @@ class CExtDisablingNodeVisitor implements Twig_NodeVisitorInterface
687
  return 0;
688
  }
689
  }
 
 
 
 
 
56
  public function getAttributeExceptions()
57
  {
58
  $tests = array(
59
+ array('{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1.', false),
60
+ array('{{ null["a"] }}', 'Impossible to access a key ("a") on a null variable in "%s" at line 1.', false),
61
+ array('{{ empty_array["a"] }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.', false),
62
+ array('{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.', false),
63
+ array('{{ array_access["a"] }}', 'Key "a" in object with ArrayAccess of class "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1.', false),
64
+ array('{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1.', false),
65
+ array('{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1.', false),
66
+ array('{{ null.a }}', 'Impossible to access an attribute ("a") on a null variable in "%s" at line 1.', false),
67
+ array('{{ null.a() }}', 'Impossible to invoke a method ("a") on a null variable in "%s" at line 1.', false),
68
+ array('{{ empty_array.a }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.', false),
69
+ array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.', false),
70
+ array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1.', false),
71
+ array('{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1.', false),
72
+ array('{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1.', false),
73
  array('{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.', false),
74
+ array('{{ object["a"] }}', 'Impossible to access a key "a" on an object of class "stdClass" that does not implement ArrayAccess interface in "%s" at line 1.', false),
75
  );
76
 
77
  if (function_exists('twig_template_get_attributes')) {
130
 
131
  /**
132
  * @dataProvider getGetAttributeWithTemplateAsObject
133
+ * @group legacy
134
  */
135
  public function testGetAttributeWithTemplateAsObject($useExt)
136
  {
137
+ // to be removed in 2.0
138
+ $twig = new Twig_Environment($this->getMockBuilder('Twig_TemplateTestLoaderInterface')->getMock());
139
+ //$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface')->getMock());
140
+
141
+ $template = new Twig_TemplateTest($twig, $useExt, 'index.twig');
142
+ $template1 = new Twig_TemplateTest($twig, false, 'index1.twig');
143
 
144
  $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'string'));
145
  $this->assertEquals('some_string', $template->getAttribute($template1, 'string'));
172
  return $bools;
173
  }
174
 
175
+ /**
176
+ * @group legacy
177
+ * @expectedDeprecation Calling "getString" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0.
178
+ * @expectedDeprecation Calling "getString" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0.
179
+ * @expectedDeprecation Calling "getTrue" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0.
180
+ * @expectedDeprecation Calling "getTrue" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0.
181
+ * @expectedDeprecation Calling "getZero" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0.
182
+ * @expectedDeprecation Calling "getZero" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0.
183
+ * @expectedDeprecation Calling "getEmpty" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0.
184
+ * @expectedDeprecation Calling "getEmpty" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0.
185
+ * @expectedDeprecation Calling "renderBlock" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use block("name") instead).
186
+ * @expectedDeprecation Calling "displayBlock" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use block("name") instead).
187
+ * @expectedDeprecation Calling "hasBlock" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use "block("name") is defined" instead).
188
+ * @expectedDeprecation Calling "render" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use include("index.twig") instead).
189
+ * @expectedDeprecation Calling "display" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use include("index.twig") instead).
190
+ * @expectedDeprecation Calling "renderBlock" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use block("name", template) instead).
191
+ * @expectedDeprecation Calling "displayBlock" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use block("name", template) instead).
192
+ * @expectedDeprecation Calling "hasBlock" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use "block("name", template) is defined" instead).
193
+ * @expectedDeprecation Calling "render" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use include("index1.twig") instead).
194
+ * @expectedDeprecation Calling "display" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use include("index1.twig") instead).
195
+ */
196
+ public function testGetAttributeWithTemplateAsObjectForDeprecations()
197
+ {
198
+ // to be removed in 2.0
199
+ $twig = new Twig_Environment($this->getMockBuilder('Twig_TemplateTestLoaderInterface')->getMock());
200
+ //$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface')->getMock());
201
+
202
+ $template = new Twig_TemplateTest($twig, false, 'index.twig');
203
+ $template1 = new Twig_TemplateTest($twig, false, 'index1.twig');
204
+
205
+ $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'string'));
206
+ $this->assertEquals('some_string', $template->getAttribute($template1, 'string'));
207
+
208
+ $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'true'));
209
+ $this->assertEquals('1', $template->getAttribute($template1, 'true'));
210
+
211
+ $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'zero'));
212
+ $this->assertEquals('0', $template->getAttribute($template1, 'zero'));
213
+
214
+ $this->assertNotInstanceof('Twig_Markup', $template->getAttribute($template1, 'empty'));
215
+ $this->assertSame('', $template->getAttribute($template1, 'empty'));
216
+
217
+ // trigger some deprecation notice messages to check them with @expectedDeprecation
218
+ $template->getAttribute($template, 'renderBlock', array('name', array()));
219
+ $template->getAttribute($template, 'displayBlock', array('name', array()));
220
+ $template->getAttribute($template, 'hasBlock', array('name', array()));
221
+ $template->getAttribute($template, 'render', array(array()));
222
+ $template->getAttribute($template, 'display', array(array()));
223
+
224
+ $template->getAttribute($template1, 'renderBlock', array('name', array()));
225
+ $template->getAttribute($template1, 'displayBlock', array('name', array()));
226
+ $template->getAttribute($template1, 'hasBlock', array('name', array()));
227
+ $template->getAttribute($template1, 'render', array(array()));
228
+ $template->getAttribute($template1, 'display', array(array()));
229
+
230
+ $this->assertFalse($template->getAttribute($template1, 'env', array(), Twig_Template::ANY_CALL, true));
231
+ $this->assertFalse($template->getAttribute($template1, 'environment', array(), Twig_Template::ANY_CALL, true));
232
+ $this->assertFalse($template->getAttribute($template1, 'getEnvironment', array(), Twig_Template::METHOD_CALL, true));
233
+ $this->assertFalse($template->getAttribute($template1, 'displayWithErrorHandling', array(), Twig_Template::METHOD_CALL, true));
234
+ }
235
+
236
  /**
237
  * @dataProvider getTestsDependingOnExtensionAvailability
238
  */
345
  'null' => null,
346
  '1' => 1,
347
  'bar' => true,
348
+ 'baz' => 'baz',
349
  '09' => '09',
350
  '+4' => '+4',
351
  );
374
  array(true, 1, 1.0),
375
  array(true, null, 'null'),
376
  array(true, true, 'bar'),
377
+ array(true, 'baz', 'baz'),
378
  array(true, '09', '09'),
379
  array(true, '+4', '+4'),
380
  );
455
 
456
  // tests when input is not an array or object
457
  $tests = array_merge($tests, array(
458
+ array(false, null, 42, 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a integer variable ("42") in "index.twig".'),
459
+ array(false, null, 'string', 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a string variable ("string") in "index.twig".'),
460
+ array(false, null, array(), 'a', array(), $anyType, false, 'Key "a" does not exist as the array is empty in "index.twig".'),
461
  ));
462
 
463
  // add twig_template_get_attributes tests
477
  class Twig_TemplateTest extends Twig_Template
478
  {
479
  protected $useExtGetAttribute = false;
480
+ private $name;
481
 
482
+ public function __construct(Twig_Environment $env, $useExtGetAttribute = false, $name = 'index.twig')
483
  {
484
  parent::__construct($env);
485
  $this->useExtGetAttribute = $useExtGetAttribute;
486
  self::$cache = array();
487
+ $this->name = $name;
488
  }
489
 
490
  public function getZero()
509
 
510
  public function getTemplateName()
511
  {
512
+ return $this->name;
513
  }
514
 
515
  public function getDebugInfo()
517
  return array();
518
  }
519
 
 
 
 
 
 
520
  protected function doGetParent(array $context)
521
  {
522
+ return false;
523
  }
524
 
525
  protected function doDisplay(array $context, array $blocks = array())
546
  'null' => null,
547
  '1' => 1,
548
  'bar' => true,
549
+ 'baz' => 'baz',
550
  '09' => '09',
551
  '+4' => '+4',
552
  );
579
  'null' => null,
580
  '1' => 1,
581
  'bar' => true,
582
+ 'baz' => 'baz',
583
  '09' => '09',
584
  '+4' => '+4',
585
  );
611
  public $zero = 0;
612
  public $null = null;
613
  public $bar = true;
614
+ public $baz = 'baz';
615
 
616
  protected $protected = 'protected';
617
  }
688
  return true;
689
  }
690
 
691
+ public function isBaz()
692
+ {
693
+ return 'should never be returned';
694
+ }
695
+
696
+ public function getBaz()
697
+ {
698
+ return 'baz';
699
+ }
700
+
701
  protected function getProtected()
702
  {
703
  return 'protected';
767
  return 0;
768
  }
769
  }
770
+
771
+ // to be removed in 2.0
772
+ interface Twig_TemplateTestLoaderInterface extends Twig_LoaderInterface, Twig_SourceContextLoaderInterface
773
+ {
774
+ }
library/twig/twig/test/Twig/Tests/TemplateWrapperTest.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Tests_TemplateWrapperTest extends PHPUnit_Framework_TestCase
12
+ {
13
+ public function testHasGetBlocks()
14
+ {
15
+ $twig = new Twig_Environment(new Twig_Loader_Array(array(
16
+ 'index' => '{% block foo %}{% endblock %}',
17
+ 'index_with_use' => '{% use "imported" %}{% block foo %}{% endblock %}',
18
+ 'index_with_extends' => '{% extends "extended" %}{% block foo %}{% endblock %}',
19
+ 'imported' => '{% block imported %}{% endblock %}',
20
+ 'extended' => '{% block extended %}{% endblock %}',
21
+ )));
22
+
23
+ $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index'));
24
+ $this->assertTrue($wrapper->hasBlock('foo'));
25
+ $this->assertFalse($wrapper->hasBlock('bar'));
26
+ $this->assertEquals(array('foo'), $wrapper->getBlockNames());
27
+
28
+ $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index_with_use'));
29
+ $this->assertTrue($wrapper->hasBlock('foo'));
30
+ $this->assertTrue($wrapper->hasBlock('imported'));
31
+ $this->assertEquals(array('imported', 'foo'), $wrapper->getBlockNames());
32
+
33
+ $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index_with_extends'));
34
+ $this->assertTrue($wrapper->hasBlock('foo'));
35
+ $this->assertTrue($wrapper->hasBlock('extended'));
36
+ $this->assertEquals(array('foo', 'extended'), $wrapper->getBlockNames());
37
+ }
38
+
39
+ public function testRenderBlock()
40
+ {
41
+ $twig = new Twig_Environment(new Twig_Loader_Array(array(
42
+ 'index' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}',
43
+ )));
44
+ $twig->addGlobal('bar', 'BAR');
45
+
46
+ $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index'));
47
+ $this->assertEquals('FOOBAR', $wrapper->renderBlock('foo', array('foo' => 'FOO')));
48
+ }
49
+
50
+ public function testDisplayBlock()
51
+ {
52
+ $twig = new Twig_Environment(new Twig_Loader_Array(array(
53
+ 'index' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}',
54
+ )));
55
+ $twig->addGlobal('bar', 'BAR');
56
+
57
+ $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index'));
58
+
59
+ ob_start();
60
+ $wrapper->displayBlock('foo', array('foo' => 'FOO'));
61
+
62
+ $this->assertEquals('FOOBAR', ob_get_clean());
63
+ }
64
+ }
library/twig/twig/test/Twig/Tests/TokenStreamTest.php CHANGED
@@ -27,6 +27,18 @@ class Twig_Tests_TokenStreamTest extends PHPUnit_Framework_TestCase
27
  );
28
  }
29
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  public function testNext()
31
  {
32
  $stream = new Twig_TokenStream(self::$tokens);
27
  );
28
  }
29
 
30
+ /**
31
+ * @group legacy
32
+ */
33
+ public function testLegacyConstructorSignature()
34
+ {
35
+ $stream = new Twig_TokenStream(array(), 'foo', '{{ foo }}');
36
+ $this->assertEquals('foo', $stream->getFilename());
37
+ $this->assertEquals('{{ foo }}', $stream->getSource());
38
+ $this->assertEquals('foo', $stream->getSourceContext()->getName());
39
+ $this->assertEquals('{{ foo }}', $stream->getSourceContext()->getCode());
40
+ }
41
+
42
  public function testNext()
43
  {
44
  $stream = new Twig_TokenStream(self::$tokens);
library/twig/twig/test/Twig/Tests/escapingTest.php CHANGED
@@ -250,7 +250,7 @@ class Twig_Test_EscapingTest extends PHPUnit_Framework_TestCase
250
  .chr($codepoint >> 6 & 0x3f | 0x80)
251
  .chr($codepoint & 0x3f | 0x80);
252
  }
253
- throw new Exception('Codepoint requested outside of Unicode range');
254
  }
255
 
256
  public function testJavascriptEscapingEscapesOwaspRecommendedRanges()
250
  .chr($codepoint >> 6 & 0x3f | 0x80)
251
  .chr($codepoint & 0x3f | 0x80);
252
  }
253
+ throw new Exception('Codepoint requested outside of Unicode range.');
254
  }
255
 
256
  public function testJavascriptEscapingEscapesOwaspRecommendedRanges()
readme.txt CHANGED
@@ -6,21 +6,30 @@ Text Domain: wpcf
6
  Domain Path: /embedded/locale
7
  License: GPLv2
8
  Requires at least: 3.7
9
- Tested up to: 4.6
10
- Stable tag: 2.2.3
11
 
12
  The complete and reliable plugin for managing custom post types, custom taxonomies and custom fields.
13
 
14
  == Description ==
15
 
16
- Types let's you customize the WordPress admin by adding content types, custom fields and taxonomies. You will be able to craft the WordPress admin and turn it into your very own content management system.
17
 
18
  [vimeo https://vimeo.com/176428571]
19
 
20
- = POWERFUL PHP API, SIMPLE GUI FOR NON-CODERS =
21
- If you're an experienced PHP developer, you'll appreciate Types comprehensive [PHP API](http://wp-types.com/documentation/functions/) and [documentation](http://wp-types.com/documentation/user-guides/).
22
 
23
- The full [Toolset](http://wp-types.com) package lets you build complete WordPress sites from within the admin dashboard.
 
 
 
 
 
 
 
 
 
24
 
25
  = CUSTOM FIELDS FOR CONTENT AND USERS =
26
  Types lets you add custom fields for both posts (meaning, WordPress posts, pages and custom content types), as well as users. You can add any field types to different user profiles.
@@ -98,7 +107,7 @@ By default, WordPress will either display your blog posts or a specific page on
98
  To display custom post types on the home-page, you have two options:
99
 
100
  1. If you're comfortable with PHP and WordPress API, edit the site's template files (probably index.php) and load the custom post types there. Different themes do this differently, so we can't really say what single approach works best. You should look at [get_posts](http://codex.wordpress.org/Template_Tags/get_posts), which is part of the WordPress Template Tags system.
101
- 2. If you want to build sites right away, without becoming an expert in WordPress API and try our [Views Toolset](http://wp-types.com/). You'll be able to load whatever content you need from the database and display it anywhere and in whatever way you choose.
102
 
103
  We're sorry, but we don't know of any third option which is both free and requires no coding.
104
 
@@ -151,6 +160,21 @@ Additionally, Types is the only plugin that lets you define parent/child relatio
151
 
152
  == Changelog ==
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  = 2.2.3 =
155
  * Fixed several security issues.
156
 
6
  Domain Path: /embedded/locale
7
  License: GPLv2
8
  Requires at least: 3.7
9
+ Tested up to: 4.7
10
+ Stable tag: 2.2.4
11
 
12
  The complete and reliable plugin for managing custom post types, custom taxonomies and custom fields.
13
 
14
  == Description ==
15
 
16
+ **Types let's you customize the WordPress admin by adding content types, custom fields and taxonomies. You will be able to craft the WordPress admin and turn it into your very own content management system.**
17
 
18
  [vimeo https://vimeo.com/176428571]
19
 
20
+ = COMPLETE DOCUMENTATION, POWERFUL API, SIMPLE GUI FOR NON-CODERS =
21
+ If you're an experienced PHP developer, you'll appreciate Types comprehensive [fields API](http://wp-types.com/documentation/functions/).
22
 
23
+ You will find detailed guides on [adding custom post types, fields and taxonomy to the front-end](https://wp-types.com/documentation/customizing-sites-using-php/), including:
24
+
25
+ * [Creating templates for single custom posts](https://wp-types.com/documentation/customizing-sites-using-php/creating-templates-single-custom-posts)
26
+ * [Creating templates for custom post type archives](https://wp-types.com/documentation/customizing-sites-using-php/creating-templates-custom-post-type-archives)
27
+ * [Creating custom user profiles](https://wp-types.com/documentation/customizing-sites-using-php/creating-custom-user-profiles)
28
+ * [Create taxonomy term archives](https://wp-types.com/documentation/customizing-sites-using-php/creating-taxonomy-term-archives)
29
+
30
+ and [more](https://wp-types.com/documentation/customizing-sites-using-php/).
31
+
32
+ **Too much technical stuff to learn?** The full [Toolset](http://wp-types.com) package lets you build complete WordPress sites from within the admin dashboard.
33
 
34
  = CUSTOM FIELDS FOR CONTENT AND USERS =
35
  Types lets you add custom fields for both posts (meaning, WordPress posts, pages and custom content types), as well as users. You can add any field types to different user profiles.
107
  To display custom post types on the home-page, you have two options:
108
 
109
  1. If you're comfortable with PHP and WordPress API, edit the site's template files (probably index.php) and load the custom post types there. Different themes do this differently, so we can't really say what single approach works best. You should look at [get_posts](http://codex.wordpress.org/Template_Tags/get_posts), which is part of the WordPress Template Tags system.
110
+ 2. If you want to build sites right away, without becoming an expert in WordPress API and try our [Toolset Views](http://wp-types.com/). You'll be able to load whatever content you need from the database and display it anywhere and in whatever way you choose.
111
 
112
  We're sorry, but we don't know of any third option which is both free and requires no coding.
113
 
160
 
161
  == Changelog ==
162
 
163
+ = 2.2.4 =
164
+
165
+ * Fix an issue with registering custom taxonomies in WordPress 4.7.
166
+ * Implement an alternative escaping mechanism for custom format setting of the date field.
167
+ * types_render_shortcode() function and [types] shortcode now allows to use "id" attribute for $parent-post selection
168
+ * Exclude Media from post relationships since the current GUI isn't able to support it properly
169
+ * Fix exporting taxonomies with legacy "object_type" setting that was causing syntax errors in the output XML.
170
+ * Support all CPTs in Toolset Dashboard.
171
+ * Change the way we store the context of a Types field for string translation (use field group name instead of ID)
172
+ * Fix a WordPress 4.7 compatibility issue with direct access to $wp_filter.
173
+ * Make the manipulation with repetitive user field values more similar to post fields. Fix a front-end notice when there is only one value in a repeating user Skype field.
174
+ * Add missing mandatory URL validation to file fields.
175
+ * Add PHP template example files.
176
+ * Fix an issue with Types export and non-latin characters in a field group slug.
177
+
178
  = 2.2.3 =
179
  * Fixed several security issues.
180
 
wpcf.php CHANGED
@@ -5,7 +5,7 @@ Plugin URI: http://wordpress.org/extend/plugins/types/
5
  Description: Toolset Types defines custom content in WordPress. Easily create custom post types, fields and taxonomy and connect everything together.
6
  Author: OnTheGoSystems
7
  Author URI: http://www.onthegosystems.com
8
- Version: 2.2.3
9
  License: GPLv2 or later
10
 
11
  Types is free software: you can redistribute it and/or modify
@@ -28,7 +28,7 @@ if( !function_exists( 'add_action' ) )
28
 
29
  // version
30
  if( ! defined( 'TYPES_VERSION' ) )
31
- define( 'TYPES_VERSION', '2.2.3' );
32
 
33
  // backward compatibility
34
  if ( ! defined( 'WPCF_VERSION' ) )
@@ -36,7 +36,8 @@ if ( ! defined( 'WPCF_VERSION' ) )
36
 
37
  // release notes
38
  if( ! defined( 'TYPES_RELEASE_NOTES' ) )
39
- define( 'TYPES_RELEASE_NOTES', 'https://wp-types.com/version/types-2-2-2/?utm_source=typesplugin&utm_campaign=types&utm_medium=release-notes-admin-notice&utm_term=Types 2.2 release notes' );
 
40
 
41
  /*
42
  * Path Constants
5
  Description: Toolset Types defines custom content in WordPress. Easily create custom post types, fields and taxonomy and connect everything together.
6
  Author: OnTheGoSystems
7
  Author URI: http://www.onthegosystems.com
8
+ Version: 2.2.4
9
  License: GPLv2 or later
10
 
11
  Types is free software: you can redistribute it and/or modify
28
 
29
  // version
30
  if( ! defined( 'TYPES_VERSION' ) )
31
+ define( 'TYPES_VERSION', '2.2.4' );
32
 
33
  // backward compatibility
34
  if ( ! defined( 'WPCF_VERSION' ) )
36
 
37
  // release notes
38
  if( ! defined( 'TYPES_RELEASE_NOTES' ) )
39
+ // Mind the end of the URL string, it contains the plugin version.
40
+ define( 'TYPES_RELEASE_NOTES', 'https://wp-types.com/version/types-2-2-4/?utm_source=typesplugin&utm_campaign=types&utm_medium=release-notes-admin-notice&utm_term=Types%202.2.4%20release%20notes' );
41
 
42
  /*
43
  * Path Constants