The Events Calendar - Version 4.9.3.3

Version Description

Download this release

Release Info

Developer borkweb
Plugin Icon The Events Calendar
Version 4.9.3.3
Comparing to
See all releases

Code changes from version 5.1.2 to 4.9.3.3

Files changed (331) hide show
  1. common/lang/readme.txt +0 -3
  2. common/lang/tribe-common-af.mo +0 -0
  3. common/lang/tribe-common-ar.mo +0 -0
  4. common/lang/tribe-common-bg_BG.mo +0 -0
  5. common/lang/tribe-common-ca.mo +0 -0
  6. common/lang/tribe-common-cs_CZ.mo +0 -0
  7. common/lang/tribe-common-da_DK.mo +0 -0
  8. common/lang/tribe-common-de_DE.mo +0 -0
  9. common/lang/tribe-common-el.mo +0 -0
  10. common/lang/tribe-common-en_GB.mo +0 -0
  11. common/lang/tribe-common-es_ES.mo +0 -0
  12. common/lang/tribe-common-et.mo +0 -0
  13. common/lang/tribe-common-fi.mo +0 -0
  14. common/lang/tribe-common-fr_CA.mo +0 -0
  15. common/lang/tribe-common-fr_FR.mo +0 -0
  16. common/lang/tribe-common-hu_HU.mo +0 -0
  17. common/lang/tribe-common-id_ID.mo +0 -0
  18. common/lang/tribe-common-is_IS.mo +0 -0
  19. common/lang/tribe-common-it_IT.mo +0 -0
  20. common/lang/tribe-common-ja.mo +0 -0
  21. common/lang/tribe-common-lt_LT.mo +0 -0
  22. common/lang/tribe-common-lv.mo +0 -0
  23. common/lang/tribe-common-nb_NO.mo +0 -0
  24. common/lang/tribe-common-nl_NL.mo +0 -0
  25. common/lang/tribe-common-pl_PL.mo +0 -0
  26. common/lang/tribe-common-pt_BR.mo +0 -0
  27. common/lang/tribe-common-pt_PT.mo +0 -0
  28. common/lang/tribe-common-ro_RO.mo +0 -0
  29. common/lang/tribe-common-ru_RU.mo +0 -0
  30. common/lang/tribe-common-sk_SK.mo +0 -0
  31. common/lang/tribe-common-sl_SI.mo +0 -0
  32. common/lang/tribe-common-sr_RS.mo +0 -0
  33. common/lang/tribe-common-sv_SE.mo +0 -0
  34. common/lang/tribe-common-tr_TR.mo +0 -0
  35. common/lang/tribe-common-zh_CN.mo +0 -0
  36. common/lang/tribe-common-zh_TW.mo +0 -0
  37. common/lang/tribe-common.pot +276 -666
  38. common/src/Tribe/Abstract_Plugin_Register.php +8 -30
  39. common/src/Tribe/Admin/Help_Page.php +11 -31
  40. common/src/Tribe/Admin/Notice/Plugin_Download.php +20 -58
  41. common/src/Tribe/Admin/Notices.php +11 -91
  42. common/src/Tribe/App_Shop.php +12 -149
  43. common/src/Tribe/Assets.php +166 -261
  44. common/src/Tribe/Autoloader.php +1 -18
  45. common/src/Tribe/Cache.php +76 -307
  46. common/src/Tribe/Cache_Listener.php +6 -42
  47. common/src/Tribe/Container.php +0 -46
  48. common/src/Tribe/Context.php +976 -1181
  49. common/src/Tribe/Context/locations.php +122 -99
  50. common/src/Tribe/Cost_Utils.php +0 -48
  51. common/src/Tribe/Credits.php +2 -2
  52. common/src/Tribe/Customizer.php +137 -23
  53. common/src/Tribe/Data.php +9 -9
  54. common/src/Tribe/Date_Utils.php +24 -380
  55. common/src/Tribe/Debug_Bar/Panels/Context.php +2 -2
  56. common/src/Tribe/Debug_Bar/Panels/Json_Ld.php +0 -84
  57. common/src/Tribe/Dependency.php +80 -130
  58. common/src/Tribe/Dialog/View.php +0 -526
  59. common/src/Tribe/Editor.php +5 -56
  60. common/src/Tribe/Editor/Blocks/Abstract.php +3 -37
  61. common/src/Tribe/Editor/Configuration.php +0 -1
  62. common/src/Tribe/Extension.php +9 -57
  63. common/src/Tribe/Feature_Detection.php +0 -106
  64. common/src/Tribe/Field.php +0 -12
  65. common/src/Tribe/Freemius.php +2 -3
  66. common/src/Tribe/Image/Uploader.php +69 -160
  67. common/src/Tribe/Languages/Locations.php +3 -3
  68. common/src/Tribe/Log.php +0 -3
  69. common/src/Tribe/Log/Action_Logger.php +0 -125
  70. common/src/Tribe/Log/Canonical_Formatter.php +0 -101
  71. common/src/Tribe/Log/File_Logger.php +1 -1
  72. common/src/Tribe/Log/Monolog_Logger.php +0 -54
  73. common/src/Tribe/Log/README.md +0 -154
  74. common/src/Tribe/Log/Service_Provider.php +0 -148
  75. common/src/Tribe/Main.php +106 -206
  76. common/src/Tribe/Models/Post_Types/Base.php +0 -206
  77. common/src/Tribe/Models/Post_Types/Nothing.php +0 -44
  78. common/src/Tribe/PUE/Checker.php +83 -93
  79. common/src/Tribe/PUE/Notices.php +14 -74
  80. common/src/Tribe/PUE/Update_Prevention.php +0 -199
  81. common/src/Tribe/Plugins.php +36 -68
  82. common/src/Tribe/Plugins_API.php +56 -141
  83. common/src/Tribe/Post_Transient.php +10 -11
  84. common/src/Tribe/Process/Handler.php +0 -40
  85. common/src/Tribe/Process/Post_Thumbnail_Setter.php +14 -58
  86. common/src/Tribe/Promoter/Auth.php +3 -60
  87. common/src/Tribe/Promoter/Connector.php +40 -106
  88. common/src/Tribe/Repository.php +24 -156
  89. common/src/Tribe/Repository/Core_Read_Interface.php +0 -402
  90. common/src/Tribe/Repository/Decorator.php +0 -9
  91. common/src/Tribe/Repository/Filter_Validation.php +0 -61
  92. common/src/Tribe/Repository/Interface.php +7 -11
  93. common/src/Tribe/Repository/Query_Filters.php +17 -156
  94. common/src/Tribe/Repository/Read_Interface.php +378 -4
  95. common/src/Tribe/Repository/Usage_Error.php +0 -41
  96. common/src/Tribe/Rewrite.php +45 -177
  97. common/src/Tribe/Service_Providers/Debug_Bar.php +0 -2
  98. common/src/Tribe/Service_Providers/Dialog.php +0 -110
  99. common/src/Tribe/Service_Providers/PUE.php +0 -50
  100. common/src/Tribe/Service_Providers/Promoter.php +0 -104
  101. common/src/Tribe/Service_Providers/Promoter_Connector.php +102 -0
  102. common/src/Tribe/Service_Providers/Shortcodes.php +0 -80
  103. common/src/Tribe/Service_Providers/Tooltip.php +4 -17
  104. common/src/Tribe/Settings.php +38 -44
  105. common/src/Tribe/Settings_Manager.php +13 -52
  106. common/src/Tribe/Shortcode/Manager.php +0 -107
  107. common/src/Tribe/Shortcode/Shortcode_Abstract.php +0 -247
  108. common/src/Tribe/Shortcode/Shortcode_Interface.php +0 -125
  109. common/src/Tribe/Support.php +4 -17
  110. common/src/Tribe/Template.php +100 -647
  111. common/src/Tribe/Timezones.php +3 -12
  112. common/src/Tribe/Tooltip/View.php +8 -39
  113. common/src/Tribe/Tracker.php +1 -18
  114. common/src/Tribe/Traits/Cache_User.php +2 -19
  115. common/src/Tribe/Utils/Array.php +364 -446
  116. common/src/Tribe/Utils/Collection_Interface.php +0 -57
  117. common/src/Tribe/Utils/Collection_Trait.php +0 -207
  118. common/src/Tribe/Utils/Date_I18n.php +0 -48
  119. common/src/Tribe/Utils/Date_I18n_Immutable.php +0 -48
  120. common/src/Tribe/Utils/Element_Attributes.php +0 -241
  121. common/src/Tribe/Utils/Element_Classes.php +0 -246
  122. common/src/Tribe/Utils/Global_ID.php +1 -1
  123. common/src/Tribe/Utils/Lazy_Collection.php +0 -117
  124. common/src/Tribe/Utils/Lazy_Events.php +0 -172
  125. common/src/Tribe/Utils/Lazy_String.php +0 -141
  126. common/src/Tribe/Utils/Post_Thumbnail.php +0 -330
  127. common/src/Tribe/Utils/Query.php +0 -72
  128. common/src/Tribe/Utils/Strings.php +0 -65
  129. common/src/Tribe/Validate.php +59 -10
  130. common/src/Tribe/Validator/Base.php +1 -3
  131. common/src/Tribe/View_Helpers.php +5 -13
  132. common/src/admin-views/app-shop.php +80 -174
  133. common/src/admin-views/tribe-options-display.php +9 -9
  134. common/src/admin-views/tribe-options-general.php +30 -33
  135. common/src/admin-views/tribe-options-help.php +1 -1
  136. common/src/functions/multibyte.php +0 -31
  137. common/src/functions/template-tags/date.php +18 -38
  138. common/src/functions/template-tags/general.php +22 -49
  139. common/src/functions/template-tags/html.php +0 -130
  140. common/src/functions/template-tags/post.php +0 -100
  141. common/src/functions/utils.php +61 -526
  142. common/src/modules/components/form/index.js +1 -0
  143. common/src/modules/components/form/select/component.js +116 -0
  144. common/src/modules/components/form/select/index.js +1 -0
  145. common/src/modules/components/form/select/style.pcss +56 -0
  146. common/src/modules/components/index.js +5 -0
  147. common/src/modules/components/plugin-block-hooks/__tests__/__snapshots__/plugin-block-hooks.spec.js.snap +131 -0
  148. common/src/modules/components/plugin-block-hooks/__tests__/plugin-block-hooks.spec.js +92 -0
  149. common/src/modules/components/plugin-block-hooks/component.js +138 -0
  150. common/src/modules/components/plugin-block-hooks/container.js +21 -0
  151. common/src/modules/components/plugin-block-hooks/index.js +1 -0
  152. common/src/modules/components/plugin-block-hooks/style.pcss +37 -0
  153. common/src/modules/components/prevent-block-close/__tests__/component.spec.js +15 -0
  154. common/src/modules/components/prevent-block-close/component.js +38 -0
  155. common/src/modules/components/prevent-block-close/index.js +1 -0
  156. common/src/modules/data/__tests__/utils.test.js +10 -0
  157. common/src/modules/data/editor/post-types.js +3 -0
  158. common/src/modules/data/forms/__tests__/__snapshots__/actions.test.js.snap +147 -0
  159. common/src/modules/data/forms/__tests__/actions.test.js +96 -0
  160. common/src/modules/data/forms/__tests__/reducer.test.js +88 -0
  161. common/src/modules/data/forms/__tests__/selectors.test.js +89 -0
  162. common/src/modules/data/forms/__tests__/types.js +19 -0
  163. common/src/modules/data/forms/actions.js +153 -0
  164. common/src/modules/data/forms/index.js +12 -0
  165. common/src/modules/data/forms/reducer.js +33 -0
  166. common/src/modules/data/forms/reducers/__tests__/__snapshots__/form.test.js.snap +84 -0
  167. common/src/modules/data/forms/reducers/__tests__/form.test.js +49 -0
  168. common/src/modules/data/forms/reducers/__tests__/volatile.test.js +23 -0
  169. common/src/modules/data/forms/reducers/form.js +58 -0
  170. common/src/modules/data/forms/reducers/index.js +2 -0
  171. common/src/modules/data/forms/reducers/volatile.js +15 -0
  172. common/src/modules/data/forms/selectors.js +39 -0
  173. common/src/modules/data/forms/types.js +15 -0
  174. common/src/modules/data/index.js +9 -0
  175. common/src/modules/data/plugins/__tests__/__snapshots__/actions.test.js.snap +19 -0
  176. common/src/modules/data/plugins/__tests__/actions.test.js +14 -0
  177. common/src/modules/data/plugins/__tests__/reducer.test.js +25 -0
  178. common/src/modules/data/plugins/__tests__/selectors.test.js +23 -0
  179. common/src/modules/data/plugins/__tests__/types.test.js +12 -0
  180. common/src/modules/data/plugins/actions.js +18 -0
  181. common/src/modules/data/plugins/constants.js +4 -0
  182. common/src/modules/data/plugins/index.js +12 -0
  183. common/src/modules/data/plugins/proptypes.js +15 -0
  184. common/src/modules/data/plugins/reducer.js +20 -0
  185. common/src/modules/data/plugins/selectors.js +9 -0
  186. common/src/modules/data/plugins/types.js +7 -0
  187. common/src/modules/data/reducers.js +15 -0
  188. common/src/modules/data/utils.js +1 -0
  189. common/src/modules/elements/accordion/__tests__/__snapshots__/element.test.js.snap +71 -0
  190. common/src/modules/elements/accordion/__tests__/element.test.js +45 -0
  191. common/src/modules/elements/accordion/element.js +58 -0
  192. common/src/modules/elements/accordion/row/__tests__/__snapshots__/template.test.js.snap +31 -0
  193. common/src/modules/elements/accordion/row/__tests__/template.test.js +56 -0
  194. common/src/modules/elements/accordion/row/template.js +129 -0
  195. common/src/modules/elements/accordion/style.pcss +8 -0
  196. common/src/modules/elements/block-icon/index.js +16 -0
  197. common/src/modules/elements/block-icon/style.pcss +24 -0
  198. common/src/modules/elements/button/__tests__/__snapshots__/element.test.js.snap +62 -0
  199. common/src/modules/elements/button/__tests__/element.test.js +60 -0
  200. common/src/modules/elements/button/element.js +56 -0
  201. common/src/modules/elements/button/style.pcss +58 -0
  202. common/src/modules/elements/checkbox-input/__tests__/__snapshots__/element.test.js.snap +47 -0
  203. common/src/modules/elements/checkbox-input/__tests__/element.test.js +39 -0
  204. common/src/modules/elements/checkbox-input/element.js +35 -0
  205. common/src/modules/elements/checkbox-input/style.pcss +25 -0
  206. common/src/modules/elements/checkbox/__tests__/__snapshots__/element.test.js.snap +168 -0
  207. common/src/modules/elements/checkbox/__tests__/element.test.js +70 -0
  208. common/src/modules/elements/checkbox/element.js +61 -0
  209. common/src/modules/elements/counter/__tests__/__snapshots__/element.test.js.snap +57 -0
  210. common/src/modules/elements/counter/__tests__/element.test.js +32 -0
  211. common/src/modules/elements/counter/element.js +37 -0
  212. common/src/modules/elements/counter/style.pcss +22 -0
  213. common/src/modules/elements/creatable-select/__tests__/__snapshots__/element.test.js.snap +241 -0
  214. common/src/modules/elements/creatable-select/__tests__/element.test.js +31 -0
  215. common/src/modules/elements/creatable-select/element.js +50 -0
  216. common/src/modules/elements/creatable-select/style.pcss +75 -0
  217. common/src/modules/elements/day-picker-input/element.js +34 -0
  218. common/src/modules/elements/day-picker-input/style.pcss +109 -0
  219. common/src/modules/elements/heading/__tests__/__snapshots__/element.test.js.snap +49 -0
  220. common/src/modules/elements/heading/__tests__/element.test.js +47 -0
  221. common/src/modules/elements/heading/element.js +32 -0
  222. common/src/modules/elements/heading/style.pcss +16 -0
  223. common/src/modules/elements/image-upload/__tests__/__snapshots__/element.test.js.snap +191 -0
  224. common/src/modules/elements/image-upload/__tests__/element.test.js +168 -0
  225. common/src/modules/elements/image-upload/element.js +114 -0
  226. common/src/modules/elements/image-upload/style.pcss +109 -0
  227. common/src/modules/elements/image/__tests__/__snapshots__/element.test.js.snap +27 -0
  228. common/src/modules/elements/image/__tests__/element.test.js +38 -0
  229. common/src/modules/elements/image/element.js +28 -0
  230. common/src/modules/elements/index.js +36 -0
  231. common/src/modules/elements/input/__tests__/__snapshots__/element.test.js.snap +23 -0
  232. common/src/modules/elements/input/__tests__/element.test.js +28 -0
  233. common/src/modules/elements/input/element.js +30 -0
  234. common/src/modules/elements/input/style.pcss +25 -0
  235. common/src/modules/elements/label-with-link/__tests__/__snapshots__/element.test.js.snap +51 -0
  236. common/src/modules/elements/label-with-link/__tests__/element.test.js +32 -0
  237. common/src/modules/elements/label-with-link/element.js +66 -0
  238. common/src/modules/elements/label-with-link/style.pcss +54 -0
  239. common/src/modules/elements/label-with-modal/__tests__/__snapshots__/element.test.js.snap +60 -0
  240. common/src/modules/elements/label-with-modal/__tests__/element.test.js +26 -0
  241. common/src/modules/elements/label-with-modal/element.js +71 -0
  242. common/src/modules/elements/label-with-modal/style.pcss +53 -0
  243. common/src/modules/elements/labeled-item/__tests__/__snapshots__/element.test.js.snap +50 -0
  244. common/src/modules/elements/labeled-item/__tests__/element.test.js +39 -0
  245. common/src/modules/elements/labeled-item/element.js +52 -0
  246. common/src/modules/elements/link/__tests__/__snapshots__/element.test.js.snap +40 -0
  247. common/src/modules/elements/link/__tests__/element.test.js +39 -0
  248. common/src/modules/elements/link/element.js +44 -0
  249. common/src/modules/elements/modal-button/__tests__/__snapshots__/element.test.js.snap +51 -0
  250. common/src/modules/elements/modal-button/__tests__/element.test.js +46 -0
  251. common/src/modules/elements/modal-button/element.js +103 -0
  252. common/src/modules/elements/number-input/__tests__/__snapshots__/element.test.js.snap +55 -0
  253. common/src/modules/elements/number-input/__tests__/element.test.js +47 -0
  254. common/src/modules/elements/number-input/element.js +40 -0
  255. common/src/modules/elements/paragraph/__tests__/__snapshots__/element.test.js.snap +17 -0
  256. common/src/modules/elements/paragraph/__tests__/element.test.js +19 -0
  257. common/src/modules/elements/paragraph/element.js +41 -0
  258. common/src/modules/elements/paragraph/style.pcss +24 -0
  259. common/src/modules/elements/placeholder/__tests__/__snapshots__/element.test.js.snap +17 -0
  260. common/src/modules/elements/placeholder/__tests__/element.test.js +18 -0
  261. common/src/modules/elements/placeholder/element.js +23 -0
  262. common/src/modules/elements/placeholder/style.pcss +12 -0
  263. common/src/modules/elements/radio-input/element.js +29 -0
  264. common/src/modules/elements/radio/element.js +50 -0
  265. common/src/modules/elements/select/__tests__/__snapshots__/element.test.js.snap +241 -0
  266. common/src/modules/elements/select/__tests__/element.test.js +31 -0
  267. common/src/modules/elements/select/element.js +41 -0
  268. common/src/modules/elements/select/style.pcss +78 -0
  269. common/src/modules/elements/style.pcss +3 -0
  270. common/src/modules/elements/textarea/element.js +20 -0
  271. common/src/modules/elements/time-picker/element.js +204 -0
  272. common/src/modules/elements/time-picker/style.pcss +86 -0
  273. common/src/modules/elements/tooltip/__tests__/__snapshots__/element.test.js.snap +22 -0
  274. common/src/modules/elements/tooltip/__tests__/element.test.js +33 -0
  275. common/src/modules/elements/tooltip/element.js +56 -0
  276. common/src/modules/elements/tooltip/style.pcss +14 -0
  277. common/src/modules/elements/url-input/element.js +27 -0
  278. common/src/modules/hoc/__tests__/__snapshots__/with-form.test.js.snap +19 -0
  279. common/src/modules/hoc/__tests__/__snapshots__/with-save-data.test.js.snap +62 -0
  280. common/src/modules/hoc/__tests__/__snapshots__/with-store.test.js.snap +18 -0
  281. common/src/modules/hoc/__tests__/with-form.test.js +89 -0
  282. common/src/modules/hoc/__tests__/with-save-data.test.js +191 -0
  283. common/src/modules/hoc/__tests__/with-selected.test.js +97 -0
  284. common/src/modules/hoc/__tests__/with-store.test.js +28 -0
  285. common/src/modules/hoc/index.js +5 -0
  286. common/src/modules/hoc/with-block-closer.js +129 -0
  287. common/src/modules/hoc/with-form.js +71 -0
  288. common/src/modules/hoc/with-save-data.js +155 -0
  289. common/src/modules/hoc/with-selected.js +64 -0
  290. common/src/modules/hoc/with-store.js +26 -0
  291. common/src/modules/package.json +17 -0
  292. common/src/modules/store/configure-store.js +37 -0
  293. common/src/modules/store/index.js +13 -0
  294. common/src/modules/store/middlewares/index.js +3 -0
  295. common/src/modules/store/middlewares/request/__tests__/__snapshots__/actions.test.js.snap +11 -0
  296. common/src/modules/store/middlewares/request/__tests__/actions.test.js +14 -0
  297. common/src/modules/store/middlewares/request/__tests__/types.test.js +11 -0
  298. common/src/modules/store/middlewares/request/__tests__/utils.test.js +61 -0
  299. common/src/modules/store/middlewares/request/__tests__/wp-request.test.js +169 -0
  300. common/src/modules/store/middlewares/request/actions.js +9 -0
  301. common/src/modules/store/middlewares/request/index.js +9 -0
  302. common/src/modules/store/middlewares/request/types.js +6 -0
  303. common/src/modules/store/middlewares/request/utils.js +32 -0
  304. common/src/modules/store/middlewares/request/wp-request.js +76 -0
  305. common/src/modules/utils/__tests__/__snapshots__/globals.test.js.snap +93 -0
  306. common/src/modules/utils/__tests__/__snapshots__/time.test.js.snap +33 -0
  307. common/src/modules/utils/__tests__/date.test.js +189 -0
  308. common/src/modules/utils/__tests__/dom.test.js +73 -0
  309. common/src/modules/utils/__tests__/globals.test.js +84 -0
  310. common/src/modules/utils/__tests__/input.test.js +20 -0
  311. common/src/modules/utils/__tests__/moment.test.js +332 -0
  312. common/src/modules/utils/__tests__/number.test.js +36 -0
  313. common/src/modules/utils/__tests__/proptypes.test.js +145 -0
  314. common/src/modules/utils/__tests__/range.test.js +65 -0
  315. common/src/modules/utils/__tests__/slide.test.js +12 -0
  316. common/src/modules/utils/__tests__/string.test.js +135 -0
  317. common/src/modules/utils/__tests__/time.test.js +222 -0
  318. common/src/modules/utils/api.js +57 -0
  319. common/src/modules/utils/date.js +151 -0
  320. common/src/modules/utils/dom.js +50 -0
  321. common/src/modules/utils/get-hidden-height.js +28 -0
  322. common/src/modules/utils/globals.js +37 -0
  323. common/src/modules/utils/index.js +27 -0
  324. common/src/modules/utils/input.js +12 -0
  325. common/src/modules/utils/moment.js +332 -0
  326. common/src/modules/utils/number.js +22 -0
  327. common/src/modules/utils/proptypes.js +73 -0
  328. common/src/modules/utils/range.js +89 -0
  329. common/src/modules/utils/slide.js +119 -0
  330. common/src/modules/utils/string.js +109 -0
  331. common/src/modules/utils/time.js +203 -0
common/lang/readme.txt DELETED
@@ -1,3 +0,0 @@
1
- If you are interested in contributing to translations, you can get started at https://translations.theeventscalendar.com/projects/tribe-common/
2
-
3
- On that site you can also find the .po translation files for doing local translations. We do not include these in the plugin itself to save space.
 
 
 
common/lang/tribe-common-af.mo CHANGED
Binary file
common/lang/tribe-common-ar.mo CHANGED
Binary file
common/lang/tribe-common-bg_BG.mo CHANGED
Binary file
common/lang/tribe-common-ca.mo CHANGED
Binary file
common/lang/tribe-common-cs_CZ.mo CHANGED
Binary file
common/lang/tribe-common-da_DK.mo CHANGED
Binary file
common/lang/tribe-common-de_DE.mo CHANGED
Binary file
common/lang/tribe-common-el.mo CHANGED
Binary file
common/lang/tribe-common-en_GB.mo CHANGED
Binary file
common/lang/tribe-common-es_ES.mo CHANGED
Binary file
common/lang/tribe-common-et.mo CHANGED
Binary file
common/lang/tribe-common-fi.mo CHANGED
Binary file
common/lang/tribe-common-fr_CA.mo CHANGED
Binary file
common/lang/tribe-common-fr_FR.mo CHANGED
Binary file
common/lang/tribe-common-hu_HU.mo CHANGED
Binary file
common/lang/tribe-common-id_ID.mo CHANGED
Binary file
common/lang/tribe-common-is_IS.mo CHANGED
Binary file
common/lang/tribe-common-it_IT.mo CHANGED
Binary file
common/lang/tribe-common-ja.mo CHANGED
Binary file
common/lang/tribe-common-lt_LT.mo CHANGED
Binary file
common/lang/tribe-common-lv.mo CHANGED
Binary file
common/lang/tribe-common-nb_NO.mo CHANGED
Binary file
common/lang/tribe-common-nl_NL.mo CHANGED
Binary file
common/lang/tribe-common-pl_PL.mo ADDED
Binary file
common/lang/tribe-common-pt_BR.mo CHANGED
Binary file
common/lang/tribe-common-pt_PT.mo CHANGED
Binary file
common/lang/tribe-common-ro_RO.mo CHANGED
Binary file
common/lang/tribe-common-ru_RU.mo CHANGED
Binary file
common/lang/tribe-common-sk_SK.mo CHANGED
Binary file
common/lang/tribe-common-sl_SI.mo CHANGED
Binary file
common/lang/tribe-common-sr_RS.mo CHANGED
Binary file
common/lang/tribe-common-sv_SE.mo CHANGED
Binary file
common/lang/tribe-common-tr_TR.mo CHANGED
Binary file
common/lang/tribe-common-zh_CN.mo CHANGED
Binary file
common/lang/tribe-common-zh_TW.mo CHANGED
Binary file
common/lang/tribe-common.pot CHANGED
@@ -1,14 +1,14 @@
1
- # Copyright (C) 2020 Modern Tribe
2
  # This file is distributed under the same license as the Tribe Common package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Tribe Common 4.12.2\n"
6
  "Report-Msgid-Bugs-To: http://m.tri.be/191x\n"
7
- "POT-Creation-Date: 2020-05-26 16:16:47+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2020-05-26 16:16\n"
12
  "Last-Translator: \n"
13
  "Language-Team: \n"
14
 
@@ -28,158 +28,146 @@ msgstr ""
28
  msgid "Return to WordPress Updates"
29
  msgstr ""
30
 
31
- #: src/Tribe/Admin/Help_Page.php:56 src/admin-views/tribe-options-help.php:50
32
- msgid "Copy to clipboard"
33
- msgstr ""
34
-
35
- #: src/Tribe/Admin/Help_Page.php:57
36
- msgid "System info copied"
37
- msgstr ""
38
-
39
- #: src/Tribe/Admin/Help_Page.php:58
40
- msgid "Press \"Cmd + C\" to copy"
41
- msgstr ""
42
-
43
- #: src/Tribe/Admin/Help_Page.php:79 src/Tribe/Customizer.php:563
44
  #: src/Tribe/Plugins_API.php:25
45
  msgid "The Events Calendar"
46
  msgstr ""
47
 
48
- #: src/Tribe/Admin/Help_Page.php:83
49
  msgid ""
50
  "The Events Calendar is a carefully crafted, extensible plugin that lets you "
51
  "easily share your events."
52
  msgstr ""
53
 
54
- #: src/Tribe/Admin/Help_Page.php:95 src/Tribe/Plugins_API.php:76
55
  msgid "Event Tickets"
56
  msgstr ""
57
 
58
- #: src/Tribe/Admin/Help_Page.php:99
59
  msgid ""
60
  "Events Tickets is a carefully crafted, extensible plugin that lets you "
61
  "easily sell tickets for your events."
62
  msgstr ""
63
 
64
- #: src/Tribe/Admin/Help_Page.php:111
65
  msgid "Advanced Post Manager"
66
  msgstr ""
67
 
68
- #: src/Tribe/Admin/Help_Page.php:115
69
  msgid ""
70
  "Turbo charge your posts admin for any custom post type with sortable filters "
71
  "and columns, and auto-registration of metaboxes."
72
  msgstr ""
73
 
74
- #: src/Tribe/Admin/Help_Page.php:190
75
  msgid " and "
76
  msgstr ""
77
 
78
- #: src/Tribe/Admin/Help_Page.php:214 src/Tribe/Plugins_API.php:59
79
  msgid "Events Calendar PRO"
80
  msgstr ""
81
 
82
- #: src/Tribe/Admin/Help_Page.php:223 src/Tribe/Plugins_API.php:179
83
  msgid "Eventbrite Tickets"
84
  msgstr ""
85
 
86
- #: src/Tribe/Admin/Help_Page.php:231 src/Tribe/Plugins_API.php:144
87
  msgid "Community Events"
88
  msgstr ""
89
 
90
- #: src/Tribe/Admin/Help_Page.php:239 src/Tribe/Plugins_API.php:42
91
  msgid "Event Aggregator"
92
  msgstr ""
93
 
94
- #: src/Tribe/Admin/Help_Page.php:247 src/Tribe/Plugins_API.php:127
95
  msgid "Filter Bar"
96
  msgstr ""
97
 
98
- #: src/Tribe/Admin/Help_Page.php:255 src/Tribe/Plugins_API.php:93
99
  msgid "Event Tickets Plus"
100
  msgstr ""
101
 
102
- #: src/Tribe/Admin/Help_Page.php:264 src/Tribe/Plugins_API.php:161
103
  msgid "Community Tickets"
104
  msgstr ""
105
 
106
- #: src/Tribe/Admin/Help_Page.php:425
107
  msgctxt "not available"
108
  msgid "n/a"
109
  msgstr ""
110
 
111
- #: src/Tribe/Admin/Help_Page.php:433
112
  msgid "You need to upgrade!"
113
  msgstr ""
114
 
115
- #: src/Tribe/Admin/Help_Page.php:433 src/Tribe/Admin/Help_Page.php:808
116
  msgid "You are up to date!"
117
  msgstr ""
118
 
119
- #: src/Tribe/Admin/Help_Page.php:798
120
  msgid "Activate %s"
121
  msgstr ""
122
 
123
- #: src/Tribe/Admin/Help_Page.php:798
124
  msgid "Activate Plugin"
125
  msgstr ""
126
 
127
- #: src/Tribe/Admin/Help_Page.php:806
128
  msgid "Upgrade Plugin"
129
  msgstr ""
130
 
131
- #: src/Tribe/Admin/Help_Page.php:822
132
  msgid "Install %s"
133
  msgstr ""
134
 
135
- #: src/Tribe/Admin/Help_Page.php:822
136
  msgid "Install Plugin"
137
  msgstr ""
138
 
139
- #: src/Tribe/Admin/Help_Page.php:839
140
  msgid "Latest Version:"
141
  msgstr ""
142
 
143
- #: src/Tribe/Admin/Help_Page.php:842
144
  msgid "Requires:"
145
  msgstr ""
146
 
147
- #: src/Tribe/Admin/Help_Page.php:843
148
  msgid "WordPress "
149
  msgstr ""
150
 
151
- #: src/Tribe/Admin/Help_Page.php:845
152
  msgid "Active Users:"
153
  msgstr ""
154
 
155
- #: src/Tribe/Admin/Help_Page.php:848
156
  msgid "Rating:"
157
  msgstr ""
158
 
159
- #: src/Tribe/Admin/Help_Page.php:867
160
  msgid "Premium Add-Ons"
161
  msgstr ""
162
 
163
- #: src/Tribe/Admin/Help_Page.php:873
164
  msgid "Plugin Active"
165
  msgstr ""
166
 
167
- #: src/Tribe/Admin/Help_Page.php:875
168
  msgid "Plugin Inactive"
169
  msgstr ""
170
 
171
- #: src/Tribe/Admin/Help_Page.php:880
172
  msgid "Visit the Add-on Page"
173
  msgstr ""
174
 
175
  #: src/Tribe/Admin/Notice/Php_Version.php:59
176
- #: src/Tribe/Admin/Notice/Plugin_Download.php:149
177
  msgctxt "separator used in a list of items"
178
  msgid ", "
179
  msgstr ""
180
 
181
  #: src/Tribe/Admin/Notice/Php_Version.php:60
182
- #: src/Tribe/Admin/Notice/Plugin_Download.php:150
183
  msgctxt "the final separator in a list of two or more items"
184
  msgid " and "
185
  msgstr ""
@@ -198,21 +186,9 @@ msgid ""
198
  "recommend using PHP 5.6 or above."
199
  msgstr ""
200
 
201
- #: src/Tribe/Admin/Notice/Plugin_Download.php:122
202
- msgid "To begin using %2$s, please install and activate %3$s."
203
- msgstr ""
204
-
205
- #: src/Tribe/Admin/Notice/Plugin_Download.php:124
206
- #: src/Tribe/PUE/Update_Prevention.php:181
207
- msgid "Read more"
208
- msgstr ""
209
-
210
- #: src/Tribe/Admin/Notice/Plugin_Download.php:125
211
  msgid ""
212
- "There’s a new version of %1$s available, but your license is expired. You’ll "
213
- "need to renew your license to get access to the latest version. If you plan "
214
- "to continue using your current version of the plugin(s), be sure to use a "
215
- "compatible version of The Events Calendar. %2$s"
216
  msgstr ""
217
 
218
  #: src/Tribe/Ajax/Dropdown.php:38
@@ -236,112 +212,6 @@ msgstr ""
236
  msgid "Event Add-Ons"
237
  msgstr ""
238
 
239
- #: src/Tribe/App_Shop.php:166
240
- msgid "Events Marketing Bundle"
241
- msgstr ""
242
-
243
- #: src/Tribe/App_Shop.php:169 src/Tribe/App_Shop.php:208
244
- #: src/Tribe/App_Shop.php:221
245
- msgid "Save over 20%"
246
- msgstr ""
247
-
248
- #: src/Tribe/App_Shop.php:170
249
- msgid "Ticket sales, attendee management, and email marketing for your events."
250
- msgstr ""
251
-
252
- #: src/Tribe/App_Shop.php:178
253
- msgid "Event Importer Bundle"
254
- msgstr ""
255
-
256
- #: src/Tribe/App_Shop.php:181
257
- msgid "Save over 25%"
258
- msgstr ""
259
-
260
- #: src/Tribe/App_Shop.php:182
261
- msgid ""
262
- "Fill your calendar with events from across the web, including Google "
263
- "Calendar, Meetup, and more."
264
- msgstr ""
265
-
266
- #: src/Tribe/App_Shop.php:205
267
- msgid "Community Manager Bundle"
268
- msgstr ""
269
-
270
- #: src/Tribe/App_Shop.php:209
271
- msgid ""
272
- "Handle event submissions with ticket sales and everything you need to build "
273
- "a robust community."
274
- msgstr ""
275
-
276
- #: src/Tribe/App_Shop.php:218
277
- msgid "Ultimate Bundle"
278
- msgstr ""
279
-
280
- #: src/Tribe/App_Shop.php:222
281
- msgid "All of our premium events management plugins at a deep discount."
282
- msgstr ""
283
-
284
- #: src/Tribe/App_Shop.php:249
285
- msgid "Website URL CTA"
286
- msgstr ""
287
-
288
- #: src/Tribe/App_Shop.php:252
289
- msgid ""
290
- "Create a strong call-to-action for attendees to \"Join Webinar\" instead of "
291
- "only sharing a website address."
292
- msgstr ""
293
-
294
- #: src/Tribe/App_Shop.php:255
295
- msgid "Link Directly to Webinar"
296
- msgstr ""
297
-
298
- #: src/Tribe/App_Shop.php:258
299
- msgid ""
300
- "When users click on the event title, they’ll be taken right to the source of "
301
- "your event, offering a direct route to join."
302
- msgstr ""
303
-
304
- #: src/Tribe/App_Shop.php:261
305
- msgid "Events Happening Now"
306
- msgstr ""
307
-
308
- #: src/Tribe/App_Shop.php:264
309
- msgid ""
310
- "Use this shortcode to display events that are currently in progress, like "
311
- "webinars and livestreams."
312
- msgstr ""
313
-
314
- #: src/Tribe/App_Shop.php:267
315
- msgid "Custom Venue Links"
316
- msgstr ""
317
-
318
- #: src/Tribe/App_Shop.php:270
319
- msgid ""
320
- "Turn the venue name for your event into a clickable URL — a great way to "
321
- "link directly to a venue’s website or a virtual meeting."
322
- msgstr ""
323
-
324
- #: src/Tribe/App_Shop.php:273
325
- msgid "Adjust Label"
326
- msgstr ""
327
-
328
- #: src/Tribe/App_Shop.php:276
329
- msgid ""
330
- "Change \"Events\" to \"Webinars,\" or \"Venues\" to \"Livestream,\" or "
331
- "\"Organizers\" to \"Hosts.\" Tailor your calendar for virtual events and "
332
- "meetings."
333
- msgstr ""
334
-
335
- #: src/Tribe/App_Shop.php:279
336
- msgid "Reach Attendees"
337
- msgstr ""
338
-
339
- #: src/Tribe/App_Shop.php:282
340
- msgid ""
341
- "From registration to attendance history, view every step of the event "
342
- "lifecycle with this HubSpot integration."
343
- msgstr ""
344
-
345
  #: src/Tribe/Cost_Utils.php:114
346
  msgid "Free"
347
  msgstr ""
@@ -358,7 +228,7 @@ msgstr ""
358
  msgid "Rate %1$sEvent Tickets%2$s %3$s"
359
  msgstr ""
360
 
361
- #: src/Tribe/Customizer.php:564
362
  msgid ""
363
  "Use the following panel of your customizer to change the styling of your "
364
  "Calendar and Event pages."
@@ -397,39 +267,6 @@ msgstr ""
397
  msgid "State"
398
  msgstr ""
399
 
400
- #: src/Tribe/Debug_Bar/Panels/Json_Ld.php:21
401
- #: src/Tribe/Debug_Bar/Panels/Json_Ld.php:40
402
- msgid "Modern Tribe JSON-LD Data"
403
- msgstr ""
404
-
405
- #: src/Tribe/Dialog/View.php:153
406
- msgid "Open the modal window"
407
- msgstr ""
408
-
409
- #: src/Tribe/Dialog/View.php:154
410
- msgid "Close this modal window"
411
- msgstr ""
412
-
413
- #: src/Tribe/Dialog/View.php:222
414
- msgid "Cancel"
415
- msgstr ""
416
-
417
- #: src/Tribe/Dialog/View.php:223
418
- msgid "Confirm"
419
- msgstr ""
420
-
421
- #: src/Tribe/Dialog/View.php:288
422
- msgid "OK"
423
- msgstr ""
424
-
425
- #: src/Tribe/Dialog/View.php:355
426
- msgid "Open the dialog window"
427
- msgstr ""
428
-
429
- #: src/Tribe/Dialog/View.php:369
430
- msgid "Close this dialog window"
431
- msgstr ""
432
-
433
  #: src/Tribe/Documentation/Swagger/Cost_Details_Definition_Provider.php:24
434
  msgid "The cost currency symbol"
435
  msgstr ""
@@ -547,24 +384,24 @@ msgid "Problem loading the block, please remove this block to restart."
547
  msgstr ""
548
 
549
  #. translators: %s: duration
550
- #: src/Tribe/Editor/Configuration.php:89
551
  msgid "%s from now"
552
  msgstr ""
553
 
554
  #. translators: %s: duration
555
- #: src/Tribe/Editor/Configuration.php:91
556
  msgid "%s ago"
557
  msgstr ""
558
 
559
- #: src/Tribe/Editor/Configuration.php:95 src/Tribe/Editor/Configuration.php:98
560
  msgid "g:i a"
561
  msgstr ""
562
 
563
- #: src/Tribe/Editor/Configuration.php:96 src/Tribe/Editor/Configuration.php:98
564
  msgid "F j, Y"
565
  msgstr ""
566
 
567
- #: src/Tribe/Editor/Configuration.php:97
568
  msgid "F j"
569
  msgstr ""
570
 
@@ -584,7 +421,7 @@ msgstr ""
584
  msgid "Tutorial"
585
  msgstr ""
586
 
587
- #: src/Tribe/Extension.php:406
588
  msgid ""
589
  "Unable to run Tribe Extensions. Your website host is running PHP 5.2 or "
590
  "older, and has likely disabled or misconfigured debug_backtrace(). You, or "
@@ -592,28 +429,19 @@ msgid ""
592
  "debug_backtrace() for Tribe Extensions to work."
593
  msgstr ""
594
 
595
- #: src/Tribe/Extension.php:421
596
- msgctxt "extension disallowed"
597
- msgid ""
598
- "This extension has been programmatically disallowed. The most common reason "
599
- "is due to another Modern Tribe plugin having absorbed or replaced this "
600
- "extension's functionality. This extension plugin has been deactivated, and "
601
- "you should likely delete it."
602
- msgstr ""
603
-
604
- #: src/Tribe/Field.php:233
605
  msgid "Invalid field type specified"
606
  msgstr ""
607
 
608
- #: src/Tribe/Field.php:541
609
  msgid "No radio options specified"
610
  msgstr ""
611
 
612
- #: src/Tribe/Field.php:577
613
  msgid "No checkbox options specified"
614
  msgstr ""
615
 
616
- #: src/Tribe/Field.php:638
617
  msgid "No select options specified"
618
  msgstr ""
619
 
@@ -949,8 +777,7 @@ msgstr ""
949
  msgid "Gambia"
950
  msgstr ""
951
 
952
- #: src/Tribe/Languages/Locations.php:138
953
- msgctxt "The country"
954
  msgid "Georgia"
955
  msgstr ""
956
 
@@ -1139,135 +966,135 @@ msgid "Macau"
1139
  msgstr ""
1140
 
1141
  #: src/Tribe/Languages/Locations.php:185
1142
- msgid "Madagascar"
1143
  msgstr ""
1144
 
1145
  #: src/Tribe/Languages/Locations.php:186
1146
- msgid "Malawi"
1147
  msgstr ""
1148
 
1149
  #: src/Tribe/Languages/Locations.php:187
1150
- msgid "Malaysia"
1151
  msgstr ""
1152
 
1153
  #: src/Tribe/Languages/Locations.php:188
1154
- msgid "Maldives"
1155
  msgstr ""
1156
 
1157
  #: src/Tribe/Languages/Locations.php:189
1158
- msgid "Mali"
1159
  msgstr ""
1160
 
1161
  #: src/Tribe/Languages/Locations.php:190
1162
- msgid "Malta"
1163
  msgstr ""
1164
 
1165
  #: src/Tribe/Languages/Locations.php:191
1166
- msgid "Marshall Islands"
1167
  msgstr ""
1168
 
1169
  #: src/Tribe/Languages/Locations.php:192
1170
- msgid "Martinique"
1171
  msgstr ""
1172
 
1173
  #: src/Tribe/Languages/Locations.php:193
1174
- msgid "Mauritania"
1175
  msgstr ""
1176
 
1177
  #: src/Tribe/Languages/Locations.php:194
1178
- msgid "Mauritius"
1179
  msgstr ""
1180
 
1181
  #: src/Tribe/Languages/Locations.php:195
1182
- msgid "Mayotte"
1183
  msgstr ""
1184
 
1185
  #: src/Tribe/Languages/Locations.php:196
1186
- msgid "Mexico"
1187
  msgstr ""
1188
 
1189
  #: src/Tribe/Languages/Locations.php:197
1190
- msgid "Micronesia, Federated States of"
1191
  msgstr ""
1192
 
1193
  #: src/Tribe/Languages/Locations.php:198
1194
- msgid "Moldova, Republic of"
1195
  msgstr ""
1196
 
1197
  #: src/Tribe/Languages/Locations.php:199
1198
- msgid "Monaco"
1199
  msgstr ""
1200
 
1201
  #: src/Tribe/Languages/Locations.php:200
1202
- msgid "Mongolia"
1203
  msgstr ""
1204
 
1205
  #: src/Tribe/Languages/Locations.php:201
1206
- msgid "Montenegro"
1207
  msgstr ""
1208
 
1209
  #: src/Tribe/Languages/Locations.php:202
1210
- msgid "Montserrat"
1211
  msgstr ""
1212
 
1213
  #: src/Tribe/Languages/Locations.php:203
1214
- msgid "Morocco"
1215
  msgstr ""
1216
 
1217
  #: src/Tribe/Languages/Locations.php:204
1218
- msgid "Mozambique"
1219
  msgstr ""
1220
 
1221
  #: src/Tribe/Languages/Locations.php:205
1222
- msgid "Myanmar"
1223
  msgstr ""
1224
 
1225
  #: src/Tribe/Languages/Locations.php:206
1226
- msgid "Namibia"
1227
  msgstr ""
1228
 
1229
  #: src/Tribe/Languages/Locations.php:207
1230
- msgid "Nauru"
1231
  msgstr ""
1232
 
1233
  #: src/Tribe/Languages/Locations.php:208
1234
- msgid "Nepal"
1235
  msgstr ""
1236
 
1237
  #: src/Tribe/Languages/Locations.php:209
1238
- msgid "Netherlands"
1239
  msgstr ""
1240
 
1241
  #: src/Tribe/Languages/Locations.php:210
1242
- msgid "New Caledonia"
1243
  msgstr ""
1244
 
1245
  #: src/Tribe/Languages/Locations.php:211
1246
- msgid "New Zealand"
1247
  msgstr ""
1248
 
1249
  #: src/Tribe/Languages/Locations.php:212
1250
- msgid "Nicaragua"
1251
  msgstr ""
1252
 
1253
  #: src/Tribe/Languages/Locations.php:213
1254
- msgid "Niger"
1255
  msgstr ""
1256
 
1257
  #: src/Tribe/Languages/Locations.php:214
1258
- msgid "Nigeria"
1259
  msgstr ""
1260
 
1261
  #: src/Tribe/Languages/Locations.php:215
1262
- msgid "Niue"
1263
  msgstr ""
1264
 
1265
  #: src/Tribe/Languages/Locations.php:216
1266
- msgid "Norfolk Island"
1267
  msgstr ""
1268
 
1269
  #: src/Tribe/Languages/Locations.php:217
1270
- msgid "North Macedonia"
1271
  msgstr ""
1272
 
1273
  #: src/Tribe/Languages/Locations.php:218
@@ -1630,11 +1457,6 @@ msgstr ""
1630
  msgid "Florida"
1631
  msgstr ""
1632
 
1633
- #: src/Tribe/Languages/Locations.php:334
1634
- msgctxt "The US state Georgia"
1635
- msgid "Georgia"
1636
- msgstr ""
1637
-
1638
  #: src/Tribe/Languages/Locations.php:335
1639
  msgid "Hawaii"
1640
  msgstr ""
@@ -1795,10 +1617,6 @@ msgstr ""
1795
  msgid "Wyoming"
1796
  msgstr ""
1797
 
1798
- #: src/Tribe/Log/Action_Logger.php:39
1799
- msgid "Action-based Logger"
1800
- msgstr ""
1801
-
1802
  #: src/Tribe/Log/Admin.php:133
1803
  msgctxt "log selector"
1804
  msgid "None currently available"
@@ -1817,110 +1635,122 @@ msgstr ""
1817
  msgid "Null logger (will log nothing)"
1818
  msgstr ""
1819
 
1820
- #: src/Tribe/Log.php:289
1821
  msgid "Cannot set %s as the current logging engine"
1822
  msgstr ""
1823
 
1824
- #: src/Tribe/Log.php:388
1825
  msgid "Disabled"
1826
  msgstr ""
1827
 
1828
- #: src/Tribe/Log.php:389
1829
  msgid "Only errors"
1830
  msgstr ""
1831
 
1832
- #: src/Tribe/Log.php:390
1833
  msgid "Warnings and errors"
1834
  msgstr ""
1835
 
1836
- #: src/Tribe/Log.php:391
1837
  msgid "Full debug (all events)"
1838
  msgstr ""
1839
 
1840
- #: src/Tribe/Main.php:313
1841
  msgid ": activate to sort column ascending"
1842
  msgstr ""
1843
 
1844
- #: src/Tribe/Main.php:314
1845
  msgid ": activate to sort column descending"
1846
  msgstr ""
1847
 
1848
- #: src/Tribe/Main.php:316
1849
  msgid "Show _MENU_ entries"
1850
  msgstr ""
1851
 
1852
- #: src/Tribe/Main.php:317
1853
  msgid "No data available in table"
1854
  msgstr ""
1855
 
1856
- #: src/Tribe/Main.php:318
1857
  msgid "Showing _START_ to _END_ of _TOTAL_ entries"
1858
  msgstr ""
1859
 
1860
- #: src/Tribe/Main.php:319
1861
  msgid "Showing 0 to 0 of 0 entries"
1862
  msgstr ""
1863
 
1864
- #: src/Tribe/Main.php:320
1865
  msgid "(filtered from _MAX_ total entries)"
1866
  msgstr ""
1867
 
1868
- #: src/Tribe/Main.php:321
1869
  msgid "No matching records found"
1870
  msgstr ""
1871
 
1872
- #: src/Tribe/Main.php:322
1873
  msgid "Search:"
1874
  msgstr ""
1875
 
1876
- #: src/Tribe/Main.php:323
1877
  msgid "All items on this page were selected. "
1878
  msgstr ""
1879
 
1880
- #: src/Tribe/Main.php:324
1881
  msgid "Select all pages"
1882
  msgstr ""
1883
 
1884
- #: src/Tribe/Main.php:325
1885
  msgid "Clear Selection."
1886
  msgstr ""
1887
 
1888
- #: src/Tribe/Main.php:327
1889
  msgid "All"
1890
  msgstr ""
1891
 
1892
- #: src/Tribe/Main.php:328 src/Tribe/Main.php:345
1893
  msgid "Next"
1894
  msgstr ""
1895
 
1896
- #: src/Tribe/Main.php:329
1897
  msgid "Previous"
1898
  msgstr ""
1899
 
1900
- #: src/Tribe/Main.php:334
1901
  msgid ": Selected %d rows"
1902
  msgstr ""
1903
 
1904
- #: src/Tribe/Main.php:335
1905
  msgid ": Selected 1 row"
1906
  msgstr ""
1907
 
1908
- #: src/Tribe/Main.php:346
1909
  msgid "Prev"
1910
  msgstr ""
1911
 
1912
- #: src/Tribe/Main.php:347 src/Tribe/Main.php:349
1913
  msgid "Today"
1914
  msgstr ""
1915
 
1916
- #: src/Tribe/Main.php:348
1917
  msgid "Done"
1918
  msgstr ""
1919
 
1920
- #: src/Tribe/Main.php:350
1921
  msgid "Clear"
1922
  msgstr ""
1923
 
 
 
 
 
 
 
 
 
 
 
 
 
1924
  #: src/Tribe/PUE/Checker.php:497
1925
  msgid "A valid license key is required for support and updates"
1926
  msgstr ""
@@ -1956,72 +1786,72 @@ msgstr ""
1956
  msgid "License key(s) updated."
1957
  msgstr ""
1958
 
1959
- #: src/Tribe/PUE/Checker.php:907
1960
  msgid ""
1961
  "Hmmm... something's wrong with this validator. Please contact %ssupport%s."
1962
  msgstr ""
1963
 
1964
- #: src/Tribe/PUE/Checker.php:920
1965
  msgid "unknown date"
1966
  msgstr ""
1967
 
1968
- #: src/Tribe/PUE/Checker.php:926
1969
  msgid "Sorry, key validation server is not available."
1970
  msgstr ""
1971
 
1972
- #: src/Tribe/PUE/Checker.php:946
1973
  msgid "Valid Key! Expires on %s"
1974
  msgstr ""
1975
 
1976
- #: src/Tribe/PUE/Checker.php:951
1977
  msgid "Thanks for setting up a valid key. It will expire on %s"
1978
  msgstr ""
1979
 
1980
- #: src/Tribe/PUE/Checker.php:978 src/Tribe/PUE/Notices.php:342
1981
  msgid "Renew Your License Now"
1982
  msgstr ""
1983
 
1984
- #: src/Tribe/PUE/Checker.php:980 src/Tribe/PUE/Notices.php:344
1985
  msgid " (opens in a new window)"
1986
  msgstr ""
1987
 
1988
- #: src/Tribe/PUE/Checker.php:997
1989
  msgid "Please refresh the page and try your request again."
1990
  msgstr ""
1991
 
1992
- #: src/Tribe/PUE/Checker.php:1017
1993
  msgid ""
1994
  "There is an update for %s. You'll need to %scheck your license%s to have "
1995
  "access to updates, downloads, and support."
1996
  msgstr ""
1997
 
1998
- #: src/Tribe/PUE/Checker.php:1074
1999
  msgid ""
2000
  "There is an update for %s. %sRenew your license%s to get access to bug "
2001
  "fixes, security updates, and new features."
2002
  msgstr ""
2003
 
2004
- #: src/Tribe/PUE/Checker.php:1104
2005
  msgid "Update now to version %s."
2006
  msgstr ""
2007
 
2008
- #: src/Tribe/PUE/Checker.php:1115
2009
  msgid "There is a new version of %1$s available. %2$s"
2010
  msgstr ""
2011
 
2012
- #: src/Tribe/PUE/Checker.php:1696
2013
  msgid "A valid license has been entered by your network administrator."
2014
  msgstr ""
2015
 
2016
- #: src/Tribe/PUE/Checker.php:1697
2017
  msgid "No license entered. Consult your network administrator."
2018
  msgstr ""
2019
 
2020
- #: src/Tribe/PUE/Checker.php:1698
2021
  msgid "Expired license. Consult your network administrator."
2022
  msgstr ""
2023
 
2024
- #: src/Tribe/PUE/Notices.php:284
2025
  msgid ""
2026
  "It looks like you're using %1$s, but the license key is invalid. Please "
2027
  "download the latest version %2$sfrom your account%3$s."
@@ -2031,7 +1861,7 @@ msgid_plural ""
2031
  msgstr[0] ""
2032
  msgstr[1] ""
2033
 
2034
- #: src/Tribe/PUE/Notices.php:329
2035
  msgid ""
2036
  "There is an update available for %1$s but your license has expired. "
2037
  "%2$sVisit the Events Calendar website to renew your license.%3$s"
@@ -2041,7 +1871,7 @@ msgid_plural ""
2041
  msgstr[0] ""
2042
  msgstr[1] ""
2043
 
2044
- #: src/Tribe/PUE/Notices.php:361
2045
  msgid ""
2046
  "You have a license key for %1$s but the key is out of installs. %2$sVisit "
2047
  "the Events Calendar website%3$s to manage your installs, upgrade your "
@@ -2053,271 +1883,125 @@ msgid_plural ""
2053
  msgstr[0] ""
2054
  msgstr[1] ""
2055
 
2056
- #: src/Tribe/PUE/Notices.php:403
2057
  msgid ""
2058
  "You can always check the status of your licenses by logging in to %1$syour "
2059
  "account on theeventscalendar.com%2$s."
2060
  msgstr ""
2061
 
2062
- #: src/Tribe/PUE/Notices.php:458 src/Tribe/PUE/Notices.php:501
2063
  msgctxt "formatted plugin list"
2064
  msgid "%1$s and %2$s"
2065
  msgstr ""
2066
 
2067
- #: src/Tribe/PUE/Update_Prevention.php:184
2068
  msgid ""
2069
- "Your update failed due to an incompatibility between the version (%1$s) of "
2070
- "the %2$s you tried to update to and the version of %3$s that you are using. "
2071
- "%4$s"
2072
  msgstr ""
2073
 
2074
- #: src/Tribe/Plugins.php:147
2075
  msgid ""
2076
- "Using this function before \"plugins_loaded\" action has fired can return "
2077
- "unreliable results."
2078
- msgstr ""
2079
-
2080
- #: src/Tribe/Plugins_API.php:28
2081
- msgid "Our flagship free calendar"
2082
- msgstr ""
2083
-
2084
- #: src/Tribe/Plugins_API.php:30 src/Tribe/Plugins_API.php:48
2085
- msgid "Customizable"
2086
- msgstr ""
2087
-
2088
- #: src/Tribe/Plugins_API.php:31
2089
- msgid "Import & export events"
2090
- msgstr ""
2091
-
2092
- #: src/Tribe/Plugins_API.php:32
2093
- msgid "Timezone support"
2094
- msgstr ""
2095
-
2096
- #: src/Tribe/Plugins_API.php:33
2097
- msgid "Multiple views"
2098
- msgstr ""
2099
-
2100
- #: src/Tribe/Plugins_API.php:45
2101
- msgid "Automated imports for your calendar"
2102
- msgstr ""
2103
-
2104
- #: src/Tribe/Plugins_API.php:47
2105
- msgid "Schedule automated imports"
2106
- msgstr ""
2107
-
2108
- #: src/Tribe/Plugins_API.php:49
2109
- msgid "Works with Google Calendar, Meetup, and more"
2110
- msgstr ""
2111
-
2112
- #: src/Tribe/Plugins_API.php:50
2113
- msgid "Refine by date, location, or keyword"
2114
- msgstr ""
2115
-
2116
- #: src/Tribe/Plugins_API.php:62
2117
- msgid "Power up your calendar with Pro"
2118
- msgstr ""
2119
-
2120
- #: src/Tribe/Plugins_API.php:64
2121
- msgid "Premium support"
2122
- msgstr ""
2123
-
2124
- #: src/Tribe/Plugins_API.php:65
2125
- msgid "Recurring events"
2126
- msgstr ""
2127
-
2128
- #: src/Tribe/Plugins_API.php:66
2129
- msgid "Additional views"
2130
- msgstr ""
2131
-
2132
- #: src/Tribe/Plugins_API.php:67
2133
- msgid "Shortcodes"
2134
- msgstr ""
2135
-
2136
- #: src/Tribe/Plugins_API.php:79
2137
- msgid "Manage ticketing and RSVPs"
2138
- msgstr ""
2139
-
2140
- #: src/Tribe/Plugins_API.php:81
2141
- msgid "Add tickets and RSVP to any post"
2142
- msgstr ""
2143
-
2144
- #: src/Tribe/Plugins_API.php:82
2145
- msgid "Paypal integration"
2146
- msgstr ""
2147
-
2148
- #: src/Tribe/Plugins_API.php:83
2149
- msgid "Attendee reports"
2150
- msgstr ""
2151
-
2152
- #: src/Tribe/Plugins_API.php:84
2153
- msgid "Customizable ticket template"
2154
- msgstr ""
2155
-
2156
- #: src/Tribe/Plugins_API.php:96
2157
- msgid "Monetize your events"
2158
- msgstr ""
2159
-
2160
- #: src/Tribe/Plugins_API.php:98
2161
- msgid "Custom registration fields"
2162
  msgstr ""
2163
 
2164
- #: src/Tribe/Plugins_API.php:99
2165
- msgid "WooCommerce compatibility"
 
 
 
 
2166
  msgstr ""
2167
 
2168
- #: src/Tribe/Plugins_API.php:100
2169
- msgid "Ticket scanning with mobile app"
 
 
 
 
2170
  msgstr ""
2171
 
2172
- #: src/Tribe/Plugins_API.php:101
2173
- msgid "Custom attendee registration fields"
 
 
 
 
 
 
2174
  msgstr ""
2175
 
2176
- #: src/Tribe/Plugins_API.php:110 src/Tribe/Promoter/PUE.php:28
2177
  #: src/views/promoter/auth.php:30 src/views/promoter/auth.php:74
2178
  msgid "Promoter"
2179
  msgstr ""
2180
 
2181
- #: src/Tribe/Plugins_API.php:113
2182
- msgid "An email marketing solution for events and the people running them"
2183
- msgstr ""
2184
-
2185
- #: src/Tribe/Plugins_API.php:115
2186
- msgid "Automate email touchpoints"
2187
- msgstr ""
2188
-
2189
- #: src/Tribe/Plugins_API.php:116
2190
- msgid "Customize email templates"
2191
- msgstr ""
2192
-
2193
- #: src/Tribe/Plugins_API.php:117
2194
- msgid "Streamline your email process"
2195
- msgstr ""
2196
-
2197
- #: src/Tribe/Plugins_API.php:118
2198
- msgid "Segment your attendee lists"
2199
- msgstr ""
2200
-
2201
- #: src/Tribe/Plugins_API.php:130
2202
- msgid "Help users find exactly the right event"
2203
- msgstr ""
2204
-
2205
- #: src/Tribe/Plugins_API.php:132
2206
- msgid "Configurable set of filters"
2207
- msgstr ""
2208
-
2209
- #: src/Tribe/Plugins_API.php:133
2210
- msgid "Horizontal or vertical"
2211
- msgstr ""
2212
-
2213
- #: src/Tribe/Plugins_API.php:134
2214
- msgid "Filter category, price, and more"
2215
- msgstr ""
2216
-
2217
- #: src/Tribe/Plugins_API.php:135
2218
- msgid "Filter distance (for Events Calendar Pro)"
2219
- msgstr ""
2220
-
2221
- #: src/Tribe/Plugins_API.php:147
2222
- msgid "Users submit events to your calendar"
2223
- msgstr ""
2224
-
2225
- #: src/Tribe/Plugins_API.php:149
2226
- msgid "Publishing Control"
2227
- msgstr ""
2228
-
2229
- #: src/Tribe/Plugins_API.php:150
2230
- msgid "Event Submission Form"
2231
- msgstr ""
2232
-
2233
- #: src/Tribe/Plugins_API.php:151
2234
- msgid "Registered User Settings"
2235
- msgstr ""
2236
-
2237
- #: src/Tribe/Plugins_API.php:152
2238
- msgid "Email notifications"
2239
- msgstr ""
2240
-
2241
- #: src/Tribe/Plugins_API.php:164
2242
- msgid "Run your own events marketplace"
2243
- msgstr ""
2244
-
2245
- #: src/Tribe/Plugins_API.php:166
2246
- msgid "Users submit events and sell tickets"
2247
  msgstr ""
2248
 
2249
- #: src/Tribe/Plugins_API.php:167
2250
- msgid "Split commission with users"
 
 
 
 
 
 
2251
  msgstr ""
2252
 
2253
- #: src/Tribe/Plugins_API.php:168
2254
- msgid "No admin access required"
 
 
 
 
 
 
2255
  msgstr ""
2256
 
2257
- #: src/Tribe/Plugins_API.php:169
2258
- msgid "Sales reporting"
 
 
 
 
2259
  msgstr ""
2260
 
2261
- #: src/Tribe/Plugins_API.php:171
2262
  msgctxt "Names of required plugins for Community Tickets"
2263
  msgid "Event Tickets Plus and Community Events"
2264
  msgstr ""
2265
 
2266
- #: src/Tribe/Plugins_API.php:182
2267
- msgid "Unite the power of TEC with the ticketing of Eventbrite"
2268
- msgstr ""
2269
-
2270
- #: src/Tribe/Plugins_API.php:184
2271
- msgid "Manage tickets from WordPress"
2272
- msgstr ""
2273
-
2274
- #: src/Tribe/Plugins_API.php:185
2275
- msgid "Ticket availability automatically updates"
2276
- msgstr ""
2277
-
2278
- #: src/Tribe/Plugins_API.php:186
2279
- msgid "Integrated with your events on Eventbrite"
2280
- msgstr ""
2281
-
2282
- #: src/Tribe/Plugins_API.php:187
2283
- msgid "Automatically import your events"
2284
  msgstr ""
2285
 
2286
- #: src/Tribe/Plugins_API.php:196
2287
  msgid "Image Widget Plus"
2288
  msgstr ""
2289
 
2290
- #: src/Tribe/Plugins_API.php:199
2291
- msgid "Beautiful display options for your favorite photos."
2292
- msgstr ""
2293
-
2294
- #: src/Tribe/Plugins_API.php:201
2295
- msgid "Multi-Image Support"
2296
- msgstr ""
2297
-
2298
- #: src/Tribe/Plugins_API.php:202
2299
- msgid "Lightbox"
2300
- msgstr ""
2301
-
2302
- #: src/Tribe/Plugins_API.php:203
2303
- msgid "Slideshow"
2304
- msgstr ""
2305
-
2306
- #: src/Tribe/Plugins_API.php:204
2307
- msgid "Random Images"
2308
- msgstr ""
2309
-
2310
- #: src/Tribe/Plugins_API.php:213
2311
- msgid "Virtual Events"
2312
- msgstr ""
2313
-
2314
- #: src/Tribe/Plugins_API.php:216
2315
- msgid "[ADD DESCRIPTION]"
2316
- msgstr ""
2317
-
2318
- #: src/Tribe/Plugins_API.php:218 src/Tribe/Plugins_API.php:219
2319
- #: src/Tribe/Plugins_API.php:220 src/Tribe/Plugins_API.php:221
2320
- msgid "Feature"
2321
  msgstr ""
2322
 
2323
  #: src/Tribe/Process/Queue.php:1024
@@ -2341,39 +2025,39 @@ msgstr ""
2341
  msgid "Events Help"
2342
  msgstr ""
2343
 
2344
- #: src/Tribe/Settings.php:355
2345
  msgid "%s Settings"
2346
  msgstr ""
2347
 
2348
- #: src/Tribe/Settings.php:369
2349
  msgid "You've requested a non-existent tab."
2350
  msgstr ""
2351
 
2352
- #: src/Tribe/Settings.php:377
2353
  msgid "Save Changes"
2354
  msgstr ""
2355
 
2356
- #: src/Tribe/Settings.php:425
2357
  msgid "You don't have permission to do that."
2358
  msgstr ""
2359
 
2360
- #: src/Tribe/Settings.php:431
2361
  msgid "The request was sent insecurely."
2362
  msgstr ""
2363
 
2364
- #: src/Tribe/Settings.php:437
2365
  msgid "The request wasn't sent from this tab."
2366
  msgstr ""
2367
 
2368
- #: src/Tribe/Settings.php:614
2369
  msgid "Your form had the following errors:"
2370
  msgstr ""
2371
 
2372
- #: src/Tribe/Settings.php:624
2373
  msgid "None of your settings were saved. Please try again."
2374
  msgstr ""
2375
 
2376
- #: src/Tribe/Settings.php:625
2377
  msgid ""
2378
  "The above setting was not saved. Other settings were successfully saved."
2379
  msgid_plural ""
@@ -2381,28 +2065,28 @@ msgid_plural ""
2381
  msgstr[0] ""
2382
  msgstr[1] ""
2383
 
2384
- #: src/Tribe/Settings.php:647
2385
  msgid "Settings saved."
2386
  msgstr ""
2387
 
2388
- #: src/Tribe/Settings_Manager.php:80
2389
  msgid "General"
2390
  msgstr ""
2391
 
2392
- #: src/Tribe/Settings_Manager.php:81
2393
  msgid "Display"
2394
  msgstr ""
2395
 
2396
- #: src/Tribe/Settings_Manager.php:254
2397
  msgid "Network"
2398
  msgstr ""
2399
 
2400
- #: src/Tribe/Settings_Manager.php:288
2401
  #: src/admin-views/tribe-options-licenses.php:57
2402
  msgid "Licenses"
2403
  msgstr ""
2404
 
2405
- #: src/Tribe/Settings_Manager.php:318
2406
  msgid "Help"
2407
  msgstr ""
2408
 
@@ -2440,42 +2124,42 @@ msgid ""
2440
  "overrides is provided below."
2441
  msgstr ""
2442
 
2443
- #: src/Tribe/Support.php:182
2444
  msgid "English"
2445
  msgstr ""
2446
 
2447
- #: src/Tribe/Support.php:201 src/Tribe/Support.php:202
2448
  msgid "Unknown or not set"
2449
  msgstr ""
2450
 
2451
- #: src/Tribe/Support.php:212
2452
  msgid ""
2453
  "Rewrite rules were purged on load of this help page. Chances are there is a "
2454
  "rewrite rule flush occurring in a plugin or theme!"
2455
  msgstr ""
2456
 
2457
- #: src/Tribe/Support.php:318
2458
  msgid ""
2459
  "Yes, automatically share my system information with the Modern Tribe support "
2460
  "team"
2461
  msgstr ""
2462
 
2463
- #: src/Tribe/Support.php:319
2464
  msgid ""
2465
  "Your system information will only be used by the Modern Tribe support team. "
2466
  "All information is stored securely. We do not share this information with "
2467
  "any third parties."
2468
  msgstr ""
2469
 
2470
- #: src/Tribe/Support.php:338 src/Tribe/Support.php:343
2471
  msgid "Invalid Key"
2472
  msgstr ""
2473
 
2474
- #: src/Tribe/Support.php:371 src/Tribe/Support.php:397
2475
  msgid "Permission Error"
2476
  msgstr ""
2477
 
2478
- #: src/Tribe/Support.php:385
2479
  msgid "Unique System Info Key Generated"
2480
  msgstr ""
2481
 
@@ -2497,185 +2181,118 @@ msgctxt "non-existant function name passed for field validation"
2497
  msgid "with function name:"
2498
  msgstr ""
2499
 
2500
- #: src/Tribe/Validate.php:118 src/Tribe/Validate.php:132
2501
  msgid "%s must contain numbers and letters only"
2502
  msgstr ""
2503
 
2504
- #: src/Tribe/Validate.php:146
2505
  msgid "%s must contain numbers, letters and dots only"
2506
  msgstr ""
2507
 
2508
- #: src/Tribe/Validate.php:160
2509
  msgid "%s must contain numbers, letters, dashes and undescores only"
2510
  msgstr ""
2511
 
2512
- #: src/Tribe/Validate.php:174
2513
  msgid "%s must not be empty"
2514
  msgstr ""
2515
 
2516
- #: src/Tribe/Validate.php:188 src/Tribe/Validate.php:212
2517
  msgid "%s must be a positive number."
2518
  msgstr ""
2519
 
2520
- #: src/Tribe/Validate.php:200
2521
  msgid "%s must be a positive number or percent."
2522
  msgstr ""
2523
 
2524
- #: src/Tribe/Validate.php:230
2525
  msgid "%s must be a whole number."
2526
  msgstr ""
2527
 
2528
- #: src/Tribe/Validate.php:250
2529
  msgid "%s must be a valid slug (numbers, letters, dashes, and underscores)."
2530
  msgstr ""
2531
 
2532
- #: src/Tribe/Validate.php:263
2533
- msgid "%s must be a valid URL."
2534
  msgstr ""
2535
 
2536
- #: src/Tribe/Validate.php:277 src/Tribe/Validate.php:289
2537
- #: src/Tribe/Validate.php:302 src/Tribe/Validate.php:322
2538
  msgid "%s must have a value that's part of its options."
2539
  msgstr ""
2540
 
2541
- #: src/Tribe/Validate.php:334
2542
  msgid ""
2543
  "Comparison validation failed because no comparison value was provided, for "
2544
  "field %s"
2545
  msgstr ""
2546
 
2547
- #: src/Tribe/Validate.php:341
2548
  msgid "%s cannot be the same as %s."
2549
  msgstr ""
2550
 
2551
- #: src/Tribe/Validate.php:343
2552
  msgid "%s cannot be a duplicate"
2553
  msgstr ""
2554
 
2555
- #: src/Tribe/Validate.php:357
2556
  msgid "%s must be a number or percentage."
2557
  msgstr ""
2558
 
2559
- #: src/Tribe/Validate.php:401
2560
  msgid "%s must be a number between 0 and 21."
2561
  msgstr ""
2562
 
2563
- #: src/Tribe/Validate.php:415
2564
  msgid ""
2565
  "%s must consist of letters, numbers, dashes, apostrophes, and spaces only."
2566
  msgstr ""
2567
 
2568
- #: src/Tribe/Validate.php:429
2569
  msgid "%s must consist of letters, spaces, apostrophes, and dashes."
2570
  msgstr ""
2571
 
2572
- #: src/Tribe/Validate.php:441
2573
  msgid "%s must consist of 5 numbers."
2574
  msgstr ""
2575
 
2576
- #: src/Tribe/Validate.php:453
2577
  msgid "%s must be a phone number."
2578
  msgstr ""
2579
 
2580
- #: src/Tribe/Validate.php:467
2581
  msgid ""
2582
  "Country List must be formatted as one country per line in the following "
2583
  "format: <br>US, United States <br> UK, United Kingdom."
2584
  msgstr ""
2585
 
2586
- #: src/Tribe/Validate.php:496
2587
  msgid "%s must be an email address."
2588
  msgstr ""
2589
 
2590
- #: src/Tribe/View_Helpers.php:59
2591
  msgid "Select a Country:"
2592
  msgstr ""
2593
 
2594
- #: src/admin-views/app-shop.php:22 src/admin-views/app-shop.php:48
2595
- #: src/admin-views/app-shop.php:100 src/admin-views/app-shop.php:122
2596
- msgid "TEC Logo"
2597
  msgstr ""
2598
 
2599
- #: src/admin-views/app-shop.php:23
2600
- msgid "Add-Ons"
2601
  msgstr ""
2602
 
2603
- #: src/admin-views/app-shop.php:27
2604
- msgid "All Solutions"
2605
- msgstr ""
2606
-
2607
- #: src/admin-views/app-shop.php:28
2608
- msgid "Save with Bundles"
2609
  msgstr ""
2610
 
2611
  #: src/admin-views/app-shop.php:29
2612
- msgid "Extensions"
2613
- msgstr ""
2614
-
2615
- #: src/admin-views/app-shop.php:39
2616
- msgid "One calendar. Countless ways to make it your own."
2617
- msgstr ""
2618
-
2619
- #: src/admin-views/app-shop.php:40
2620
- msgid ""
2621
- "Calendars, ticketing, and powerful WordPress tools to manage your events "
2622
- "from start to finish."
2623
- msgstr ""
2624
-
2625
- #: src/admin-views/app-shop.php:42
2626
- msgid "Already Installed"
2627
- msgstr ""
2628
-
2629
- #: src/admin-views/app-shop.php:52
2630
- msgid "Active"
2631
- msgstr ""
2632
-
2633
- #: src/admin-views/app-shop.php:54 src/admin-views/app-shop.php:153
2634
- msgid "FREE"
2635
  msgstr ""
2636
 
2637
- #: src/admin-views/app-shop.php:79
2638
- msgid "Manage"
2639
- msgstr ""
2640
-
2641
- #: src/admin-views/app-shop.php:81
2642
- msgid "Learn More"
2643
- msgstr ""
2644
-
2645
- #: src/admin-views/app-shop.php:94
2646
- msgid "The plugins you need at one discounted price"
2647
- msgstr ""
2648
-
2649
- #: src/admin-views/app-shop.php:95
2650
- msgid ""
2651
- "We've packaged our most popular plugins into bundles jam-packed with value."
2652
- msgstr ""
2653
-
2654
- #: src/admin-views/app-shop.php:107 src/admin-views/app-shop.php:134
2655
- msgid "Save With A Bundle"
2656
- msgstr ""
2657
-
2658
- #: src/admin-views/app-shop.php:113
2659
- msgid "Includes"
2660
- msgstr ""
2661
-
2662
- #: src/admin-views/app-shop.php:147
2663
- msgid "Free extensions to power up your plugins"
2664
- msgstr ""
2665
-
2666
- #: src/admin-views/app-shop.php:148
2667
- msgid ""
2668
- "Extensions are quick solutions our team came up with to solve specific "
2669
- "issues you may need. (Just a note - extensions are not covered by our "
2670
- "support team.)"
2671
- msgstr ""
2672
-
2673
- #: src/admin-views/app-shop.php:162
2674
- msgid "Download"
2675
- msgstr ""
2676
-
2677
- #: src/admin-views/app-shop.php:166
2678
- msgid "Browse Extensions"
2679
  msgstr ""
2680
 
2681
  #: src/admin-views/event-log.php:21
@@ -2710,13 +2327,11 @@ msgid ""
2710
  msgstr ""
2711
 
2712
  #: src/admin-views/tribe-options-display.php:36
2713
- msgid "Compact Date Format"
2714
  msgstr ""
2715
 
2716
  #: src/admin-views/tribe-options-display.php:37
2717
- msgid ""
2718
- "Select the date format used for elements with minimal space, such as in "
2719
- "datepickers."
2720
  msgstr ""
2721
 
2722
  #: src/admin-views/tribe-options-general.php:10
@@ -2768,7 +2383,7 @@ msgstr ""
2768
  msgid "Debug mode"
2769
  msgstr ""
2770
 
2771
- #: src/admin-views/tribe-options-general.php:55
2772
  msgid ""
2773
  "Enable this option to log debug information. By default this will log to "
2774
  "your server PHP error log. If you'd like to see the log messages in your "
@@ -2957,11 +2572,6 @@ msgstr ""
2957
  msgid "Hide the following settings tabs on every site:"
2958
  msgstr ""
2959
 
2960
- #: src/functions/template-tags/html.php:99
2961
- msgctxt "The associated field is required."
2962
- msgid "(required)"
2963
- msgstr ""
2964
-
2965
  #: src/views/promoter/auth.php:35
2966
  msgid "Promoter would like to sync with your site"
2967
  msgstr ""
1
+ # Copyright (C) 2019 Modern Tribe
2
  # This file is distributed under the same license as the Tribe Common package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Tribe Common 4.9.10\n"
6
  "Report-Msgid-Bugs-To: http://m.tri.be/191x\n"
7
+ "POT-Creation-Date: 2019-06-05 16:13:28+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2019-06-05 16:13\n"
12
  "Last-Translator: \n"
13
  "Language-Team: \n"
14
 
28
  msgid "Return to WordPress Updates"
29
  msgstr ""
30
 
31
+ #: src/Tribe/Admin/Help_Page.php:59 src/Tribe/Customizer.php:642
 
 
 
 
 
 
 
 
 
 
 
 
32
  #: src/Tribe/Plugins_API.php:25
33
  msgid "The Events Calendar"
34
  msgstr ""
35
 
36
+ #: src/Tribe/Admin/Help_Page.php:63
37
  msgid ""
38
  "The Events Calendar is a carefully crafted, extensible plugin that lets you "
39
  "easily share your events."
40
  msgstr ""
41
 
42
+ #: src/Tribe/Admin/Help_Page.php:75 src/Tribe/Plugins_API.php:57
43
  msgid "Event Tickets"
44
  msgstr ""
45
 
46
+ #: src/Tribe/Admin/Help_Page.php:79
47
  msgid ""
48
  "Events Tickets is a carefully crafted, extensible plugin that lets you "
49
  "easily sell tickets for your events."
50
  msgstr ""
51
 
52
+ #: src/Tribe/Admin/Help_Page.php:91
53
  msgid "Advanced Post Manager"
54
  msgstr ""
55
 
56
+ #: src/Tribe/Admin/Help_Page.php:95
57
  msgid ""
58
  "Turbo charge your posts admin for any custom post type with sortable filters "
59
  "and columns, and auto-registration of metaboxes."
60
  msgstr ""
61
 
62
+ #: src/Tribe/Admin/Help_Page.php:170
63
  msgid " and "
64
  msgstr ""
65
 
66
+ #: src/Tribe/Admin/Help_Page.php:194 src/Tribe/Plugins_API.php:43
67
  msgid "Events Calendar PRO"
68
  msgstr ""
69
 
70
+ #: src/Tribe/Admin/Help_Page.php:203 src/Tribe/Plugins_API.php:121
71
  msgid "Eventbrite Tickets"
72
  msgstr ""
73
 
74
+ #: src/Tribe/Admin/Help_Page.php:211 src/Tribe/Plugins_API.php:100
75
  msgid "Community Events"
76
  msgstr ""
77
 
78
+ #: src/Tribe/Admin/Help_Page.php:219 src/Tribe/Plugins_API.php:34
79
  msgid "Event Aggregator"
80
  msgstr ""
81
 
82
+ #: src/Tribe/Admin/Help_Page.php:227 src/Tribe/Plugins_API.php:90
83
  msgid "Filter Bar"
84
  msgstr ""
85
 
86
+ #: src/Tribe/Admin/Help_Page.php:235 src/Tribe/Plugins_API.php:66
87
  msgid "Event Tickets Plus"
88
  msgstr ""
89
 
90
+ #: src/Tribe/Admin/Help_Page.php:244 src/Tribe/Plugins_API.php:110
91
  msgid "Community Tickets"
92
  msgstr ""
93
 
94
+ #: src/Tribe/Admin/Help_Page.php:405
95
  msgctxt "not available"
96
  msgid "n/a"
97
  msgstr ""
98
 
99
+ #: src/Tribe/Admin/Help_Page.php:413
100
  msgid "You need to upgrade!"
101
  msgstr ""
102
 
103
+ #: src/Tribe/Admin/Help_Page.php:413 src/Tribe/Admin/Help_Page.php:788
104
  msgid "You are up to date!"
105
  msgstr ""
106
 
107
+ #: src/Tribe/Admin/Help_Page.php:778
108
  msgid "Activate %s"
109
  msgstr ""
110
 
111
+ #: src/Tribe/Admin/Help_Page.php:778
112
  msgid "Activate Plugin"
113
  msgstr ""
114
 
115
+ #: src/Tribe/Admin/Help_Page.php:786
116
  msgid "Upgrade Plugin"
117
  msgstr ""
118
 
119
+ #: src/Tribe/Admin/Help_Page.php:802
120
  msgid "Install %s"
121
  msgstr ""
122
 
123
+ #: src/Tribe/Admin/Help_Page.php:802
124
  msgid "Install Plugin"
125
  msgstr ""
126
 
127
+ #: src/Tribe/Admin/Help_Page.php:819
128
  msgid "Latest Version:"
129
  msgstr ""
130
 
131
+ #: src/Tribe/Admin/Help_Page.php:822 src/admin-views/app-shop.php:77
132
  msgid "Requires:"
133
  msgstr ""
134
 
135
+ #: src/Tribe/Admin/Help_Page.php:823
136
  msgid "WordPress "
137
  msgstr ""
138
 
139
+ #: src/Tribe/Admin/Help_Page.php:825
140
  msgid "Active Users:"
141
  msgstr ""
142
 
143
+ #: src/Tribe/Admin/Help_Page.php:828
144
  msgid "Rating:"
145
  msgstr ""
146
 
147
+ #: src/Tribe/Admin/Help_Page.php:847
148
  msgid "Premium Add-Ons"
149
  msgstr ""
150
 
151
+ #: src/Tribe/Admin/Help_Page.php:853
152
  msgid "Plugin Active"
153
  msgstr ""
154
 
155
+ #: src/Tribe/Admin/Help_Page.php:855
156
  msgid "Plugin Inactive"
157
  msgstr ""
158
 
159
+ #: src/Tribe/Admin/Help_Page.php:860
160
  msgid "Visit the Add-on Page"
161
  msgstr ""
162
 
163
  #: src/Tribe/Admin/Notice/Php_Version.php:59
164
+ #: src/Tribe/Admin/Notice/Plugin_Download.php:117
165
  msgctxt "separator used in a list of items"
166
  msgid ", "
167
  msgstr ""
168
 
169
  #: src/Tribe/Admin/Notice/Php_Version.php:60
170
+ #: src/Tribe/Admin/Notice/Plugin_Download.php:118
171
  msgctxt "the final separator in a list of two or more items"
172
  msgid " and "
173
  msgstr ""
186
  "recommend using PHP 5.6 or above."
187
  msgstr ""
188
 
189
+ #: src/Tribe/Admin/Notice/Plugin_Download.php:101
 
 
 
 
 
 
 
 
 
190
  msgid ""
191
+ "To begin using %2$s, please install and activate the latest version of %3$s."
 
 
 
192
  msgstr ""
193
 
194
  #: src/Tribe/Ajax/Dropdown.php:38
212
  msgid "Event Add-Ons"
213
  msgstr ""
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  #: src/Tribe/Cost_Utils.php:114
216
  msgid "Free"
217
  msgstr ""
228
  msgid "Rate %1$sEvent Tickets%2$s %3$s"
229
  msgstr ""
230
 
231
+ #: src/Tribe/Customizer.php:643
232
  msgid ""
233
  "Use the following panel of your customizer to change the styling of your "
234
  "Calendar and Event pages."
267
  msgid "State"
268
  msgstr ""
269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  #: src/Tribe/Documentation/Swagger/Cost_Details_Definition_Provider.php:24
271
  msgid "The cost currency symbol"
272
  msgstr ""
384
  msgstr ""
385
 
386
  #. translators: %s: duration
387
+ #: src/Tribe/Editor/Configuration.php:88
388
  msgid "%s from now"
389
  msgstr ""
390
 
391
  #. translators: %s: duration
392
+ #: src/Tribe/Editor/Configuration.php:90
393
  msgid "%s ago"
394
  msgstr ""
395
 
396
+ #: src/Tribe/Editor/Configuration.php:94 src/Tribe/Editor/Configuration.php:97
397
  msgid "g:i a"
398
  msgstr ""
399
 
400
+ #: src/Tribe/Editor/Configuration.php:95 src/Tribe/Editor/Configuration.php:97
401
  msgid "F j, Y"
402
  msgstr ""
403
 
404
+ #: src/Tribe/Editor/Configuration.php:96
405
  msgid "F j"
406
  msgstr ""
407
 
421
  msgid "Tutorial"
422
  msgstr ""
423
 
424
+ #: src/Tribe/Extension.php:377
425
  msgid ""
426
  "Unable to run Tribe Extensions. Your website host is running PHP 5.2 or "
427
  "older, and has likely disabled or misconfigured debug_backtrace(). You, or "
429
  "debug_backtrace() for Tribe Extensions to work."
430
  msgstr ""
431
 
432
+ #: src/Tribe/Field.php:231
 
 
 
 
 
 
 
 
 
433
  msgid "Invalid field type specified"
434
  msgstr ""
435
 
436
+ #: src/Tribe/Field.php:532
437
  msgid "No radio options specified"
438
  msgstr ""
439
 
440
+ #: src/Tribe/Field.php:568
441
  msgid "No checkbox options specified"
442
  msgstr ""
443
 
444
+ #: src/Tribe/Field.php:626
445
  msgid "No select options specified"
446
  msgstr ""
447
 
777
  msgid "Gambia"
778
  msgstr ""
779
 
780
+ #: src/Tribe/Languages/Locations.php:138 src/Tribe/Languages/Locations.php:334
 
781
  msgid "Georgia"
782
  msgstr ""
783
 
966
  msgstr ""
967
 
968
  #: src/Tribe/Languages/Locations.php:185
969
+ msgid "Macedonia"
970
  msgstr ""
971
 
972
  #: src/Tribe/Languages/Locations.php:186
973
+ msgid "Madagascar"
974
  msgstr ""
975
 
976
  #: src/Tribe/Languages/Locations.php:187
977
+ msgid "Malawi"
978
  msgstr ""
979
 
980
  #: src/Tribe/Languages/Locations.php:188
981
+ msgid "Malaysia"
982
  msgstr ""
983
 
984
  #: src/Tribe/Languages/Locations.php:189
985
+ msgid "Maldives"
986
  msgstr ""
987
 
988
  #: src/Tribe/Languages/Locations.php:190
989
+ msgid "Mali"
990
  msgstr ""
991
 
992
  #: src/Tribe/Languages/Locations.php:191
993
+ msgid "Malta"
994
  msgstr ""
995
 
996
  #: src/Tribe/Languages/Locations.php:192
997
+ msgid "Marshall Islands"
998
  msgstr ""
999
 
1000
  #: src/Tribe/Languages/Locations.php:193
1001
+ msgid "Martinique"
1002
  msgstr ""
1003
 
1004
  #: src/Tribe/Languages/Locations.php:194
1005
+ msgid "Mauritania"
1006
  msgstr ""
1007
 
1008
  #: src/Tribe/Languages/Locations.php:195
1009
+ msgid "Mauritius"
1010
  msgstr ""
1011
 
1012
  #: src/Tribe/Languages/Locations.php:196
1013
+ msgid "Mayotte"
1014
  msgstr ""
1015
 
1016
  #: src/Tribe/Languages/Locations.php:197
1017
+ msgid "Mexico"
1018
  msgstr ""
1019
 
1020
  #: src/Tribe/Languages/Locations.php:198
1021
+ msgid "Micronesia, Federated States of"
1022
  msgstr ""
1023
 
1024
  #: src/Tribe/Languages/Locations.php:199
1025
+ msgid "Moldova, Republic of"
1026
  msgstr ""
1027
 
1028
  #: src/Tribe/Languages/Locations.php:200
1029
+ msgid "Monaco"
1030
  msgstr ""
1031
 
1032
  #: src/Tribe/Languages/Locations.php:201
1033
+ msgid "Mongolia"
1034
  msgstr ""
1035
 
1036
  #: src/Tribe/Languages/Locations.php:202
1037
+ msgid "Montenegro"
1038
  msgstr ""
1039
 
1040
  #: src/Tribe/Languages/Locations.php:203
1041
+ msgid "Montserrat"
1042
  msgstr ""
1043
 
1044
  #: src/Tribe/Languages/Locations.php:204
1045
+ msgid "Morocco"
1046
  msgstr ""
1047
 
1048
  #: src/Tribe/Languages/Locations.php:205
1049
+ msgid "Mozambique"
1050
  msgstr ""
1051
 
1052
  #: src/Tribe/Languages/Locations.php:206
1053
+ msgid "Myanmar"
1054
  msgstr ""
1055
 
1056
  #: src/Tribe/Languages/Locations.php:207
1057
+ msgid "Namibia"
1058
  msgstr ""
1059
 
1060
  #: src/Tribe/Languages/Locations.php:208
1061
+ msgid "Nauru"
1062
  msgstr ""
1063
 
1064
  #: src/Tribe/Languages/Locations.php:209
1065
+ msgid "Nepal"
1066
  msgstr ""
1067
 
1068
  #: src/Tribe/Languages/Locations.php:210
1069
+ msgid "Netherlands"
1070
  msgstr ""
1071
 
1072
  #: src/Tribe/Languages/Locations.php:211
1073
+ msgid "New Caledonia"
1074
  msgstr ""
1075
 
1076
  #: src/Tribe/Languages/Locations.php:212
1077
+ msgid "New Zealand"
1078
  msgstr ""
1079
 
1080
  #: src/Tribe/Languages/Locations.php:213
1081
+ msgid "Nicaragua"
1082
  msgstr ""
1083
 
1084
  #: src/Tribe/Languages/Locations.php:214
1085
+ msgid "Niger"
1086
  msgstr ""
1087
 
1088
  #: src/Tribe/Languages/Locations.php:215
1089
+ msgid "Nigeria"
1090
  msgstr ""
1091
 
1092
  #: src/Tribe/Languages/Locations.php:216
1093
+ msgid "Niue"
1094
  msgstr ""
1095
 
1096
  #: src/Tribe/Languages/Locations.php:217
1097
+ msgid "Norfolk Island"
1098
  msgstr ""
1099
 
1100
  #: src/Tribe/Languages/Locations.php:218
1457
  msgid "Florida"
1458
  msgstr ""
1459
 
 
 
 
 
 
1460
  #: src/Tribe/Languages/Locations.php:335
1461
  msgid "Hawaii"
1462
  msgstr ""
1617
  msgid "Wyoming"
1618
  msgstr ""
1619
 
 
 
 
 
1620
  #: src/Tribe/Log/Admin.php:133
1621
  msgctxt "log selector"
1622
  msgid "None currently available"
1635
  msgid "Null logger (will log nothing)"
1636
  msgstr ""
1637
 
1638
+ #: src/Tribe/Log.php:286
1639
  msgid "Cannot set %s as the current logging engine"
1640
  msgstr ""
1641
 
1642
+ #: src/Tribe/Log.php:385
1643
  msgid "Disabled"
1644
  msgstr ""
1645
 
1646
+ #: src/Tribe/Log.php:386
1647
  msgid "Only errors"
1648
  msgstr ""
1649
 
1650
+ #: src/Tribe/Log.php:387
1651
  msgid "Warnings and errors"
1652
  msgstr ""
1653
 
1654
+ #: src/Tribe/Log.php:388
1655
  msgid "Full debug (all events)"
1656
  msgstr ""
1657
 
1658
+ #: src/Tribe/Main.php:266
1659
  msgid ": activate to sort column ascending"
1660
  msgstr ""
1661
 
1662
+ #: src/Tribe/Main.php:267
1663
  msgid ": activate to sort column descending"
1664
  msgstr ""
1665
 
1666
+ #: src/Tribe/Main.php:269
1667
  msgid "Show _MENU_ entries"
1668
  msgstr ""
1669
 
1670
+ #: src/Tribe/Main.php:270
1671
  msgid "No data available in table"
1672
  msgstr ""
1673
 
1674
+ #: src/Tribe/Main.php:271
1675
  msgid "Showing _START_ to _END_ of _TOTAL_ entries"
1676
  msgstr ""
1677
 
1678
+ #: src/Tribe/Main.php:272
1679
  msgid "Showing 0 to 0 of 0 entries"
1680
  msgstr ""
1681
 
1682
+ #: src/Tribe/Main.php:273
1683
  msgid "(filtered from _MAX_ total entries)"
1684
  msgstr ""
1685
 
1686
+ #: src/Tribe/Main.php:274
1687
  msgid "No matching records found"
1688
  msgstr ""
1689
 
1690
+ #: src/Tribe/Main.php:275
1691
  msgid "Search:"
1692
  msgstr ""
1693
 
1694
+ #: src/Tribe/Main.php:276
1695
  msgid "All items on this page were selected. "
1696
  msgstr ""
1697
 
1698
+ #: src/Tribe/Main.php:277
1699
  msgid "Select all pages"
1700
  msgstr ""
1701
 
1702
+ #: src/Tribe/Main.php:278
1703
  msgid "Clear Selection."
1704
  msgstr ""
1705
 
1706
+ #: src/Tribe/Main.php:280
1707
  msgid "All"
1708
  msgstr ""
1709
 
1710
+ #: src/Tribe/Main.php:281 src/Tribe/Main.php:298
1711
  msgid "Next"
1712
  msgstr ""
1713
 
1714
+ #: src/Tribe/Main.php:282
1715
  msgid "Previous"
1716
  msgstr ""
1717
 
1718
+ #: src/Tribe/Main.php:287
1719
  msgid ": Selected %d rows"
1720
  msgstr ""
1721
 
1722
+ #: src/Tribe/Main.php:288
1723
  msgid ": Selected 1 row"
1724
  msgstr ""
1725
 
1726
+ #: src/Tribe/Main.php:299
1727
  msgid "Prev"
1728
  msgstr ""
1729
 
1730
+ #: src/Tribe/Main.php:300 src/Tribe/Main.php:302
1731
  msgid "Today"
1732
  msgstr ""
1733
 
1734
+ #: src/Tribe/Main.php:301
1735
  msgid "Done"
1736
  msgstr ""
1737
 
1738
+ #: src/Tribe/Main.php:303
1739
  msgid "Clear"
1740
  msgstr ""
1741
 
1742
+ #: src/Tribe/Main.php:309 src/admin-views/tribe-options-help.php:50
1743
+ msgid "Copy to clipboard"
1744
+ msgstr ""
1745
+
1746
+ #: src/Tribe/Main.php:310
1747
+ msgid "System info copied"
1748
+ msgstr ""
1749
+
1750
+ #: src/Tribe/Main.php:311
1751
+ msgid "Press \"Cmd + C\" to copy"
1752
+ msgstr ""
1753
+
1754
  #: src/Tribe/PUE/Checker.php:497
1755
  msgid "A valid license key is required for support and updates"
1756
  msgstr ""
1786
  msgid "License key(s) updated."
1787
  msgstr ""
1788
 
1789
+ #: src/Tribe/PUE/Checker.php:899
1790
  msgid ""
1791
  "Hmmm... something's wrong with this validator. Please contact %ssupport%s."
1792
  msgstr ""
1793
 
1794
+ #: src/Tribe/PUE/Checker.php:912
1795
  msgid "unknown date"
1796
  msgstr ""
1797
 
1798
+ #: src/Tribe/PUE/Checker.php:918
1799
  msgid "Sorry, key validation server is not available."
1800
  msgstr ""
1801
 
1802
+ #: src/Tribe/PUE/Checker.php:938
1803
  msgid "Valid Key! Expires on %s"
1804
  msgstr ""
1805
 
1806
+ #: src/Tribe/PUE/Checker.php:943
1807
  msgid "Thanks for setting up a valid key. It will expire on %s"
1808
  msgstr ""
1809
 
1810
+ #: src/Tribe/PUE/Checker.php:970 src/Tribe/PUE/Notices.php:337
1811
  msgid "Renew Your License Now"
1812
  msgstr ""
1813
 
1814
+ #: src/Tribe/PUE/Checker.php:972 src/Tribe/PUE/Notices.php:339
1815
  msgid " (opens in a new window)"
1816
  msgstr ""
1817
 
1818
+ #: src/Tribe/PUE/Checker.php:987
1819
  msgid "Please refresh the page and try your request again."
1820
  msgstr ""
1821
 
1822
+ #: src/Tribe/PUE/Checker.php:1008
1823
  msgid ""
1824
  "There is an update for %s. You'll need to %scheck your license%s to have "
1825
  "access to updates, downloads, and support."
1826
  msgstr ""
1827
 
1828
+ #: src/Tribe/PUE/Checker.php:1065
1829
  msgid ""
1830
  "There is an update for %s. %sRenew your license%s to get access to bug "
1831
  "fixes, security updates, and new features."
1832
  msgstr ""
1833
 
1834
+ #: src/Tribe/PUE/Checker.php:1095
1835
  msgid "Update now to version %s."
1836
  msgstr ""
1837
 
1838
+ #: src/Tribe/PUE/Checker.php:1106
1839
  msgid "There is a new version of %1$s available. %2$s"
1840
  msgstr ""
1841
 
1842
+ #: src/Tribe/PUE/Checker.php:1686
1843
  msgid "A valid license has been entered by your network administrator."
1844
  msgstr ""
1845
 
1846
+ #: src/Tribe/PUE/Checker.php:1687
1847
  msgid "No license entered. Consult your network administrator."
1848
  msgstr ""
1849
 
1850
+ #: src/Tribe/PUE/Checker.php:1688
1851
  msgid "Expired license. Consult your network administrator."
1852
  msgstr ""
1853
 
1854
+ #: src/Tribe/PUE/Notices.php:279
1855
  msgid ""
1856
  "It looks like you're using %1$s, but the license key is invalid. Please "
1857
  "download the latest version %2$sfrom your account%3$s."
1861
  msgstr[0] ""
1862
  msgstr[1] ""
1863
 
1864
+ #: src/Tribe/PUE/Notices.php:324
1865
  msgid ""
1866
  "There is an update available for %1$s but your license has expired. "
1867
  "%2$sVisit the Events Calendar website to renew your license.%3$s"
1871
  msgstr[0] ""
1872
  msgstr[1] ""
1873
 
1874
+ #: src/Tribe/PUE/Notices.php:356
1875
  msgid ""
1876
  "You have a license key for %1$s but the key is out of installs. %2$sVisit "
1877
  "the Events Calendar website%3$s to manage your installs, upgrade your "
1883
  msgstr[0] ""
1884
  msgstr[1] ""
1885
 
1886
+ #: src/Tribe/PUE/Notices.php:398
1887
  msgid ""
1888
  "You can always check the status of your licenses by logging in to %1$syour "
1889
  "account on theeventscalendar.com%2$s."
1890
  msgstr ""
1891
 
1892
+ #: src/Tribe/PUE/Notices.php:441
1893
  msgctxt "formatted plugin list"
1894
  msgid "%1$s and %2$s"
1895
  msgstr ""
1896
 
1897
+ #: src/Tribe/Plugins_API.php:28
1898
  msgid ""
1899
+ "Create an events calendar and manage it with ease. The Events Calendar "
1900
+ "plugin provides professional-level quality and features backed by a team you "
1901
+ "can trust."
1902
  msgstr ""
1903
 
1904
+ #: src/Tribe/Plugins_API.php:37
1905
  msgid ""
1906
+ "Event Aggregator adds massive import functionality to your calendar. Before "
1907
+ "you know it, you’ll be importing events from Meetup, Eventbrite, Google "
1908
+ "Calendar, iCalendar, and other URLs with ease. Schedule imports to run "
1909
+ "automatically behind-the-scenes or run them manually when you’re ready. Go "
1910
+ "ahead and import to your heart’s content—Event Aggregator hooks you up with "
1911
+ "a central dashboard in the admin to make managing your imports a breeze."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1912
  msgstr ""
1913
 
1914
+ #: src/Tribe/Plugins_API.php:48
1915
+ msgid ""
1916
+ "The Events Calendar PRO is a paid Add-On to our open source WordPress plugin "
1917
+ "%1$sThe Events Calendar%2$s. PRO offers a whole host of calendar features "
1918
+ "including recurring events, custom event attributes, saved venues and "
1919
+ "organizers, venue pages, advanced event admin and lots more."
1920
  msgstr ""
1921
 
1922
+ #: src/Tribe/Plugins_API.php:60
1923
+ msgid ""
1924
+ "Event Tickets provides a simple way for visitors to RSVP to your events. As "
1925
+ "a standalone plugin, it enables you to add RSVP functionality to posts or "
1926
+ "pages. When paired with The Events Calendar, you can add that same RSVP "
1927
+ "functionality directly to your event listings."
1928
  msgstr ""
1929
 
1930
+ #: src/Tribe/Plugins_API.php:71
1931
+ msgid ""
1932
+ "Event Tickets Plus allows you to sell tickets to your events using "
1933
+ "WooCommerce, Easy Digital Downloads, or our built in Tribe Commerce tool. "
1934
+ "Add tickets to your posts and pages, or add %1$sThe Events Calendar%2$s and "
1935
+ "sell tickets from your event listings. Create custom registration forms, "
1936
+ "manage attendees, use custom capacity options, and more. Guest check in is "
1937
+ "easy with QR codes and our custom scanning app."
1938
  msgstr ""
1939
 
1940
+ #: src/Tribe/Plugins_API.php:80 src/Tribe/Promoter/PUE.php:28
1941
  #: src/views/promoter/auth.php:30 src/views/promoter/auth.php:74
1942
  msgid "Promoter"
1943
  msgstr ""
1944
 
1945
+ #: src/Tribe/Plugins_API.php:84
1946
+ msgid ""
1947
+ "With Promoter, you’ll connect with your community via email through every "
1948
+ "stage of your event, bolster event attendance, and manage notifications more "
1949
+ "efficiently than ever. Increase event attendance and engagement by "
1950
+ "automatically sending reminders for on-sale dates, event times and more."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1951
  msgstr ""
1952
 
1953
+ #: src/Tribe/Plugins_API.php:94
1954
+ msgid ""
1955
+ "It is awesome that your calendar is <em>THE PLACE</em> to get hooked up with "
1956
+ "prime choice ways to spend time. You have more events than Jabba the Hutt "
1957
+ "has rolls. Too bad visitors are hiring a personal assistant to go through "
1958
+ "all the choices. Ever wish you could just filter the calendar to only show "
1959
+ "events in walking distance, on a weekend, that are free? BOOM. Now you can. "
1960
+ "Introducing… the Filter Bar."
1961
  msgstr ""
1962
 
1963
+ #: src/Tribe/Plugins_API.php:104
1964
+ msgid ""
1965
+ "Accept user-submitted events on your site! With Community Events, you can "
1966
+ "accept public submissions or require account sign-on. Settings give you the "
1967
+ "options to save as a draft or publish automatically, enable categories and "
1968
+ "tags, and choose whether users can edit/manage their own events or simply "
1969
+ "submit. Best of all - setup is easy! Just activate, configure the options, "
1970
+ "and off you go."
1971
  msgstr ""
1972
 
1973
+ #: src/Tribe/Plugins_API.php:114
1974
+ msgid ""
1975
+ "Enable Community Events organizers to offer tickets to their events. You can "
1976
+ "set flexible payment and fee options. They can even check-in attendees to "
1977
+ "their events! All of this managed from the front-end of your site without "
1978
+ "ever needing to grant access to your admin"
1979
  msgstr ""
1980
 
1981
+ #: src/Tribe/Plugins_API.php:115
1982
  msgctxt "Names of required plugins for Community Tickets"
1983
  msgid "Event Tickets Plus and Community Events"
1984
  msgstr ""
1985
 
1986
+ #: src/Tribe/Plugins_API.php:126
1987
+ msgid ""
1988
+ "The Eventbrite Tickets add-on allows you to create & sell tickets through "
1989
+ "The Events Calendar using the power of %1$sEventbrite%2$s. Whether you’re "
1990
+ "creating your ticket on the WordPress dashboard or importing the details of "
1991
+ "an already-existing event from %1$sEventbrite.com%2$s, this add-on brings "
1992
+ "the power of the Eventbrite API to your calendar."
 
 
 
 
 
 
 
 
 
 
 
1993
  msgstr ""
1994
 
1995
+ #: src/Tribe/Plugins_API.php:135
1996
  msgid "Image Widget Plus"
1997
  msgstr ""
1998
 
1999
+ #: src/Tribe/Plugins_API.php:139
2000
+ msgid ""
2001
+ "Take your image widgets to the next level with Image Widget Plus! We've "
2002
+ "taken the simple functionality of our basic Image Widget and amped it up "
2003
+ "with several popular feature requests - multiple image support, slideshow, "
2004
+ "lightbox, and random image - all backed by a full year of premium support."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2005
  msgstr ""
2006
 
2007
  #: src/Tribe/Process/Queue.php:1024
2025
  msgid "Events Help"
2026
  msgstr ""
2027
 
2028
+ #: src/Tribe/Settings.php:349
2029
  msgid "%s Settings"
2030
  msgstr ""
2031
 
2032
+ #: src/Tribe/Settings.php:363
2033
  msgid "You've requested a non-existent tab."
2034
  msgstr ""
2035
 
2036
+ #: src/Tribe/Settings.php:371
2037
  msgid "Save Changes"
2038
  msgstr ""
2039
 
2040
+ #: src/Tribe/Settings.php:419
2041
  msgid "You don't have permission to do that."
2042
  msgstr ""
2043
 
2044
+ #: src/Tribe/Settings.php:425
2045
  msgid "The request was sent insecurely."
2046
  msgstr ""
2047
 
2048
+ #: src/Tribe/Settings.php:431
2049
  msgid "The request wasn't sent from this tab."
2050
  msgstr ""
2051
 
2052
+ #: src/Tribe/Settings.php:608
2053
  msgid "Your form had the following errors:"
2054
  msgstr ""
2055
 
2056
+ #: src/Tribe/Settings.php:618
2057
  msgid "None of your settings were saved. Please try again."
2058
  msgstr ""
2059
 
2060
+ #: src/Tribe/Settings.php:619
2061
  msgid ""
2062
  "The above setting was not saved. Other settings were successfully saved."
2063
  msgid_plural ""
2065
  msgstr[0] ""
2066
  msgstr[1] ""
2067
 
2068
+ #: src/Tribe/Settings.php:641
2069
  msgid "Settings saved."
2070
  msgstr ""
2071
 
2072
+ #: src/Tribe/Settings_Manager.php:54
2073
  msgid "General"
2074
  msgstr ""
2075
 
2076
+ #: src/Tribe/Settings_Manager.php:55
2077
  msgid "Display"
2078
  msgstr ""
2079
 
2080
+ #: src/Tribe/Settings_Manager.php:219
2081
  msgid "Network"
2082
  msgstr ""
2083
 
2084
+ #: src/Tribe/Settings_Manager.php:253
2085
  #: src/admin-views/tribe-options-licenses.php:57
2086
  msgid "Licenses"
2087
  msgstr ""
2088
 
2089
+ #: src/Tribe/Settings_Manager.php:279
2090
  msgid "Help"
2091
  msgstr ""
2092
 
2124
  "overrides is provided below."
2125
  msgstr ""
2126
 
2127
+ #: src/Tribe/Support.php:171
2128
  msgid "English"
2129
  msgstr ""
2130
 
2131
+ #: src/Tribe/Support.php:188 src/Tribe/Support.php:189
2132
  msgid "Unknown or not set"
2133
  msgstr ""
2134
 
2135
+ #: src/Tribe/Support.php:199
2136
  msgid ""
2137
  "Rewrite rules were purged on load of this help page. Chances are there is a "
2138
  "rewrite rule flush occurring in a plugin or theme!"
2139
  msgstr ""
2140
 
2141
+ #: src/Tribe/Support.php:305
2142
  msgid ""
2143
  "Yes, automatically share my system information with the Modern Tribe support "
2144
  "team"
2145
  msgstr ""
2146
 
2147
+ #: src/Tribe/Support.php:306
2148
  msgid ""
2149
  "Your system information will only be used by the Modern Tribe support team. "
2150
  "All information is stored securely. We do not share this information with "
2151
  "any third parties."
2152
  msgstr ""
2153
 
2154
+ #: src/Tribe/Support.php:325 src/Tribe/Support.php:330
2155
  msgid "Invalid Key"
2156
  msgstr ""
2157
 
2158
+ #: src/Tribe/Support.php:358 src/Tribe/Support.php:384
2159
  msgid "Permission Error"
2160
  msgstr ""
2161
 
2162
+ #: src/Tribe/Support.php:372
2163
  msgid "Unique System Info Key Generated"
2164
  msgstr ""
2165
 
2181
  msgid "with function name:"
2182
  msgstr ""
2183
 
2184
+ #: src/Tribe/Validate.php:120 src/Tribe/Validate.php:136
2185
  msgid "%s must contain numbers and letters only"
2186
  msgstr ""
2187
 
2188
+ #: src/Tribe/Validate.php:152
2189
  msgid "%s must contain numbers, letters and dots only"
2190
  msgstr ""
2191
 
2192
+ #: src/Tribe/Validate.php:168
2193
  msgid "%s must contain numbers, letters, dashes and undescores only"
2194
  msgstr ""
2195
 
2196
+ #: src/Tribe/Validate.php:184
2197
  msgid "%s must not be empty"
2198
  msgstr ""
2199
 
2200
+ #: src/Tribe/Validate.php:200 src/Tribe/Validate.php:228
2201
  msgid "%s must be a positive number."
2202
  msgstr ""
2203
 
2204
+ #: src/Tribe/Validate.php:214
2205
  msgid "%s must be a positive number or percent."
2206
  msgstr ""
2207
 
2208
+ #: src/Tribe/Validate.php:248
2209
  msgid "%s must be a whole number."
2210
  msgstr ""
2211
 
2212
+ #: src/Tribe/Validate.php:267
2213
  msgid "%s must be a valid slug (numbers, letters, dashes, and underscores)."
2214
  msgstr ""
2215
 
2216
+ #: src/Tribe/Validate.php:282
2217
+ msgid "%s must be a valid absolute URL."
2218
  msgstr ""
2219
 
2220
+ #: src/Tribe/Validate.php:298 src/Tribe/Validate.php:310
2221
+ #: src/Tribe/Validate.php:323 src/Tribe/Validate.php:345
2222
  msgid "%s must have a value that's part of its options."
2223
  msgstr ""
2224
 
2225
+ #: src/Tribe/Validate.php:359
2226
  msgid ""
2227
  "Comparison validation failed because no comparison value was provided, for "
2228
  "field %s"
2229
  msgstr ""
2230
 
2231
+ #: src/Tribe/Validate.php:366
2232
  msgid "%s cannot be the same as %s."
2233
  msgstr ""
2234
 
2235
+ #: src/Tribe/Validate.php:368
2236
  msgid "%s cannot be a duplicate"
2237
  msgstr ""
2238
 
2239
+ #: src/Tribe/Validate.php:384
2240
  msgid "%s must be a number or percentage."
2241
  msgstr ""
2242
 
2243
+ #: src/Tribe/Validate.php:438
2244
  msgid "%s must be a number between 0 and 21."
2245
  msgstr ""
2246
 
2247
+ #: src/Tribe/Validate.php:454
2248
  msgid ""
2249
  "%s must consist of letters, numbers, dashes, apostrophes, and spaces only."
2250
  msgstr ""
2251
 
2252
+ #: src/Tribe/Validate.php:470
2253
  msgid "%s must consist of letters, spaces, apostrophes, and dashes."
2254
  msgstr ""
2255
 
2256
+ #: src/Tribe/Validate.php:484
2257
  msgid "%s must consist of 5 numbers."
2258
  msgstr ""
2259
 
2260
+ #: src/Tribe/Validate.php:498
2261
  msgid "%s must be a phone number."
2262
  msgstr ""
2263
 
2264
+ #: src/Tribe/Validate.php:514
2265
  msgid ""
2266
  "Country List must be formatted as one country per line in the following "
2267
  "format: <br>US, United States <br> UK, United Kingdom."
2268
  msgstr ""
2269
 
2270
+ #: src/Tribe/Validate.php:545
2271
  msgid "%s must be an email address."
2272
  msgstr ""
2273
 
2274
+ #: src/Tribe/View_Helpers.php:51
2275
  msgid "Select a Country:"
2276
  msgstr ""
2277
 
2278
+ #: src/admin-views/app-shop.php:4
2279
+ msgid "Events Add-Ons"
 
2280
  msgstr ""
2281
 
2282
+ #: src/admin-views/app-shop.php:5
2283
+ msgid "Browse All Add-Ons"
2284
  msgstr ""
2285
 
2286
+ #: src/admin-views/app-shop.php:26
2287
+ msgid "Buy This Add-On"
 
 
 
 
2288
  msgstr ""
2289
 
2290
  #: src/admin-views/app-shop.php:29
2291
+ msgid "Installed Add-Ons"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2292
  msgstr ""
2293
 
2294
+ #: src/admin-views/app-shop.php:31
2295
+ msgid "Installed"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2296
  msgstr ""
2297
 
2298
  #: src/admin-views/event-log.php:21
2327
  msgstr ""
2328
 
2329
  #: src/admin-views/tribe-options-display.php:36
2330
+ msgid "Datepicker Date Format"
2331
  msgstr ""
2332
 
2333
  #: src/admin-views/tribe-options-display.php:37
2334
+ msgid "Select the date format to use in datepickers"
 
 
2335
  msgstr ""
2336
 
2337
  #: src/admin-views/tribe-options-general.php:10
2383
  msgid "Debug mode"
2384
  msgstr ""
2385
 
2386
+ #: src/admin-views/tribe-options-general.php:59
2387
  msgid ""
2388
  "Enable this option to log debug information. By default this will log to "
2389
  "your server PHP error log. If you'd like to see the log messages in your "
2572
  msgid "Hide the following settings tabs on every site:"
2573
  msgstr ""
2574
 
 
 
 
 
 
2575
  #: src/views/promoter/auth.php:35
2576
  msgid "Promoter would like to sync with your site"
2577
  msgstr ""
common/src/Tribe/Abstract_Plugin_Register.php CHANGED
@@ -17,42 +17,23 @@ abstract class Tribe__Abstract_Plugin_Register {
17
  * @var string
18
  */
19
  protected $base_dir;
20
-
21
- /**
22
- * @var string
23
- */
24
  protected $main_class;
25
-
26
- /**
27
- * @var string
28
- */
29
  protected $version;
30
-
31
- /**
32
- * @since 4.9.17
33
- *
34
- * @var array
35
- */
36
- protected $classes_req = [];
37
-
38
- /**
39
- * @var array
40
- */
41
- protected $dependencies = [
42
- 'parent-dependencies' => [],
43
- 'co-dependencies' => [],
44
- 'addon-dependencies' => [],
45
- ];
46
 
47
  /**
48
  * Registers a plugin with dependencies
49
  */
50
  public function register_plugin() {
51
- tribe_register_plugin(
52
  $this->base_dir,
53
  $this->main_class,
54
  $this->version,
55
- $this->classes_req,
56
  $this->dependencies
57
  );
58
  }
@@ -62,11 +43,8 @@ abstract class Tribe__Abstract_Plugin_Register {
62
  *
63
  * This is basically an aliased function - register_plugins, upon
64
  * second calling, returns whether or not a plugin should load.
65
- *
66
- * @deprecated since 4.9.17 It is unused by any Tribe plugins and returned void.
67
- * @todo remove in 4.11
68
  */
69
  public function has_valid_dependencies() {
70
- _deprecated_function( __METHOD__, '4.9.17' );
71
  }
72
  }
17
  * @var string
18
  */
19
  protected $base_dir;
 
 
 
 
20
  protected $main_class;
 
 
 
 
21
  protected $version;
22
+ protected $dependencies = array(
23
+ 'parent-dependencies' => array(),
24
+ 'co-dependencies' => array(),
25
+ 'addon-dependencies' => array(),
26
+ );
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  /**
29
  * Registers a plugin with dependencies
30
  */
31
  public function register_plugin() {
32
+ return tribe_register_plugin(
33
  $this->base_dir,
34
  $this->main_class,
35
  $this->version,
36
+ array(),
37
  $this->dependencies
38
  );
39
  }
43
  *
44
  * This is basically an aliased function - register_plugins, upon
45
  * second calling, returns whether or not a plugin should load.
 
 
 
46
  */
47
  public function has_valid_dependencies() {
48
+ return $this->register_plugin();
49
  }
50
  }
common/src/Tribe/Admin/Help_Page.php CHANGED
@@ -12,13 +12,23 @@ if ( ! defined( 'ABSPATH' ) ) {
12
  *
13
  */
14
  class Tribe__Admin__Help_Page {
 
 
 
 
 
 
15
  /**
16
  * Static Singleton Factory Method
17
  *
18
  * @return Tribe__Admin__Help_Page
19
  */
20
  public static function instance() {
21
- return tribe( static::class );
 
 
 
 
22
  }
23
 
24
  /**
@@ -32,36 +42,6 @@ class Tribe__Admin__Help_Page {
32
  return Tribe__Admin__Helpers::instance()->is_screen( 'tribe_events_page_tribe-help' ) || Tribe__Admin__Helpers::instance()->is_screen( 'settings_page_tribe-common-help-network' );
33
  }
34
 
35
- /**
36
- * Register the Admin assets for the help page
37
- *
38
- * @since 4.9.12
39
- *
40
- * @return void
41
- */
42
- public function register_assets() {
43
- $plugin = Tribe__Main::instance();
44
- tribe_asset(
45
- $plugin,
46
- 'tribe-admin-help-page',
47
- 'admin/help-page.js',
48
- [ 'tribe-clipboard', 'tribe-common' ],
49
- 'admin_enqueue_scripts',
50
- [
51
- 'conditionals' => [ $this, 'is_current_page' ],
52
- 'localize' => [
53
- 'name' => 'tribe_system_info',
54
- 'data' => [
55
- 'sysinfo_optin_nonce' => wp_create_nonce( 'sysinfo_optin_nonce' ),
56
- 'clipboard_btn_text' => __( 'Copy to clipboard', 'tribe-common' ),
57
- 'clipboard_copied_text' => __( 'System info copied', 'tribe-common' ),
58
- 'clipboard_fail_text' => __( 'Press "Cmd + C" to copy', 'tribe-common' ),
59
- ],
60
- ],
61
- ]
62
- );
63
- }
64
-
65
  /**
66
  * Get the list of plugins
67
  *
12
  *
13
  */
14
  class Tribe__Admin__Help_Page {
15
+ /**
16
+ * Static Singleton Holder
17
+ * @var Tribe__Admin__Help_Page|null
18
+ */
19
+ protected static $instance;
20
+
21
  /**
22
  * Static Singleton Factory Method
23
  *
24
  * @return Tribe__Admin__Help_Page
25
  */
26
  public static function instance() {
27
+ if ( ! isset( self::$instance ) ) {
28
+ self::$instance = new self;
29
+ }
30
+
31
+ return self::$instance;
32
  }
33
 
34
  /**
42
  return Tribe__Admin__Helpers::instance()->is_screen( 'tribe_events_page_tribe-help' ) || Tribe__Admin__Helpers::instance()->is_screen( 'settings_page_tribe-common-help-network' );
43
  }
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  /**
46
  * Get the list of plugins
47
  *
common/src/Tribe/Admin/Notice/Plugin_Download.php CHANGED
@@ -26,33 +26,25 @@ class Tribe__Admin__Notice__Plugin_Download {
26
  *
27
  * @since 4.8.3 Method introduced.
28
  * @since 4.9 Added $version and $addon parameters.
29
- * @since 4.9.12 Add $has_pue_notice param
30
- * @since 4.9.17 Appended "+" to all version numbers to indicate "or any later version".
31
  *
32
- * @param string $name Name of the required plugin
33
- * @param null $thickbox_url Download or purchase URL for plugin from within /wp-admin/ thickbox
34
- * @param bool $is_active Indicates if the plugin is installed and active or not
35
- * @param string $version Optional version number of the required plugin
36
- * @param bool $addon Indicates if the plugin is an add-on for The Events Calendar or Event Tickets
37
- * @param bool $has_pue_notice Indicates that we need to change the messaging due to expired key.
38
  */
39
- public function add_required_plugin( $name, $thickbox_url = null, $is_active = null, $version = null, $addon = false, $has_pue_notice = false ) {
40
- $this->plugins_required[ $name ] = [
41
- 'name' => $name,
42
- 'thickbox_url' => $thickbox_url,
43
- 'is_active' => $is_active,
44
- 'version' => $version ? $version . '+' : null,
45
- 'addon' => $addon,
46
- 'has_pue_notice' => $has_pue_notice,
47
- ];
48
  }
49
 
50
  /**
51
  * Echoes the admin notice, attach to admin_notices
52
- *
53
- * @see \Tribe__Admin__Notice__Plugin_Download::add_required_plugin()
54
- *
55
- * @since 4.9.17 Altered the notice to remove "latest version" verbiage since "+" is now added to the version numbers.
56
  */
57
  public function show_inactive_plugins_alert() {
58
  if ( ! current_user_can( 'activate_plugins' ) ) {
@@ -66,13 +58,6 @@ class Tribe__Admin__Notice__Plugin_Download {
66
  return;
67
  }
68
 
69
- // Make sure Thickbox is available and consistent appearance regardless of which admin page we're on
70
- wp_enqueue_style( 'plugin-install' );
71
- wp_enqueue_script( 'plugin-install' );
72
- add_thickbox();
73
-
74
- $has_pue_notices = false;
75
-
76
  foreach ( $this->plugins_required as $req_plugin ) {
77
  $item = $req_plugin['name'];
78
  $version = empty( $req_plugin['version'] ) ? '' : ' (' . str_replace( '-dev', '', $req_plugin['version'] ) . ')';
@@ -99,11 +84,6 @@ class Tribe__Admin__Notice__Plugin_Download {
99
  }
100
 
101
  $req_plugins[] = $item;
102
-
103
- // If any of the items has PUE notice we will warn the user.
104
- if ( $req_plugin['has_pue_notice'] ) {
105
- $has_pue_notices = true;
106
- }
107
  }
108
 
109
  // If empty then add in the default name.
@@ -116,30 +96,18 @@ class Tribe__Admin__Notice__Plugin_Download {
116
  'a' => array( 'href' => array() ),
117
  );
118
 
119
- $plugin_names_clean_text = wp_kses( $this->implode_with_grammar( $plugin_name ), $allowed_html );
120
- $req_plugin_names_clean_text = wp_kses( $this->implode_with_grammar( $req_plugins ), $allowed_html );
121
-
122
- $notice_html_content = '<p>' . esc_html__( 'To begin using %2$s, please install and activate %3$s.', 'tribe-common' ) . '</p>';
123
-
124
- $read_more_link = '<a href="http://m.tri.be/1aev" target="_blank">' . esc_html__( 'Read more', 'tribe-common' ) . '.</a>';
125
- $pue_notice_text = esc_html__( 'There’s a new version of %1$s available, but your license is expired. You’ll need to renew your license to get access to the latest version. If you plan to continue using your current version of the plugin(s), be sure to use a compatible version of The Events Calendar. %2$s', 'tribe-common' );
126
- $pue_notice_html = '<p>' . sprintf( $pue_notice_text, $plugin_names_clean_text, $read_more_link ) . '</p>';
127
-
128
  printf(
129
- '<div class="error tribe-notice tribe-dependency-error" data-plugin="%1$s">'
130
- . $notice_html_content
131
- . ( $has_pue_notices ? $pue_notice_html : '' )
132
- . '</div>',
133
  esc_attr( sanitize_title( $plugin_data['Name'] ) ),
134
- $plugin_names_clean_text,
135
- $req_plugin_names_clean_text
136
  );
137
  }
138
 
139
  /**
140
- * Implodes a list of items with proper grammar.
141
- *
142
- * If only 1 item, no grammar. If 2 items, just conjunction. If 3+ items, commas with conjunction.
143
  *
144
  * @param array $items List of items to implode
145
  *
@@ -151,13 +119,7 @@ class Tribe__Admin__Notice__Plugin_Download {
151
  $output = $last_item = array_pop( $items );
152
 
153
  if ( $items ) {
154
- $output = implode( $separator, $items );
155
-
156
- if ( 1 < count( $items ) ) {
157
- $output .= $separator;
158
- }
159
-
160
- $output .= $conjunction . $last_item;
161
  }
162
 
163
  return $output;
26
  *
27
  * @since 4.8.3 Method introduced.
28
  * @since 4.9 Added $version and $addon parameters.
 
 
29
  *
30
+ * @param string $name Name of the required plugin
31
+ * @param null $thickbox_url Download or purchase URL for plugin from within /wp-admin/ thickbox
32
+ * @param bool $is_active Indicates if the plugin is installed and active or not
33
+ * @param string $version Optional version number of the required plugin
34
+ * @param bool $addon Indicates if the plugin is an add-on for The Events Calendar or Event Tickets
 
35
  */
36
+ public function add_required_plugin( $name, $thickbox_url = null, $is_active = null, $version = null, $addon = false ) {
37
+ $this->plugins_required[ $name ] = array(
38
+ 'name' => $name,
39
+ 'thickbox_url' => $thickbox_url,
40
+ 'is_active' => $is_active,
41
+ 'version' => $version,
42
+ 'addon' => $addon,
43
+ );
 
44
  }
45
 
46
  /**
47
  * Echoes the admin notice, attach to admin_notices
 
 
 
 
48
  */
49
  public function show_inactive_plugins_alert() {
50
  if ( ! current_user_can( 'activate_plugins' ) ) {
58
  return;
59
  }
60
 
 
 
 
 
 
 
 
61
  foreach ( $this->plugins_required as $req_plugin ) {
62
  $item = $req_plugin['name'];
63
  $version = empty( $req_plugin['version'] ) ? '' : ' (' . str_replace( '-dev', '', $req_plugin['version'] ) . ')';
84
  }
85
 
86
  $req_plugins[] = $item;
 
 
 
 
 
87
  }
88
 
89
  // If empty then add in the default name.
96
  'a' => array( 'href' => array() ),
97
  );
98
 
 
 
 
 
 
 
 
 
 
99
  printf(
100
+ '<div class="error tribe-notice tribe-dependency-error" data-plugin="%1$s"><p>'
101
+ . esc_html__( 'To begin using %2$s, please install and activate the latest version of %3$s.', 'tribe-common' )
102
+ . '</p></div>',
 
103
  esc_attr( sanitize_title( $plugin_data['Name'] ) ),
104
+ wp_kses( $this->implode_with_grammar( $plugin_name ), $allowed_html ),
105
+ wp_kses( $this->implode_with_grammar( $req_plugins ), $allowed_html )
106
  );
107
  }
108
 
109
  /**
110
+ * Implodes a list items using 'and' as the final separator and a comma everywhere else
 
 
111
  *
112
  * @param array $items List of items to implode
113
  *
119
  $output = $last_item = array_pop( $items );
120
 
121
  if ( $items ) {
122
+ $output = implode( $separator, $items ) . $conjunction . $last_item;
 
 
 
 
 
 
123
  }
124
 
125
  return $output;
common/src/Tribe/Admin/Notices.php CHANGED
@@ -111,15 +111,23 @@ class Tribe__Admin__Notices {
111
  $transients = $this->get_transients();
112
 
113
  foreach ( $transients as $slug => $transient ) {
114
- if ( $this->transient_notice_expired( $slug ) ) {
 
115
  continue;
116
  }
117
- list( $html, $args, $expire ) = $transients[ $slug ];
118
  $this->register( $slug, $html, $args );
119
  }
120
 
121
  foreach ( $this->notices as $notice ) {
122
- if ( ! $this->showing_notice( $notice->slug ) ) {
 
 
 
 
 
 
 
 
123
  continue;
124
  }
125
 
@@ -549,12 +557,6 @@ class Tribe__Admin__Notices {
549
  * @return array An associative array in the shape [ <slug> => [ <html>, <args>, <expire timestamp> ] ]
550
  */
551
  protected function get_transients() {
552
- $cached = tribe( 'cache' )['transient_admin_notices'];
553
-
554
- if ( false !== $cached ) {
555
- return $cached;
556
- }
557
-
558
  $transient = self::$transient_notices_name;
559
  $notices = get_transient( $transient );
560
  $notices = is_array( $notices ) ? $notices : array();
@@ -570,8 +572,6 @@ class Tribe__Admin__Notices {
570
  }
571
  }
572
 
573
- tribe( 'cache' )['transient_admin_notices'] = $notices;
574
-
575
  return $notices;
576
  }
577
 
@@ -586,84 +586,4 @@ class Tribe__Admin__Notices {
586
  $transient = self::$transient_notices_name;
587
  set_transient( $transient, $notices, MONTH_IN_SECONDS );
588
  }
589
-
590
- /**
591
- * Checks whether a specific transient admin notices is being shown or not, depending on its expiration and
592
- * dismissible status.
593
- *
594
- *
595
- * @since 4.11.1
596
- *
597
- * @param string|array $slug The slug, or slugs, of the transient notices to check. This is the same slug used
598
- * to register the transient notice in the `tribe_transient_notice` function or the
599
- * `Tribe__Admin__Notices::register_transient()` method.
600
- *
601
- * @return bool Whether the transient notice is showing or not.
602
- */
603
- public function showing_transient_notice( $slug ) {
604
- $transient_notices = (array) $this->get_transients();
605
-
606
- return isset( $transient_notices[ $slug ] )
607
- && ! $this->has_user_dimissed( $slug )
608
- && ! $this->transient_notice_expired( $slug );
609
- }
610
-
611
- /**
612
- * Checks whether a transient notice expired or not.
613
- *
614
- * @since 4.11.1
615
- *
616
- * @param string|array $slug The slug, or slugs, of the transient notices to check. This is the same slug used
617
- * to register the transient notice in the `tribe_transient_notice` function or the
618
- * `Tribe__Admin__Notices::register_transient()` method.
619
- *
620
- * @return bool Whether the transient notice is expired or not.
621
- */
622
- protected function transient_notice_expired( $slug ) {
623
- $transients = (array) $this->get_transients();
624
-
625
- if ( ! isset( $transients[ $slug ] ) ) {
626
- return true;
627
- }
628
-
629
- list( $html, $args, $expire ) = $transients[ $slug ];
630
- if ( $expire < time() ) {
631
- return true;
632
- }
633
-
634
- return false;
635
- }
636
-
637
- /**
638
- * Checks whether a notice is being shown or not; the result takes the notice callback and dismissible status into
639
- * account.
640
- *
641
- * @since 4.11.1
642
- *
643
- * @param string|array $slug The slug, or slugs, of the transient notices to check. This is the same slug used
644
- * to register the transient notice in the `tribe_transient_notice` function or the
645
- * `Tribe__Admin__Notices::register_transient()` method.
646
- *
647
- * @return bool Whether the notice is showing or not.
648
- */
649
- public function showing_notice( $slug ) {
650
- if ( ! isset( $this->notices[ $slug ] ) ) {
651
- return false;
652
- }
653
-
654
- $notice = $this->notices[ $slug ];
655
- if ( $notice->dismiss && $this->has_user_dimissed( $notice->slug ) ) {
656
- return false;
657
- }
658
-
659
- if (
660
- ! empty( $notice->active_callback )
661
- && is_callable( $notice->active_callback )
662
- && false == call_user_func( $notice->active_callback )
663
- ) {
664
- return false;
665
- }
666
-
667
- return true;
668
- }
669
  }
111
  $transients = $this->get_transients();
112
 
113
  foreach ( $transients as $slug => $transient ) {
114
+ list( $html, $args, $expire ) = $transient;
115
+ if ( $expire < time() ) {
116
  continue;
117
  }
 
118
  $this->register( $slug, $html, $args );
119
  }
120
 
121
  foreach ( $this->notices as $notice ) {
122
+ if ( $notice->dismiss && $this->has_user_dimissed( $notice->slug ) ) {
123
+ continue;
124
+ }
125
+
126
+ if (
127
+ ! empty( $notice->active_callback )
128
+ && is_callable( $notice->active_callback )
129
+ && false == call_user_func( $notice->active_callback )
130
+ ) {
131
  continue;
132
  }
133
 
557
  * @return array An associative array in the shape [ <slug> => [ <html>, <args>, <expire timestamp> ] ]
558
  */
559
  protected function get_transients() {
 
 
 
 
 
 
560
  $transient = self::$transient_notices_name;
561
  $notices = get_transient( $transient );
562
  $notices = is_array( $notices ) ? $notices : array();
572
  }
573
  }
574
 
 
 
575
  return $notices;
576
  }
577
 
586
  $transient = self::$transient_notices_name;
587
  set_transient( $transient, $notices, MONTH_IN_SECONDS );
588
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
589
  }
common/src/Tribe/App_Shop.php CHANGED
@@ -123,169 +123,32 @@ if ( ! class_exists( 'Tribe__App_Shop' ) ) {
123
  public function do_menu_page() {
124
  $main = Tribe__Main::instance();
125
  $products = $this->get_all_products();
126
- $bundles = $this->get_bundles();
127
- $extensions = $this->get_extensions();
128
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/app-shop.php';
129
  }
130
 
131
  /**
132
- * Gets all products from the API
133
  *
134
  * @return array|WP_Error
135
  */
136
  private function get_all_products() {
137
  $all_products = tribe( 'plugins.api' )->get_products();
138
 
139
- $products = [
140
- 'the-events-calendar' => (object) $all_products['the-events-calendar'],
141
- 'events-calendar-pro' => (object) $all_products['events-calendar-pro'],
142
- /// this is coming soon, but not yet finalized
143
- // 'events-virtual' => (object) $all_products['events-virtual'],
144
- 'event-aggregator' => (object) $all_products['event-aggregator'],
145
- 'event-tickets' => (object) $all_products['event-tickets'],
146
- 'event-tickets-plus' => (object) $all_products['event-tickets-plus'],
147
- 'promoter' => (object) $all_products['promoter'],
148
- 'tribe-filterbar' => (object) $all_products['tribe-filterbar'],
149
- 'events-community' => (object) $all_products['events-community'],
150
- 'events-community-tickets' => (object) $all_products['events-community-tickets'],
151
- 'tribe-eventbrite' => (object) $all_products['tribe-eventbrite'],
152
- 'image-widget-plus' => (object) $all_products['image-widget-plus'],
153
- ];
154
 
155
  return $products;
156
  }
157
 
158
- /**
159
- * Gets product bundles
160
- *
161
- * @return array|WP_Error
162
- */
163
- private function get_bundles() {
164
- $bundles = [
165
- (object) [
166
- 'title' => __( 'Events Marketing Bundle', 'tribe-common' ),
167
- 'logo' => 'images/logo/bundle-event-marketing.svg',
168
- 'link' => 'https://m.tri.be/1aj3',
169
- 'discount' => __( 'Save over 20%', 'tribe-common' ),
170
- 'description' => __( 'Ticket sales, attendee management, and email marketing for your events.', 'tribe-common' ),
171
- 'includes' => [
172
- 'events-calendar-pro',
173
- 'event-tickets-plus',
174
- 'promoter',
175
- ],
176
- ],
177
- (object) [
178
- 'title' => __( 'Event Importer Bundle', 'tribe-common' ),
179
- 'logo' => 'images/logo/bundle-event-importer.svg',
180
- 'link' => 'https://m.tri.be/1aj2',
181
- 'discount' => __( 'Save over 25%', 'tribe-common' ),
182
- 'description' => __( 'Fill your calendar with events from across the web, including Google Calendar, Meetup, and more.', 'tribe-common' ),
183
- 'includes' => [
184
- 'events-calendar-pro',
185
- 'tribe-filterbar',
186
- 'event-aggregator'
187
- ],
188
- ],
189
- /* this is coming soon, but not yet finalized
190
- (object) [
191
- 'title' => __( 'Virtual Event Marketing Bundle', 'tribe-common' ),
192
- 'logo' => 'images/logo/bundle-virtual-events.svg',
193
- 'link' => 'https://m.tri.be/somewhere', // code review: fix this
194
- 'discount' => __( 'Save over 20%', 'tribe-common' ), // code review: fix this
195
- 'description' => __( '[description]', 'tribe-common' ), // code review: fix this
196
- 'includes' => [
197
- 'events-calendar-pro',
198
- 'event-tickets-plus',
199
- 'events-virtual',
200
- 'promoter',
201
- ],
202
- ],
203
- */
204
- (object) [
205
- 'title' => __( 'Community Manager Bundle', 'tribe-common' ),
206
- 'logo' => 'images/logo/bundle-community-manager.svg',
207
- 'link' => 'https://m.tri.be/1aj4',
208
- 'discount' => __( 'Save over 20%', 'tribe-common' ), /* code review: fix this */
209
- 'description' => __( 'Handle event submissions with ticket sales and everything you need to build a robust community.', 'tribe-common' ),
210
- 'includes' => [
211
- 'event-tickets-plus',
212
- 'events-community',
213
- 'events-community-tickets',
214
- 'tribe-filterbar',
215
- ],
216
- ],
217
- (object) [
218
- 'title' => __( 'Ultimate Bundle', 'tribe-common' ),
219
- 'logo' => 'images/logo/bundle-ultimate.svg',
220
- 'link' => 'https://m.tri.be/1aj5',
221
- 'discount' => __( 'Save over 20%', 'tribe-common' ), /* code review: fix this */
222
- 'description' => __( 'All of our premium events management plugins at a deep discount.', 'tribe-common' ),
223
- 'includes' => [
224
- 'events-calendar-pro',
225
- 'event-tickets-plus',
226
- //'events-virtual', // not yet added to the bundle
227
- 'events-community',
228
- 'events-community-tickets',
229
- 'tribe-filterbar',
230
- 'event-aggregator',
231
- 'tribe-eventbrite',
232
- //'promoter', // not yet added to the bundle
233
- ],
234
- ],
235
-
236
- ];
237
-
238
- return $bundles;
239
- }
240
-
241
- /**
242
- * Gets product extensions
243
- *
244
- * @return array|WP_Error
245
- */
246
- private function get_extensions() {
247
- $extensions = [
248
- (object) [
249
- 'title' => __( 'Website URL CTA', 'tribe-common' ),
250
- 'link' => 'https://m.tri.be/1aj6',
251
- 'image' => 'images/shop/extension-web-url-cta.jpg',
252
- 'description' => __( 'Create a strong call-to-action for attendees to "Join Webinar" instead of only sharing a website address.', 'tribe-common' ),
253
- ],
254
- (object) [
255
- 'title' => __( 'Link Directly to Webinar', 'tribe-common' ),
256
- 'link' => 'https://m.tri.be/1aj7',
257
- 'image' => 'images/shop/extension-link-to-webinar.jpg',
258
- 'description' => __( 'When users click on the event title, they’ll be taken right to the source of your event, offering a direct route to join.', 'tribe-common' ),
259
- ],
260
- (object) [
261
- 'title' => __( 'Events Happening Now', 'tribe-common' ),
262
- 'link' => 'https://m.tri.be/1aj8',
263
- 'image' => 'images/shop/extension-events-happening-now.jpg',
264
- 'description' => __( 'Use this shortcode to display events that are currently in progress, like webinars and livestreams.', 'tribe-common' ),
265
- ],
266
- (object) [
267
- 'title' => __( 'Custom Venue Links', 'tribe-common' ),
268
- 'link' => 'https://m.tri.be/1aj9',
269
- 'image' => 'images/shop/extension-custom-venue-links.jpg',
270
- 'description' => __( 'Turn the venue name for your event into a clickable URL — a great way to link directly to a venue’s website or a virtual meeting.', 'tribe-common' ),
271
- ],
272
- (object) [
273
- 'title' => __( 'Adjust Label', 'tribe-common' ),
274
- 'link' => 'https://m.tri.be/1aja',
275
- 'image' => 'images/shop/extension-change-label.jpg',
276
- 'description' => __( 'Change "Events" to "Webinars," or "Venues" to "Livestream," or "Organizers" to "Hosts." Tailor your calendar for virtual events and meetings.', 'tribe-common' ),
277
- ],
278
- (object) [
279
- 'title' => __( 'Reach Attendees', 'tribe-common' ),
280
- 'link' => 'https://m.tri.be/1ajc',
281
- 'image' => 'images/shop/extension-advanced-options.jpg',
282
- 'description' => __( 'From registration to attendance history, view every step of the event lifecycle with this HubSpot integration.', 'tribe-common' ),
283
- ],
284
- ];
285
-
286
- return $extensions;
287
- }
288
-
289
  /**
290
  * Static Singleton Factory Method
291
  *
123
  public function do_menu_page() {
124
  $main = Tribe__Main::instance();
125
  $products = $this->get_all_products();
 
 
126
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/app-shop.php';
127
  }
128
 
129
  /**
130
+ * Get's all products from the API
131
  *
132
  * @return array|WP_Error
133
  */
134
  private function get_all_products() {
135
  $all_products = tribe( 'plugins.api' )->get_products();
136
 
137
+ $products = array(
138
+ (object) $all_products['event-aggregator'],
139
+ (object) $all_products['events-calendar-pro'],
140
+ (object) $all_products['event-tickets-plus'],
141
+ (object) $all_products['promoter'],
142
+ (object) $all_products['tribe-filterbar'],
143
+ (object) $all_products['events-community'],
144
+ (object) $all_products['events-community-tickets'],
145
+ (object) $all_products['tribe-eventbrite'],
146
+ (object) $all_products['image-widget-plus'],
147
+ );
 
 
 
 
148
 
149
  return $products;
150
  }
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  /**
153
  * Static Singleton Factory Method
154
  *
common/src/Tribe/Assets.php CHANGED
@@ -1,26 +1,26 @@
1
  <?php
2
  /**
3
- * Class used to register and enqueue assets across our plugins.
4
  *
5
  * @since 4.3
6
  */
7
  class Tribe__Assets {
8
  /**
9
- * Stores all the Assets and it's configurations.
10
  *
11
  * @var array
12
  */
13
- protected $assets = [];
14
 
15
  /**
16
- * Stores the localized scripts for reference.
17
  *
18
  * @var array
19
  */
20
- private $localized = [];
21
 
22
  /**
23
- * Static Singleton Factory Method.
24
  *
25
  * @since 4.3
26
  *
@@ -31,17 +31,17 @@ class Tribe__Assets {
31
  }
32
 
33
  /**
34
- * Register the Methods in the correct places.
35
  *
36
  * @since 4.3
37
  */
38
  public function __construct() {
39
- // Hook the actual registering of.
40
- add_action( 'init', [ $this, 'register_in_wp' ], 1, 0 );
41
  }
42
 
43
  /**
44
- * Register the Assets on the correct hooks.
45
  *
46
  * @since 4.3
47
  *
@@ -49,53 +49,37 @@ class Tribe__Assets {
49
  */
50
  public function register_in_wp( $assets = null ) {
51
  if ( is_null( $assets ) ) {
52
- $assets = $this->get();
53
  }
54
 
55
  if ( ! is_array( $assets ) ) {
56
- $assets = [ $assets ];
57
  }
58
 
59
- foreach ( $assets as $asset ) {
60
- // Asset is already registered.
61
- if ( $asset->is_registered ) {
62
- continue;
63
- }
64
 
 
65
  if ( 'js' === $asset->type ) {
66
- // Script is already registered.
67
- if ( wp_script_is( $asset->slug, 'registered' ) ) {
68
- continue;
69
- }
70
-
71
  wp_register_script( $asset->slug, $asset->url, $asset->deps, $asset->version, $asset->in_footer );
72
-
73
- // Register that this asset is actually registered on the WP methods.
74
- $asset->is_registered = wp_script_is( $asset->slug, 'registered' );
75
  } else {
76
- // Style is already registered.
77
- if ( wp_style_is( $asset->slug, 'registered' ) ) {
78
- continue;
79
- }
80
-
81
  wp_register_style( $asset->slug, $asset->url, $asset->deps, $asset->version, $asset->media );
82
-
83
- // Register that this asset is actually registered on the WP methods.
84
- $asset->is_registered = wp_style_is( $asset->slug, 'registered' );
85
  }
86
 
87
- // If we don't have an action we don't even register the action to enqueue.
 
 
 
88
  if ( empty( $asset->action ) ) {
89
  continue;
90
  }
91
 
92
- // Now add an action to enqueue the registered assets.
93
  foreach ( (array) $asset->action as $action ) {
94
- // Enqueue the registered assets at the appropriate time.
95
  if ( did_action( $action ) > 0 ) {
96
  $this->enqueue();
97
  } else {
98
- add_action( $action, [ $this, 'enqueue' ], $asset->priority, 0 );
99
  }
100
  }
101
  }
@@ -104,26 +88,29 @@ class Tribe__Assets {
104
  /**
105
  * Enqueues registered assets based on their groups.
106
  *
107
- * @since 4.7
 
 
108
  *
109
- * @uses Tribe__Assets::enqueue()
110
  *
111
- * @param string|array $groups Which groups will be enqueued.
112
  */
113
  public function enqueue_group( $groups ) {
114
- $assets = $this->get( null, false );
115
- $enqueue = [];
116
 
117
  foreach ( $assets as $asset ) {
118
  if ( empty( $asset->groups ) ) {
119
  continue;
120
  }
121
 
122
- $intersect = array_intersect( (array) $groups, $asset->groups );
123
 
124
- if ( empty( $intersect ) ) {
125
  continue;
126
  }
 
127
  $enqueue[] = $asset->slug;
128
  }
129
 
@@ -145,23 +132,19 @@ class Tribe__Assets {
145
  */
146
  public function enqueue( $forcibly_enqueue = null ) {
147
  $forcibly_enqueue = array_filter( (array) $forcibly_enqueue );
148
- if ( ! empty( $forcibly_enqueue ) ) {
149
- $assets = (array) $this->get( $forcibly_enqueue );
150
- } else {
151
- $assets = $this->get();
152
- }
153
 
154
  foreach ( $assets as $asset ) {
155
  // Should this asset be enqueued regardless of the current filter/any conditional requirements?
156
  $must_enqueue = in_array( $asset->slug, $forcibly_enqueue );
157
  $in_filter = in_array( current_filter(), (array) $asset->action );
158
 
159
- // Skip if we are not on the correct filter (unless we are forcibly enqueuing).
160
  if ( ! $in_filter && ! $must_enqueue ) {
161
  continue;
162
  }
163
 
164
- // If any single conditional returns true, then we need to enqueue the asset.
165
  if ( empty( $asset->action ) && ! $must_enqueue ) {
166
  continue;
167
  }
@@ -172,17 +155,17 @@ class Tribe__Assets {
172
  }
173
 
174
  // Default to enqueuing the asset if there are no conditionals,
175
- // and default to not enqueuing it if there *are* conditionals.
176
  $enqueue = empty( $asset->conditionals );
177
 
178
  if ( ! $enqueue ) {
179
- // Reset Enqueue.
180
- $enqueue = [];
181
 
182
  // Which is the operator?
183
  $conditional_operator = Tribe__Utils__Array::get( $asset->conditionals, 'operator', 'OR' );
184
 
185
- // If we have a set of conditionals we loop on then and get if they are true.
186
  foreach ( $asset->conditionals as $key => $conditional ) {
187
  // Avoid doing anything to the operator
188
  if ( 'operator' === $key ) {
@@ -192,7 +175,7 @@ class Tribe__Assets {
192
  $enqueue[] = call_user_func( $conditional );
193
  }
194
 
195
- // By default we use OR for backwards compatibility.
196
  if ( 'OR' === $conditional_operator ) {
197
  $enqueue = in_array( true, $enqueue );
198
  } else {
@@ -201,12 +184,12 @@ class Tribe__Assets {
201
  }
202
 
203
  /**
204
- * Allows developers to hook-in and prevent an asset from been loaded.
205
  *
206
  * @since 4.3
207
  *
208
- * @param bool $enqueue If we should enqueue or not a given asset.
209
- * @param object $asset Which asset we are dealing with.
210
  */
211
  $enqueue = apply_filters( 'tribe_asset_enqueue', $enqueue, $asset );
212
 
@@ -215,10 +198,10 @@ class Tribe__Assets {
215
  *
216
  * @since 4.3
217
  *
218
- * @param bool $enqueue If we should enqueue or not a given asset.
219
- * @param object $asset Which asset we are dealing with.
220
  */
221
- $enqueue = apply_filters( "tribe_asset_enqueue_{$asset->slug}", $enqueue, $asset );
222
 
223
  if ( ! $enqueue && ! $must_enqueue ) {
224
  continue;
@@ -227,18 +210,17 @@ class Tribe__Assets {
227
  if ( 'js' === $asset->type ) {
228
  wp_enqueue_script( $asset->slug );
229
 
230
- // Only localize on JS and if we have data.
231
  if ( ! empty( $asset->localize ) ) {
232
- // Makes sure we have an Array of Localize data.
233
  if ( is_object( $asset->localize ) ) {
234
- $localization = [ $asset->localize ];
235
  } else {
236
  $localization = (array) $asset->localize;
237
  }
238
 
239
  /**
240
- * Check to ensure we haven't already localized it before.
241
- *
242
  * @since 4.5.8
243
  */
244
  foreach ( $localization as $localize ) {
@@ -246,9 +228,9 @@ class Tribe__Assets {
246
  continue;
247
  }
248
 
249
- // If we have a Callable as the Localize data we execute it.
250
  if ( is_callable( $localize->data ) ) {
251
- $localize->data = call_user_func( $localize->data, $asset );
252
  }
253
 
254
  wp_localize_script( $asset->slug, $localize->name, $localize->data );
@@ -275,126 +257,95 @@ class Tribe__Assets {
275
  * @return string|false The url to the minified version or false, if file not found.
276
  */
277
  public static function maybe_get_min_file( $url ) {
278
- static $wpmu_plugin_url;
279
- static $wp_plugin_url;
280
- static $wp_content_url;
281
- static $plugins_url;
282
- static $base_dirs;
283
-
284
- $urls = [];
285
- if ( ! isset( $wpmu_plugin_url ) ) {
286
- $wpmu_plugin_url = set_url_scheme( WPMU_PLUGIN_URL );
287
- }
288
-
289
- if ( ! isset( $wp_plugin_url ) ) {
290
- $wp_plugin_url = set_url_scheme( WP_PLUGIN_URL );
291
- }
292
-
293
- if ( ! isset( $wp_content_url ) ) {
294
- $wp_content_url = set_url_scheme( WP_CONTENT_URL );
295
- }
296
-
297
- if ( ! isset( $plugins_url ) ) {
298
- $plugins_url = plugins_url();
299
- }
300
-
301
- if ( ! isset( $base_dirs ) ) {
302
- $base_dirs[ WPMU_PLUGIN_DIR ] = wp_normalize_path( WPMU_PLUGIN_DIR );
303
- $base_dirs[ WP_PLUGIN_DIR ] = wp_normalize_path( WP_PLUGIN_DIR );
304
- $base_dirs[ WP_CONTENT_DIR ] = wp_normalize_path( WP_CONTENT_DIR );
305
- }
306
 
307
  if ( 0 === strpos( $url, $wpmu_plugin_url ) ) {
308
  // URL inside WPMU plugin dir.
309
- $base_dir = $base_dirs[ WPMU_PLUGIN_DIR ];
310
  $base_url = $wpmu_plugin_url;
311
  } elseif ( 0 === strpos( $url, $wp_plugin_url ) ) {
312
  // URL inside WP plugin dir.
313
- $base_dir = $base_dirs[ WP_PLUGIN_DIR ];
314
  $base_url = $wp_plugin_url;
315
  } elseif ( 0 === strpos( $url, $wp_content_url ) ) {
316
  // URL inside WP content dir.
317
- $base_dir = $base_dirs[ WP_CONTENT_DIR ];
318
  $base_url = $wp_content_url;
319
  } elseif ( 0 === strpos( $url, $plugins_url ) ) {
320
- $base_dir = $base_dirs[ WP_PLUGIN_DIR ];
321
  $base_url = $plugins_url;
322
  } else {
323
  // Resource needs to be inside wp-content or a plugins dir.
324
  return false;
325
  }
326
 
327
- $script_debug = defined( 'SCRIPT_DEBUG' ) && tribe_is_truthy( SCRIPT_DEBUG );
328
-
329
  // Strip the plugin URL and make this relative.
330
  $relative_location = str_replace( $base_url, '', $url );
331
 
332
- if ( $script_debug ) {
333
- // Add the actual url after having the min file added.
334
- $urls[] = $relative_location;
335
- }
336
-
337
  // If needed add the Min Files.
338
- if ( substr( $relative_location, -3, 3 ) === '.js' ) {
339
- $urls[] = substr_replace( $relative_location, '.min', - 3, 0 );
340
- } elseif ( substr( $relative_location, -4, 4 ) === '.css' ) {
341
- $urls[] = substr_replace( $relative_location, '.min', - 4, 0 );
342
- }
343
 
344
- if ( ! $script_debug ) {
345
- // Add the actual url after having the min file added.
346
- $urls[] = $relative_location;
347
  }
348
 
 
 
 
349
  // Check for all Urls added to the array.
350
  foreach ( $urls as $partial_path ) {
351
  $file_path = wp_normalize_path( $base_dir . $partial_path );
352
- $file_url = $base_url . $partial_path;
353
 
354
  if ( file_exists( $file_path ) ) {
355
  return $file_url;
356
  }
357
  }
358
 
359
- // If we don't have any real file return false.
360
  return false;
361
  }
362
 
363
  /**
364
- * Register an Asset and attach a callback to the required action to display it correctly.
365
  *
366
  * @since 4.3
367
  *
368
- * @param object $origin The main object for the plugin you are enqueueing the asset for.
369
- * @param string $slug Slug to save the asset - passes through `sanitize_title_with_dashes()`.
370
- * @param string $file The asset file to load (CSS or JS), including non-minified file extension.
371
- * @param array $deps The list of dependencies.
372
- * @param string|array|null $action The WordPress action(s) to enqueue on, such as `wp_enqueue_scripts`,
373
- * `admin_enqueue_scripts`, or `login_enqueue_scripts`.
374
- * @param string|array $arguments {
375
- * Optional. Array or string of parameters for this asset.
376
- *
377
- * @type array|string|null $action The WordPress action(s) this asset will be enqueued on.
378
- * @type int $priority Priority in which this asset will be loaded on the WordPress action.
379
- * @type string $file The relative path to the File that will be enqueued, uses the $origin to get the full path.
380
- * @type string $type Asset Type, `js` or `css`.
381
- * @type array $deps An array of other asset as dependencies.
382
- * @type string $version Version number, used for cache expiring.
383
- * @type string $media Used only for CSS, when to load the file.
384
- * @type bool $in_footer A boolean determining if the javascript should be loaded on the footer.
385
- * @type array|object $localize {
386
- * Variables needed on the JavaScript side.
387
- *
388
- * @type string $name Name of the JS variable.
389
- * @type string|array $data Contents of the JS variable.
390
  * }
391
- * @type callable[] $conditionals An callable method or an array of them, that will determine if the asset is loaded or not.
392
  * }
393
  *
394
- * @return object|false The registered object or false on error.
395
  */
396
- public function register( $origin, $slug, $file, $deps = [], $action = null, $arguments = [] ) {
397
- // Prevent weird stuff here.
398
  $slug = sanitize_title_with_dashes( $slug );
399
 
400
  if ( $this->exists( $slug ) ) {
@@ -402,9 +353,9 @@ class Tribe__Assets {
402
  }
403
 
404
  if ( is_string( $origin ) ) {
405
- // Origin needs to be a class with a `instance` method and a Version constant.
406
  if ( class_exists( $origin ) && method_exists( $origin, 'instance' ) && defined( $origin . '::VERSION' ) ) {
407
- $origin = call_user_func( [ $origin, 'instance' ] );
408
  }
409
  }
410
 
@@ -412,26 +363,26 @@ class Tribe__Assets {
412
  $origin_name = get_class( $origin );
413
 
414
  if ( ! defined( $origin_name . '::VERSION' ) ) {
415
- // If we have a Object and we don't have instance or version.
416
  return false;
417
  }
418
  } else {
419
  return false;
420
  }
421
 
422
- // Fetches the version on the Origin Version constant.
423
  $version = constant( $origin_name . '::VERSION' );
424
 
425
- // Default variables to prevent notices.
426
- $defaults = [
427
  'slug' => null,
428
  'file' => false,
429
  'url' => false,
430
  'action' => null,
431
  'priority' => 10,
432
  'type' => null,
433
- 'deps' => [],
434
- 'groups' => [],
435
  'version' => $version,
436
  'media' => 'all',
437
  'in_footer' => true,
@@ -440,15 +391,15 @@ class Tribe__Assets {
440
  'origin_url' => null,
441
  'origin_name' => null,
442
 
443
- // Bigger Variables at the end.
444
- 'localize' => [],
445
- 'conditionals' => [],
446
- ];
447
 
448
- // Merge Arguments.
449
  $asset = (object) wp_parse_args( $arguments, $defaults );
450
 
451
- // Enforce these one.
452
  $asset->slug = $slug;
453
  $asset->file = $file;
454
  $asset->deps = $deps;
@@ -456,14 +407,14 @@ class Tribe__Assets {
456
  $asset->origin_path = trailingslashit( ! empty( $origin->plugin_path ) ? $origin->plugin_path : $origin->pluginPath );
457
  $asset->origin_name = $origin_name;
458
 
459
- // Origin URL might throw notices so we double check.
460
  $asset->origin_url = ! empty( $origin->plugin_url ) ? $origin->plugin_url : null;
461
  $asset->origin_url = ! empty( $origin->pluginUrl ) ? $origin->pluginUrl : null;
462
  if ( ! empty( $asset->origin_url ) ) {
463
  $asset->origin_url = trailingslashit( $asset->origin_url );
464
  }
465
 
466
- // If we don't have a type on the arguments we grab from the File path.
467
  if ( is_null( $asset->type ) ) {
468
  if ( substr( $asset->file, -3, 3 ) === '.js' ) {
469
  $asset->type = 'js';
@@ -472,13 +423,13 @@ class Tribe__Assets {
472
  }
473
  }
474
 
475
- // If asset type is wrong don't register.
476
- if ( ! in_array( $asset->type, [ 'js', 'css' ], true ) ) {
477
  return false;
478
  }
479
 
480
  /**
481
- * Deprecated filter to allow changing version based on the type of Asset.
482
  *
483
  * @todo remove on 4.6
484
  * @deprecated 4.3
@@ -488,9 +439,7 @@ class Tribe__Assets {
488
  $asset->version = apply_filters( "tribe_events_{$asset->type}_version", $asset->version );
489
 
490
  /**
491
- * Filter to change version number on assets.
492
- *
493
- * @since 4.3
494
  *
495
  * @param string $version
496
  * @param object $asset
@@ -502,29 +451,42 @@ class Tribe__Assets {
502
  $asset->in_footer = (bool) $asset->in_footer;
503
  $asset->media = esc_attr( $asset->media );
504
 
505
- // Ensures that we have a priority over 1.
506
  if ( $asset->priority < 1 ) {
507
  $asset->priority = 1;
508
  }
509
 
510
  $is_vendor = strpos( $asset->file, 'vendor/' ) !== false ? true : false;
511
 
512
- // Setup the actual URL.
513
  if ( filter_var( $asset->file, FILTER_VALIDATE_URL ) ) {
514
  $asset->url = $asset->file;
515
  } else {
516
  $asset->url = $this->maybe_get_min_file( tribe_resource_url( $asset->file, false, ( $is_vendor ? '' : null ), $origin ) );
517
  }
518
 
519
- // Parse the Localize asset arguments.
520
- $asset = $this->parse_argument_localize( $asset );
 
 
 
 
 
 
 
 
 
 
 
 
 
521
 
522
- // Looks for a single conditional callable and places it in an Array.
523
  if ( ! empty( $asset->conditionals ) && is_callable( $asset->conditionals ) ) {
524
- $asset->conditionals = [ $asset->conditionals ];
525
  }
526
 
527
- // Groups is always an array of unique strings.
528
  if ( ! empty( $asset->groups ) ) {
529
  $asset->groups = (array) $asset->groups;
530
  $asset->groups = array_filter( $asset->groups, 'is_string' );
@@ -532,62 +494,28 @@ class Tribe__Assets {
532
  }
533
 
534
  /**
535
- * Filter an Asset loading variables.
536
- *
537
- * @since 4.3
538
  *
539
  * @param object $asset
540
  */
541
  $asset = apply_filters( 'tribe_asset_pre_register', $asset );
542
 
543
- // Set the Asset on the array of notices.
544
  $this->assets[ $slug ] = $asset;
545
 
546
- // Return the Slug because it might be modified.
547
- return $asset;
548
- }
549
-
550
- /**
551
- * Parse the localize argument for a given asset object.
552
- *
553
- * @since 4.9.12
554
- *
555
- * @param stdClass $asset Argument that set that asset.
556
- *
557
- * @return stdClass
558
- */
559
- public function parse_argument_localize( stdClass $asset ) {
560
- if ( empty( $asset->localize ) ) {
561
- return $asset;
562
- }
563
-
564
- if ( ! is_array( $asset->localize ) && ! is_object( $asset->localize ) ) {
565
- return $asset;
566
- }
567
-
568
- // Cast to array for safety.
569
- $asset->localize = (array) $asset->localize;
570
-
571
- // Allow passing of a single instance.
572
- if ( ! empty( $asset->localize['name'] ) ) {
573
- // Reset to empty when name was not empty data was not set.
574
- $asset->localize = ! isset( $asset->localize['data'] ) ? [] : [ (object) $asset->localize ];
575
- }
576
-
577
- // Cast all instances as object.
578
- $asset->localize = array_map( function( $values ) {
579
- return (object) $values;
580
- }, $asset->localize );
581
 
 
582
  return $asset;
583
  }
584
 
585
  /**
586
- * Removes an Asset from been registered and enqueue.
587
  *
588
  * @since 4.3
589
  *
590
- * @param string $slug Slug of the Asset.
591
  *
592
  * @return bool
593
  */
@@ -601,59 +529,22 @@ class Tribe__Assets {
601
  }
602
 
603
  /**
604
- * Get the Asset Object configuration.
605
  *
606
  * @since 4.3
607
- * @since 4.11.0 Added $sort param.
608
  *
609
- * @param string|array $slug Slug of the Asset.
610
- * @param boolean $sort If we should do any sorting before returning.
611
  *
612
- * @return array|object|null Array of asset objects, single asset object, or null if looking for a single asset but
613
- * it was not in the array of objects.
614
  */
615
- public function get( $slug = null, $sort = true ) {
 
 
616
  if ( is_null( $slug ) ) {
617
- if ( $sort ) {
618
- $cache_key_count = __METHOD__ . ':count';
619
- // Sorts by priority.
620
- $cache_count = tribe_get_var( $cache_key_count, 0 );
621
- $count = count( $this->assets );
622
-
623
- if ( $count !== $cache_count ) {
624
- uasort( $this->assets, 'tribe_sort_by_priority' );
625
- tribe_set_var( $cache_key_count, $count );
626
- }
627
- }
628
  return $this->assets;
629
  }
630
 
631
- // If slug is an array we return all of those.
632
- if ( is_array( $slug ) ) {
633
- $assets = [];
634
- foreach ( $slug as $asset_slug ) {
635
- $asset_slug = sanitize_title_with_dashes( $asset_slug );
636
- // Skip empty assets.
637
- if ( empty( $this->assets[ $asset_slug ] ) ) {
638
- continue;
639
- }
640
-
641
- $assets[ $asset_slug ] = $this->assets[ $asset_slug ];
642
- }
643
-
644
- if ( empty( $assets ) ) {
645
- return null;
646
- }
647
-
648
- if ( $sort ) {
649
- // Sorts by priority.
650
- uasort( $assets, 'tribe_sort_by_priority' );
651
- }
652
-
653
- return $assets;
654
- }
655
-
656
- // Prevent weird stuff here.
657
  $slug = sanitize_title_with_dashes( $slug );
658
 
659
  if ( ! empty( $this->assets[ $slug ] ) ) {
@@ -664,9 +555,23 @@ class Tribe__Assets {
664
  }
665
 
666
  /**
667
- * Checks if an Asset exists.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
  *
669
- * @param string|array $slug Slug of the Asset.
670
  *
671
  * @return bool
672
  */
1
  <?php
2
  /**
3
+ * Class used to register and enqueue assets across our plugins
4
  *
5
  * @since 4.3
6
  */
7
  class Tribe__Assets {
8
  /**
9
+ * Stores all the Assets and it's configurations
10
  *
11
  * @var array
12
  */
13
+ protected $assets = array();
14
 
15
  /**
16
+ * Stores the localized scripts for reference
17
  *
18
  * @var array
19
  */
20
+ private $localized = array();
21
 
22
  /**
23
+ * Static Singleton Factory Method
24
  *
25
  * @since 4.3
26
  *
31
  }
32
 
33
  /**
34
+ * Register the Methods in the correct places
35
  *
36
  * @since 4.3
37
  */
38
  public function __construct() {
39
+ // Hook the actual registering of
40
+ add_action( 'init', array( $this, 'register_in_wp' ), 1, 0 );
41
  }
42
 
43
  /**
44
+ * Register the Assets on the correct hooks
45
  *
46
  * @since 4.3
47
  *
49
  */
50
  public function register_in_wp( $assets = null ) {
51
  if ( is_null( $assets ) ) {
52
+ $assets = $this->assets;
53
  }
54
 
55
  if ( ! is_array( $assets ) ) {
56
+ $assets = array( $assets );
57
  }
58
 
59
+ uasort( $assets, array( $this, 'order_by_priority' ) );
 
 
 
 
60
 
61
+ foreach ( $assets as $asset ) {
62
  if ( 'js' === $asset->type ) {
 
 
 
 
 
63
  wp_register_script( $asset->slug, $asset->url, $asset->deps, $asset->version, $asset->in_footer );
 
 
 
64
  } else {
 
 
 
 
 
65
  wp_register_style( $asset->slug, $asset->url, $asset->deps, $asset->version, $asset->media );
 
 
 
66
  }
67
 
68
+ // Register that this asset is actually registered on the WP methods
69
+ $asset->is_registered = true;
70
+
71
+ // If we don't have an action we don't even register the action to enqueue
72
  if ( empty( $asset->action ) ) {
73
  continue;
74
  }
75
 
76
+ // Now add an action to enqueue the registered assets
77
  foreach ( (array) $asset->action as $action ) {
78
+ // Enqueue the registered assets at the appropriate time
79
  if ( did_action( $action ) > 0 ) {
80
  $this->enqueue();
81
  } else {
82
+ add_action( $action, array( $this, 'enqueue' ), $asset->priority );
83
  }
84
  }
85
  }
88
  /**
89
  * Enqueues registered assets based on their groups.
90
  *
91
+ * @since 4.7
92
+ *
93
+ * @uses self::enqueue
94
  *
95
+ * @param string|array $groups Which groups will be enqueued
96
  *
97
+ * @return void
98
  */
99
  public function enqueue_group( $groups ) {
100
+ $assets = $this->get();
101
+ $enqueue = array();
102
 
103
  foreach ( $assets as $asset ) {
104
  if ( empty( $asset->groups ) ) {
105
  continue;
106
  }
107
 
108
+ $instersect = array_intersect( (array) $groups, $asset->groups );
109
 
110
+ if ( empty( $instersect ) ) {
111
  continue;
112
  }
113
+
114
  $enqueue[] = $asset->slug;
115
  }
116
 
132
  */
133
  public function enqueue( $forcibly_enqueue = null ) {
134
  $forcibly_enqueue = array_filter( (array) $forcibly_enqueue );
135
+ $assets = $this->get();
 
 
 
 
136
 
137
  foreach ( $assets as $asset ) {
138
  // Should this asset be enqueued regardless of the current filter/any conditional requirements?
139
  $must_enqueue = in_array( $asset->slug, $forcibly_enqueue );
140
  $in_filter = in_array( current_filter(), (array) $asset->action );
141
 
142
+ // Skip if we are not on the correct filter (unless we are forcibly enqueuing)
143
  if ( ! $in_filter && ! $must_enqueue ) {
144
  continue;
145
  }
146
 
147
+ // If any single conditional returns true, then we need to enqueue the asset
148
  if ( empty( $asset->action ) && ! $must_enqueue ) {
149
  continue;
150
  }
155
  }
156
 
157
  // Default to enqueuing the asset if there are no conditionals,
158
+ // and default to not enqueuing it if there *are* conditionals
159
  $enqueue = empty( $asset->conditionals );
160
 
161
  if ( ! $enqueue ) {
162
+ // Reset Enqeue
163
+ $enqueue = array();
164
 
165
  // Which is the operator?
166
  $conditional_operator = Tribe__Utils__Array::get( $asset->conditionals, 'operator', 'OR' );
167
 
168
+ // If we have a set of conditionals we loop on then and get if they are true
169
  foreach ( $asset->conditionals as $key => $conditional ) {
170
  // Avoid doing anything to the operator
171
  if ( 'operator' === $key ) {
175
  $enqueue[] = call_user_func( $conditional );
176
  }
177
 
178
+ // By default we use OR for backwards compatibility
179
  if ( 'OR' === $conditional_operator ) {
180
  $enqueue = in_array( true, $enqueue );
181
  } else {
184
  }
185
 
186
  /**
187
+ * Allows developers to hook-in and prevent an asset from been loaded
188
  *
189
  * @since 4.3
190
  *
191
+ * @param bool $enqueue If we should enqueue or not a given asset
192
+ * @param object $asset Which asset we are dealing with
193
  */
194
  $enqueue = apply_filters( 'tribe_asset_enqueue', $enqueue, $asset );
195
 
198
  *
199
  * @since 4.3
200
  *
201
+ * @param bool $enqueue If we should enqueue or not a given asset
202
+ * @param object $asset Which asset we are dealing with
203
  */
204
+ $enqueue = apply_filters( 'tribe_asset_enqueue_' . $asset->slug, $enqueue, $asset );
205
 
206
  if ( ! $enqueue && ! $must_enqueue ) {
207
  continue;
210
  if ( 'js' === $asset->type ) {
211
  wp_enqueue_script( $asset->slug );
212
 
213
+ // Only localize on JS and if we have data
214
  if ( ! empty( $asset->localize ) ) {
215
+ // Makes sure we have an Array of Localize data
216
  if ( is_object( $asset->localize ) ) {
217
+ $localization = array( $asset->localize );
218
  } else {
219
  $localization = (array) $asset->localize;
220
  }
221
 
222
  /**
223
+ * Check to ensure we haven't already localized it before
 
224
  * @since 4.5.8
225
  */
226
  foreach ( $localization as $localize ) {
228
  continue;
229
  }
230
 
231
+ // If we have a Callable as the Localize data we execute it
232
  if ( is_callable( $localize->data ) ) {
233
+ $localize->data = call_user_func_array( $localize->data, array( $asset ) );
234
  }
235
 
236
  wp_localize_script( $asset->slug, $localize->name, $localize->data );
257
  * @return string|false The url to the minified version or false, if file not found.
258
  */
259
  public static function maybe_get_min_file( $url ) {
260
+ $urls = array();
261
+ $wpmu_plugin_url = set_url_scheme( WPMU_PLUGIN_URL );
262
+ $wp_plugin_url = set_url_scheme( WP_PLUGIN_URL );
263
+ $wp_content_url = set_url_scheme( WP_CONTENT_URL );
264
+ $plugins_url = plugins_url();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
  if ( 0 === strpos( $url, $wpmu_plugin_url ) ) {
267
  // URL inside WPMU plugin dir.
268
+ $base_dir = wp_normalize_path( WPMU_PLUGIN_DIR );
269
  $base_url = $wpmu_plugin_url;
270
  } elseif ( 0 === strpos( $url, $wp_plugin_url ) ) {
271
  // URL inside WP plugin dir.
272
+ $base_dir = wp_normalize_path( WP_PLUGIN_DIR );
273
  $base_url = $wp_plugin_url;
274
  } elseif ( 0 === strpos( $url, $wp_content_url ) ) {
275
  // URL inside WP content dir.
276
+ $base_dir = wp_normalize_path( WP_CONTENT_DIR );
277
  $base_url = $wp_content_url;
278
  } elseif ( 0 === strpos( $url, $plugins_url ) ) {
279
+ $base_dir = wp_normalize_path( WP_PLUGIN_DIR );
280
  $base_url = $plugins_url;
281
  } else {
282
  // Resource needs to be inside wp-content or a plugins dir.
283
  return false;
284
  }
285
 
 
 
286
  // Strip the plugin URL and make this relative.
287
  $relative_location = str_replace( $base_url, '', $url );
288
 
 
 
 
 
 
289
  // If needed add the Min Files.
290
+ if ( ! defined( 'SCRIPT_DEBUG' ) || SCRIPT_DEBUG === false ) {
291
+ if ( substr( $relative_location, - 3, 3 ) === '.js' ) {
292
+ $urls[] = substr_replace( $relative_location, '.min', - 3, 0 );
293
+ }
 
294
 
295
+ if ( substr( $relative_location, - 4, 4 ) === '.css' ) {
296
+ $urls[] = substr_replace( $relative_location, '.min', - 4, 0 );
297
+ }
298
  }
299
 
300
+ // Add the actual url after having the min file added.
301
+ $urls[] = $relative_location;
302
+
303
  // Check for all Urls added to the array.
304
  foreach ( $urls as $partial_path ) {
305
  $file_path = wp_normalize_path( $base_dir . $partial_path );
306
+ $file_url = plugins_url( basename( $file_path ), $file_path );
307
 
308
  if ( file_exists( $file_path ) ) {
309
  return $file_url;
310
  }
311
  }
312
 
313
+ // If we don't have any real file return false
314
  return false;
315
  }
316
 
317
  /**
318
+ * Register an Asset and attach a callback to the required action to display it correctly
319
  *
320
  * @since 4.3
321
  *
322
+ * @param object $origin The main Object for the plugin you are enqueueing the script/style for
323
+ * @param string $slug Slug to save the asset
324
+ * @param string $file Which file will be loaded, either CSS or JS
325
+ * @param array $deps Dependencies
326
+ * @param string|null $action (Optional) A WordPress Action, if set needs to happen after: `wp_enqueue_scripts`, `admin_enqueue_scripts`, or `login_enqueue_scripts`
327
+ * @param string|array $arguments {
328
+ * Optional. Array or string of parameters for this asset
329
+ *
330
+ * @type string|null $action Which WordPress action this asset will be loaded on
331
+ * @type int $priority Priority in which this asset will be loaded on the WordPress action
332
+ * @type string $file The relative path to the File that will be enqueued, uses the $origin to get the full path
333
+ * @type string $type Asset Type, `js` or `css`
334
+ * @type array $deps An array of other asset as dependencies
335
+ * @type string $version Version number, used for cache expiring
336
+ * @type string $media Used only for CSS, when to load the file
337
+ * @type bool $in_footer A boolean determining if the javascript should be loaded on the footer
338
+ * @type array|object $localize Variables needed on the JavaScript side {
339
+ * @type string $name Name of the JS variable
340
+ * @type string|array $data Contents of the JS variable
 
 
 
341
  * }
342
+ * @type callable[] $conditionals An callable method or an array of them, that will determine if the asset is loaded or not
343
  * }
344
  *
345
+ * @return string
346
  */
347
+ public function register( $origin, $slug, $file, $deps = array(), $action = null, $arguments = array() ) {
348
+ // Prevent weird stuff here
349
  $slug = sanitize_title_with_dashes( $slug );
350
 
351
  if ( $this->exists( $slug ) ) {
353
  }
354
 
355
  if ( is_string( $origin ) ) {
356
+ // Origin needs to be a class with a `instance` method and a Version constant
357
  if ( class_exists( $origin ) && method_exists( $origin, 'instance' ) && defined( $origin . '::VERSION' ) ) {
358
+ $origin = call_user_func( array( $origin, 'instance' ) );
359
  }
360
  }
361
 
363
  $origin_name = get_class( $origin );
364
 
365
  if ( ! defined( $origin_name . '::VERSION' ) ) {
366
+ // If we have a Object and we don't have instance or version
367
  return false;
368
  }
369
  } else {
370
  return false;
371
  }
372
 
373
+ // Fetches the version on the Origin Version constant
374
  $version = constant( $origin_name . '::VERSION' );
375
 
376
+ // Default variables to prevent notices
377
+ $defaults = array(
378
  'slug' => null,
379
  'file' => false,
380
  'url' => false,
381
  'action' => null,
382
  'priority' => 10,
383
  'type' => null,
384
+ 'deps' => array(),
385
+ 'groups' => array(),
386
  'version' => $version,
387
  'media' => 'all',
388
  'in_footer' => true,
391
  'origin_url' => null,
392
  'origin_name' => null,
393
 
394
+ // Bigger Variables at the end
395
+ 'localize' => array(),
396
+ 'conditionals' => array(),
397
+ );
398
 
399
+ // Merge Arguments
400
  $asset = (object) wp_parse_args( $arguments, $defaults );
401
 
402
+ // Enforce these one
403
  $asset->slug = $slug;
404
  $asset->file = $file;
405
  $asset->deps = $deps;
407
  $asset->origin_path = trailingslashit( ! empty( $origin->plugin_path ) ? $origin->plugin_path : $origin->pluginPath );
408
  $asset->origin_name = $origin_name;
409
 
410
+ // Origin URL might throw notices so we double check
411
  $asset->origin_url = ! empty( $origin->plugin_url ) ? $origin->plugin_url : null;
412
  $asset->origin_url = ! empty( $origin->pluginUrl ) ? $origin->pluginUrl : null;
413
  if ( ! empty( $asset->origin_url ) ) {
414
  $asset->origin_url = trailingslashit( $asset->origin_url );
415
  }
416
 
417
+ // If we don't have a type on the arguments we grab from the File path
418
  if ( is_null( $asset->type ) ) {
419
  if ( substr( $asset->file, -3, 3 ) === '.js' ) {
420
  $asset->type = 'js';
423
  }
424
  }
425
 
426
+ // If asset type is wrong don't register
427
+ if ( ! in_array( $asset->type, array( 'js', 'css' ) ) ) {
428
  return false;
429
  }
430
 
431
  /**
432
+ * Deprecated filter to allow changing version based on the type of Asset
433
  *
434
  * @todo remove on 4.6
435
  * @deprecated 4.3
439
  $asset->version = apply_filters( "tribe_events_{$asset->type}_version", $asset->version );
440
 
441
  /**
442
+ * Filter to change version number on assets
 
 
443
  *
444
  * @param string $version
445
  * @param object $asset
451
  $asset->in_footer = (bool) $asset->in_footer;
452
  $asset->media = esc_attr( $asset->media );
453
 
454
+ // Ensures that we have a priority over 1
455
  if ( $asset->priority < 1 ) {
456
  $asset->priority = 1;
457
  }
458
 
459
  $is_vendor = strpos( $asset->file, 'vendor/' ) !== false ? true : false;
460
 
461
+ // Setup the actual URL
462
  if ( filter_var( $asset->file, FILTER_VALIDATE_URL ) ) {
463
  $asset->url = $asset->file;
464
  } else {
465
  $asset->url = $this->maybe_get_min_file( tribe_resource_url( $asset->file, false, ( $is_vendor ? '' : null ), $origin ) );
466
  }
467
 
468
+ // If you are passing localize, you need `name` and `data`
469
+ if ( ! empty( $asset->localize ) && ( is_array( $asset->localize ) || is_object( $asset->localize ) ) ) {
470
+ if ( is_array( $asset->localize ) && empty( $asset->localize['name'] ) ) {
471
+ foreach ( $asset->localize as $index => $local ) {
472
+ $asset->localize[ $index ] = (object) $local;
473
+ }
474
+ } else {
475
+ $asset->localize = (object) $asset->localize;
476
+
477
+ // if we don't have both reset localize
478
+ if ( ! isset( $asset->localize->data, $asset->localize->name ) ) {
479
+ $asset->localize = array();
480
+ }
481
+ }
482
+ }
483
 
484
+ // Looks for a single conditional callable and places it in an Array
485
  if ( ! empty( $asset->conditionals ) && is_callable( $asset->conditionals ) ) {
486
+ $asset->conditionals = array( $asset->conditionals );
487
  }
488
 
489
+ // Groups is always an array of unique strings
490
  if ( ! empty( $asset->groups ) ) {
491
  $asset->groups = (array) $asset->groups;
492
  $asset->groups = array_filter( $asset->groups, 'is_string' );
494
  }
495
 
496
  /**
497
+ * Filter an Asset loading variables
 
 
498
  *
499
  * @param object $asset
500
  */
501
  $asset = apply_filters( 'tribe_asset_pre_register', $asset );
502
 
503
+ // Set the Asset on the array of notices
504
  $this->assets[ $slug ] = $asset;
505
 
506
+ // Sorts by priority
507
+ uasort( $this->assets, array( $this, 'order_by_priority' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
 
509
+ // Return the Slug because it might be modified
510
  return $asset;
511
  }
512
 
513
  /**
514
+ * Removes an Asset from been registered and enqueue
515
  *
516
  * @since 4.3
517
  *
518
+ * @param string $slug Slug of the Asset
519
  *
520
  * @return bool
521
  */
529
  }
530
 
531
  /**
532
+ * Get the Asset Object configuration
533
  *
534
  * @since 4.3
 
535
  *
536
+ * @param string $slug Slug of the Asset
 
537
  *
538
+ * @return bool
 
539
  */
540
+ public function get( $slug = null ) {
541
+ uasort( $this->assets, array( $this, 'order_by_priority' ) );
542
+
543
  if ( is_null( $slug ) ) {
 
 
 
 
 
 
 
 
 
 
 
544
  return $this->assets;
545
  }
546
 
547
+ // Prevent weird stuff here
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  $slug = sanitize_title_with_dashes( $slug );
549
 
550
  if ( ! empty( $this->assets[ $slug ] ) ) {
555
  }
556
 
557
  /**
558
+ * Add the Priority ordering, which was causing an issue of not respecting which order stuff was registered
559
+ *
560
+ * @since 4.7
561
+ *
562
+ * @param object $a First Subject to compare
563
+ * @param object $b Second subject to compare
564
+ *
565
+ * @return boolean
566
+ */
567
+ public function order_by_priority( $a, $b ) {
568
+ return (int) $a->priority === (int) $b->priority ? 0 : (int) $a->priority > (int) $b->priority;
569
+ }
570
+
571
+ /**
572
+ * Checks if an Asset exists
573
  *
574
+ * @param string $slug Slug of the Asset
575
  *
576
  * @return bool
577
  */
common/src/Tribe/Autoloader.php CHANGED
@@ -121,14 +121,6 @@
121
  public function register_prefix( $prefix, $root_dir, $slug = '' ) {
122
  $root_dir = $this->normalize_root_dir( $root_dir );
123
 
124
- // Determine if we need to normalize the $prefix.
125
- $is_namespaced = false !== strpos( $prefix, '\\' );
126
-
127
- if ( $is_namespaced ) {
128
- // If the prefix is a namespace, then normalize it.
129
- $prefix = trim( $prefix, '\\' ) . '\\';
130
- }
131
-
132
  if ( ! isset( $this->prefixes[ $prefix ] ) ) {
133
  $this->prefixes[ $prefix ] = array();
134
  }
@@ -171,20 +163,11 @@
171
 
172
  protected function get_prefixed_path( $class ) {
173
  foreach ( $this->prefixes as $prefix => $dirs ) {
174
- $is_namespaced = false !== strpos( $prefix, '\\' );
175
-
176
  if ( strpos( $class, $prefix ) !== 0 ) {
177
  continue;
178
  }
179
-
180
  $class_name = str_replace( $prefix, '', $class );
181
-
182
- if ( ! $is_namespaced ) {
183
- $class_path_frag = implode( '/', explode( $this->dir_separator, $class_name ) ) . '.php';
184
- } else {
185
- $class_path_frag = implode( '/', explode( '\\', $class_name ) ) . '.php';
186
- }
187
-
188
  foreach ( $dirs as $dir ) {
189
  $path = $dir . '/' . $class_path_frag;
190
  if ( ! file_exists( $path ) ) {
121
  public function register_prefix( $prefix, $root_dir, $slug = '' ) {
122
  $root_dir = $this->normalize_root_dir( $root_dir );
123
 
 
 
 
 
 
 
 
 
124
  if ( ! isset( $this->prefixes[ $prefix ] ) ) {
125
  $this->prefixes[ $prefix ] = array();
126
  }
163
 
164
  protected function get_prefixed_path( $class ) {
165
  foreach ( $this->prefixes as $prefix => $dirs ) {
 
 
166
  if ( strpos( $class, $prefix ) !== 0 ) {
167
  continue;
168
  }
 
169
  $class_name = str_replace( $prefix, '', $class );
170
+ $class_path_frag = implode( '/', explode( $this->dir_separator, $class_name ) ) . '.php';
 
 
 
 
 
 
171
  foreach ( $dirs as $dir ) {
172
  $path = $dir . '/' . $class_path_frag;
173
  if ( ! file_exists( $path ) ) {
common/src/Tribe/Cache.php CHANGED
@@ -10,41 +10,23 @@
10
  * When used in its ArrayAccess API the cache will provide non persistent storage.
11
  */
12
  class Tribe__Cache implements ArrayAccess {
13
- const SCHEDULED_EVENT_DELETE_TRANSIENT = 'tribe_schedule_transient_purge';
14
-
15
- const NO_EXPIRATION = 0;
16
-
17
- const NON_PERSISTENT = -1;
18
 
19
  /**
20
  * @var array
21
  */
22
- protected $non_persistent_keys = [];
23
-
24
- /**
25
- * Bootstrap hook
26
- *
27
- * @since 4.11.0
28
- */
29
- public function hook() {
30
- if ( ! wp_next_scheduled( self::SCHEDULED_EVENT_DELETE_TRANSIENT ) ) {
31
- wp_schedule_event( time(), 'twicedaily', self::SCHEDULED_EVENT_DELETE_TRANSIENT );
32
- }
33
-
34
- add_action( self::SCHEDULED_EVENT_DELETE_TRANSIENT, [ $this, 'delete_expired_transients' ] );
35
-
36
- add_action( 'shutdown', [ $this, 'maybe_delete_expired_transients' ] );
37
- }
38
 
39
  public static function setup() {
40
- wp_cache_add_non_persistent_groups( [ 'tribe-events-non-persistent' ] );
41
  }
42
 
43
  /**
44
- * @param string $id
45
- * @param mixed $value
46
- * @param int $expiration
47
- * @param string|array $expiration_trigger
48
  *
49
  * @return bool
50
  */
@@ -55,13 +37,13 @@ class Tribe__Cache implements ArrayAccess {
55
  * Filters the expiration for cache objects to provide the ability
56
  * to make non-persistent objects be treated as persistent.
57
  *
58
- * @since 4.8
 
 
 
 
59
  *
60
- * @param int $expiration Cache expiration time.
61
- * @param string $id Cache ID.
62
- * @param mixed $value Cache value.
63
- * @param string|array $expiration_trigger Action that triggers automatic expiration.
64
- * @param string $key Unique cache key based on Cache ID and expiration trigger last run time.
65
  */
66
  $expiration = apply_filters( 'tribe_cache_expiration', $expiration, $id, $value, $expiration_trigger, $key );
67
 
@@ -79,10 +61,10 @@ class Tribe__Cache implements ArrayAccess {
79
  }
80
 
81
  /**
82
- * @param $id
83
- * @param $value
84
- * @param int $expiration
85
- * @param string|array $expiration_trigger
86
  *
87
  * @return bool
88
  */
@@ -95,18 +77,17 @@ class Tribe__Cache implements ArrayAccess {
95
  *
96
  * Note: When a default value or callback is specified, this value gets set in the cache.
97
  *
98
- * @param string $id The key for the cached value.
99
- * @param string|array $expiration_trigger Optional. Hook to trigger cache invalidation.
100
- * @param mixed $default Optional. A default value or callback that returns a default value.
101
- * @param int $expiration Optional. When the default value expires, if it gets set.
102
- * @param mixed $args Optional. Args passed to callback.
103
  *
104
  * @return mixed
105
  */
106
- public function get( $id, $expiration_trigger = '', $default = false, $expiration = 0, $args = [] ) {
107
- $flipped = array_flip( $this->non_persistent_keys );
108
- $group = isset( $flipped[ $id ] ) ? 'tribe-events-non-persistent' : 'tribe-events';
109
- $value = wp_cache_get( $this->get_id( $id, $expiration_trigger ), $group );
110
 
111
  // Value found.
112
  if ( false !== $value ) {
@@ -130,8 +111,8 @@ class Tribe__Cache implements ArrayAccess {
130
  }
131
 
132
  /**
133
- * @param string $id
134
- * @param string|array $expiration_trigger
135
  *
136
  * @return mixed
137
  */
@@ -140,8 +121,8 @@ class Tribe__Cache implements ArrayAccess {
140
  }
141
 
142
  /**
143
- * @param string $id
144
- * @param string|array $expiration_trigger
145
  *
146
  * @return bool
147
  */
@@ -150,8 +131,8 @@ class Tribe__Cache implements ArrayAccess {
150
  }
151
 
152
  /**
153
- * @param string $id
154
- * @param string|array $expiration_trigger
155
  *
156
  * @return bool
157
  */
@@ -160,175 +141,39 @@ class Tribe__Cache implements ArrayAccess {
160
  }
161
 
162
  /**
163
- * Purge all expired tribe_ transients.
164
- *
165
- * This uses a modification of the the query from https://core.trac.wordpress.org/ticket/20316
166
- *
167
- * @since 4.11.0
168
- *
169
- * @return void Just execute the database SQL no return required.
170
- */
171
- public function delete_expired_transients() {
172
- if ( tribe_get_var( 'has_deleted_expired_transients', false ) ) {
173
- return;
174
- }
175
-
176
- global $wpdb;
177
-
178
- $time = time();
179
-
180
- $sql = "
181
- DELETE
182
- a,
183
- b
184
- FROM
185
- {$wpdb->options} a
186
- INNER JOIN {$wpdb->options} b
187
- ON b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
188
- AND b.option_value < {$time}
189
- WHERE
190
- a.option_name LIKE '\_transient\_tribe\_%'
191
- AND a.option_name NOT LIKE '\_transient\_timeout\_tribe\_%'
192
- ";
193
-
194
- /**
195
- * Allow third party filtering of the SQL used for deleting expired transients.
196
- *
197
- * @since 4.11.5
198
- *
199
- * @param string $sql The SQL we execute to delete all the expired transients.
200
- * @param int $time Time we are using to determine what is expired.
201
- */
202
- $sql = apply_filters( 'tribe_cache_delete_expired_transients_sql', $sql, $time );
203
-
204
- if ( empty( $sql ) ) {
205
- return;
206
- }
207
-
208
- $wpdb->query( $sql );
209
-
210
- // Set the variable to prevent this call from running twice.
211
- tribe_set_var( 'has_deleted_expired_transients', true );
212
- }
213
-
214
- /**
215
- * Flag if we should delete
216
- *
217
- * @since 4.11.5
218
- *
219
- * @param boolean $value If we should delete transients or not on shutdown.
220
- *
221
- * @return void No return for setting the flag.
222
- */
223
- public function flag_required_delete_transients( $value = true ) {
224
- tribe_set_var( 'should_delete_expired_transients', $value );
225
- }
226
-
227
- /**
228
- * Runs on hook `shutdown` and will delete transients on the end of the request.
229
- *
230
- * @since 4.11.5
231
- *
232
- * @return void No return for action hook method.
233
- */
234
- public function maybe_delete_expired_transients() {
235
- if ( ! tribe_get_var( 'should_delete_expired_transients', false ) ) {
236
- return;
237
- }
238
-
239
- $this->delete_expired_transients();
240
- }
241
-
242
- /**
243
- * @param string $key
244
- * @param string|array $expiration_trigger
245
  *
246
  * @return string
247
  */
248
  public function get_id( $key, $expiration_trigger = '' ) {
249
- if ( is_array( $expiration_trigger ) ) {
250
- $triggers = $expiration_trigger;
251
- } else {
252
- $triggers = array_filter( explode( '|', $expiration_trigger ) );
253
- }
254
-
255
- $last = 0;
256
- foreach ( $triggers as $trigger ) {
257
- // Bail on empty trigger otherwise it creates a `tribe_last_` opt on the DB.
258
- if ( empty( $trigger ) ) {
259
- continue;
260
- }
261
-
262
- $occurrence = $this->get_last_occurrence( $trigger );
263
-
264
- if ( $occurrence > $last ) {
265
- $last = $occurrence;
266
- }
267
- }
268
-
269
- $last = empty( $last ) ? '' : $last;
270
  $id = $key . $last;
271
- if ( strlen( $id ) > 80 ) {
272
- $id = 'tribe_' . md5( $id );
273
  }
274
 
275
  return $id;
276
  }
277
 
278
  /**
279
- * Returns the time of an action last occurrence.
280
- *
281
- * @since 4.9.14 Changed the return value type from `int` to `float`.
282
- *
283
- * @param string $action The action to return the time for.
284
  *
285
- * @return float The time (microtime) an action last occurred, or the current microtime if it never occurred.
286
  */
287
  public function get_last_occurrence( $action ) {
288
- static $cache_var_name = __METHOD__;
289
-
290
- $cache_last_actions = tribe_get_var( $cache_var_name, [] );
291
-
292
- if ( isset( $cache_last_actions[ $action ] ) ) {
293
- return $cache_last_actions[ $action ];
294
- }
295
-
296
- $last_action = (float) get_option( 'tribe_last_' . $action, null );
297
-
298
- if ( ! $last_action ) {
299
- $last_action = microtime( true );
300
- $this->set_last_occurrence( $action, $last_action );
301
- }
302
-
303
- $cache_last_actions[ $action ] = (float) $last_action;
304
-
305
- tribe_set_var( $cache_var_name, $cache_last_actions );
306
-
307
- return $cache_last_actions[ $action ];
308
  }
309
 
310
  /**
311
- * Sets the time (microtime) for an action last occurrence.
312
- *
313
- * @since 4.9.14 Changed the type of the time stored from an `int` to a `float`.
314
- *
315
- * @param string $action The action to record the last occurrence of.
316
- * @param int|float $timestamp The timestamp to assign to the action last occurrence or the current time (microtime).
317
- *
318
- * @return boolean IF we were able to set the last occurrence or not.
319
  */
320
  public function set_last_occurrence( $action, $timestamp = 0 ) {
321
  if ( empty( $timestamp ) ) {
322
- $timestamp = microtime( true );
323
  }
324
- $updated = update_option( 'tribe_last_' . $action, (float) $timestamp );
325
-
326
- // For performance reasons we will only expire cache once per request, when needed.
327
- if ( $updated ) {
328
- $this->flag_required_delete_transients( true );
329
- }
330
-
331
- return $updated;
332
  }
333
 
334
  /**
@@ -336,14 +181,13 @@ class Tribe__Cache implements ArrayAccess {
336
  *
337
  * @param mixed $components Either a single component of the key or an array of key components.
338
  * @param string $prefix
339
- * @param bool $sort Whether component arrays should be sorted or not to generate the key; defaults to
340
- * `true`.
341
  *
342
  * @return string The resulting key.
343
  */
344
  public function make_key( $components, $prefix = '', $sort = true ) {
345
- $key = '';
346
- $components = is_array( $components ) ? $components : [ $components ];
347
  foreach ( $components as $component ) {
348
  if ( $sort && is_array( $component ) ) {
349
  $is_associative = count( array_filter( array_keys( $component ), 'is_numeric' ) ) < count( array_keys( $component ) );
@@ -360,140 +204,65 @@ class Tribe__Cache implements ArrayAccess {
360
  }
361
 
362
  /**
363
- * Whether a offset exists.
364
- *
365
- * @since 4.11.0
366
  *
367
  * @link http://php.net/manual/en/arrayaccess.offsetexists.php
368
- *
369
- * @param mixed $offset An offset to check for.
370
- *
371
- * @return boolean Whether the offset exists in the cache.
 
 
 
 
372
  */
373
  public function offsetExists( $offset ) {
374
- $flipped = array_flip( $this->non_persistent_keys );
375
-
376
- return isset( $flipped[ $offset ] );
377
  }
378
 
379
  /**
380
- * Offset to retrieve.
381
  *
382
  * @link http://php.net/manual/en/arrayaccess.offsetget.php
383
- *
384
- * @since 4.11.0
385
- *
386
- * @param mixed $offset The offset to retrieve.
387
- *
388
  * @return mixed Can return all value types.
 
389
  */
390
  public function offsetGet( $offset ) {
391
  return $this->get( $offset );
392
  }
393
 
394
  /**
395
- * Offset to set.
396
- *
397
- * @since 4.11.0
398
  *
399
  * @link http://php.net/manual/en/arrayaccess.offsetset.php
400
- *
401
- * @param mixed $offset The offset to assign the value to.
402
- * @param mixed $value The value to set.
403
- *
 
 
404
  * @return void
 
405
  */
406
  public function offsetSet( $offset, $value ) {
407
  $this->set( $offset, $value, self::NON_PERSISTENT );
408
  }
409
 
410
  /**
411
- * Offset to unset.
412
- *
413
- * @since 4.11.0
414
  *
415
  * @link http://php.net/manual/en/arrayaccess.offsetunset.php
416
- *
417
- * @param mixed $offset The offset to unset.
418
- *
419
  * @return void
 
420
  */
421
  public function offsetUnset( $offset ) {
422
  $this->delete( $offset );
423
  }
 
424
 
425
- /**
426
- * Warms up the caches for a collection of posts.
427
- *
428
- * @since 4.10.2
429
- *
430
- * @param array|int $post_ids A post ID, or a collection of post IDs.
431
- * @param bool $update_post_meta_cache Whether to warm-up the post meta cache for the posts or not.
432
- */
433
- public function warmup_post_caches( $post_ids, $update_post_meta_cache = false ) {
434
- if ( empty( $post_ids ) ) {
435
- return;
436
- }
437
-
438
- $post_ids = (array) $post_ids;
439
-
440
- global $wpdb;
441
-
442
- $already_cached_ids = [];
443
- foreach ( $post_ids as $post_id ) {
444
- if ( wp_cache_get( $post_id, 'posts' ) instanceof \WP_Post ) {
445
- $already_cached_ids[] = $post_id;
446
- }
447
- }
448
-
449
- $required = array_diff( $post_ids, $already_cached_ids );
450
-
451
- if ( empty( $required ) ) {
452
- return;
453
- }
454
-
455
- /** @var Tribe__Feature_Detection $feature_detection */
456
- $feature_detection = tribe( 'feature-detection' );
457
- $limit = $feature_detection->mysql_limit_for_example( 'post_result' );
458
-
459
- /**
460
- * Filters the LIMIT that should be used to warm-up post caches and postmeta caches (if the
461
- * `$update_post_meta_cache` parameter is `true`).
462
- *
463
- * Lower this value on less powerful hosts. Return `0` to disable the warm-up completely, and `-1` to remove the
464
- * limit (not recommended).
465
- *
466
- * @since 4.10.2
467
- *
468
- * @param int $limit The number of posts whose caches will be warmed up, per query.
469
- */
470
- $limit = (int) apply_filters( 'tribe_cache_warmup_post_cache_limit', min( $limit, count( $post_ids ) ) );
471
-
472
- if ( 0 === $limit ) {
473
- // Warmup disabled.
474
- return;
475
- }
476
-
477
- $buffer = $post_ids;
478
- $page = 0;
479
-
480
- do {
481
- $limit_clause = $limit < 0 ? sprintf( 'LIMIT %d,%d', $limit * $page, $limit ) : '';
482
- $page++;
483
- $these_ids = array_splice( $buffer, 0, $limit );
484
- $interval = implode( ',', array_map( 'absint', $these_ids ) );
485
- $posts_query = "SELECT * FROM {$wpdb->posts} WHERE ID IN ({$interval}) {$limit_clause}";
486
- $post_objects = $wpdb->get_results( $posts_query );
487
- if ( is_array( $post_objects ) && ! empty( $post_objects ) ) {
488
- foreach ( $post_objects as $post_object ) {
489
- $post = new \WP_Post( $post_object );
490
- wp_cache_set( $post_object->ID, $post, 'posts' );
491
- }
492
-
493
- if ( $update_post_meta_cache ) {
494
- update_meta_cache( 'post', $these_ids );
495
- }
496
- }
497
- } while ( ! empty( $post_objects ) && is_array( $post_objects ) && count( $post_objects ) < count( $post_ids ) );
498
- }
499
- }
10
  * When used in its ArrayAccess API the cache will provide non persistent storage.
11
  */
12
  class Tribe__Cache implements ArrayAccess {
13
+ const NO_EXPIRATION = 0;
14
+ const NON_PERSISTENT = - 1;
 
 
 
15
 
16
  /**
17
  * @var array
18
  */
19
+ protected $non_persistent_keys = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  public static function setup() {
22
+ wp_cache_add_non_persistent_groups( array( 'tribe-events-non-persistent' ) );
23
  }
24
 
25
  /**
26
+ * @param string $id
27
+ * @param mixed $value
28
+ * @param int $expiration
29
+ * @param string $expiration_trigger
30
  *
31
  * @return bool
32
  */
37
  * Filters the expiration for cache objects to provide the ability
38
  * to make non-persistent objects be treated as persistent.
39
  *
40
+ * @param int $expiration Cache expiration time.
41
+ * @param string $id Cache ID.
42
+ * @param mixed $value Cache value.
43
+ * @param string $expiration_trigger Action that triggers automatic expiration.
44
+ * @param string $key Unique cache key based on Cache ID and expiration trigger last run time.
45
  *
46
+ * @since 4.8
 
 
 
 
47
  */
48
  $expiration = apply_filters( 'tribe_cache_expiration', $expiration, $id, $value, $expiration_trigger, $key );
49
 
61
  }
62
 
63
  /**
64
+ * @param $id
65
+ * @param $value
66
+ * @param int $expiration
67
+ * @param string $expiration_trigger
68
  *
69
  * @return bool
70
  */
77
  *
78
  * Note: When a default value or callback is specified, this value gets set in the cache.
79
  *
80
+ * @param string $id The key for the cached value.
81
+ * @param string $expiration_trigger Optional. Hook to trigger cache invalidation.
82
+ * @param mixed $default Optional. A default value or callback that returns a default value.
83
+ * @param int $expiration Optional. When the default value expires, if it gets set.
84
+ * @param mixed $args Optional. Args passed to callback.
85
  *
86
  * @return mixed
87
  */
88
+ public function get( $id, $expiration_trigger = '', $default = false, $expiration = 0, $args = array() ) {
89
+ $group = in_array( $id, $this->non_persistent_keys ) ? 'tribe-events-non-persistent' : 'tribe-events';
90
+ $value = wp_cache_get( $this->get_id( $id, $expiration_trigger ), $group );
 
91
 
92
  // Value found.
93
  if ( false !== $value ) {
111
  }
112
 
113
  /**
114
+ * @param string $id
115
+ * @param string $expiration_trigger
116
  *
117
  * @return mixed
118
  */
121
  }
122
 
123
  /**
124
+ * @param string $id
125
+ * @param string $expiration_trigger
126
  *
127
  * @return bool
128
  */
131
  }
132
 
133
  /**
134
+ * @param string $id
135
+ * @param string $expiration_trigger
136
  *
137
  * @return bool
138
  */
141
  }
142
 
143
  /**
144
+ * @param string $key
145
+ * @param string $expiration_trigger
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  *
147
  * @return string
148
  */
149
  public function get_id( $key, $expiration_trigger = '' ) {
150
+ $last = empty( $expiration_trigger ) ? '' : $this->get_last_occurrence( $expiration_trigger );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  $id = $key . $last;
152
+ if ( strlen( $id ) > 40 ) {
153
+ $id = md5( $id );
154
  }
155
 
156
  return $id;
157
  }
158
 
159
  /**
160
+ * @param string $action
 
 
 
 
161
  *
162
+ * @return int
163
  */
164
  public function get_last_occurrence( $action ) {
165
+ return (int) get_option( 'tribe_last_' . $action, time() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  }
167
 
168
  /**
169
+ * @param string $action
170
+ * @param int $timestamp
 
 
 
 
 
 
171
  */
172
  public function set_last_occurrence( $action, $timestamp = 0 ) {
173
  if ( empty( $timestamp ) ) {
174
+ $timestamp = time();
175
  }
176
+ update_option( 'tribe_last_' . $action, (int) $timestamp );
 
 
 
 
 
 
 
177
  }
178
 
179
  /**
181
  *
182
  * @param mixed $components Either a single component of the key or an array of key components.
183
  * @param string $prefix
184
+ * @param bool $sort Whether component arrays should be sorted or not to generate the key; defaults to `true`.
 
185
  *
186
  * @return string The resulting key.
187
  */
188
  public function make_key( $components, $prefix = '', $sort = true ) {
189
+ $key = '';
190
+ $components = is_array( $components ) ? $components : array( $components );
191
  foreach ( $components as $component ) {
192
  if ( $sort && is_array( $component ) ) {
193
  $is_associative = count( array_filter( array_keys( $component ), 'is_numeric' ) ) < count( array_keys( $component ) );
204
  }
205
 
206
  /**
207
+ * Whether a offset exists
 
 
208
  *
209
  * @link http://php.net/manual/en/arrayaccess.offsetexists.php
210
+ * @param mixed $offset <p>
211
+ * An offset to check for.
212
+ * </p>
213
+ * @return boolean true on success or false on failure.
214
+ * </p>
215
+ * <p>
216
+ * The return value will be casted to boolean if non-boolean was returned.
217
+ * @since 5.0.0
218
  */
219
  public function offsetExists( $offset ) {
220
+ return in_array( $offset, $this->non_persistent_keys );
 
 
221
  }
222
 
223
  /**
224
+ * Offset to retrieve
225
  *
226
  * @link http://php.net/manual/en/arrayaccess.offsetget.php
227
+ * @param mixed $offset <p>
228
+ * The offset to retrieve.
229
+ * </p>
 
 
230
  * @return mixed Can return all value types.
231
+ * @since 5.0.0
232
  */
233
  public function offsetGet( $offset ) {
234
  return $this->get( $offset );
235
  }
236
 
237
  /**
238
+ * Offset to set
 
 
239
  *
240
  * @link http://php.net/manual/en/arrayaccess.offsetset.php
241
+ * @param mixed $offset <p>
242
+ * The offset to assign the value to.
243
+ * </p>
244
+ * @param mixed $value <p>
245
+ * The value to set.
246
+ * </p>
247
  * @return void
248
+ * @since 5.0.0
249
  */
250
  public function offsetSet( $offset, $value ) {
251
  $this->set( $offset, $value, self::NON_PERSISTENT );
252
  }
253
 
254
  /**
255
+ * Offset to unset
 
 
256
  *
257
  * @link http://php.net/manual/en/arrayaccess.offsetunset.php
258
+ * @param mixed $offset <p>
259
+ * The offset to unset.
260
+ * </p>
261
  * @return void
262
+ * @since 5.0.0
263
  */
264
  public function offsetUnset( $offset ) {
265
  $this->delete( $offset );
266
  }
267
+ }
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Cache_Listener.php CHANGED
@@ -14,11 +14,6 @@
14
  */
15
  const TRIGGER_SAVE_POST = 'save_post';
16
 
17
- /**
18
- * The name of the trigger that will be fired when an option is updated
19
- */
20
- const TRIGGER_UPDATED_OPTION = 'updated_option';
21
-
22
  /**
23
  * The singleton instance of the class.
24
  *
@@ -58,7 +53,6 @@
58
  */
59
  private function add_hooks() {
60
  add_action( 'save_post', [ $this, 'save_post' ], 0, 2 );
61
- add_action( 'updated_option', [ $this, 'update_last_updated_option' ], 10, 3 );
62
  add_action( 'updated_option', [ $this, 'update_last_save_post' ], 10, 3 );
63
  add_action( 'generate_rewrite_rules', [ $this, 'generate_rewrite_rules' ] );
64
  }
@@ -85,46 +79,16 @@
85
  * @param mixed $value The new option value.
86
  */
87
  public function update_last_save_post( $option_name, $old_value, $value ) {
88
- $triggers = [
89
- 'tribe_events_calendar_options' => true,
90
- 'permalink_structure' => true,
91
- 'rewrite_rules' => true,
92
- 'start_of_week' => true,
93
- ];
94
- if ( ! empty( $triggers[ $option_name ] ) ) {
95
  $this->cache->set_last_occurrence( self::TRIGGER_SAVE_POST );
96
  }
97
  }
98
 
99
- /**
100
- * Run the caching functionality that is executed on saving tribe calendar options.
101
- *
102
- * @see 'updated_option'
103
- *
104
- * @since 4.11.0
105
- *
106
- * @param string $option_name Name of the updated option.
107
- * @param mixed $old_value The old option value.
108
- * @param mixed $value The new option value.
109
- */
110
- public function update_last_updated_option( $option_name, $old_value, $value ) {
111
- $triggers = [
112
- 'active_plugins' => true,
113
- 'tribe_events_calendar_options' => true,
114
- 'permalink_structure' => true,
115
- 'rewrite_rules' => true,
116
- 'start_of_week' => true,
117
- 'sidebars_widgets' => true,
118
- 'stylesheet' => true,
119
- 'template' => true,
120
- 'WPLANG' => true,
121
- ];
122
-
123
- if ( ! empty( $triggers[ $option_name ] ) ) {
124
- $this->cache->set_last_occurrence( self::TRIGGER_UPDATED_OPTION );
125
- }
126
- }
127
-
128
  /**
129
  * For any hook that doesn't need any additional filtering
130
  *
14
  */
15
  const TRIGGER_SAVE_POST = 'save_post';
16
 
 
 
 
 
 
17
  /**
18
  * The singleton instance of the class.
19
  *
53
  */
54
  private function add_hooks() {
55
  add_action( 'save_post', [ $this, 'save_post' ], 0, 2 );
 
56
  add_action( 'updated_option', [ $this, 'update_last_save_post' ], 10, 3 );
57
  add_action( 'generate_rewrite_rules', [ $this, 'generate_rewrite_rules' ] );
58
  }
79
  * @param mixed $value The new option value.
80
  */
81
  public function update_last_save_post( $option_name, $old_value, $value ) {
82
+ $triggers = array(
83
+ 'tribe_events_calendar_options',
84
+ 'permalink_structure',
85
+ 'rewrite_rules',
86
+ );
87
+ if ( in_array( $option_name, $triggers, true ) ) {
 
88
  $this->cache->set_last_occurrence( self::TRIGGER_SAVE_POST );
89
  }
90
  }
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  /**
93
  * For any hook that doesn't need any additional filtering
94
  *
common/src/Tribe/Container.php CHANGED
@@ -224,52 +224,6 @@ if ( ! function_exists( 'tribe_get_var' ) ) {
224
  }
225
  }
226
 
227
- if ( ! function_exists( 'tribe_unset_var' ) ) {
228
- /**
229
- * Returns the value of a registered variable.
230
- *
231
- * Example use:
232
- *
233
- * tribe_set_var( 'tec.url', 'http://example.com' );
234
- *
235
- * tribe_unset_var( 'tec.url' );
236
- *
237
- * @since 4.11.0
238
- *
239
- * @param string $slug The slug of the variable registered using `tribe_unset_var`.
240
- *
241
- * @return void
242
- */
243
- function tribe_unset_var( $slug ) {
244
- $container = Tribe__Container::init();
245
- try {
246
- $container->offsetUnset( $slug );
247
- } catch ( Exception $e ) {}
248
- }
249
- }
250
-
251
- if ( ! function_exists( 'tribe_isset_var' ) ) {
252
- /**
253
- * Returns the value of a registered variable.
254
- *
255
- * Example use:
256
- *
257
- * tribe_set_var( 'tec.url', 'http://example.com' );
258
- *
259
- * tribe_isset_var( 'tec.url' );
260
- *
261
- * @since 4.11.0
262
- *
263
- * @param string $slug The slug of the variable checked using `tribe_isset_var`.
264
- *
265
- * @return boolean Either a the given slug exists.
266
- */
267
- function tribe_isset_var( $slug ) {
268
- $container = Tribe__Container::init();
269
- return $container->offsetExists( $slug );
270
- }
271
- }
272
-
273
  if ( ! function_exists( 'tribe_register_provider' ) ) {
274
  /**
275
  * Registers a service provider in the container.
224
  }
225
  }
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  if ( ! function_exists( 'tribe_register_provider' ) ) {
228
  /**
229
  * Registers a service provider in the container.
common/src/Tribe/Context.php CHANGED
@@ -59,13 +59,6 @@ class Tribe__Context {
59
  */
60
  const QUERY_PROP = 'query_prop';
61
 
62
- /**
63
- * The key to locate a context value as the value of the main query (global `$wp_query`) method return value.
64
- *
65
- * @since 4.9.20
66
- */
67
- const QUERY_METHOD = 'query_method';
68
-
69
  /**
70
  * The key to locate a context value as the value of a constant.
71
  *
@@ -136,13 +129,6 @@ class Tribe__Context {
136
  */
137
  const WP_MATCHED_QUERY = 'wp_matched_query';
138
 
139
- /**
140
- * The key to indicate a location should be read by applying a callback to the value of another context location.
141
- *
142
- * @since 4.9.18
143
- */
144
- const LOCATION_FUNC = 'location_func';
145
-
146
  /*
147
  *
148
  * An array defining the properties the context will be able to read and (dangerously) write.
@@ -169,7 +155,6 @@ class Tribe__Context {
169
  * method - get the value calling a method on a tribe() container binding.
170
  * func - get the value from a function or a closure.
171
  * filter - get the value by applying a filter.
172
- * location_func - get the value by applying a callback to the value of a location.
173
  *
174
  * For each location additional arguments can be specified:
175
  * orm_arg - if `false` then the location will never produce an ORM argument, if provided the ORM arg produced bye the
@@ -182,1164 +167,1146 @@ class Tribe__Context {
182
  *
183
  * @var array
184
  */
185
- protected static $locations = [];
186
 
187
- /**
188
- * A utility static property keeping track of write locations that
189
- * will be defined as associative arrays.
190
- *
191
- * @var array
192
- */
193
- protected static $associative_locations = array(
194
- self::TRANSIENT,
195
- self::METHOD,
196
- self::STATIC_METHOD,
197
- self::PROP,
198
- self::STATIC_PROP,
199
- );
200
 
201
- /**
202
- * Whether the static dynamic locations were set or not.
203
- *
204
- * @var bool
205
- */
206
- protected static $did_populate_locations = false;
207
 
208
- /**
209
- * A list of override locations to read and write from.
210
- *
211
- * This list has the same format and options as the static `$locations` property
212
- * but allows a context instance to override, or add, read and write locations.
213
- *
214
- * @var array
215
- */
216
- protected $override_locations = array();
217
 
218
- /**
219
- * Whether the context of the current HTTP request is an AJAX one or not.
220
- *
221
- * @var bool
222
- */
223
- protected $doing_ajax;
224
 
225
- /**
226
- * Whether the context of the current HTTP request is a Cron one or not.
227
- *
228
- * @var bool
229
- */
230
- protected $doing_cron;
231
 
232
- /**
233
- * A request-based array cache to store the values fetched by the context.
234
- *
235
- * @var array
236
- */
237
- protected $request_cache = array();
238
- /**
239
- * Whether this context should use the default locations or not.
240
- * This flag property is set to `false` when a context is obtained using
241
- * the `set_locations` method; it will otherwise be set to `true`.
242
- *
243
- * @var bool
244
- */
245
- protected $use_default_locations = true;
246
 
247
- /**
248
- * Whether we are currently creating a new post, a post of post type(s) or not.
249
- *
250
- * @since 4.7.7
251
- *
252
- * @param null $post_type The optional post type to check.
253
- *
254
- * @return bool Whether we are currently creating a new post, a post of post type(s) or not.
255
- */
256
- public function is_new_post( $post_type = null ) {
257
- global $pagenow;
258
- $is_new = 'post-new.php' === $pagenow;
259
 
260
- return $is_new && $this->is_editing_post( $post_type );
261
- }
 
 
 
 
 
 
 
 
 
 
262
 
263
- /**
264
- * Whether we are currently editing a post(s), post type(s) or not.
265
- *
266
- * @since 4.7.7
267
- *
268
- * @param null|array|string|int $post_or_type A post ID, post type, an array of post types or post IDs, `null`
269
- * to just make sure we are currently editing a post.
270
- *
271
- * @return bool
272
- */
273
- public function is_editing_post( $post_or_type = null ) {
274
- global $pagenow;
275
- $is_new = 'post-new.php' === $pagenow;
276
- $is_post = 'post.php' === $pagenow;
277
- $is_editing = 'edit.php' === $pagenow;
278
-
279
- if ( ! ( $is_new || $is_post || $is_editing ) ) {
280
- return false;
281
  }
282
 
283
- if ( ! empty( $post_or_type ) ) {
284
- $lookup = array( $_GET, $_POST, $_REQUEST );
285
-
286
- $current_post = Tribe__Utils__Array::get_in_any( $lookup, 'post', get_post() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
- if ( is_numeric( $post_or_type ) ) {
 
289
 
290
- $post = $is_post ? get_post( $post_or_type ) : null;
291
 
292
- return ! empty( $post ) && $post == $current_post;
293
- }
294
 
295
- $post_types = is_array( $post_or_type ) ? $post_or_type : array( $post_or_type );
296
 
297
- $post = $is_post ? get_post( $current_post ) : null;
 
298
 
299
- if ( count( array_filter( $post_types, 'is_numeric' ) ) === count( $post_types ) ) {
300
- return ! empty( $post ) && in_array( $post->ID, $post_types );
301
- }
302
 
303
- if ( $is_post && $post instanceof WP_Post ) {
304
- $post_type = $post->post_type;
305
- } else {
306
- $post_type = Tribe__Utils__Array::get_in_any( $lookup, 'post_type', 'post' );
307
- }
308
 
309
- return (bool) count( array_intersect( $post_types, array( $post_type ) ) );
310
- }
 
311
 
312
- return $is_new || $is_post;
313
- }
 
 
 
314
 
315
- /**
316
- * Helper function to indicate whether the current execution context is AJAX.
317
- *
318
- * This method exists to allow us test code that behaves differently depending on the execution
319
- * context.
320
- *
321
- * @since 4.7.12
322
- * @since 4.9.5 Removed the $doing_ajax parameter.
323
- *
324
- * @return boolean
325
- */
326
- public function doing_ajax() {
327
- return function_exists( 'wp_doing_ajax' )
328
- ? wp_doing_ajax()
329
- : defined( 'DOING_AJAX' ) && DOING_AJAX;
330
- }
331
 
332
- /**
333
- * Checks whether the context of the current HTTP request is a Cron one or not.
334
- *
335
- * @since 4.7.23
336
- * @since 4.9.5 Removed the $doing_cron parameter.
337
- *
338
- * @return bool Whether the context of the current HTTP request is a Cron one or not.
339
- */
340
- public function doing_cron() {
341
- return function_exists( 'wp_doing_cron' )
342
- ? wp_doing_cron()
343
- : defined( 'DOING_CRON' ) && DOING_CRON;
344
- }
345
 
346
- /**
347
- * Gets a value reading it from the location(s) defined in the `Tribe__Context::$props
348
- *
349
- * @since 4.9.5
350
- *
351
- * @param string $key The key of the variable to fetch.
352
- * @param mixed|null $default The default value to return if not found.
353
- * @param bool $force Whether to force the re-fetch of the value from the context or
354
- * not; defaults to `false`.
355
- *
356
- * @return mixed The value from the first location that can provide it or the default
357
- * value if not found.
358
- */
359
- public function get( $key, $default = null, $force = false ) {
360
  /**
361
- * Filters the value of a context variable skipping all of its logic.
362
  *
363
- * @since 4.9.5
 
 
 
 
364
  *
365
- * @param mixed $value The value for the key before it's fetched from the context.
366
- * @param string $key The key of the value to fetch from the context.
367
- * @param mixed $default The default value that should be returned if the value is
368
- * not set in the context.
369
- * @param bool $force Whether to force the re-fetch of the value from the context or
370
- * not; defaults to `false`.
371
  */
372
- $value = apply_filters( "tribe_context_pre_{$key}", null, $key, $default, $force );
373
- if ( null !== $value ) {
374
- return $value;
 
375
  }
376
 
377
- $value = $default;
378
- $locations = $this->get_locations();
379
- $found = false;
380
-
381
- if ( ! $force && isset( $this->request_cache[ $key ] ) ) {
382
- $value = $this->request_cache[ $key ];
383
- } elseif ( ! empty( $locations[ $key ]['read'] ) ) {
384
- foreach ( $locations[ $key ]['read'] as $location => $keys ) {
385
- $the_value = $this->$location( (array) $keys, $default );
386
-
387
- if ( $default !== $the_value && static::NOT_FOUND !== $the_value ) {
388
- $found = true;
389
- $value = $the_value;
390
- break;
391
- }
392
- }
393
  }
394
 
395
  /**
396
- * Filters the value fetched from the context for a key.
397
- *
398
- * Useful for testing and local override.
399
  *
400
  * @since 4.9.5
401
  *
402
- * @param mixed $value The value as fetched from the context.
 
 
 
 
 
 
403
  */
404
- $value = apply_filters( "tribe_context_{$key}", $value );
405
-
406
- // Only cache if the value was found.
407
- if ( $found ) {
408
- $this->request_cache[ $key ] = $value;
409
- }
 
 
 
 
 
 
 
 
 
 
 
410
 
411
- return $value;
412
- }
413
 
414
- /**
415
- * Alters the context.
416
- *
417
- * Due to its immutable nature setting values on the context will NOT modify the
418
- * context but return a modified clone.
419
- * If you need to modify the global context update the location(s) it should read from
420
- * and call the `refresh` method.
421
- * Example: `$widget_context = tribe_context()->alter( $widget_args );`.
422
- *
423
- * @since 4.9.5
424
- *
425
- * @param array $values An associative array of key-value pairs to modify the context.
426
- *
427
- * @return \Tribe__Context A clone, with modified, values, of the context the method was called on.
428
- */
429
- public function alter( array $values ) {
430
- $clone = clone $this;
431
 
432
- $clone->request_cache = array_merge( $clone->request_cache, $values );
 
 
 
 
 
433
 
434
- return $clone;
435
- }
 
 
 
 
 
 
 
 
436
 
437
- /**
438
- * Clears the context cache forcing a re-fetch of the variables from the context.
439
- *
440
- * @since 4.9.5
441
- *
442
- * @param string $key An optional specific key to refresh, if passed only this key
443
- * will be refreshed.
444
- */
445
- public function refresh( $key = null ) {
446
- if ( null !== $key ) {
447
- unset( $this->request_cache[ $key ] );
448
- } else {
449
- $this->request_cache = array();
450
  }
451
- }
452
 
453
- /**
454
- * Returns the read and write locations set on the context.
455
- *
456
- * @since 4.9.5
457
- *
458
- * @return array An array of read and write location in the shape of the `Tribe__Context::$locations` one,
459
- * `[ <location> => [ 'read' => <read_locations>, 'write' => <write_locations> ] ]`.
460
- */
461
- public function get_locations() {
462
- $this->populate_locations();
463
-
464
- $locations = $this->use_default_locations
465
- ? array_merge( self::$locations, $this->override_locations )
466
- : $this->override_locations;
467
-
468
- if ( $this->use_default_locations ) {
469
- /**
470
- * Filters the locations registered in the Context.
471
- *
472
- * @since 4.10.2
473
- *
474
- * @param array $locations An array of locations registered on the Context object.
475
- */
476
- $locations = apply_filters( 'tribe_context_locations', $locations, $this );
477
- }
478
 
479
- return $locations;
480
- }
481
 
482
- /**
483
- * Reads the value from one or more $_REQUEST vars.
484
- *
485
- * @since 4.9.5
486
- *
487
- * @param array $request_vars The list of request vars to lookup, in order.
488
- * @param mixed $default The default value to return.
489
- *
490
- * @return mixed The first valid value found or the default value.
491
- */
492
- protected function request_var( array $request_vars, $default ) {
493
- $value = $default;
494
-
495
- foreach ( $request_vars as $request_var ) {
496
- $the_value = tribe_get_request_var( $request_var, self::NOT_FOUND );
497
- if ( $the_value !== self::NOT_FOUND ) {
498
- $value = $the_value;
499
- break;
500
- }
501
  }
502
 
503
- return $value;
504
- }
505
-
506
- /**
507
- * Reads the value from one or more global WP_Query object query variables.
508
- *
509
- * @since 4.9.5
510
- *
511
- * @param array $query_vars The list of query vars to look up, in order.
512
- * @param mixed $default The default value to return.
513
- *
514
- * @return mixed The first valid value found or the default value.
515
- */
516
- protected function query_var( array $query_vars, $default ) {
517
- $value = $default;
518
-
519
- global $wp_query;
520
-
521
- if ( ! $wp_query instanceof \WP_Query ) {
522
- return $value;
523
  }
524
 
525
- foreach ( $query_vars as $query_var ) {
526
- $the_value = $wp_query->get( $query_var, self::NOT_FOUND );
527
- if ( $the_value !== self::NOT_FOUND ) {
528
- $value = $the_value;
529
- break;
530
- }
 
 
 
 
 
 
531
  }
532
 
533
- return $value;
534
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
 
536
- /**
537
- * Reads the value from one or more global WP_Query object properties.
538
- *
539
- * @since 4.9.5
540
- *
541
- * @param array $query_props The list of properties to look up, in order.
542
- * @param mixed $default The default value to return.
543
- *
544
- * @return mixed The first valid value found or the default value.
545
- */
546
- protected function query_prop( array $query_props, $default ) {
547
- $value = $default;
548
-
549
- global $wp_query;
550
- foreach ( $query_props as $query_prop ) {
551
- $the_value = isset( $wp_query->{$query_prop} ) ? $wp_query->{$query_prop} : self::NOT_FOUND;
552
- if ( $the_value !== self::NOT_FOUND ) {
553
- $value = $the_value;
554
- break;
555
- }
556
  }
557
 
558
- return $value;
559
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
 
561
- /**
562
- * Reads the value from one more more `tribe_option`s.
563
- *
564
- * @since 4.9.5
565
- *
566
- * @param array $tribe_options The list of `tribe_option`s to lookup, in order.
567
- * @param mixed $default The default value to return.
568
- *
569
- * @return mixed The first valid value found or the default value.
570
- */
571
- protected function tribe_option( array $tribe_options, $default ) {
572
- $value = $default;
573
-
574
- foreach ( $tribe_options as $option_name ) {
575
- $the_value = tribe_get_option( $option_name, self::NOT_FOUND );
576
- if ( $the_value !== self::NOT_FOUND ) {
577
- $value = $the_value;
578
- break;
579
- }
580
  }
581
 
582
- return $value;
583
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
 
585
- /**
586
- * Reads the value from one or more options.
587
- *
588
- * @since 4.9.5
589
- *
590
- * @param array $options The list of options to lookup, in order.
591
- * @param mixed $default The default value to return.
592
- *
593
- * @return mixed The first valid value found or the default value.
594
- */
595
- protected function option( array $options, $default ) {
596
- $value = $default;
597
-
598
- foreach ( $options as $option_name ) {
599
- $the_value = get_option( $option_name, self::NOT_FOUND );
600
- if ( $the_value !== self::NOT_FOUND ) {
601
- $value = $the_value;
602
- break;
603
- }
604
  }
605
 
606
- return $value;
607
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
 
609
- /**
610
- * Reads the value from one or more transients.
611
- *
612
- * @since 4.9.5
613
- *
614
- * @param array $transients The list of transients to lookup, in order.
615
- * @param mixed $default The default value to return.
616
- *
617
- * @return mixed The first valid value found or the default value.
618
- */
619
- protected function transient( array $transients, $default ) {
620
- $value = $default;
621
-
622
- foreach ( $transients as $transient ) {
623
- $the_value = get_transient( $transient );
624
- if ( false !== $the_value ) {
625
- $value = $the_value;
626
- /*
627
- * This will fail when the value is actually `false`.
628
- */
629
- break;
630
- }
631
  }
632
 
633
- return $value;
634
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
 
636
- /**
637
- * Reads the value from one or more constants.
638
- *
639
- * @since 4.9.5
640
- *
641
- * @param array $constants The list of constants to lookup, in order.
642
- * @param mixed $default The default value to return.
643
- *
644
- * @return mixed The first valid value found or the default value.
645
- */
646
- protected function constant( array $constants, $default ) {
647
- $value = $default;
648
-
649
- foreach ( $constants as $constant ) {
650
- $the_value = defined( $constant ) ? constant( $constant ) : self::NOT_FOUND;
651
- if ( $the_value !== self::NOT_FOUND ) {
652
- $value = $the_value;
653
- break;
654
- }
655
  }
656
 
657
- return $value;
658
- }
659
-
660
- /**
661
- * Reads the value from one or more global variable.
662
- *
663
- * @since 4.9.5
664
- *
665
- * @param array $global_vars The list of global variables to look up, in order.
666
- * @param mixed $default The default value to return.
667
- *
668
- * @return mixed The first valid value found or the default value.
669
- */
670
- protected function global_var( array $global_vars, $default ) {
671
- $value = $default;
672
-
673
- foreach ( $global_vars as $var ) {
674
- $the_value = isset( $GLOBALS[ $var ] ) ? $GLOBALS[ $var ] : self::NOT_FOUND;
675
- if ( $the_value !== self::NOT_FOUND ) {
676
- $value = $the_value;
677
- break;
678
- }
679
- }
680
 
681
- return $value;
682
- }
683
 
684
- /**
685
- * Reads the value from one or more class static properties.
686
- *
687
- * @since 4.9.5
688
- *
689
- * @param array $classes_and_props An associative array in the shape [ <class> => <prop> ].
690
- * @param mixed $default The default value to return.
691
- *
692
- * @return mixed The first valid value found or the default value.
693
- */
694
- protected function static_prop( array $classes_and_props, $default ) {
695
- $value = $default;
696
-
697
- foreach ( $classes_and_props as $class => $prop ) {
698
- if ( class_exists( $class ) ) {
699
- // PHP 5.2 compat, on PHP 5.3+ $class::$$prop
700
- $vars = get_class_vars( $class );
701
- $the_value = isset( $vars[ $prop ] ) ? $vars[ $prop ] : self::NOT_FOUND;
702
-
703
- if ( $the_value !== self::NOT_FOUND ) {
704
- $value = $the_value;
705
- break;
706
  }
707
- }
 
708
  }
709
 
710
- return $value;
711
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
712
 
713
- /**
714
- * Reads the value from one or more properties of implementations bound in the `tribe()` container.
715
- *
716
- * @since 4.9.5
717
- *
718
- * @param array $bindings_and_props An associative array in the shape [ <binding> => <prop> ].
719
- * @param mixed $default The default value to return.
720
- *
721
- * @return mixed The first valid value found or the default value.
722
- */
723
- protected function prop( array $bindings_and_props, $default ) {
724
- $value = $default;
725
 
726
- foreach ( $bindings_and_props as $binding => $prop ) {
727
- $the_value = tribe()->offsetExists( $binding ) && property_exists( tribe( $binding ), $prop )
728
- ? tribe( $binding )->{$prop}
729
- : self::NOT_FOUND;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
 
731
- if ( $the_value !== self::NOT_FOUND ) {
732
- $value = $the_value;
733
- break;
734
- }
735
  }
736
 
737
- return $value;
738
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
739
 
740
- /**
741
- * Reads the values from one or more static class methods.
742
- *
743
- * @since 4.9.5
744
- *
745
- * @param array $classes_and_methods An associative array in the shape [ <class> => <method> ].
746
- * @param mixed $default The default value to return.
747
- *
748
- * @return mixed The first value that's not equal to the default one, the default value
749
- * otherwise.
750
- */
751
- protected function static_method( array $classes_and_methods, $default ) {
752
- $value = $default;
753
 
754
- foreach ( $classes_and_methods as $class => $method ) {
755
- $the_value = class_exists( $class ) && method_exists( $class, $method )
756
- ? call_user_func( array( $class, $method ) )
757
- : self::NOT_FOUND;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
 
759
- if ( $the_value !== self::NOT_FOUND ) {
760
- $value = $the_value;
761
- break;
762
- }
763
  }
764
 
765
- return $value;
766
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
 
768
- /**
769
- * Reads the value from one or more methods called on implementations bound in the `tribe()` container.
770
- *
771
- * @since 4.9.5
772
- *
773
- * @param array $bindings_and_methods An associative array in the shape [ <binding> => <method> ].
774
- * @param mixed $default The default value to return.
775
- *
776
- * @return mixed The first value that's not equal to the default one, the default value
777
- * otherwise.
778
- */
779
- protected function method( array $bindings_and_methods, $default ) {
780
- $value = $default;
781
- $the_value = self::NOT_FOUND;
782
 
783
- foreach ( $bindings_and_methods as $binding => $method ) {
784
- if ( tribe()->offsetExists( $binding ) ) {
785
- $implementation = tribe( $binding );
786
- if ( method_exists( $implementation, $method ) ) {
787
- $the_value = $implementation->$method();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788
  }
789
- }
790
 
791
- if ( $the_value !== self::NOT_FOUND ) {
792
- $value = $the_value;
793
- break;
794
- }
795
  }
796
 
797
- return $value;
798
- }
799
-
800
- /**
801
- * Reads the value from one or more functions until one returns a value that's not the default one.
802
- *
803
- * @since 4.9.5
804
- *
805
- * @param array $functions An array of functions to call, in order.
806
- * @param mixed $default The default value to return.
807
- *
808
- * @return mixed The first value that's not equal to the default one, the default value
809
- * otherwise.
810
- */
811
- protected function func( array $functions, $default ) {
812
- $value = $default;
813
- $the_value = self::NOT_FOUND;
 
814
 
815
- foreach ( $functions as $function ) {
816
- if ( is_callable( $function ) || function_exists( $function ) ) {
817
- $the_value = $function();
818
- }
 
819
 
820
- if ( $the_value !== self::NOT_FOUND ) {
821
- $value = $the_value;
822
- break;
823
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
824
  }
825
 
826
- return $value;
827
- }
828
-
829
- /**
830
- * Modifies the global context using the defined write locations to persist the altered values.
831
- *
832
- * Please keep in mind this will set the the global context for the whole request and, when the
833
- * write location is an option, to the database.
834
- * With great power comes great responsibility: think a lot before using this.
835
- *
836
- * @param array|null $fields An optional whitelist or blacklist of fields to write
837
- * depending on the value of the `$whitelist` parameter;
838
- * defaults to writing all available fields.
839
- * @param bool $whitelist Whether the list of fields provided in the `$fields`
840
- * parameter should be treated as a whitelist (`true`) or
841
- * blacklist (`false`).
842
- *
843
- * @since 4.9.5
844
- */
845
- public function dangerously_set_global_context( array $fields = null, $whitelist = true ) {
846
- $locations = $this->get_locations();
847
-
848
- if ( null !== $fields ) {
849
- $locations = $whitelist
850
- ? array_intersect_key( $locations, array_combine( $fields, $fields ) )
851
- : array_diff_key( $locations, array_combine( $fields, $fields ) );
852
  }
853
 
854
  /**
855
- * Here we intersect with the request cache to only write values we've actually read
856
- * or modified. If none of the two happened then there's no need to write anything.
 
 
 
 
857
  */
858
- foreach ( array_intersect_key( $this->request_cache, $locations ) as $key => $value ) {
859
- if ( ! isset( $locations[ $key ]['write'] ) ) {
860
- continue;
861
- }
862
 
863
- foreach ( (array) $locations[ $key ]['write'] as $location => $targets ) {
864
- $targets = (array) $targets;
865
- $write_func = 'write_' . $location;
866
 
867
- foreach ( $targets as $arg_1 => $arg_2 ) {
868
- if ( self::FUNC === $location && is_array( $arg_2 ) && is_callable( $arg_2 ) ) {
869
- // Handles write functions specified as an array.
870
- $location_args = array( $arg_2 );
871
- } else {
872
- $location_args = in_array( $location, self::$associative_locations, true )
873
- ? array( $arg_1, $arg_2 )
874
- : (array) $arg_2;
875
- }
876
 
877
- $args = array_merge( $location_args, array( $value ) );
 
 
 
 
 
 
 
 
 
878
 
879
- call_user_func_array( array( $this, $write_func ), $args );
 
880
  }
881
- }
882
- }
883
- }
884
 
885
- /**
886
- * Writes an altered context value to a request var.
887
- *
888
- * @since 4.9.5
889
- *
890
- * @param string $request_var The request var to write.
891
- * @param mixed $value The value to set on the request var.
892
- */
893
- protected function write_request_var( $request_var, $value ) {
894
- if ( isset( $_REQUEST ) ) {
895
- $_REQUEST[ $request_var ] = $value;
896
  }
897
- if ( isset( $_GET ) ) {
898
- $_GET[ $request_var ] = $value;
 
 
 
 
 
 
 
 
 
899
  }
900
- if ( isset( $_POST ) ) {
901
- $_POST[ $request_var ] = $value;
 
 
 
 
 
 
 
 
 
902
  }
903
- }
904
 
905
- /**
906
- * Writes an altered context value to a global WP_Query object properties.
907
- *
908
- * @since 4.9.5
909
- *
910
- * @param string $query_prop The global WP_Query object property to write.
911
- * @param mixed $value The value to set on the query property.
912
- */
913
- protected function write_query_prop( $query_prop, $value ) {
914
- global $wp_query;
 
 
915
 
916
- if ( ! $wp_query instanceof WP_Query ) {
917
- return;
 
 
 
 
 
 
 
 
 
 
 
918
  }
919
 
920
- $wp_query->{$query_prop} = $value;
921
- }
 
 
 
 
 
 
 
 
 
922
 
923
- /**
924
- * Writes an altered context value to a global WP_Query object query var.
925
- *
926
- * @since 4.9.5
927
- *
928
- * @param string $query_var The global WP_Query query var to write.
929
- * @param mixed $value The value to set on the query var.
930
- */
931
- protected function write_query_var( $query_var, $value ) {
932
- global $wp_query;
 
 
 
933
 
934
- if ( ! $wp_query instanceof WP_Query ) {
935
- return;
936
  }
937
 
938
- $wp_query->set( $query_var, $value );
939
- }
940
-
941
- /**
942
- * Writes an altered context value to a `tribe_option`.
943
- *
944
- * @since 4.9.5
945
- *
946
- * @param string $tribe_option The `tribe_option` to write.
947
- * @param mixed $value The value to set on the `tribe_option`.
948
- */
949
- protected function write_tribe_option( $tribe_option, $value ) {
950
- tribe_update_option( $tribe_option, $value );
951
- }
952
 
953
- /**
954
- * Writes an altered context value to an option.
955
- *
956
- * @since 4.9.5
957
- *
958
- * @param string $option_name The option to write.
959
- * @param mixed $value The value to set on the option.
960
- */
961
- protected function write_option( $option_name, $value ) {
962
- update_option( $option_name, $value );
963
- }
964
 
965
- /**
966
- * Writes an altered context value to a transient.
967
- *
968
- * @since 4.9.5
969
- *
970
- * @param string $transient The transient to write.
971
- * @param int $expiration The transient expiration time, in seconds.
972
- * @param mixed $value The value to set on the transient.
973
- */
974
- protected function write_transient( $transient, $expiration, $value ) {
975
- set_transient( $transient, $value, $expiration );
976
- }
977
 
978
- /**
979
- * Writes an altered context value to a constant.
980
- *
981
- * @since 4.9.5
982
- *
983
- * @param string $constant The constant to define.
984
- * @param mixed $value The value to set on the constant.
985
- */
986
- protected function write_constant( $constant, $value ) {
987
- if ( defined( $constant ) ) {
988
- return;
989
  }
990
- define( $constant, $value );
991
- }
992
 
993
- /**
994
- * Writes an altered context value to a global var.
995
- *
996
- * @since 4.9.5
997
- *
998
- * @param string $global_var The global var to set.
999
- * @param mixed $value The value to set on the global_var.
1000
- */
1001
- protected function write_global_var( $global_var, $value ) {
1002
- $GLOBALS[ $global_var ] = $value;
1003
- }
1004
-
1005
- /**
1006
- * Writes an altered context value setting a public static property on a class.
1007
- *
1008
- * @since 4.9.5
1009
- *
1010
- * @param string $class The class to set the static public property on.
1011
- * @param string $prop The static public property to set.
1012
- * @param mixed $value The value to set on the property.
1013
- */
1014
- protected function write_static_prop( $class, $prop, $value ) {
1015
- if ( ! ( class_exists( $class ) && property_exists( $class, $prop ) ) ) {
1016
- return;
1017
  }
1018
 
1019
- $class::$$prop = $value;
1020
- }
1021
-
1022
- /**
1023
- * Writes an altered context value setting a public property on a `tribe()` binding.
1024
- *
1025
- * @since 4.9.5
1026
- *
1027
- * @param string $binding The container binding to set the public property on.
1028
- * @param string $prop The public property to set.
1029
- * @param mixed $value The value to set on the property.
1030
- */
1031
- protected function write_prop( $binding, $prop, $value ) {
1032
- if ( ! tribe()->offsetExists( $binding ) ) {
1033
- return;
1034
  }
1035
 
1036
- $implementation = tribe( $binding );
1037
-
1038
- if ( ! property_exists( $implementation, $prop ) ) {
1039
- return;
 
 
 
 
 
 
 
 
 
1040
  }
1041
 
1042
- $implementation->{$prop} = $value;
1043
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1044
 
1045
- /**
1046
- * Writes an altered context value calling a public static method on a class.
1047
- *
1048
- * @since 4.9.5
1049
- *
1050
- * @param string $class The class to call the public static method on.
1051
- * @param string $method The static method to call.
1052
- * @param mixed $value The value to pass to the public static method.
1053
- */
1054
- protected function write_static_method( $class, $method, $value ) {
1055
- if ( ! class_exists( $class ) ) {
1056
- return;
1057
  }
1058
- call_user_func( array( $class, $method ), $value );
1059
- }
1060
 
1061
- /**
1062
- * Writes an altered context value calling a public method on a `tribe()` binding.
1063
- *
1064
- * @since 4.9.5
1065
- *
1066
- * @param string $binding The `tribe()` container binding to call the public method on.
1067
- * @param string $method The method to call.
1068
- * @param mixed $value The value to pass to the public method.
1069
- */
1070
- protected function write_method( $binding, $method, $value ) {
1071
- if ( ! tribe()->offsetExists( $binding ) ) {
1072
- return;
1073
- }
1074
- call_user_func( array( tribe( $binding ), $method ), $value );
1075
- }
 
 
1076
 
1077
- /**
1078
- * Writes an altered context value calling a function or closure.
1079
- *
1080
- * @since 4.9.5
1081
- *
1082
- * @param callable $func function, closure or callable to call.
1083
- * @param mixed $value The value to pass to the callable.
1084
- */
1085
- protected function write_func( $func, $value ) {
1086
- if ( ! is_callable( $func ) ) {
1087
- return;
1088
  }
1089
- call_user_func( $func, $value );
1090
- }
1091
 
1092
- /**
1093
- * Adds/replaces read and write locations to a context.
1094
- *
1095
- * Locations are merged with an `array_merge` call. To refine the locations get them first with the
1096
- * `get_locations` method.
1097
- *
1098
- * @since 4.9.5
1099
- *
1100
- * @param array $locations An array of read and write locations to add to the context.
1101
- * The array should have the same shape as the static `$locations`
1102
- * one: `[ <location> => [ 'read' => <read_locations>, 'write' => <write_locations> ] ]`.
1103
- *
1104
- *
1105
- * @return \Tribe__Context A clone of the current context with the additional read and
1106
- * write locations added.
1107
- */
1108
- public function add_locations( array $locations ) {
1109
- $clone = clone $this;
1110
- $clone->override_locations = array_merge( $clone->override_locations, $locations );
1111
-
1112
- return $clone;
1113
- }
1114
-
1115
- /**
1116
- * Sets, replacing them, the locations used by this context.
1117
- *
1118
- *
1119
- * @since 4.9.5
1120
- *
1121
- * @param array $locations An array of locations to replace the current ones.
1122
- * @param bool $use_default_locations Whether the context should use the default
1123
- * locations defined in the static `$locations`
1124
- * property or not.
1125
- *
1126
- * @return \Tribe__Context A clone of the current context with modified locations.
1127
- */
1128
- public function set_locations( array $locations, $use_default_locations = true ) {
1129
- $clone = clone $this;
1130
- $clone->override_locations = $locations;
1131
- $clone->use_default_locations = (bool) $use_default_locations;
1132
-
1133
- return $clone;
1134
- }
1135
 
1136
- /**
1137
- * Returns an array representation of the context.
1138
- *
1139
- * @since 4.9.5
1140
- *
1141
- * @return array An associative array of the context keys and values.
1142
- */
1143
- public function to_array( ) {
1144
- $locations = array_keys( array_merge( $this->get_locations(), $this->request_cache ) );
1145
- $dump = array();
1146
 
1147
- foreach ( $locations as $location ) {
1148
- $the_value = $this->get( $location, self::NOT_FOUND );
 
1149
 
1150
- if ( self::NOT_FOUND === $the_value ) {
1151
- continue;
1152
- }
1153
 
1154
- $dump[ $location ] = $the_value;
1155
  }
1156
 
1157
- return $dump;
1158
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1159
 
1160
- /**
1161
- * Returns the current context state in a format suitable to hydrate a Redux-like
1162
- * store on the front-end.
1163
- *
1164
- * This method is a filtered wrapper around the the `Tribe__Context::to_array` method to allow the
1165
- * customization of the format when producing a store-compatible state.
1166
- *
1167
- * @param array|null $fields An optional whitelist or blacklist of fields to include
1168
- * depending on the value of the `$whitelist` parameter;
1169
- * defaults to returning all available fields.
1170
- * @param bool $whitelist Whether the list of fields provided in the `$fields`
1171
- * parameter should be treated as a whitelist (`true`) or
1172
- * blacklist (`false`).
1173
- *
1174
- * @since 4.9.5
1175
- *
1176
- * @return array
1177
- */
1178
- public function get_state( array $fields = null, $whitelist = true ) {
1179
- $state = $this->to_array();
1180
- $is_global_context = tribe_context() === $this;
1181
-
1182
- if ( null !== $fields ) {
1183
- $state = $whitelist
1184
- ? array_intersect_key( $state, array_combine( $fields, $fields ) )
1185
- : array_diff_key( $state, array_combine( $fields, $fields ) );
 
 
1186
  }
1187
 
1188
  /**
1189
- * Filters the Redux store compatible state produced from the current context.
1190
  *
1191
  * @since 4.9.5
1192
  *
1193
- * @param array $state The Redux store compatible state produced from the current context.
1194
- * @param bool $is_global_context Whether the context producing the state is the global one
1195
- * or a modified clone of it.
1196
- * @param Tribe__Context The context object producing the state.
 
 
 
 
1197
  */
1198
- $state = apply_filters( 'tribe_context_state', $state, $is_global_context, $this );
1199
-
1200
- if ( $is_global_context ) {
1201
- /**
1202
- * Filters the Redux store compatible state produced from the global context.
1203
- *
1204
- * While the `tribe_context_state` filter will apply to all contexts producing a
1205
- * state this filter will only apply to the global context.
1206
- *
1207
- * @since 4.9.5
1208
- *
1209
- * @param array $state The Redux store compatible state produced from the global context.
1210
- * @param Tribe__Context The global context object producing the state.
1211
- */
1212
- $state = apply_filters( 'tribe_global_context_state', $state, $this );
1213
- }
1214
-
1215
- return $state;
1216
- }
1217
-
1218
- /**
1219
- * Returns an array of ORM arguments generated from the current context values.
1220
- *
1221
- * @since 4.9.5
1222
- *
1223
- * @param array|null $fields An optional whitelist or blacklist of fields to include
1224
- * depending on the value of the `$whitelist` parameter;
1225
- * defaults to returning all available fields.
1226
- * @param bool $whitelist Whether the list of fields provided in the `$fields`
1227
- * parameter should be treated as a whitelist (`true`) or
1228
- * blacklist (`false`).
1229
- *
1230
- * @return array A map of ORM fields produced from the context current values.
1231
- */
1232
- public function get_orm_args( array $fields = null, $whitelist = true ) {
1233
- $locations = $this->get_locations();
1234
- $dump = $this->to_array();
1235
- $orm_args = array();
1236
- $is_global_context = tribe_context() === $this;
1237
-
1238
- foreach ( $dump as $key => $value ) {
1239
- $alias = isset( $locations[ $key ]['orm_arg'] )
1240
- ? $locations[ $key ]['orm_arg']
1241
- : $key;
1242
-
1243
- if ( false === $alias ) {
1244
- // Do not provide the variable as an ORM arg.
1245
- continue;
1246
- }
1247
 
1248
- if ( isset( $locations[ $key ]['orm_transform'] ) ) {
1249
- $value = call_user_func( $locations[ $key ]['orm_transform'], $value );
1250
- }
 
 
 
 
 
 
1251
 
1252
- $orm_args[ $alias ] = $value;
1253
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1254
 
1255
- if ( null !== $fields ) {
1256
- /*
1257
- * Only keep wanted fields, the filtering is done on the resolved aliases,
1258
- * from the perspective of the client code that might ignore the source keys.
1259
- */
1260
- $orm_args = $whitelist
1261
- ? array_intersect_key( $orm_args, array_combine( $fields, $fields ) )
1262
- : array_diff_key( $orm_args, array_combine( $fields, $fields ) );
1263
  }
1264
 
1265
  /**
1266
- * Filters the ORM arguments produced from the current context.
1267
  *
1268
- * @since 4.9.5
1269
  *
1270
- * @param array $orm_args The ORM args produced from the current context.
1271
- * @param bool $is_global_context Whether the context producing the ORM args is the global one
1272
- * or a modified clone of it.
1273
- * @param Tribe__Context The context object producing the ORM args.
1274
  */
1275
- $orm_args = apply_filters( 'tribe_context_orm_args', $orm_args, $is_global_context, $this );
1276
-
1277
- if ( $is_global_context ) {
1278
- /**
1279
- * Filters the ORM arguments produced from the global context.
1280
- *
1281
- * While the `tribe_context_orm_args` filter will apply to all contexts producing ORM
1282
- * args this filter will only apply to the global context.
1283
- *
1284
- * @since 4.9.5
1285
- *
1286
- * @param array $orm_args The ORM args produced from the global context.
1287
- * @param Tribe__Context The global context object producing the ORM args.
1288
- */
1289
- $orm_args = apply_filters( 'tribe_global_context_orm_args', $orm_args, $this );
1290
- }
1291
 
1292
- return $orm_args;
1293
- }
1294
 
1295
- /**
1296
- * Sets some locations that can only be set at runtime.
1297
- *
1298
- * Using a flag locations are added only once per request.
1299
- *
1300
- * @since 4.9.8
1301
- */
1302
- protected function populate_locations() {
1303
- if ( static::$did_populate_locations ) {
1304
- return;
1305
- }
1306
 
1307
- // To improve the class readability, and as a small optimization, locations are loaded from a file.
1308
- static::$locations = include __DIR__ . '/Context/locations.php';
1309
 
1310
  /**
1311
- * Filters the locations registered in the Context.
1312
  *
1313
  * @since 4.9.8
1314
  *
1315
- * @param array $locations An array of locations registered on the Context object.
 
 
 
1316
  */
1317
- static::$locations = apply_filters( 'tribe_context_locations', static::$locations, $this );
1318
-
1319
- static::$did_populate_locations = true;
1320
- }
 
 
 
1321
 
1322
- /**
1323
- * Reads (gets) the value applying one or more filters.
1324
- *
1325
- * @since 4.9.8
1326
- *
1327
- * @param array $filters The list of filters to apply, in order.
1328
- * @param mixed $default The default value to return.
1329
- *
1330
- * @return mixed The first valid value found or the default value.
1331
- */
1332
- public function filter( array $filters, $default ) {
1333
- foreach ( $filters as $filter ) {
1334
- $the_value = apply_filters( $filter, $default );
1335
- if ( $the_value !== $default ) {
1336
- return $the_value;
1337
- }
1338
  }
1339
 
1340
- return $default;
1341
- }
1342
-
1343
  /**
1344
  * Reads (gets) the value reading it from a query var parsed from the global `$wp` object.
1345
  *
@@ -1449,176 +1416,4 @@ class Tribe__Context {
1449
 
1450
  return $mapped;
1451
  }
1452
-
1453
- /**
1454
- * Translates sub-locations to their respective location key.
1455
- *
1456
- * This method leverages the inherent knowledge of aliases stored in the Context locations to "translate" a
1457
- * sub-location to its location key.
1458
- * E.g. assume the `car` location is `read` from the [ 'carriage', 'vehicle', 'transport_mean' ] query var; calling
1459
- * `$context->populate_aliases( [ 'vehicle' => 'hyunday' ], 'read', Context::QUERY_VAR )` would yield
1460
- * `[ 'car' => 'hyunday' ]`.
1461
- *
1462
- * @since 4.9.12
1463
- *
1464
- * @param array $values An associative array of value to use as "masters" to populate the aliases.
1465
- * @param string $type The type of Context location to use, e.g. `Tribe__Context::QUERY_VAR`.
1466
- * @param string $direction The direction to use for the location, one of `read` or `write`.
1467
- *
1468
- * @return array The original array, merged with the populated values.
1469
- */
1470
- public function translate_sub_locations( array $values, $type, $direction = 'read' ) {
1471
- if ( ! in_array( $direction, [ 'read', 'write' ], true ) ) {
1472
- throw new \InvalidArgumentException(
1473
- "Direction must be one of `read` or `write`; `{$direction}` is not valid."
1474
- );
1475
- }
1476
-
1477
- $filled = [];
1478
- $locations = $this->get_locations();
1479
- $matching_locations = array_filter( $locations, static function ( $location ) use ( $type, $direction ) {
1480
- return isset( $location[ $direction ][ $type ] );
1481
- } );
1482
-
1483
- foreach ( $matching_locations as $key => $location ) {
1484
- $entry = (array)$location[ $direction ][ $type ];
1485
- $found = array_intersect( array_keys( $values ), array_merge( $entry, [ $key ] ) );
1486
- if ( $found ) {
1487
- $filled[ $key ] = $values[ reset( $found ) ];
1488
- }
1489
- }
1490
-
1491
- return $filled;
1492
- }
1493
-
1494
- /**
1495
- * Convenience method to get and check if a location has a truthy value or not.
1496
- *
1497
- * @since 4.9.18
1498
- *
1499
- * @param string $flag_key The location to check.
1500
- * @param bool $default The default value to return if the location is not set.
1501
- *
1502
- * @return bool Whether the location has a truthy value or not.
1503
- */
1504
- public function is( $flag_key, $default = false ) {
1505
- $val = $this->get( $flag_key, $default );
1506
-
1507
- return ! empty( $val ) || tribe_is_truthy( $val );
1508
- }
1509
-
1510
- /**
1511
- * Reads the value from one callback, passing it the value of another Context location.
1512
- *
1513
- * @since 4.9.18
1514
- *
1515
- * @param array $location_and_callback An array of two elements: the location key and the callback to call on the
1516
- * location value. The callback will receive the location value as argument.
1517
- *
1518
- * @return mixed The return value of the callback, called on the location value.
1519
- */
1520
- public function location_func( array $location_and_callback ) {
1521
- list( $location, $callback ) = $location_and_callback;
1522
-
1523
- return $callback( $this->get( $location ) );
1524
- }
1525
-
1526
- /**
1527
- * Checks whether the current request is a REST API one or not.
1528
- *
1529
- * @since 4.9.20
1530
- *
1531
- * @return bool Whether the current request is a REST API one or not.
1532
- */
1533
- public function doing_rest() {
1534
- return defined( 'REST_REQUEST' ) && REST_REQUEST;
1535
- }
1536
-
1537
- /**
1538
- * Reads the value from one or more global WP_Query object methods.
1539
- *
1540
- * @since 4.9.20
1541
- *
1542
- * @param array $query_vars The list of query methods to call, in order.
1543
- * @param mixed $default The default value to return if no method was defined on the global `WP_Query` object.
1544
- *
1545
- * @return mixed The first valid value found or the default value.
1546
- */
1547
- public function query_method( $methods, $default ) {
1548
- global $wp_query;
1549
- $found = $default;
1550
-
1551
- foreach ( $methods as $method ) {
1552
- $this_value = $wp_query instanceof WP_Query && method_exists( $wp_query, $method )
1553
- ? call_user_func( [ $wp_query, $method ] )
1554
- : static::NOT_FOUND;
1555
-
1556
- if ( static::NOT_FOUND !== $this_value ) {
1557
- return $this_value;
1558
- }
1559
- }
1560
-
1561
- return $found;
1562
- }
1563
-
1564
- /**
1565
- * Whether the current request is for a PHP-rendered initial state or not.
1566
- *
1567
- * This method is a shortcut to make sure we're not doing an AJAX, REST or Cron request.
1568
- *
1569
- * @since 4.9.20
1570
- *
1571
- * @return bool Whether the current request is for a PHP-rendered initial state or not.
1572
- */
1573
- public function doing_php_initial_state() {
1574
- return ! $this->doing_rest() && ! $this->doing_ajax() && ! $this->doing_cron();
1575
- }
1576
-
1577
- /**
1578
- * Returns the first key, if there are many, that will be used to read a location.
1579
- *
1580
- * The type ar
1581
- *
1582
- * @since 4.9.20
1583
- *
1584
- * @param string $location The location to get the read key for.
1585
- * @param string|null $type The type of read location to return the key for; default to `static::REQUEST_VAR`.
1586
- *
1587
- * @return string Either the first key for the type of read location, or the input location if not found.
1588
- */
1589
- public function get_read_key_for( $location, $type = null ) {
1590
- $type = $type ?: static::REQUEST_VAR;
1591
- $locations = $this->get_locations();
1592
- if ( isset( $locations[ $location ]['read'][ $type ] ) ) {
1593
- $keys = (array) $locations[ $location ]['read'][ $type ];
1594
- return reset( $keys );
1595
- }
1596
-
1597
- return $location;
1598
- }
1599
-
1600
- /**
1601
- * Safely set the value of a group of locations.
1602
- *
1603
- * This method can only augment the context, without altering it; it can only add new values.
1604
- *
1605
- * @since 4.10.2
1606
- *
1607
- * @param array|string $values The values to set, if not already set or the key of the value to set, requires
1608
- * the `$value` to be passed.
1609
- * @param mixed|null $value The value to set for the key, this parameter will be ignored if the `$values_or_key`
1610
- * parameter is not a string.
1611
- */
1612
- public function safe_set( $values_or_key, $value = null ) {
1613
- $values = func_num_args() === 2
1614
- ? [ $values_or_key => $value ]
1615
- : $values_or_key;
1616
-
1617
- foreach ( $values as $key => $val ) {
1618
- if ( static::NOT_FOUND !== $this->get( $key, static::NOT_FOUND ) ) {
1619
- continue;
1620
- }
1621
- $this->request_cache[ $key ] = $val;
1622
- }
1623
- }
1624
  }
59
  */
60
  const QUERY_PROP = 'query_prop';
61
 
 
 
 
 
 
 
 
62
  /**
63
  * The key to locate a context value as the value of a constant.
64
  *
129
  */
130
  const WP_MATCHED_QUERY = 'wp_matched_query';
131
 
 
 
 
 
 
 
 
132
  /*
133
  *
134
  * An array defining the properties the context will be able to read and (dangerously) write.
155
  * method - get the value calling a method on a tribe() container binding.
156
  * func - get the value from a function or a closure.
157
  * filter - get the value by applying a filter.
 
158
  *
159
  * For each location additional arguments can be specified:
160
  * orm_arg - if `false` then the location will never produce an ORM argument, if provided the ORM arg produced bye the
167
  *
168
  * @var array
169
  */
170
+ protected static $locations = [];
171
 
172
+ /**
173
+ * A utility static property keeping track of write locations that
174
+ * will be defined as associative arrays.
175
+ *
176
+ * @var array
177
+ */
178
+ protected static $associative_locations = array(
179
+ self::TRANSIENT,
180
+ self::METHOD,
181
+ self::STATIC_METHOD,
182
+ self::PROP,
183
+ self::STATIC_PROP,
184
+ );
185
 
186
+ /**
187
+ * Whether the static dynamic locations were set or not.
188
+ *
189
+ * @var bool
190
+ */
191
+ protected static $did_populate_locations = false;
192
 
193
+ /**
194
+ * A list of override locations to read and write from.
195
+ *
196
+ * This list has the same format and options as the static `$locations` property
197
+ * but allows a context instance to override, or add, read and write locations.
198
+ *
199
+ * @var array
200
+ */
201
+ protected $override_locations = array();
202
 
203
+ /**
204
+ * Whether the context of the current HTTP request is an AJAX one or not.
205
+ *
206
+ * @var bool
207
+ */
208
+ protected $doing_ajax;
209
 
210
+ /**
211
+ * Whether the context of the current HTTP request is a Cron one or not.
212
+ *
213
+ * @var bool
214
+ */
215
+ protected $doing_cron;
216
 
217
+ /**
218
+ * A request-based array cache to store the values fetched by the context.
219
+ *
220
+ * @var array
221
+ */
222
+ protected $request_cache = array();
223
+ /**
224
+ * Whether this context should use the default locations or not.
225
+ * This flag property is set to `false` when a context is obtained using
226
+ * the `set_locations` method; it will otherwise be set to `true`.
227
+ *
228
+ * @var bool
229
+ */
230
+ protected $use_default_locations = true;
231
 
232
+ /**
233
+ * Tribe__Context constructor.
234
+ *
235
+ * @since 4.9.8
236
+ */
237
+ public function __construct( ) {
238
+ $this->populate_locations();
239
+ }
 
 
 
 
240
 
241
+ /**
242
+ * Whether we are currently creating a new post, a post of post type(s) or not.
243
+ *
244
+ * @since 4.7.7
245
+ *
246
+ * @param null $post_type The optional post type to check.
247
+ *
248
+ * @return bool Whether we are currently creating a new post, a post of post type(s) or not.
249
+ */
250
+ public function is_new_post( $post_type = null ) {
251
+ global $pagenow;
252
+ $is_new = 'post-new.php' === $pagenow;
253
 
254
+ return $is_new && $this->is_editing_post( $post_type );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  }
256
 
257
+ /**
258
+ * Whether we are currently editing a post(s), post type(s) or not.
259
+ *
260
+ * @since 4.7.7
261
+ *
262
+ * @param null|array|string|int $post_or_type A post ID, post type, an array of post types or post IDs, `null`
263
+ * to just make sure we are currently editing a post.
264
+ *
265
+ * @return bool
266
+ */
267
+ public function is_editing_post( $post_or_type = null ) {
268
+ global $pagenow;
269
+ $is_new = 'post-new.php' === $pagenow;
270
+ $is_post = 'post.php' === $pagenow;
271
+ $is_editing = 'edit.php' === $pagenow;
272
+
273
+ if ( ! ( $is_new || $is_post || $is_editing ) ) {
274
+ return false;
275
+ }
276
 
277
+ if ( null !== $post_or_type ) {
278
+ $lookup = array( $_GET, $_POST, $_REQUEST );
279
 
280
+ $current_post = Tribe__Utils__Array::get_in_any( $lookup, 'post', get_post() );
281
 
282
+ if ( is_numeric( $post_or_type ) ) {
 
283
 
284
+ $post = $is_post ? get_post( $post_or_type ) : null;
285
 
286
+ return ! empty( $post ) && $post == $current_post;
287
+ }
288
 
289
+ $post_types = is_array( $post_or_type ) ? $post_or_type : array( $post_or_type );
 
 
290
 
291
+ $post = $is_post ? $current_post : null;
 
 
 
 
292
 
293
+ if ( count( array_filter( $post_types, 'is_numeric' ) ) === count( $post_types ) ) {
294
+ return ! empty( $post ) && in_array( $post->ID, $post_types );
295
+ }
296
 
297
+ if ( $is_post && $post instanceof WP_Post ) {
298
+ $post_type = $post->post_type;
299
+ } else {
300
+ $post_type = Tribe__Utils__Array::get_in_any( $lookup, 'post_type', 'post' );
301
+ }
302
 
303
+ return (bool) count( array_intersect( $post_types, array( $post_type ) ) );
304
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
 
306
+ return $is_new || $is_post;
307
+ }
 
 
 
 
 
 
 
 
 
 
 
308
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  /**
310
+ * Helper function to indicate whether the current execution context is AJAX.
311
  *
312
+ * This method exists to allow us test code that behaves differently depending on the execution
313
+ * context.
314
+ *
315
+ * @since 4.7.12
316
+ * @since 4.9.5 Removed the $doing_ajax parameter.
317
  *
318
+ * @return boolean
 
 
 
 
 
319
  */
320
+ public function doing_ajax() {
321
+ return function_exists( 'wp_doing_ajax' )
322
+ ? wp_doing_ajax()
323
+ : defined( 'DOING_AJAX' ) && DOING_AJAX;
324
  }
325
 
326
+ /**
327
+ * Checks whether the context of the current HTTP request is a Cron one or not.
328
+ *
329
+ * @since 4.7.23
330
+ * @since 4.9.5 Removed the $doing_cron parameter.
331
+ *
332
+ * @return bool Whether the context of the current HTTP request is a Cron one or not.
333
+ */
334
+ public function doing_cron() {
335
+ return function_exists( 'wp_doing_cron' )
336
+ ? wp_doing_cron()
337
+ : defined( 'DOING_CRON' ) && DOING_CRON;
 
 
 
 
338
  }
339
 
340
  /**
341
+ * Gets a value reading it from the location(s) defined in the `Tribe__Context::$props
 
 
342
  *
343
  * @since 4.9.5
344
  *
345
+ * @param string $key The key of the variable to fetch.
346
+ * @param mixed|null $default The default value to return if not found.
347
+ * @param bool $force Whether to force the re-fetch of the value from the context or
348
+ * not; defaults to `false`.
349
+ *
350
+ * @return mixed The value from the first location that can provide it or the default
351
+ * value if not found.
352
  */
353
+ public function get( $key, $default = null, $force = false ) {
354
+ /**
355
+ * Filters the value of a context variable skipping all of its logic.
356
+ *
357
+ * @since 4.9.5
358
+ *
359
+ * @param mixed $value The value for the key before it's fetched from the context.
360
+ * @param string $key The key of the value to fetch from the context.
361
+ * @param mixed $default The default value that should be returned if the value is
362
+ * not set in the context.
363
+ * @param bool $force Whether to force the re-fetch of the value from the context or
364
+ * not; defaults to `false`.
365
+ */
366
+ $value = apply_filters( "tribe_context_pre_{$key}", null, $key, $default, $force );
367
+ if ( null !== $value ) {
368
+ return $value;
369
+ }
370
 
371
+ $value = $default;
372
+ $locations = $this->get_locations();
373
 
374
+ if ( ! $force && isset( $this->request_cache[ $key ] ) ) {
375
+ $value = $this->request_cache[ $key ];
376
+ } elseif ( ! empty( $locations[ $key ]['read'] ) ) {
377
+ foreach ( $locations[ $key ]['read'] as $location => $keys ) {
378
+ $the_value = $this->$location( (array) $keys, $default );
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
+ if ( $default !== $the_value ) {
381
+ $value = $the_value;
382
+ break;
383
+ }
384
+ }
385
+ }
386
 
387
+ /**
388
+ * Filters the value fetched from the context for a key.
389
+ *
390
+ * Useful for testing and local override.
391
+ *
392
+ * @since 4.9.5
393
+ *
394
+ * @param mixed $value The value as fetched from the context.
395
+ */
396
+ $value = apply_filters( "tribe_context_{$key}", $value );
397
 
398
+ return $value;
 
 
 
 
 
 
 
 
 
 
 
 
399
  }
 
400
 
401
+ /**
402
+ * Alters the context.
403
+ *
404
+ * Due to its immutable nature setting values on the context will NOT modify the
405
+ * context but return a modified clone.
406
+ * If you need to modify the global context update the location(s) it should read from
407
+ * and call the `refresh` method.
408
+ * Example: `$widget_context = tribe_context()->alter( $widget_args );`.
409
+ *
410
+ * @since 4.9.5
411
+ *
412
+ * @param array $values An associative array of key-value pairs to modify the context.
413
+ *
414
+ * @return \Tribe__Context A clone, with modified, values, of the context the method was called on.
415
+ */
416
+ public function alter( array $values ) {
417
+ $clone = clone $this;
 
 
 
 
 
 
 
 
418
 
419
+ $clone->request_cache = array_merge( $clone->request_cache, $values );
 
420
 
421
+ return $clone;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
  }
423
 
424
+ /**
425
+ * Clears the context cache forcing a re-fetch of the variables from the context.
426
+ *
427
+ * @since 4.9.5
428
+ *
429
+ * @param string $key An optional specific key to refresh, if passed only this key
430
+ * will be refreshed.
431
+ */
432
+ public function refresh( $key = null ) {
433
+ if ( null !== $key ) {
434
+ unset( $this->request_cache[ $key ] );
435
+ } else {
436
+ $this->request_cache = array();
437
+ }
 
 
 
 
 
 
438
  }
439
 
440
+ /**
441
+ * Returns the read and write locations set on the context.
442
+ *
443
+ * @since 4.9.5
444
+ *
445
+ * @return array An array of read and write location in the shape of the `Tribe__Context::$locations` one,
446
+ * `[ <location> => [ 'read' => <read_locations>, 'write' => <write_locations> ] ]`.
447
+ */
448
+ public function get_locations() {
449
+ return $this->use_default_locations
450
+ ? array_merge( self::$locations, $this->override_locations )
451
+ : $this->override_locations;
452
  }
453
 
454
+ /**
455
+ * Reads the value from one or more $_REQUEST vars.
456
+ *
457
+ * @since 4.9.5
458
+ *
459
+ * @param array $request_vars The list of request vars to lookup, in order.
460
+ * @param mixed $default The default value to return.
461
+ *
462
+ * @return mixed The first valid value found or the default value.
463
+ */
464
+ protected function request_var( array $request_vars, $default ) {
465
+ $value = $default;
466
+
467
+ foreach ( $request_vars as $request_var ) {
468
+ $the_value = tribe_get_request_var( $request_var, self::NOT_FOUND );
469
+ if ( $the_value !== self::NOT_FOUND ) {
470
+ $value = $the_value;
471
+ break;
472
+ }
473
+ }
474
 
475
+ return $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  }
477
 
478
+ /**
479
+ * Reads the value from one or more global WP_Query object query variables.
480
+ *
481
+ * @since 4.9.5
482
+ *
483
+ * @param array $query_vars The list of query vars to look up, in order.
484
+ * @param mixed $default The default value to return.
485
+ *
486
+ * @return mixed The first valid value found or the default value.
487
+ */
488
+ protected function query_var( array $query_vars, $default ) {
489
+ $value = $default;
490
+
491
+ global $wp_query;
492
+ foreach ( $query_vars as $query_var ) {
493
+ $the_value = $wp_query->get( $query_var, self::NOT_FOUND );
494
+ if ( $the_value !== self::NOT_FOUND ) {
495
+ $value = $the_value;
496
+ break;
497
+ }
498
+ }
499
 
500
+ return $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  }
502
 
503
+ /**
504
+ * Reads the value from one or more global WP_Query object properties.
505
+ *
506
+ * @since 4.9.5
507
+ *
508
+ * @param array $query_props The list of properties to look up, in order.
509
+ * @param mixed $default The default value to return.
510
+ *
511
+ * @return mixed The first valid value found or the default value.
512
+ */
513
+ protected function query_prop( array $query_props, $default ) {
514
+ $value = $default;
515
+
516
+ global $wp_query;
517
+ foreach ( $query_props as $query_prop ) {
518
+ $the_value = isset( $wp_query->{$query_prop} ) ? $wp_query->{$query_prop} : self::NOT_FOUND;
519
+ if ( $the_value !== self::NOT_FOUND ) {
520
+ $value = $the_value;
521
+ break;
522
+ }
523
+ }
524
 
525
+ return $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  }
527
 
528
+ /**
529
+ * Reads the value from one more more `tribe_option`s.
530
+ *
531
+ * @since 4.9.5
532
+ *
533
+ * @param array $tribe_options The list of `tribe_option`s to lookup, in order.
534
+ * @param mixed $default The default value to return.
535
+ *
536
+ * @return mixed The first valid value found or the default value.
537
+ */
538
+ protected function tribe_option( array $tribe_options, $default ) {
539
+ $value = $default;
540
+
541
+ foreach ( $tribe_options as $option_name ) {
542
+ $the_value = tribe_get_option( $option_name, self::NOT_FOUND );
543
+ if ( $the_value !== self::NOT_FOUND ) {
544
+ $value = $the_value;
545
+ break;
546
+ }
547
+ }
548
 
549
+ return $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
  }
551
 
552
+ /**
553
+ * Reads the value from one or more options.
554
+ *
555
+ * @since 4.9.5
556
+ *
557
+ * @param array $options The list of options to lookup, in order.
558
+ * @param mixed $default The default value to return.
559
+ *
560
+ * @return mixed The first valid value found or the default value.
561
+ */
562
+ protected function option( array $options, $default ) {
563
+ $value = $default;
564
+
565
+ foreach ( $options as $option_name ) {
566
+ $the_value = get_option( $option_name, self::NOT_FOUND );
567
+ if ( $the_value !== self::NOT_FOUND ) {
568
+ $value = $the_value;
569
+ break;
570
+ }
571
+ }
572
 
573
+ return $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
  }
575
 
576
+ /**
577
+ * Reads the value from one or more transients.
578
+ *
579
+ * @since 4.9.5
580
+ *
581
+ * @param array $transients The list of transients to lookup, in order.
582
+ * @param mixed $default The default value to return.
583
+ *
584
+ * @return mixed The first valid value found or the default value.
585
+ */
586
+ protected function transient( array $transients, $default ) {
587
+ $value = $default;
588
+
589
+ foreach ( $transients as $transient ) {
590
+ $the_value = get_transient( $transient );
591
+ if ( false !== $the_value ) {
592
+ $value = $the_value;
593
+ /*
594
+ * This will fail when the value is actually `false`.
595
+ */
596
+ break;
597
+ }
598
+ }
599
 
600
+ return $value;
601
+ }
602
 
603
+ /**
604
+ * Reads the value from one or more constants.
605
+ *
606
+ * @since 4.9.5
607
+ *
608
+ * @param array $constants The list of constants to lookup, in order.
609
+ * @param mixed $default The default value to return.
610
+ *
611
+ * @return mixed The first valid value found or the default value.
612
+ */
613
+ protected function constant( array $constants, $default ) {
614
+ $value = $default;
615
+
616
+ foreach ( $constants as $constant ) {
617
+ $the_value = defined( $constant ) ? constant( $constant ) : self::NOT_FOUND;
618
+ if ( $the_value !== self::NOT_FOUND ) {
619
+ $value = $the_value;
620
+ break;
621
+ }
 
 
 
622
  }
623
+
624
+ return $value;
625
  }
626
 
627
+ /**
628
+ * Reads the value from one or more global variable.
629
+ *
630
+ * @since 4.9.5
631
+ *
632
+ * @param array $global_vars The list of global variables to look up, in order.
633
+ * @param mixed $default The default value to return.
634
+ *
635
+ * @return mixed The first valid value found or the default value.
636
+ */
637
+ protected function global_var( array $global_vars, $default ) {
638
+ $value = $default;
639
+
640
+ foreach ( $global_vars as $var ) {
641
+ $the_value = isset( $GLOBALS[ $var ] ) ? $GLOBALS[ $var ] : self::NOT_FOUND;
642
+ if ( $the_value !== self::NOT_FOUND ) {
643
+ $value = $the_value;
644
+ break;
645
+ }
646
+ }
647
 
648
+ return $value;
649
+ }
 
 
 
 
 
 
 
 
 
 
650
 
651
+ /**
652
+ * Reads the value from one or more class static properties.
653
+ *
654
+ * @since 4.9.5
655
+ *
656
+ * @param array $classes_and_props An associative array in the shape [ <class> => <prop> ].
657
+ * @param mixed $default The default value to return.
658
+ *
659
+ * @return mixed The first valid value found or the default value.
660
+ */
661
+ protected function static_prop( array $classes_and_props, $default ) {
662
+ $value = $default;
663
+
664
+ foreach ( $classes_and_props as $class => $prop ) {
665
+ if ( class_exists( $class ) ) {
666
+ // PHP 5.2 compat, on PHP 5.3+ $class::$$prop
667
+ $vars = get_class_vars( $class );
668
+ $the_value = isset( $vars[ $prop ] ) ? $vars[ $prop ] : self::NOT_FOUND;
669
+
670
+ if ( $the_value !== self::NOT_FOUND ) {
671
+ $value = $the_value;
672
+ break;
673
+ }
674
+ }
675
+ }
676
 
677
+ return $value;
 
 
 
678
  }
679
 
680
+ /**
681
+ * Reads the value from one or more properties of implementations bound in the `tribe()` container.
682
+ *
683
+ * @since 4.9.5
684
+ *
685
+ * @param array $bindings_and_props An associative array in the shape [ <binding> => <prop> ].
686
+ * @param mixed $default The default value to return.
687
+ *
688
+ * @return mixed The first valid value found or the default value.
689
+ */
690
+ protected function prop( array $bindings_and_props, $default ) {
691
+ $value = $default;
692
+
693
+ foreach ( $bindings_and_props as $binding => $prop ) {
694
+ $the_value = tribe()->offsetExists( $binding ) && property_exists( tribe( $binding ), $prop )
695
+ ? tribe( $binding )->{$prop}
696
+ : self::NOT_FOUND;
697
+
698
+ if ( $the_value !== self::NOT_FOUND ) {
699
+ $value = $the_value;
700
+ break;
701
+ }
702
+ }
703
 
704
+ return $value;
705
+ }
 
 
 
 
 
 
 
 
 
 
 
706
 
707
+ /**
708
+ * Reads the values from one or more static class methods.
709
+ *
710
+ * @since 4.9.5
711
+ *
712
+ * @param array $classes_and_methods An associative array in the shape [ <class> => <method> ].
713
+ * @param mixed $default The default value to return.
714
+ *
715
+ * @return mixed The first value that's not equal to the default one, the default value
716
+ * otherwise.
717
+ */
718
+ protected function static_method( array $classes_and_methods, $default ) {
719
+ $value = $default;
720
+
721
+ foreach ( $classes_and_methods as $class => $method ) {
722
+ $the_value = class_exists( $class ) && method_exists( $class, $method )
723
+ ? call_user_func( array( $class, $method ) )
724
+ : self::NOT_FOUND;
725
+
726
+ if ( $the_value !== self::NOT_FOUND ) {
727
+ $value = $the_value;
728
+ break;
729
+ }
730
+ }
731
 
732
+ return $value;
 
 
 
733
  }
734
 
735
+ /**
736
+ * Reads the value from one or more methods called on implementations bound in the `tribe()` container.
737
+ *
738
+ * @since 4.9.5
739
+ *
740
+ * @param array $bindings_and_methods An associative array in the shape [ <binding> => <method> ].
741
+ * @param mixed $default The default value to return.
742
+ *
743
+ * @return mixed The first value that's not equal to the default one, the default value
744
+ * otherwise.
745
+ */
746
+ protected function method( array $bindings_and_methods, $default ) {
747
+ $value = $default;
748
+ $the_value = self::NOT_FOUND;
749
+
750
+ foreach ( $bindings_and_methods as $binding => $method ) {
751
+ if ( tribe()->offsetExists( $binding ) ) {
752
+ $implementation = tribe( $binding );
753
+ if ( method_exists( $implementation, $method ) ) {
754
+ $the_value = $implementation->$method();
755
+ }
756
+ }
757
+
758
+ if ( $the_value !== self::NOT_FOUND ) {
759
+ $value = $the_value;
760
+ break;
761
+ }
762
+ }
763
 
764
+ return $value;
765
+ }
 
 
 
 
 
 
 
 
 
 
 
 
766
 
767
+ /**
768
+ * Reads the value from one or more functions until one returns a value that's not the default one.
769
+ *
770
+ * @since 4.9.5
771
+ *
772
+ * @param array $functions An array of functions to call, in order.
773
+ * @param mixed $default The default value to return.
774
+ *
775
+ * @return mixed The first value that's not equal to the default one, the default value
776
+ * otherwise.
777
+ */
778
+ protected function func( array $functions, $default ) {
779
+ $value = $default;
780
+ $the_value = self::NOT_FOUND;
781
+
782
+ foreach ( $functions as $function ) {
783
+ if ( is_callable( $function ) || function_exists( $function ) ) {
784
+ $the_value = $function();
785
+ }
786
+
787
+ if ( $the_value !== self::NOT_FOUND ) {
788
+ $value = $the_value;
789
+ break;
790
+ }
791
  }
 
792
 
793
+ return $value;
 
 
 
794
  }
795
 
796
+ /**
797
+ * Modifies the global context using the defined write locations to persist the altered values.
798
+ *
799
+ * Please keep in mind this will set the the global context for the whole request and, when the
800
+ * write location is an option, to the database.
801
+ * With great power comes great responsibility: think a lot before using this.
802
+ *
803
+ * @param array|null $fields An optional whitelist or blacklist of fields to write
804
+ * depending on the value of the `$whitelist` parameter;
805
+ * defaults to writing all available fields.
806
+ * @param bool $whitelist Whether the list of fields provided in the `$fields`
807
+ * parameter should be treated as a whitelist (`true`) or
808
+ * blacklist (`false`).
809
+ *
810
+ * @since 4.9.5
811
+ */
812
+ public function dangerously_set_global_context( array $fields = null, $whitelist = true ) {
813
+ $locations = $this->get_locations();
814
 
815
+ if ( null !== $fields ) {
816
+ $locations = $whitelist
817
+ ? array_intersect_key( $locations, array_combine( $fields, $fields ) )
818
+ : array_diff_key( $locations, array_combine( $fields, $fields ) );
819
+ }
820
 
821
+ /**
822
+ * Here we intersect with the request cache to only write values we've actually read
823
+ * or modified. If none of the two happened then there's no need to write anything.
824
+ */
825
+ foreach ( array_intersect_key( $this->request_cache, $locations ) as $key => $value ) {
826
+ if ( ! isset( $locations[ $key ]['write'] ) ) {
827
+ continue;
828
+ }
829
+
830
+ foreach ( (array) $locations[ $key ]['write'] as $location => $targets ) {
831
+ $targets = (array) $targets;
832
+ $write_func = 'write_' . $location;
833
+
834
+ foreach ( $targets as $arg_1 => $arg_2 ) {
835
+ if ( self::FUNC === $location && is_array( $arg_2 ) && is_callable( $arg_2 ) ) {
836
+ // Handles write functions specified as an array.
837
+ $location_args = array( $arg_2 );
838
+ } else {
839
+ $location_args = in_array( $location, self::$associative_locations, true )
840
+ ? array( $arg_1, $arg_2 )
841
+ : (array) $arg_2;
842
+ }
843
+
844
+ $args = array_merge( $location_args, array( $value ) );
845
+
846
+ call_user_func_array( array( $this, $write_func ), $args );
847
+ }
848
+ }
849
+ }
850
  }
851
 
852
+ /**
853
+ * Writes an altered context value to a request var.
854
+ *
855
+ * @since 4.9.5
856
+ *
857
+ * @param string $request_var The request var to write.
858
+ * @param mixed $value The value to set on the request var.
859
+ */
860
+ protected function write_request_var( $request_var, $value ) {
861
+ if ( isset( $_REQUEST ) ) {
862
+ $_REQUEST[ $request_var ] = $value;
863
+ }
864
+ if ( isset( $_GET ) ) {
865
+ $_GET[ $request_var ] = $value;
866
+ }
867
+ if ( isset( $_POST ) ) {
868
+ $_POST[ $request_var ] = $value;
869
+ }
 
 
 
 
 
 
 
 
870
  }
871
 
872
  /**
873
+ * Writes an altered context value to a global WP_Query object properties.
874
+ *
875
+ * @since 4.9.5
876
+ *
877
+ * @param string $query_prop The global WP_Query object property to write.
878
+ * @param mixed $value The value to set on the query property.
879
  */
880
+ protected function write_query_prop( $query_prop, $value ) {
881
+ global $wp_query;
 
 
882
 
883
+ if ( ! $wp_query instanceof WP_Query ) {
884
+ return;
885
+ }
886
 
887
+ $wp_query->{$query_prop} = $value;
888
+ }
 
 
 
 
 
 
 
889
 
890
+ /**
891
+ * Writes an altered context value to a global WP_Query object query var.
892
+ *
893
+ * @since 4.9.5
894
+ *
895
+ * @param string $query_var The global WP_Query query var to write.
896
+ * @param mixed $value The value to set on the query var.
897
+ */
898
+ protected function write_query_var( $query_var, $value ) {
899
+ global $wp_query;
900
 
901
+ if ( ! $wp_query instanceof WP_Query ) {
902
+ return;
903
  }
 
 
 
904
 
905
+ $wp_query->set( $query_var, $value );
 
 
 
 
 
 
 
 
 
 
906
  }
907
+
908
+ /**
909
+ * Writes an altered context value to a `tribe_option`.
910
+ *
911
+ * @since 4.9.5
912
+ *
913
+ * @param string $tribe_option The `tribe_option` to write.
914
+ * @param mixed $value The value to set on the `tribe_option`.
915
+ */
916
+ protected function write_tribe_option( $tribe_option, $value ) {
917
+ tribe_update_option( $tribe_option, $value );
918
  }
919
+
920
+ /**
921
+ * Writes an altered context value to an option.
922
+ *
923
+ * @since 4.9.5
924
+ *
925
+ * @param string $option_name The option to write.
926
+ * @param mixed $value The value to set on the option.
927
+ */
928
+ protected function write_option( $option_name, $value ) {
929
+ update_option( $option_name, $value );
930
  }
 
931
 
932
+ /**
933
+ * Writes an altered context value to a transient.
934
+ *
935
+ * @since 4.9.5
936
+ *
937
+ * @param string $transient The transient to write.
938
+ * @param int $expiration The transient expiration time, in seconds.
939
+ * @param mixed $value The value to set on the transient.
940
+ */
941
+ protected function write_transient( $transient, $expiration, $value ) {
942
+ set_transient( $transient, $value, $expiration );
943
+ }
944
 
945
+ /**
946
+ * Writes an altered context value to a constant.
947
+ *
948
+ * @since 4.9.5
949
+ *
950
+ * @param string $constant The constant to define.
951
+ * @param mixed $value The value to set on the constant.
952
+ */
953
+ protected function write_constant( $constant, $value ) {
954
+ if ( defined( $constant ) ) {
955
+ return;
956
+ }
957
+ define( $constant, $value );
958
  }
959
 
960
+ /**
961
+ * Writes an altered context value to a global var.
962
+ *
963
+ * @since 4.9.5
964
+ *
965
+ * @param string $global_var The global var to set.
966
+ * @param mixed $value The value to set on the global_var.
967
+ */
968
+ protected function write_global_var( $global_var, $value ) {
969
+ $GLOBALS[ $global_var ] = $value;
970
+ }
971
 
972
+ /**
973
+ * Writes an altered context value setting a public static property on a class.
974
+ *
975
+ * @since 4.9.5
976
+ *
977
+ * @param string $class The class to set the static public property on.
978
+ * @param string $prop The static public property to set.
979
+ * @param mixed $value The value to set on the property.
980
+ */
981
+ protected function write_static_prop( $class, $prop, $value ) {
982
+ if ( ! ( class_exists( $class ) && property_exists( $class, $prop ) ) ) {
983
+ return;
984
+ }
985
 
986
+ $class::$$prop = $value;
 
987
  }
988
 
989
+ /**
990
+ * Writes an altered context value setting a public property on a `tribe()` binding.
991
+ *
992
+ * @since 4.9.5
993
+ *
994
+ * @param string $binding The container binding to set the public property on.
995
+ * @param string $prop The public property to set.
996
+ * @param mixed $value The value to set on the property.
997
+ */
998
+ protected function write_prop( $binding, $prop, $value ) {
999
+ if ( ! tribe()->offsetExists( $binding ) ) {
1000
+ return;
1001
+ }
 
1002
 
1003
+ $implementation = tribe( $binding );
 
 
 
 
 
 
 
 
 
 
1004
 
1005
+ if ( ! property_exists( $implementation, $prop ) ) {
1006
+ return;
1007
+ }
 
 
 
 
 
 
 
 
 
1008
 
1009
+ $implementation->{$prop} = $value;
 
 
 
 
 
 
 
 
 
 
1010
  }
 
 
1011
 
1012
+ /**
1013
+ * Writes an altered context value calling a public static method on a class.
1014
+ *
1015
+ * @since 4.9.5
1016
+ *
1017
+ * @param string $class The class to call the public static method on.
1018
+ * @param string $method The static method to call.
1019
+ * @param mixed $value The value to pass to the public static method.
1020
+ */
1021
+ protected function write_static_method( $class, $method, $value ) {
1022
+ if ( ! class_exists( $class ) ) {
1023
+ return;
1024
+ }
1025
+ call_user_func( array( $class, $method ), $value );
 
 
 
 
 
 
 
 
 
 
1026
  }
1027
 
1028
+ /**
1029
+ * Writes an altered context value calling a public method on a `tribe()` binding.
1030
+ *
1031
+ * @since 4.9.5
1032
+ *
1033
+ * @param string $binding The `tribe()` container binding to call the public method on.
1034
+ * @param string $method The method to call.
1035
+ * @param mixed $value The value to pass to the public method.
1036
+ */
1037
+ protected function write_method( $binding, $method, $value ) {
1038
+ if ( ! tribe()->offsetExists( $binding ) ) {
1039
+ return;
1040
+ }
1041
+ call_user_func( array( tribe( $binding ), $method ), $value );
 
1042
  }
1043
 
1044
+ /**
1045
+ * Writes an altered context value calling a function or closure.
1046
+ *
1047
+ * @since 4.9.5
1048
+ *
1049
+ * @param callable $func function, closure or callable to call.
1050
+ * @param mixed $value The value to pass to the callable.
1051
+ */
1052
+ protected function write_func( $func, $value ) {
1053
+ if ( ! is_callable( $func ) ) {
1054
+ return;
1055
+ }
1056
+ call_user_func( $func, $value );
1057
  }
1058
 
1059
+ /**
1060
+ * Adds/replaces read and write locations to a context.
1061
+ *
1062
+ * Locations are merged with an `array_merge` call. To refine the locations get them first with the
1063
+ * `get_locations` method.
1064
+ *
1065
+ * @since 4.9.5
1066
+ *
1067
+ * @param array $locations An array of read and write locations to add to the context.
1068
+ * The array should have the same shape as the static `$locations`
1069
+ * one: `[ <location> => [ 'read' => <read_locations>, 'write' => <write_locations> ] ]`.
1070
+ *
1071
+ *
1072
+ * @return \Tribe__Context A clone of the current context with the additional read and
1073
+ * write locations added.
1074
+ */
1075
+ public function add_locations( array $locations ) {
1076
+ $clone = clone $this;
1077
+ $clone->override_locations = array_merge( $clone->override_locations, $locations );
1078
 
1079
+ return $clone;
 
 
 
 
 
 
 
 
 
 
 
1080
  }
 
 
1081
 
1082
+ /**
1083
+ * Sets, replacing them, the locations used by this context.
1084
+ *
1085
+ *
1086
+ * @since 4.9.5
1087
+ *
1088
+ * @param array $locations An array of locations to replace the current ones.
1089
+ * @param bool $use_default_locations Whether the context should use the default
1090
+ * locations defined in the static `$locations`
1091
+ * property or not.
1092
+ *
1093
+ * @return \Tribe__Context A clone of the current context with modified locations.
1094
+ */
1095
+ public function set_locations( array $locations, $use_default_locations = true ) {
1096
+ $clone = clone $this;
1097
+ $clone->override_locations = $locations;
1098
+ $clone->use_default_locations = (bool) $use_default_locations;
1099
 
1100
+ return $clone;
 
 
 
 
 
 
 
 
 
 
1101
  }
 
 
1102
 
1103
+ /**
1104
+ * Returns an array representation of the context.
1105
+ *
1106
+ * @since 4.9.5
1107
+ *
1108
+ * @return array An associative array of the context keys and values.
1109
+ */
1110
+ public function to_array( ) {
1111
+ $locations = array_keys( array_merge( $this->get_locations(), $this->request_cache ) );
1112
+ $dump = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1113
 
1114
+ foreach ( $locations as $location ) {
1115
+ $the_value = $this->get( $location, self::NOT_FOUND );
 
 
 
 
 
 
 
 
1116
 
1117
+ if ( self::NOT_FOUND === $the_value ) {
1118
+ continue;
1119
+ }
1120
 
1121
+ $dump[ $location ] = $the_value;
1122
+ }
 
1123
 
1124
+ return $dump;
1125
  }
1126
 
1127
+ /**
1128
+ * Returns the current context state in a format suitable to hydrate a Redux-like
1129
+ * store on the front-end.
1130
+ *
1131
+ * This method is a filtered wrapper around the the `Tribe__Context::to_array` method to allow the
1132
+ * customization of the format when producing a store-compatible state.
1133
+ *
1134
+ * @param array|null $fields An optional whitelist or blacklist of fields to include
1135
+ * depending on the value of the `$whitelist` parameter;
1136
+ * defaults to returning all available fields.
1137
+ * @param bool $whitelist Whether the list of fields provided in the `$fields`
1138
+ * parameter should be treated as a whitelist (`true`) or
1139
+ * blacklist (`false`).
1140
+ *
1141
+ * @since 4.9.5
1142
+ *
1143
+ * @return array
1144
+ */
1145
+ public function get_state( array $fields = null, $whitelist = true ) {
1146
+ $state = $this->to_array();
1147
+ $is_global_context = tribe_context() === $this;
1148
+
1149
+ if ( null !== $fields ) {
1150
+ $state = $whitelist
1151
+ ? array_intersect_key( $state, array_combine( $fields, $fields ) )
1152
+ : array_diff_key( $state, array_combine( $fields, $fields ) );
1153
+ }
1154
 
1155
+ /**
1156
+ * Filters the Redux store compatible state produced from the current context.
1157
+ *
1158
+ * @since 4.9.5
1159
+ *
1160
+ * @param array $state The Redux store compatible state produced from the current context.
1161
+ * @param bool $is_global_context Whether the context producing the state is the global one
1162
+ * or a modified clone of it.
1163
+ * @param Tribe__Context The context object producing the state.
1164
+ */
1165
+ $state = apply_filters( 'tribe_context_state', $state, $is_global_context, $this );
1166
+
1167
+ if ( $is_global_context ) {
1168
+ /**
1169
+ * Filters the Redux store compatible state produced from the global context.
1170
+ *
1171
+ * While the `tribe_context_state` filter will apply to all contexts producing a
1172
+ * state this filter will only apply to the global context.
1173
+ *
1174
+ * @since 4.9.5
1175
+ *
1176
+ * @param array $state The Redux store compatible state produced from the global context.
1177
+ * @param Tribe__Context The global context object producing the state.
1178
+ */
1179
+ $state = apply_filters( 'tribe_global_context_state', $state, $this );
1180
+ }
1181
+
1182
+ return $state;
1183
  }
1184
 
1185
  /**
1186
+ * Returns an array of ORM arguments generated from the current context values.
1187
  *
1188
  * @since 4.9.5
1189
  *
1190
+ * @param array|null $fields An optional whitelist or blacklist of fields to include
1191
+ * depending on the value of the `$whitelist` parameter;
1192
+ * defaults to returning all available fields.
1193
+ * @param bool $whitelist Whether the list of fields provided in the `$fields`
1194
+ * parameter should be treated as a whitelist (`true`) or
1195
+ * blacklist (`false`).
1196
+ *
1197
+ * @return array A map of ORM fields produced from the context current values.
1198
  */
1199
+ public function get_orm_args( array $fields = null, $whitelist = true ) {
1200
+ $locations = $this->get_locations();
1201
+ $dump = $this->to_array();
1202
+ $orm_args = array();
1203
+ $is_global_context = tribe_context() === $this;
1204
+
1205
+ foreach ( $dump as $key => $value ) {
1206
+ $alias = isset( $locations[ $key ]['orm_arg'] )
1207
+ ? $locations[ $key ]['orm_arg']
1208
+ : $key;
1209
+
1210
+ if ( false === $alias ) {
1211
+ // Do not provide the variable as an ORM arg.
1212
+ continue;
1213
+ }
1214
+
1215
+ if ( isset( $locations[ $key ]['orm_transform'] ) ) {
1216
+ $value = call_user_func( $locations[ $key ]['orm_transform'], $value );
1217
+ }
1218
+
1219
+ $orm_args[ $alias ] = $value;
1220
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1221
 
1222
+ if ( null !== $fields ) {
1223
+ /*
1224
+ * Only keep wanted fields, the filtering is done on the resolved aliases,
1225
+ * from the perspective of the client code that might ignore the source keys.
1226
+ */
1227
+ $orm_args = $whitelist
1228
+ ? array_intersect_key( $orm_args, array_combine( $fields, $fields ) )
1229
+ : array_diff_key( $orm_args, array_combine( $fields, $fields ) );
1230
+ }
1231
 
1232
+ /**
1233
+ * Filters the ORM arguments produced from the current context.
1234
+ *
1235
+ * @since 4.9.5
1236
+ *
1237
+ * @param array $orm_args The ORM args produced from the current context.
1238
+ * @param bool $is_global_context Whether the context producing the ORM args is the global one
1239
+ * or a modified clone of it.
1240
+ * @param Tribe__Context The context object producing the ORM args.
1241
+ */
1242
+ $orm_args = apply_filters( 'tribe_context_orm_args', $orm_args, $is_global_context, $this );
1243
+
1244
+ if ( $is_global_context ) {
1245
+ /**
1246
+ * Filters the ORM arguments produced from the global context.
1247
+ *
1248
+ * While the `tribe_context_orm_args` filter will apply to all contexts producing ORM
1249
+ * args this filter will only apply to the global context.
1250
+ *
1251
+ * @since 4.9.5
1252
+ *
1253
+ * @param array $orm_args The ORM args produced from the global context.
1254
+ * @param Tribe__Context The global context object producing the ORM args.
1255
+ */
1256
+ $orm_args = apply_filters( 'tribe_global_context_orm_args', $orm_args, $this );
1257
+ }
1258
 
1259
+ return $orm_args;
 
 
 
 
 
 
 
1260
  }
1261
 
1262
  /**
1263
+ * Sets some locations that can only be set at runtime.
1264
  *
1265
+ * Using a flag locations are added only once per request.
1266
  *
1267
+ * @since 4.9.8
 
 
 
1268
  */
1269
+ protected function populate_locations() {
1270
+ if ( static::$did_populate_locations ) {
1271
+ return;
1272
+ }
 
 
 
 
 
 
 
 
 
 
 
 
1273
 
1274
+ // To improve the class readability, and as a small optimization, locations are loaded from a file.
1275
+ static::$locations = include __DIR__ . '/Context/locations.php';
1276
 
1277
+ /**
1278
+ * Filters the locations registered in the Context.
1279
+ *
1280
+ * @since 4.9.8
1281
+ *
1282
+ * @param array $locations An array of locations registered on the Context object.
1283
+ */
1284
+ static::$locations = apply_filters( 'tribe_context_locations', static::$locations, $this );
 
 
 
1285
 
1286
+ static::$did_populate_locations = true;
1287
+ }
1288
 
1289
  /**
1290
+ * Reads (gets) the value applying one or more filters.
1291
  *
1292
  * @since 4.9.8
1293
  *
1294
+ * @param array $filters The list of filters to apply, in order.
1295
+ * @param mixed $default The default value to return.
1296
+ *
1297
+ * @return mixed The first valid value found or the default value.
1298
  */
1299
+ public function filter( array $filters, $default ) {
1300
+ foreach ( $filters as $filter ) {
1301
+ $the_value = apply_filters( $filter, $default );
1302
+ if ( $the_value !== $default ) {
1303
+ return $the_value;
1304
+ }
1305
+ }
1306
 
1307
+ return $default;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1308
  }
1309
 
 
 
 
1310
  /**
1311
  * Reads (gets) the value reading it from a query var parsed from the global `$wp` object.
1312
  *
1416
 
1417
  return $mapped;
1418
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1419
  }
common/src/Tribe/Context/locations.php CHANGED
@@ -9,147 +9,170 @@
9
  *
10
  * @since 4.9.11
11
  */
 
12
  return [
13
- 'post_id' => [
14
- 'read' => [
15
- Tribe__Context::FUNC => static function () {
16
- return get_the_ID();
17
- }
 
 
 
18
  ],
19
  ],
20
- 'permalink_structure' => [
21
- 'read' => [
22
- Tribe__Context::OPTION => [ 'permalink_structure' ],
 
 
 
 
 
 
 
 
23
  ],
24
  ],
25
- 'plain_permalink' => [
26
- 'read' => [
27
- Tribe__Context::LOCATION_FUNC => [
28
- 'permalink_structure',
29
- static function( $struct ){
30
- return empty( $struct );
31
- },
32
- ],
 
 
 
33
  ],
34
  ],
35
- 'posts_per_page' => [
36
  'read' => [
37
- Tribe__Context::REQUEST_VAR => 'posts_per_page',
38
- Tribe__Context::OPTION => 'posts_per_page',
39
- Tribe__Context::TRIBE_OPTION => [ 'posts_per_page', 'postsPerPage' ],
40
  ],
41
  'write' => [
42
- Tribe__Context::REQUEST_VAR => 'posts_per_page',
 
43
  ],
44
  ],
45
- 'is_main_query' => [
46
  'read' => [
47
- Tribe__Context::FUNC => static function () {
48
- global $wp_query;
49
-
50
- return $wp_query->is_main_query();
51
- },
52
  ],
53
  'write' => [
54
- Tribe__Context::FUNC => static function () {
55
- global $wp_query, $wp_the_query;
56
- $wp_the_query = $wp_query;
57
- },
58
  ],
59
  ],
60
- 'paged' => [
61
  'read' => [
62
- Tribe__Context::REQUEST_VAR => [ 'paged', 'page' ],
63
- Tribe__Context::QUERY_VAR => [ 'paged', 'page' ],
64
  ],
65
  'write' => [
66
- Tribe__Context::REQUEST_VAR => 'paged',
67
- Tribe__Context::QUERY_VAR => 'paged',
68
  ],
69
  ],
70
- 'page' => [
71
  'read' => [
72
- Tribe__Context::REQUEST_VAR => [ 'page', 'paged' ],
73
- Tribe__Context::QUERY_VAR => [ 'page', 'paged' ],
74
  ],
75
  'write' => [
76
- Tribe__Context::REQUEST_VAR => 'page',
77
- Tribe__Context::QUERY_VAR => 'page',
78
  ],
79
  ],
80
- 'name' => [
81
  'read' => [
82
- Tribe__Context::REQUEST_VAR => [ 'name', 'post_name' ],
83
- Tribe__Context::WP_PARSED => [ 'name', 'post_name' ],
84
- Tribe__Context::QUERY_VAR => [ 'name', 'post_name' ],
85
  ],
86
  'write' => [
87
- Tribe__Context::REQUEST_VAR => [ 'name', 'post_name' ],
88
- Tribe__Context::QUERY_VAR => [ 'name', 'post_name' ],
89
  ],
90
  ],
91
- 'post_type' => [
92
- 'read' => [
93
- Tribe__Context::FUNC => static function() {
94
- $post_type_objs = get_post_types(
95
- [
96
- 'public' => true,
97
- '_builtin' => false,
98
- ],
99
- 'objects'
100
- );
101
-
102
- foreach( $post_type_objs as $post_type ) {
103
- if ( empty( $post_type->query_var ) ) {
104
- continue;
105
- }
106
-
107
- $url_value = tribe_get_request_var( $post_type->query_var, false );
108
- if ( empty( $url_value ) ) {
109
- continue;
110
- }
111
-
112
- return $post_type->name;
113
- }
114
-
115
- return Tribe__Context::NOT_FOUND;
116
- },
117
- Tribe__Context::QUERY_PROP => 'post_type',
118
- Tribe__Context::QUERY_VAR => 'post_type',
119
- Tribe__Context::REQUEST_VAR => 'post_type',
120
  ],
121
  ],
122
- 'single' => [
123
- 'read' => [ Tribe__Context::QUERY_METHOD => 'is_single' ]
 
 
 
 
 
 
 
124
  ],
125
- 'taxonomy' => [
126
- 'read' => [
127
- Tribe__Context::QUERY_PROP => [ 'taxonomy' ],
128
- Tribe__Context::QUERY_VAR => [ 'taxonomy' ],
129
- Tribe__Context::REQUEST_VAR => [ 'taxonomy' ],
 
 
 
130
  ],
131
  ],
132
- 'post_tag' => [
133
- 'read' => [
134
- Tribe__Context::QUERY_PROP => [ 'post_tag', 'tag' ],
135
- Tribe__Context::QUERY_VAR => [ 'post_tag', 'tag' ],
136
- Tribe__Context::REQUEST_VAR => [ 'post_tag', 'tag' ],
 
 
 
137
  ],
138
  ],
139
- 'bulk_edit' => [
140
- 'read' => [
141
- Tribe__Context::REQUEST_VAR => [ 'bulk_edit' ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  ],
143
  ],
144
- 'inline_save' => [
 
 
 
 
 
145
  'read' => [
146
- Tribe__Context::FUNC => [
147
- static function () {
148
- return tribe_get_request_var( 'action', false ) === 'inline-save'
149
- ? true
150
- : Tribe__Context::NOT_FOUND;
151
- }
152
- ],
153
  ],
154
  ],
155
  ];
9
  *
10
  * @since 4.9.11
11
  */
12
+
13
  return [
14
+ 'posts_per_page' => [
15
+ 'read' => [
16
+ Tribe__Context::REQUEST_VAR => 'posts_per_page',
17
+ Tribe__Context::TRIBE_OPTION => [ 'posts_per_page', 'postsPerPage' ],
18
+ Tribe__Context::OPTION => 'posts_per_page',
19
+ ],
20
+ 'write' => [
21
+ Tribe__Context::REQUEST_VAR => 'posts_per_page',
22
  ],
23
  ],
24
+ 'event_display' => [
25
+ 'read' => [
26
+ Tribe__Context::WP_MATCHED_QUERY => [ 'eventDisplay' ],
27
+ Tribe__Context::WP_PARSED => [ 'eventDisplay' ],
28
+ Tribe__Context::REQUEST_VAR => [ 'view', 'tribe_view', 'tribe_event_display', 'eventDisplay' ],
29
+ Tribe__Context::QUERY_VAR => 'eventDisplay',
30
+ Tribe__Context::TRIBE_OPTION => 'viewOption',
31
+ ],
32
+ 'write' => [
33
+ Tribe__Context::REQUEST_VAR => [ 'view', 'tribe_view', 'tribe_event_display', 'eventDisplay' ],
34
+ Tribe__Context::QUERY_VAR => 'eventDisplay',
35
  ],
36
  ],
37
+ 'view' => [
38
+ 'read' => [
39
+ Tribe__Context::WP_MATCHED_QUERY => [ 'eventDisplay' ],
40
+ Tribe__Context::WP_PARSED => [ 'eventDisplay' ],
41
+ Tribe__Context::REQUEST_VAR => [ 'view', 'tribe_view', 'tribe_event_display', 'eventDisplay' ],
42
+ Tribe__Context::QUERY_VAR => [ 'tribe_view', 'eventDisplay' ],
43
+ Tribe__Context::TRIBE_OPTION => 'viewOption',
44
+ ],
45
+ 'write' => [
46
+ Tribe__Context::REQUEST_VAR => [ 'view', 'tribe_view', 'tribe_event_display', 'eventDisplay' ],
47
+ Tribe__Context::QUERY_VAR => [ 'tribe_view', 'eventDisplay' ],
48
  ],
49
  ],
50
+ 'view_data' => [
51
  'read' => [
52
+ Tribe__Context::REQUEST_VAR => 'tribe_view_data',
53
+ Tribe__Context::QUERY_VAR => 'tribe_view_data',
54
+ Tribe__Context::FILTER => 'tribe_view_data',
55
  ],
56
  'write' => [
57
+ Tribe__Context::REQUEST_VAR => 'tribe_view_data',
58
+ Tribe__Context::QUERY_VAR => 'tribe_view_data',
59
  ],
60
  ],
61
+ 'event_date' => [
62
  'read' => [
63
+ Tribe__Context::REQUEST_VAR => 'eventDate',
64
+ Tribe__Context::QUERY_VAR => 'eventDate',
 
 
 
65
  ],
66
  'write' => [
67
+ Tribe__Context::REQUEST_VAR => 'eventDate',
68
+ Tribe__Context::QUERY_VAR => 'eventDate',
 
 
69
  ],
70
  ],
71
+ 'event_sequence' => [
72
  'read' => [
73
+ Tribe__Context::REQUEST_VAR => 'eventSequence',
74
+ Tribe__Context::QUERY_VAR => 'eventSequence',
75
  ],
76
  'write' => [
77
+ Tribe__Context::REQUEST_VAR => 'eventSequence',
78
+ Tribe__Context::QUERY_VAR => 'eventSequence',
79
  ],
80
  ],
81
+ 'ical' => [
82
  'read' => [
83
+ Tribe__Context::REQUEST_VAR => 'ical',
84
+ Tribe__Context::QUERY_VAR => 'ical',
85
  ],
86
  'write' => [
87
+ Tribe__Context::REQUEST_VAR => 'ical',
88
+ Tribe__Context::QUERY_VAR => 'ical',
89
  ],
90
  ],
91
+ 'start_date' => [
92
  'read' => [
93
+ Tribe__Context::REQUEST_VAR => 'start_date',
94
+ Tribe__Context::QUERY_VAR => 'start_date',
 
95
  ],
96
  'write' => [
97
+ Tribe__Context::REQUEST_VAR => 'start_date',
98
+ Tribe__Context::QUERY_VAR => 'start_date',
99
  ],
100
  ],
101
+ 'end_date' => [
102
+ 'read' => [
103
+ Tribe__Context::REQUEST_VAR => 'end_date',
104
+ Tribe__Context::QUERY_VAR => 'end_date',
105
+ ],
106
+ 'write' => [
107
+ Tribe__Context::REQUEST_VAR => 'end_date',
108
+ Tribe__Context::QUERY_VAR => 'end_date',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  ],
110
  ],
111
+ 'featured' => [
112
+ 'read' => [
113
+ Tribe__Context::REQUEST_VAR => 'featured',
114
+ Tribe__Context::QUERY_VAR => 'featured',
115
+ ],
116
+ 'write' => [
117
+ Tribe__Context::REQUEST_VAR => 'featured',
118
+ Tribe__Context::QUERY_VAR => 'featured',
119
+ ],
120
  ],
121
+ 'tribe_events_cat' => [
122
+ 'read' => [
123
+ Tribe__Context::REQUEST_VAR => 'tribe_events_cat',
124
+ Tribe__Context::QUERY_VAR => 'tribe_events_cat',
125
+ ],
126
+ 'write' => [
127
+ Tribe__Context::REQUEST_VAR => 'tribe_events_cat',
128
+ Tribe__Context::QUERY_VAR => 'tribe_events_cat',
129
  ],
130
  ],
131
+ 'remove_date_filters' => [
132
+ 'read' => [
133
+ Tribe__Context::REQUEST_VAR => 'tribe_remove_date_filters',
134
+ Tribe__Context::QUERY_VAR => 'tribe_remove_date_filters',
135
+ ],
136
+ 'write' => [
137
+ Tribe__Context::REQUEST_VAR => 'tribe_remove_date_filters',
138
+ Tribe__Context::QUERY_VAR => 'tribe_remove_date_filters',
139
  ],
140
  ],
141
+ 'is_main_query' => [
142
+ 'read' => [
143
+ Tribe__Context::FUNC => static function () {
144
+ global $wp_query;
145
+
146
+ return $wp_query->is_main_query();
147
+ },
148
+ ],
149
+ 'write' => [
150
+ Tribe__Context::FUNC => static function () {
151
+ global $wp_query, $wp_the_query;
152
+ $wp_the_query = $wp_query;
153
+ },
154
+ ],
155
+ ],
156
+ 'paged' => [
157
+ 'read' => [
158
+ Tribe__Context::REQUEST_VAR => 'paged',
159
+ Tribe__Context::QUERY_VAR => 'paged',
160
+ ],
161
+ 'write' => [
162
+ Tribe__Context::REQUEST_VAR => 'paged',
163
+ Tribe__Context::QUERY_VAR => 'paged',
164
  ],
165
  ],
166
+ 'event_display_mode' => [
167
+ /**
168
+ * We use the `eventDisplay` query var with duplicity: when parsed from the path it represents the View, when
169
+ * appended as a query var it represents the "view mode". Here we invert the order to read the appended query
170
+ * var first and get, from its position, a clean variable we can consume in Views.
171
+ */
172
  'read' => [
173
+ Tribe__Context::REQUEST_VAR => [ 'view', 'tribe_view', 'tribe_event_display', 'eventDisplay' ],
174
+ Tribe__Context::WP_PARSED => [ 'eventDisplay' ],
175
+ Tribe__Context::QUERY_VAR => 'eventDisplay',
 
 
 
 
176
  ],
177
  ],
178
  ];
common/src/Tribe/Cost_Utils.php CHANGED
@@ -486,52 +486,4 @@ class Tribe__Cost_Utils {
486
 
487
  return ! empty( $currency_positions ) ? reset( $currency_positions ) : false;
488
  }
489
-
490
- /**
491
- * Parses the cost value and current locale to infer decimal and thousands separators.
492
- *
493
- * The cost values stored in the meta table might not use the same decimal and thousands separator as the current
494
- * locale.
495
- * To work around this we parse the value assuming the decimal separator will be the last non-numeric symbol,
496
- * if any.
497
- *
498
- * @since 4.9.12
499
- *
500
- * @param string|int|float $value The cost value to parse.
501
- *
502
- * @return array An array containing the parsed decimal and thousands separator symbols.
503
- */
504
- public function parse_separators( $value ) {
505
- global $wp_locale;
506
- $locale_decimal_point = $wp_locale->number_format['decimal_point'];
507
- $locale_thousands_sep = $wp_locale->number_format['thousands_sep'];
508
- $decimal_sep = $locale_decimal_point;
509
- $thousands_sep = $locale_thousands_sep;
510
-
511
- preg_match_all( '/[\\.,]+/', $value, $matches );
512
-
513
- if ( ! empty( $matches[0] ) ) {
514
- $matched_separators = $matches[0];
515
- if ( count( array_unique( $matched_separators ) ) > 1 ) {
516
- // We have both, the decimal separator will be the last non-numeric symbol.
517
- $decimal_sep = end( $matched_separators );
518
- $thousands_sep = reset( $matched_separators );
519
- } else {
520
- /*
521
- * We only have one, we can assume it's the decimal separator if it comes before a number of numeric
522
- * symbols that is not exactly 3. If there are exactly 3 number after the symbols we fall back on the
523
- * locale; we did our best and cannot guess any further.
524
- */
525
- $frags = explode( end( $matched_separators ), $value );
526
- if ( strlen( end( $frags ) ) !== 3 ) {
527
- $decimal_sep = end( $matched_separators );
528
- $thousands_sep = $decimal_sep === $locale_decimal_point ?
529
- $locale_thousands_sep
530
- : $locale_decimal_point;
531
- }
532
- }
533
- }
534
-
535
- return [ $decimal_sep, $thousands_sep ];
536
- }
537
  }
486
 
487
  return ! empty( $currency_positions ) ? reset( $currency_positions ) : false;
488
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  }
common/src/Tribe/Credits.php CHANGED
@@ -55,7 +55,7 @@ class Tribe__Credits {
55
  esc_html__( 'Rate %1$sThe Events Calendar%2$s %3$s', 'tribe-common' ),
56
  '<strong>',
57
  '</strong>',
58
- '<a href="' . $review_url . '" target="_blank" class="tribe-rating">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
59
  );
60
  } else {
61
  $review_url = 'https://wordpress.org/support/plugin/event-tickets/reviews/?filter=5';
@@ -64,7 +64,7 @@ class Tribe__Credits {
64
  esc_html__( 'Rate %1$sEvent Tickets%2$s %3$s', 'tribe-common' ),
65
  '<strong>',
66
  '</strong>',
67
- '<a href="' . $review_url . '" target="_blank" class="tribe-rating">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
68
  );
69
  }
70
  }
55
  esc_html__( 'Rate %1$sThe Events Calendar%2$s %3$s', 'tribe-common' ),
56
  '<strong>',
57
  '</strong>',
58
+ '<a href="' . $review_url . '" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
59
  );
60
  } else {
61
  $review_url = 'https://wordpress.org/support/plugin/event-tickets/reviews/?filter=5';
64
  esc_html__( 'Rate %1$sEvent Tickets%2$s %3$s', 'tribe-common' ),
65
  '<strong>',
66
  '</strong>',
67
+ '<a href="' . $review_url . '" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
68
  );
69
  }
70
  }
common/src/Tribe/Customizer.php CHANGED
@@ -104,6 +104,17 @@ final class Tribe__Customizer {
104
  return;
105
  }
106
 
 
 
 
 
 
 
 
 
 
 
 
107
  /**
108
  * Filters the Panel ID, which is also the `wp_option` name for the Customizer settings
109
  *
@@ -316,6 +327,18 @@ final class Tribe__Customizer {
316
  $option = $sections;
317
  }
318
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  /**
320
  * Apply Filters After finding the variable
321
  *
@@ -370,6 +393,18 @@ final class Tribe__Customizer {
370
  return false;
371
  }
372
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  /**
374
  * Use this filter to add more CSS, using Underscore Template style
375
  *
@@ -379,7 +414,7 @@ final class Tribe__Customizer {
379
  *
380
  * @param string $template
381
  */
382
- $css_template = trim( apply_filters( 'tribe_customizer_css_template', '' ) );
383
 
384
  // If we don't have anything on the customizer don't print empty styles
385
  // On Customize Page, we don't care we need this
@@ -404,7 +439,8 @@ final class Tribe__Customizer {
404
  * @return void
405
  */
406
  public function inline_style() {
407
- // Only load once on front-end.
 
408
  if ( is_customize_preview() || is_admin() || $this->inline_style ) {
409
  return false;
410
  }
@@ -425,30 +461,29 @@ final class Tribe__Customizer {
425
  return false;
426
  }
427
 
428
- $sheets = [];
 
429
 
430
- /**
431
- * Allow plugins to add themselves to this list.
432
- *
433
- * @since 4.12.1
434
- *
435
- * @param array<string> $sheets An array of sheets to search for.
436
- * @param string $css_template String containing the inline css to add.
437
- */
438
- $sheets = apply_filters( 'tribe_customizer_inline_stylesheets', $sheets, $css_template );
439
 
440
- if ( empty( $sheets ) ) {
441
- return false;
442
  }
443
 
444
- // add customizer styles inline with whichever stylesheet is enqueued.
445
- foreach ( $sheets as $sheet ) {
446
- if ( wp_style_is( $sheet ) ) {
447
- wp_add_inline_style( $sheet, wp_strip_all_tags( $this->parse_css_template( $css_template ) ) );
448
- $this->inline_style = true;
449
 
450
- break;
451
- }
 
 
 
 
 
 
 
 
 
 
452
  }
453
  }
454
 
@@ -496,6 +531,17 @@ final class Tribe__Customizer {
496
  // Set the Cutomizer on a class variable
497
  $this->manager = $customizer;
498
 
 
 
 
 
 
 
 
 
 
 
 
499
  /**
500
  * Allow users to filter the Panel
501
  *
@@ -504,7 +550,18 @@ final class Tribe__Customizer {
504
  * @param WP_Customize_Panel $panel
505
  * @param Tribe__Customizer $customizer
506
  */
507
- $this->panel = apply_filters( 'tribe_customizer_panel', $this->register_panel(), $this );
 
 
 
 
 
 
 
 
 
 
 
508
 
509
  /**
510
  * Filter the Sections within our Panel before they are added to the Cutomize Manager
@@ -519,6 +576,17 @@ final class Tribe__Customizer {
519
  foreach ( $this->sections as $id => $section ) {
520
  $this->sections[ $id ] = $this->register_section( $id, $section );
521
 
 
 
 
 
 
 
 
 
 
 
 
522
  /**
523
  * Allows people to Register and de-register the method to register more Fields
524
  *
@@ -530,6 +598,17 @@ final class Tribe__Customizer {
530
  do_action( "tribe_customizer_register_{$id}_settings", $this->sections[ $id ], $this->manager );
531
  }
532
 
 
 
 
 
 
 
 
 
 
 
 
533
  /**
534
  * Filter the Sections within our Panel, now using the actual WP_Customize_Section
535
  *
@@ -567,6 +646,18 @@ final class Tribe__Customizer {
567
  'priority' => 125,
568
  );
569
 
 
 
 
 
 
 
 
 
 
 
 
 
570
  /**
571
  * Filter the Panel Arguments for WP Customize
572
  *
@@ -598,6 +689,17 @@ final class Tribe__Customizer {
598
  * @return WP_Customize_Section
599
  */
600
  public function register_section( $id, $args ) {
 
 
 
 
 
 
 
 
 
 
 
601
  /**
602
  * Filter the Section ID
603
  *
@@ -606,7 +708,7 @@ final class Tribe__Customizer {
606
  * @param string $section_id
607
  * @param Tribe__Customizer $customizer
608
  */
609
- $section_id = apply_filters( 'tribe_customizer_section_id', $id, $this );
610
 
611
  // Tries to fetch the section
612
  $section = $this->manager->get_section( $section_id );
@@ -616,6 +718,18 @@ final class Tribe__Customizer {
616
  return $section;
617
  }
618
 
 
 
 
 
 
 
 
 
 
 
 
 
619
  /**
620
  * Filter the Section arguments, so that developers can filter arguments based on $section_id
621
  *
104
  return;
105
  }
106
 
107
+ /**
108
+ * Filters the Panel ID, which is also the `wp_option` name for the Customizer settings
109
+ *
110
+ * @deprecated
111
+ * @since 4.0
112
+ *
113
+ * @param string $ID
114
+ * @param self $customizer
115
+ */
116
+ $this->ID = apply_filters( 'tribe_events_pro_customizer_panel_id', 'tribe_customizer', $this );
117
+
118
  /**
119
  * Filters the Panel ID, which is also the `wp_option` name for the Customizer settings
120
  *
327
  $option = $sections;
328
  }
329
 
330
+ /**
331
+ * Apply Filters After finding the variable
332
+ *
333
+ * @deprecated
334
+ * @since 4.0
335
+ *
336
+ * @param mixed $option
337
+ * @param array $search
338
+ * @param array $sections
339
+ */
340
+ $option = apply_filters( 'tribe_events_pro_customizer_get_option', $option, $search, $sections );
341
+
342
  /**
343
  * Apply Filters After finding the variable
344
  *
393
  return false;
394
  }
395
 
396
+ /**
397
+ * Use this filter to add more CSS, using Underscore Template style
398
+ *
399
+ * @deprecated
400
+ * @since 4.0
401
+ *
402
+ * @link http://underscorejs.org/#template
403
+ *
404
+ * @param string $template
405
+ */
406
+ $css_template = trim( apply_filters( 'tribe_events_pro_customizer_css_template', '' ) );
407
+
408
  /**
409
  * Use this filter to add more CSS, using Underscore Template style
410
  *
414
  *
415
  * @param string $template
416
  */
417
+ $css_template = trim( apply_filters( 'tribe_customizer_css_template', $css_template ) );
418
 
419
  // If we don't have anything on the customizer don't print empty styles
420
  // On Customize Page, we don't care we need this
439
  * @return void
440
  */
441
  public function inline_style() {
442
+
443
+ //Only load on front end
444
  if ( is_customize_preview() || is_admin() || $this->inline_style ) {
445
  return false;
446
  }
461
  return false;
462
  }
463
 
464
+ // add customizer styles inline with either main stylesheet is enqueued or widgets
465
+ if ( wp_style_is( 'tribe-events-calendar-style' ) ) {
466
 
467
+ wp_add_inline_style( 'tribe-events-calendar-style', wp_strip_all_tags( $this->parse_css_template( $css_template ) ) );
468
+ $this->inline_style = true;
 
 
 
 
 
 
 
469
 
470
+ return;
 
471
  }
472
 
473
+ if ( wp_style_is( 'tribe-events-calendar-pro-style' ) ) {
 
 
 
 
474
 
475
+ wp_add_inline_style( 'tribe-events-calendar-pro-style', wp_strip_all_tags( $this->parse_css_template( $css_template ) ) );
476
+ $this->inline_style = true;
477
+
478
+ return;
479
+ }
480
+
481
+ if ( wp_style_is( 'widget-calendar-pro-style' ) ) {
482
+
483
+ wp_add_inline_style( 'widget-calendar-pro-style', wp_strip_all_tags( $this->parse_css_template( $css_template ) ) );
484
+ $this->inline_style = true;
485
+
486
+ return;
487
  }
488
  }
489
 
531
  // Set the Cutomizer on a class variable
532
  $this->manager = $customizer;
533
 
534
+ /**
535
+ * Allow users to filter the Panel
536
+ *
537
+ * @deprecated
538
+ * @since 4.0
539
+ *
540
+ * @param WP_Customize_Panel $panel
541
+ * @param Tribe__Customizer $customizer
542
+ */
543
+ $this->panel = apply_filters( 'tribe_events_pro_customizer_panel', $this->register_panel(), $this );
544
+
545
  /**
546
  * Allow users to filter the Panel
547
  *
550
  * @param WP_Customize_Panel $panel
551
  * @param Tribe__Customizer $customizer
552
  */
553
+ $this->panel = apply_filters( 'tribe_customizer_panel', $this->panel, $this );
554
+
555
+ /**
556
+ * Filter the Sections within our Panel before they are added to the Cutomize Manager
557
+ *
558
+ * @deprecated
559
+ * @since 4.0
560
+ *
561
+ * @param array $sections
562
+ * @param Tribe__Customizer $customizer
563
+ */
564
+ $this->sections = apply_filters( 'tribe_events_pro_customizer_pre_sections', $this->sections, $this );
565
 
566
  /**
567
  * Filter the Sections within our Panel before they are added to the Cutomize Manager
576
  foreach ( $this->sections as $id => $section ) {
577
  $this->sections[ $id ] = $this->register_section( $id, $section );
578
 
579
+ /**
580
+ * Allows people to Register and de-register the method to register more Fields
581
+ *
582
+ * @deprecated
583
+ * @since 4.0
584
+ *
585
+ * @param array $section
586
+ * @param WP_Customize_Manager $manager
587
+ */
588
+ do_action( "tribe_events_pro_customizer_register_{$id}_settings", $this->sections[ $id ], $this->manager );
589
+
590
  /**
591
  * Allows people to Register and de-register the method to register more Fields
592
  *
598
  do_action( "tribe_customizer_register_{$id}_settings", $this->sections[ $id ], $this->manager );
599
  }
600
 
601
+ /**
602
+ * Filter the Sections within our Panel, now using the actual WP_Customize_Section
603
+ *
604
+ * @deprecated
605
+ * @since 4.0
606
+ *
607
+ * @param array $sections
608
+ * @param Tribe__Customizer $customizer
609
+ */
610
+ $this->sections = apply_filters( 'tribe_events_pro_customizer_sections', $this->sections, $this );
611
+
612
  /**
613
  * Filter the Sections within our Panel, now using the actual WP_Customize_Section
614
  *
646
  'priority' => 125,
647
  );
648
 
649
+ /**
650
+ * Filter the Panel Arguments for WP Customize
651
+ *
652
+ * @deprecated
653
+ * @since 4.0
654
+ *
655
+ * @param array $args
656
+ * @param string $ID
657
+ * @param Tribe__Customizer $customizer
658
+ */
659
+ $panel_args = apply_filters( 'tribe_events_pro_customizer_panel_args', $panel_args, $this->ID, $this );
660
+
661
  /**
662
  * Filter the Panel Arguments for WP Customize
663
  *
689
  * @return WP_Customize_Section
690
  */
691
  public function register_section( $id, $args ) {
692
+ /**
693
+ * Filter the Section ID
694
+ *
695
+ * @deprecated
696
+ * @since 4.0
697
+ *
698
+ * @param string $section_id
699
+ * @param Tribe__Customizer $customizer
700
+ */
701
+ $section_id = apply_filters( 'tribe_events_pro_customizer_section_id', $id, $this );
702
+
703
  /**
704
  * Filter the Section ID
705
  *
708
  * @param string $section_id
709
  * @param Tribe__Customizer $customizer
710
  */
711
+ $section_id = apply_filters( 'tribe_customizer_section_id', $section_id, $this );
712
 
713
  // Tries to fetch the section
714
  $section = $this->manager->get_section( $section_id );
718
  return $section;
719
  }
720
 
721
+ /**
722
+ * Filter the Section arguments, so that developers can filter arguments based on $section_id
723
+ *
724
+ * @deprecated
725
+ * @since 4.0
726
+ *
727
+ * @param array $args
728
+ * @param string $section_id
729
+ * @param Tribe__Customizer $customizer
730
+ */
731
+ $section_args = apply_filters( 'tribe_events_pro_customizer_section_args', $args, $section_id, $this );
732
+
733
  /**
734
  * Filter the Section arguments, so that developers can filter arguments based on $section_id
735
  *
common/src/Tribe/Data.php CHANGED
@@ -65,7 +65,7 @@ class Tribe__Data implements ArrayAccess, Iterator {
65
  * </p>
66
  * <p>
67
  * The return value will be casted to boolean if non-boolean was returned.
68
- * @since 4.11.0
69
  */
70
  public function offsetExists( $offset ) {
71
  return isset( $this->data[ $offset ] );
@@ -79,7 +79,7 @@ class Tribe__Data implements ArrayAccess, Iterator {
79
  * The offset to retrieve.
80
  * </p>
81
  * @return mixed Can return all value types.
82
- * @since 4.11.0
83
  */
84
  public function offsetGet( $offset ) {
85
  return isset( $this->data[ $offset ] )
@@ -98,7 +98,7 @@ class Tribe__Data implements ArrayAccess, Iterator {
98
  * The value to set.
99
  * </p>
100
  * @return void
101
- * @since 4.11.0
102
  */
103
  public function offsetSet( $offset, $value ) {
104
  $this->data[ $offset ] = $value;
@@ -112,7 +112,7 @@ class Tribe__Data implements ArrayAccess, Iterator {
112
  * The offset to unset.
113
  * </p>
114
  * @return void
115
- * @since 4.11.0
116
  */
117
  public function offsetUnset( $offset ) {
118
  unset( $this->data[ $offset ] );
@@ -159,7 +159,7 @@ class Tribe__Data implements ArrayAccess, Iterator {
159
  *
160
  * @link http://php.net/manual/en/iterator.current.php
161
  * @return mixed Can return any type.
162
- * @since 4.11.0
163
  */
164
  public function current() {
165
  $keys = array_keys( $this->data );
@@ -172,7 +172,7 @@ class Tribe__Data implements ArrayAccess, Iterator {
172
  *
173
  * @link http://php.net/manual/en/iterator.next.php
174
  * @return void Any returned value is ignored.
175
- * @since 4.11.0
176
  */
177
  public function next() {
178
  $keys = array_keys( $this->data );
@@ -189,7 +189,7 @@ class Tribe__Data implements ArrayAccess, Iterator {
189
  *
190
  * @link http://php.net/manual/en/iterator.key.php
191
  * @return mixed scalar on success, or null on failure.
192
- * @since 4.11.0
193
  */
194
  public function key() {
195
  $keys = array_keys( $this->data );
@@ -203,7 +203,7 @@ class Tribe__Data implements ArrayAccess, Iterator {
203
  * @link http://php.net/manual/en/iterator.valid.php
204
  * @return boolean The return value will be casted to boolean and then evaluated.
205
  * Returns true on success or false on failure.
206
- * @since 4.11.0
207
  */
208
  public function valid() {
209
  $keys = array_keys( $this->data );
@@ -216,7 +216,7 @@ class Tribe__Data implements ArrayAccess, Iterator {
216
  *
217
  * @link http://php.net/manual/en/iterator.rewind.php
218
  * @return void Any returned value is ignored.
219
- * @since 4.11.0
220
  */
221
  public function rewind() {
222
  $this->index = 0;
65
  * </p>
66
  * <p>
67
  * The return value will be casted to boolean if non-boolean was returned.
68
+ * @since 5.0.0
69
  */
70
  public function offsetExists( $offset ) {
71
  return isset( $this->data[ $offset ] );
79
  * The offset to retrieve.
80
  * </p>
81
  * @return mixed Can return all value types.
82
+ * @since 5.0.0
83
  */
84
  public function offsetGet( $offset ) {
85
  return isset( $this->data[ $offset ] )
98
  * The value to set.
99
  * </p>
100
  * @return void
101
+ * @since 5.0.0
102
  */
103
  public function offsetSet( $offset, $value ) {
104
  $this->data[ $offset ] = $value;
112
  * The offset to unset.
113
  * </p>
114
  * @return void
115
+ * @since 5.0.0
116
  */
117
  public function offsetUnset( $offset ) {
118
  unset( $this->data[ $offset ] );
159
  *
160
  * @link http://php.net/manual/en/iterator.current.php
161
  * @return mixed Can return any type.
162
+ * @since 5.0.0
163
  */
164
  public function current() {
165
  $keys = array_keys( $this->data );
172
  *
173
  * @link http://php.net/manual/en/iterator.next.php
174
  * @return void Any returned value is ignored.
175
+ * @since 5.0.0
176
  */
177
  public function next() {
178
  $keys = array_keys( $this->data );
189
  *
190
  * @link http://php.net/manual/en/iterator.key.php
191
  * @return mixed scalar on success, or null on failure.
192
+ * @since 5.0.0
193
  */
194
  public function key() {
195
  $keys = array_keys( $this->data );
203
  * @link http://php.net/manual/en/iterator.valid.php
204
  * @return boolean The return value will be casted to boolean and then evaluated.
205
  * Returns true on success or false on failure.
206
+ * @since 5.0.0
207
  */
208
  public function valid() {
209
  $keys = array_keys( $this->data );
216
  *
217
  * @link http://php.net/manual/en/iterator.rewind.php
218
  * @return void Any returned value is ignored.
219
+ * @since 5.0.0
220
  */
221
  public function rewind() {
222
  $this->index = 0;
common/src/Tribe/Date_Utils.php CHANGED
@@ -3,9 +3,6 @@
3
  * Date utility functions used throughout TEC + Addons
4
  */
5
 
6
- use Tribe\Utils\Date_I18n;
7
- use Tribe\Utils\Date_I18n_Immutable;
8
-
9
  // Don't load directly
10
 
11
  if ( ! defined( 'ABSPATH' ) ) {
@@ -26,38 +23,11 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
26
  const DBTIMEFORMAT = 'H:i:s';
27
  const DBYEARMONTHTIMEFORMAT = 'Y-m';
28
 
29
- /**
30
- * Default datepicker format index.
31
- *
32
- * @since 4.11.0.1
33
- *
34
- * @var int
35
- */
36
- private static $default_datepicker_format_index = 1;
37
-
38
  private static $localized_months_full = array();
39
  private static $localized_months_short = array();
40
  private static $localized_weekdays = array();
41
  private static $localized_months = array();
42
 
43
- /**
44
- * Get the datepickerFormat index.
45
- *
46
- * @since 4.11.0.1
47
- *
48
- * @return int
49
- */
50
- public static function get_datepicker_format_index() {
51
- /**
52
- * Filter the datepickerFormat index.
53
- *
54
- * @since 4.11.0.1
55
- *
56
- * @param int $format_index Index of datepickerFormat.
57
- */
58
- return apply_filters( 'tribe_datepicker_format_index', tribe_get_option( 'datepickerFormat', static::$default_datepicker_format_index ) );
59
- }
60
-
61
  /**
62
  * Try to format a Date to the Default Datepicker format
63
  *
@@ -69,14 +39,14 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
69
  */
70
  public static function maybe_format_from_datepicker( $date, $datepicker = null ) {
71
  if ( ! is_numeric( $datepicker ) ) {
72
- $datepicker = self::get_datepicker_format_index();
73
  }
74
 
75
  if ( is_numeric( $datepicker ) ) {
76
  $datepicker = self::datepicker_formats( $datepicker );
77
  }
78
 
79
- $default_datepicker = self::datepicker_formats( 1 );
80
 
81
  // If the current datepicker is the default we don't care
82
  if ( $datepicker === $default_datepicker ) {
@@ -96,37 +66,25 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
96
 
97
  // The datepicker has issues when a period separator and no leading zero is used. Those formats are purposefully omitted.
98
  $formats = array(
99
- 0 => 'Y-m-d',
100
- 1 => 'n/j/Y',
101
- 2 => 'm/d/Y',
102
- 3 => 'j/n/Y',
103
- 4 => 'd/m/Y',
104
- 5 => 'n-j-Y',
105
- 6 => 'm-d-Y',
106
- 7 => 'j-n-Y',
107
- 8 => 'd-m-Y',
108
- 9 => 'Y.m.d',
109
- 10 => 'm.d.Y',
110
- 11 => 'd.m.Y',
111
- 'm0' => 'Y-m',
112
- 'm1' => 'n/Y',
113
- 'm2' => 'm/Y',
114
- 'm3' => 'n/Y',
115
- 'm4' => 'm/Y',
116
- 'm5' => 'n-Y',
117
- 'm6' => 'm-Y',
118
- 'm7' => 'n-Y',
119
- 'm8' => 'm-Y',
120
- 'm9' => 'Y.m',
121
- 'm10' => 'm.Y',
122
- 'm11' => 'm.Y',
123
  );
124
 
125
  if ( is_null( $translate ) ) {
126
  return $formats;
127
  }
128
 
129
- return isset( $formats[ $translate ] ) ? $formats[ $translate ] : $formats[ static::get_datepicker_format_index() ];
130
  }
131
 
132
  /**
@@ -262,22 +220,6 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
262
  return date( $format, $date );
263
  }
264
 
265
- /**
266
- * Returns as string the nearest half a hour for a given valid string datetime.
267
- *
268
- * @since 4.10.2
269
- *
270
- * @param string $date Valid DateTime string.
271
- *
272
- * @return string Rounded datetime string
273
- */
274
- public static function round_nearest_half_hour( $date ) {
275
- $date_object = static::build_date_object( $date );
276
- $rounded_minutes = floor( $date_object->format( 'i' ) / 30 ) * 30;
277
-
278
- return $date_object->format( 'Y-m-d H:' ) . $rounded_minutes . ':00';
279
- }
280
-
281
  /**
282
  * Returns the time only.
283
  *
@@ -1249,38 +1191,34 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
1249
  return clone $datetime;
1250
  }
1251
 
1252
- if ( class_exists( 'DateTimeImmutable' ) && $datetime instanceof DateTimeImmutable ) {
1253
  // Return the mutable version of the date.
1254
- return Date_I18n::createFromImmutable( $datetime );
1255
  }
1256
 
1257
  $timezone_object = null;
1258
- $datetime = empty( $datetime ) ? 'now' : $datetime;
1259
 
1260
  try {
1261
  // PHP 5.2 will not throw an exception but will generate an error.
1262
  $utc = new DateTimeZone( 'UTC' );
1263
- $timezone_object = Tribe__Timezones::build_timezone_object( $timezone );
1264
 
1265
  if ( self::is_timestamp( $datetime ) ) {
1266
- $timestamp_timezone = $timezone ? $timezone_object : $utc;
1267
-
1268
- return new Date_I18n( '@' . $datetime, $timestamp_timezone );
1269
  }
1270
 
 
 
1271
  set_error_handler( 'tribe_catch_and_throw' );
1272
- $date = new Date_I18n( $datetime, $timezone_object );
1273
  restore_error_handler();
1274
  } catch ( Exception $e ) {
1275
- // If we encounter an error, we need to restore after catching.
1276
- restore_error_handler();
1277
-
1278
  if ( $timezone_object === null ) {
1279
  $timezone_object = Tribe__Timezones::build_timezone_object( $timezone );
1280
  }
1281
 
1282
  return $with_fallback
1283
- ? new Date_I18n( 'now', $timezone_object )
1284
  : false;
1285
  }
1286
 
@@ -1298,301 +1236,7 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
1298
  * like `strtotime`, or not.
1299
  */
1300
  public static function is_valid_date( $date ) {
1301
- static $cache_var_name = __FUNCTION__;
1302
-
1303
- $cache_date_check = tribe_get_var( $cache_var_name, [] );
1304
-
1305
- if ( isset( $cache_date_check[ $date ] ) ) {
1306
- return $cache_date_check[ $date ];
1307
- }
1308
-
1309
- $cache_date_check[ $date ] = self::build_date_object( $date, null, false ) instanceof DateTimeInterface;
1310
-
1311
- tribe_set_var( $cache_var_name, $cache_date_check );
1312
-
1313
- return $cache_date_check[ $date ];
1314
- }
1315
-
1316
- /**
1317
- * Returns the DateTime object representing the start of the week for a date.
1318
- *
1319
- * @since 4.9.21
1320
- *
1321
- * @throws Exception
1322
- *
1323
- * @param string|int|\DateTime $date The date string, timestamp or object.
1324
- * @param int|null $start_of_week The number representing the start of week day as handled by
1325
- * WordPress: `0` (for Sunday) through `6` (for Saturday).
1326
- *
1327
- * @return array An array of objects representing the week start and end days, or `false` if the
1328
- * supplied date is invalid. The timezone of the returned object is set to the site one.
1329
- * The week start has its time set to `00:00:00`, the week end will have its time set
1330
- * `23:59:59`.
1331
- */
1332
- public static function get_week_start_end( $date, $start_of_week = null ) {
1333
- static $cache_var_name = __FUNCTION__;
1334
-
1335
- $cache_week_start_end = tribe_get_var( $cache_var_name, [] );
1336
-
1337
- $date_obj = static::build_date_object( $date );
1338
- $date_obj->setTime( 0, 0, 0 );
1339
-
1340
- $date_string = $date_obj->format( static::DBDATEFORMAT );
1341
-
1342
- // `0` (for Sunday) through `6` (for Saturday), the way WP handles the `start_of_week` option.
1343
- $week_start_day = null !== $start_of_week
1344
- ? (int) $start_of_week
1345
- : (int) get_option( 'start_of_week', 0 );
1346
-
1347
- $memory_cache_key = "{$date_string}:{$week_start_day}";
1348
-
1349
- if ( isset( $cache_week_start_end[ $memory_cache_key ] ) ) {
1350
- return $cache_week_start_end[ $memory_cache_key ];
1351
- }
1352
-
1353
- $cache_key = md5(
1354
- __METHOD__ . serialize( [ $date_obj->format( static::DBDATEFORMAT ), $week_start_day ] )
1355
- );
1356
- $cache = tribe( 'cache' );
1357
-
1358
- if ( false !== $cached = $cache[ $cache_key ] ) {
1359
- return $cached;
1360
- }
1361
-
1362
- // `0` (for Sunday) through `6` (for Saturday), the way WP handles the `start_of_week` option.
1363
- $date_day = (int) $date_obj->format( 'w' );
1364
-
1365
- $week_offset = 0;
1366
- if ( 0 === $date_day && 0 !== $week_start_day ) {
1367
- $week_offset = 0;
1368
- } elseif ( $date_day < $week_start_day ) {
1369
- // If the current date of the week is before the start of the week, move back a week.
1370
- $week_offset = -1;
1371
- } elseif ( 0 === $date_day ) {
1372
- // When start of the week is on a sunday we add a week.
1373
- $week_offset = 1;
1374
- }
1375
-
1376
- $week_start = clone $date_obj;
1377
-
1378
- /*
1379
- * From the PHP docs, the `W` format stands for:
1380
- * - ISO-8601 week number of year, weeks starting on Monday
1381
- */
1382
- $week_start->setISODate(
1383
- (int) $week_start->format( 'o' ),
1384
- (int) $week_start->format( 'W' ) + $week_offset,
1385
- $week_start_day
1386
- );
1387
-
1388
- $week_end = clone $week_start;
1389
- // Add 6 days, then move at the end of the day.
1390
- $week_end->add( new DateInterval( 'P6D' ) );
1391
- $week_end->setTime( 23, 59, 59 );
1392
-
1393
- $week_start = static::immutable( $week_start );
1394
- $week_end = static::immutable( $week_end );
1395
-
1396
- $cache[ $cache_key ] = [ $week_start, $week_end ];
1397
- $cache_week_start_end[ $memory_cache_key ] = [ $week_start, $week_end ];
1398
-
1399
- tribe_set_var( $cache_var_name, $cache_week_start_end );
1400
-
1401
- return [ $week_start, $week_end ];
1402
- }
1403
-
1404
- /**
1405
- * Given a specific DateTime we determine the end of that day based on our Internal End of Day Cut-off.
1406
- *
1407
- * @since 4.11.2
1408
- *
1409
- * @param string|DateTimeInterface $date Date that we are getting the end of day from.
1410
- * @param null|string $cutoff Which cutoff to use.
1411
- *
1412
- * @return DateTimeInterface|false Returns a DateTimeInterface when a valid date is given or false.
1413
- */
1414
- public static function get_shifted_end_of_day( $date, $cutoff = null ) {
1415
- $date_obj = static::build_date_object( $date );
1416
-
1417
- if ( ! $date_obj ) {
1418
- return false;
1419
- }
1420
-
1421
- $start_of_day = clone $date_obj;
1422
- $end_of_day = clone $date_obj;
1423
-
1424
- if ( empty( $cutoff ) || ! is_string( $cutoff ) || false === strpos( $cutoff, ':' ) ) {
1425
- $cutoff = tribe_get_option( 'multiDayCutoff', '00:00' );
1426
- }
1427
-
1428
- list( $hours_to_add, $minutes_to_add ) = array_map( 'absint', explode( ':', $cutoff ) );
1429
-
1430
- $seconds_to_add = ( $hours_to_add * HOUR_IN_SECONDS ) + ( $minutes_to_add * MINUTE_IN_SECONDS );
1431
- if ( 0 !== $seconds_to_add ) {
1432
- $interval = static::interval( "PT{$seconds_to_add}S" );
1433
- }
1434
-
1435
- $start_of_day->setTime( '0', '0', '0' );
1436
- $end_of_day->setTime( '23', '59', '59' );
1437
-
1438
- if ( 0 !== $seconds_to_add ) {
1439
- $start_of_day->add( $interval );
1440
- $end_of_day->add( $interval );
1441
- }
1442
-
1443
- if ( $end_of_day >= $date_obj && $date_obj >= $start_of_day ) {
1444
- return $end_of_day;
1445
- }
1446
-
1447
- $start_of_day->sub( static::interval( 'P1D' ) );
1448
-
1449
- if ( $start_of_day < $date_obj ) {
1450
- $end_of_day->sub( static::interval( 'P1D' ) );
1451
- }
1452
-
1453
- return $end_of_day;
1454
- }
1455
-
1456
- /**
1457
- * Given a specific DateTime we determine the start of that day based on our Internal End of Day Cut-off.
1458
- *
1459
- * @since 4.11.2
1460
- *
1461
- * @param string|DateTimeInterface $date Date that we are getting the start of day from.
1462
- * @param null|string $cutoff Which cutoff to use.
1463
- *
1464
- * @return DateTimeInterface|false Returns a DateTimeInterface when a valid date is given or false.
1465
- */
1466
- public static function get_shifted_start_of_day( $date, $cutoff = null ) {
1467
- $date_obj = static::build_date_object( $date );
1468
-
1469
- if ( ! $date_obj ) {
1470
- return false;
1471
- }
1472
-
1473
- $start_of_day = clone $date_obj;
1474
- $end_of_day = clone $date_obj;
1475
-
1476
- if ( empty( $cutoff ) || ! is_string( $cutoff ) || false === strpos( $cutoff, ':' ) ) {
1477
- $cutoff = tribe_get_option( 'multiDayCutoff', '00:00' );
1478
- }
1479
-
1480
- list( $hours_to_add, $minutes_to_add ) = array_map( 'absint', explode( ':', $cutoff ) );
1481
-
1482
- $seconds_to_add = ( $hours_to_add * HOUR_IN_SECONDS ) + ( $minutes_to_add * MINUTE_IN_SECONDS );
1483
- if ( 0 !== $seconds_to_add ) {
1484
- $interval = static::interval( "PT{$seconds_to_add}S" );
1485
- }
1486
-
1487
- $start_of_day->setTime( '0', '0', '0' );
1488
- $end_of_day->setTime( '23', '59', '59' );
1489
-
1490
- if ( 0 !== $seconds_to_add ) {
1491
- $start_of_day->add( $interval );
1492
- $end_of_day->add( $interval );
1493
- }
1494
-
1495
- if ( $end_of_day <= $date_obj && $date_obj >= $start_of_day ) {
1496
- return $start_of_day;
1497
- }
1498
-
1499
- $end_of_day->sub( static::interval( 'P1D' ) );
1500
-
1501
- if ( $end_of_day > $date_obj ) {
1502
- $start_of_day->sub( static::interval( 'P1D' ) );
1503
- }
1504
-
1505
- return $start_of_day;
1506
- }
1507
-
1508
- /**
1509
- * Builds and returns a `DateInterval` object from the interval specification.
1510
- *
1511
- * For performance purposes the use of `DateInterval` specifications is preferred, so `P1D` is better than
1512
- * `1 day`.
1513
- *
1514
- * @since 4.10.2
1515
- *
1516
- * @return DateInterval The built date interval object.
1517
- */
1518
- public static function interval( $interval_spec ) {
1519
- try {
1520
- $interval = new \DateInterval( $interval_spec );
1521
- } catch ( \Exception $e ) {
1522
- $interval = DateInterval::createFromDateString( $interval_spec );
1523
- }
1524
-
1525
- return $interval;
1526
- }
1527
-
1528
- /**
1529
- * Builds the immutable version of a date from a string, integer (timestamp) or \DateTime object.
1530
- *
1531
- * It's the immutable version of the `Tribe__Date_Utils::build_date_object` method.
1532
- *
1533
- * @since 4.10.2
1534
- *
1535
- * @param string|DateTime|int $datetime A `strtotime` parse-able string, a DateTime object or
1536
- * a timestamp; defaults to `now`.
1537
- * @param string|DateTimeZone|null $timezone A timezone string, UTC offset or DateTimeZone object;
1538
- * defaults to the site timezone; this parameter is ignored
1539
- * if the `$datetime` parameter is a DatTime object.
1540
- * @param bool $with_fallback Whether to return a DateTime object even when the date data is
1541
- * invalid or not; defaults to `true`.
1542
- *
1543
- * @return DateTimeImmutable|false A DateTime object built using the specified date, time and timezone; if
1544
- * `$with_fallback` is set to `false` then `false` will be returned if a
1545
- * DateTime object could not be built.
1546
- */
1547
- static function immutable( $datetime = 'now', $timezone = null, $with_fallback = true ) {
1548
- if ( $datetime instanceof DateTimeImmutable ) {
1549
- return $datetime;
1550
- }
1551
-
1552
- if ( $datetime instanceof DateTime ) {
1553
- return Date_I18n_Immutable::createFromMutable( $datetime );
1554
- }
1555
-
1556
- $mutable = static::build_date_object( $datetime, $timezone, $with_fallback );
1557
-
1558
- if ( false === $mutable ) {
1559
- return false;
1560
- }
1561
-
1562
- $cache_key = md5( ( __METHOD__ . $mutable->getTimezone()->getName() . $mutable->getTimestamp() ) );
1563
- $cache = tribe( 'cache' );
1564
-
1565
- if ( false !== $cached = $cache[ $cache_key ] ) {
1566
- return $cached;
1567
- }
1568
-
1569
- $immutable = Date_I18n_Immutable::createFromMutable( $mutable );
1570
-
1571
- $cache[ $cache_key ] = $immutable;
1572
-
1573
- return $immutable;
1574
- }
1575
-
1576
- /**
1577
- * Builds a date object from a given datetime and timezone.
1578
- *
1579
- * An alias of the `Tribe__Date_Utils::build_date_object` function.
1580
- *
1581
- * @since 4.10.2
1582
- *
1583
- * @param string|DateTime|int $datetime A `strtotime` parse-able string, a DateTime object or
1584
- * a timestamp; defaults to `now`.
1585
- * @param string|DateTimeZone|null $timezone A timezone string, UTC offset or DateTimeZone object;
1586
- * defaults to the site timezone; this parameter is ignored
1587
- * if the `$datetime` parameter is a DatTime object.
1588
- * @param bool $with_fallback Whether to return a DateTime object even when the date data is
1589
- * invalid or not; defaults to `true`.
1590
- *
1591
- * @return DateTime|false A DateTime object built using the specified date, time and timezone; if `$with_fallback`
1592
- * is set to `false` then `false` will be returned if a DateTime object could not be built.
1593
- */
1594
- public static function mutable( $datetime = 'now', $timezone = null, $with_fallback = true ) {
1595
- return static::build_date_object( $datetime, $timezone, $with_fallback );
1596
  }
1597
  }
1598
  }
3
  * Date utility functions used throughout TEC + Addons
4
  */
5
 
 
 
 
6
  // Don't load directly
7
 
8
  if ( ! defined( 'ABSPATH' ) ) {
23
  const DBTIMEFORMAT = 'H:i:s';
24
  const DBYEARMONTHTIMEFORMAT = 'Y-m';
25
 
 
 
 
 
 
 
 
 
 
26
  private static $localized_months_full = array();
27
  private static $localized_months_short = array();
28
  private static $localized_weekdays = array();
29
  private static $localized_months = array();
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  /**
32
  * Try to format a Date to the Default Datepicker format
33
  *
39
  */
40
  public static function maybe_format_from_datepicker( $date, $datepicker = null ) {
41
  if ( ! is_numeric( $datepicker ) ) {
42
+ $datepicker = tribe_get_option( 'datepickerFormat' );
43
  }
44
 
45
  if ( is_numeric( $datepicker ) ) {
46
  $datepicker = self::datepicker_formats( $datepicker );
47
  }
48
 
49
+ $default_datepicker = self::datepicker_formats( 0 );
50
 
51
  // If the current datepicker is the default we don't care
52
  if ( $datepicker === $default_datepicker ) {
66
 
67
  // The datepicker has issues when a period separator and no leading zero is used. Those formats are purposefully omitted.
68
  $formats = array(
69
+ 'Y-m-d',
70
+ 'n/j/Y',
71
+ 'm/d/Y',
72
+ 'j/n/Y',
73
+ 'd/m/Y',
74
+ 'n-j-Y',
75
+ 'm-d-Y',
76
+ 'j-n-Y',
77
+ 'd-m-Y',
78
+ 'Y.m.d',
79
+ 'm.d.Y',
80
+ 'd.m.Y',
 
 
 
 
 
 
 
 
 
 
 
 
81
  );
82
 
83
  if ( is_null( $translate ) ) {
84
  return $formats;
85
  }
86
 
87
+ return isset( $formats[ $translate ] ) ? $formats[ $translate ] : $formats[0];
88
  }
89
 
90
  /**
220
  return date( $format, $date );
221
  }
222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  /**
224
  * Returns the time only.
225
  *
1191
  return clone $datetime;
1192
  }
1193
 
1194
+ if ( class_exists('DateTimeImmutable') && $datetime instanceof DateTimeImmutable ) {
1195
  // Return the mutable version of the date.
1196
+ return new DateTime( $datetime->format( 'Y-m-d H:i:s' ), $datetime->getTimezone() );
1197
  }
1198
 
1199
  $timezone_object = null;
 
1200
 
1201
  try {
1202
  // PHP 5.2 will not throw an exception but will generate an error.
1203
  $utc = new DateTimeZone( 'UTC' );
 
1204
 
1205
  if ( self::is_timestamp( $datetime ) ) {
1206
+ // Timestamps timezone is always UTC.
1207
+ return new DateTime( '@' . $datetime, $utc );
 
1208
  }
1209
 
1210
+ $timezone_object = Tribe__Timezones::build_timezone_object( $timezone );
1211
+
1212
  set_error_handler( 'tribe_catch_and_throw' );
1213
+ $date = new DateTime( $datetime, $timezone_object );
1214
  restore_error_handler();
1215
  } catch ( Exception $e ) {
 
 
 
1216
  if ( $timezone_object === null ) {
1217
  $timezone_object = Tribe__Timezones::build_timezone_object( $timezone );
1218
  }
1219
 
1220
  return $with_fallback
1221
+ ? new DateTime( 'now', $timezone_object )
1222
  : false;
1223
  }
1224
 
1236
  * like `strtotime`, or not.
1237
  */
1238
  public static function is_valid_date( $date ) {
1239
+ return self::build_date_object( $date, null, false ) instanceof DateTime;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1240
  }
1241
  }
1242
  }
common/src/Tribe/Debug_Bar/Panels/Context.php CHANGED
@@ -76,8 +76,8 @@ class Tribe__Debug_Bar__Panels__Context extends Debug_Bar_Panel {
76
 
77
  $html .= '<tr>';
78
  $html .= '<td><code>' . $key . '</code></td>';
79
- $html .= '<td><code>' . ( isset( $context[ $key ] ) ? print_r( $context[ $key ], true ) : 'undefined' ) . '</code></td>';
80
- $html .= '<td>' . ( false !== $orm_arg_key ? '<code>' . print_r( $orm_arg_key, true ) . ' => ' . print_r( $orm_arg_value, true ) . '</code>' : '' ) . '</td>';
81
  $html .= '<td><code>' . ( isset( $locations[ $key ]['read'] ) ? 'yes' : 'no' ) . '</code></td>';
82
  $html .= '<td><code>' . ( isset( $locations[ $key ]['write'] ) ? 'yes' : 'no' ) . '</code></td>';
83
  $html .= '</tr>';
76
 
77
  $html .= '<tr>';
78
  $html .= '<td><code>' . $key . '</code></td>';
79
+ $html .= '<td><code>' . ( isset( $context[ $key ] ) ? $context[ $key ] : 'undefined' ) . '</code></td>';
80
+ $html .= '<td>' . ( false !== $orm_arg_key ? '<code>' . $orm_arg_key . ' => ' . $orm_arg_value . '</code>' : '' ) . '</td>';
81
  $html .= '<td><code>' . ( isset( $locations[ $key ]['read'] ) ? 'yes' : 'no' ) . '</code></td>';
82
  $html .= '<td><code>' . ( isset( $locations[ $key ]['write'] ) ? 'yes' : 'no' ) . '</code></td>';
83
  $html .= '</tr>';
common/src/Tribe/Debug_Bar/Panels/Json_Ld.php DELETED
@@ -1,84 +0,0 @@
1
- <?php
2
- /**
3
- * JSON-LD information Debug Bar panel.
4
- *
5
- * @since 4.11.2
6
- *
7
- * @package Tribe\Debug_Bar\Panels
8
- */
9
-
10
- class Tribe__Debug_Bar__Panels__Json_Ld extends Debug_Bar_Panel {
11
- /**
12
- * Returns the Panel name.
13
- *
14
- * @since 4.11.2
15
- *
16
- * @param null $title The panel input title.
17
- *
18
- * @return string The panel title
19
- */
20
- public function title( $title = null ) {
21
- return __( 'Modern Tribe JSON-LD Data', 'tribe-common' );
22
- }
23
-
24
- /**
25
- * Renders the panel contents.
26
- *
27
- * @since 4.9.5
28
- */
29
- public function render() {
30
- $html = '<style>
31
- #mt-debug-bar .mt-debug-bar-title {
32
- margin-bottom: 1em;
33
- }
34
- #mt-debug-bar .mt-debug-bar-section {
35
- padding: .5em .5em .5em 1em;
36
- }
37
- </style>';
38
- $html .= '<div id="mt-debug-bar" class="mt-debug-bar-json-ld">';
39
-
40
- $html .= '<header class="mt-debug-bar-title"><h2>' . esc_html__( 'Modern Tribe JSON-LD Data',
41
- 'tribe-common' ) . '</h2></header>';
42
-
43
-
44
- $json_ld_data = array_filter( (array) tribe_cache()['json-ld-data'] );
45
-
46
- if ( ! empty( $json_ld_data ) ) {
47
- $html .= '<div class="mt-debug-bar-section">';
48
- $html .= sprintf(
49
- '<header>The request produced %d JSON-LD data %s.</header><br>',
50
- count( $json_ld_data ),
51
- count( $json_ld_data ) > 1 ? 'scripts' : 'script'
52
- );
53
-
54
- $html .= '<p>Copy the code below and paste it into ' .
55
- '<a href="https://search.google.com/structured-data/testing-tool/u/0/" target="_blank">' .
56
- 'Google\'s Structured Data Testing Tool' .
57
- '</a>' .
58
- ' to test it using the Code Snippet option.</p><br>';
59
-
60
- foreach ( $json_ld_data as $full_entry ) {
61
- preg_match(
62
- '/(?<open>^\\s*<script[^>]*?>\\s*)(?<json>.*)(?<close>\\s<\\/script>)$/uism',
63
- $full_entry,
64
- $frags
65
- );
66
-
67
- if ( isset( $frags['open'], $frags['json'], $frags['close'] ) ) {
68
- // Let's try and format it if we've got all the pieces.
69
- $full_entry = $frags['open']
70
- . json_encode( json_decode( $frags['json'], true ), JSON_PRETTY_PRINT )
71
- . $frags['close'];
72
- }
73
-
74
- $html .= sprintf( '<pre><code>%s</code></pre>', esc_html( $full_entry ) );
75
- }
76
-
77
- $html .= '</div>';
78
- }
79
-
80
- $html .= '</div>';
81
-
82
- echo $html;
83
- }
84
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Dependency.php CHANGED
@@ -4,7 +4,7 @@ defined( 'WPINC' ) or die;
4
 
5
  if ( ! class_exists( 'Tribe__Dependency' ) ) {
6
  /**
7
- * Tracks which Tribe (or related) plugins are registered, activated, or requirements satisfied.
8
  */
9
  class Tribe__Dependency {
10
 
@@ -17,7 +17,7 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
17
  * 'path' => 'Path to the main plugin/bootstrap file' (optional)
18
  * )
19
  */
20
- protected $active_plugins = [];
21
 
22
  /**
23
  * A multidimensional array of active tribe plugins in the following format
@@ -29,7 +29,7 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
29
  * 'dependencies' => 'A multidimensional of dependencies' (optional)
30
  * )
31
  */
32
- protected $registered_plugins = [];
33
 
34
  /**
35
  * An array of class Tribe__Admin__Notice__Plugin_Download per plugin
@@ -37,19 +37,40 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
37
  * @since 4.9
38
  *
39
  */
40
- protected $admin_messages = [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  /**
43
  * Adds a plugin to the active list
44
  *
45
  * @since 4.9
46
  *
47
- * @param string $main_class Main/base class for this plugin
48
- * @param null|string $version Version number of plugin
49
- * @param null|string $path Path to the main plugin/bootstrap file
50
- * @param array $dependencies An array of dependencies for a plugin
51
  */
52
  public function add_registered_plugin( $main_class, $version = null, $path = null, $dependencies = array() ) {
 
53
  $plugin = array(
54
  'class' => $main_class,
55
  'version' => $version,
@@ -62,6 +83,7 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
62
  if ( $path ) {
63
  $this->admin_messages[ $main_class ] = new Tribe__Admin__Notice__Plugin_Download( $path );
64
  }
 
65
  }
66
 
67
  /**
@@ -83,6 +105,7 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
83
  * @param string $path Path to the main plugin/bootstrap file
84
  */
85
  public function add_active_plugin( $main_class, $version = null, $path = null ) {
 
86
  $plugin = array(
87
  'class' => $main_class,
88
  'version' => $version,
@@ -228,6 +251,7 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
228
  * @return bool
229
  */
230
  public function is_plugin_version( $main_class, $version, $compare = '>=' ) {
 
231
  //active plugin check to see if the correct version is active
232
  if ( ! $this->is_plugin_active( $main_class ) ) {
233
  return false;
@@ -250,6 +274,7 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
250
  * @return bool
251
  */
252
  public function is_plugin_version_registered( $main_class, $version, $compare = '>=' ) {
 
253
  //registered plugin check if addon as it tests if it might load
254
  if ( ! $this->is_plugin_registered( $main_class ) ) {
255
  return false;
@@ -268,6 +293,7 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
268
  * @return bool
269
  */
270
  public function has_requisite_plugins( $plugins_required = array() ) {
 
271
  foreach ( $plugins_required as $class => $version ) {
272
  // Return false if the plugin is not set or is a lesser version
273
  if ( ! $this->is_plugin_active( $class ) ) {
@@ -296,30 +322,31 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
296
  }
297
 
298
  /**
299
- * Gets all dependencies or single class requirements if parent, co, add does not exist use array as is if they
300
- * do exist check each one in turn.
 
301
  *
302
  * @since 4.9
303
  *
304
- * @param array $plugin An array of data for given registered plugin.,
305
- * @param array $dependencies An array of dependencies for a plugin.
306
- * @param bool $addon Indicates if the plugin is an add-on for The Events Calendar or Event Tickets.
307
  *
308
- * @return true|int The number of failed dependency checks; `true` or `0` to indicate no checks failed.
309
  */
310
  public function has_valid_dependencies( $plugin, $dependencies = array(), $addon = false ) {
 
311
  if ( empty( $dependencies ) ) {
312
  return true;
313
  }
314
 
315
  $failed_dependency = 0;
316
-
317
- $tribe_plugins = new Tribe__Plugins();
318
 
319
  foreach ( $dependencies as $class => $version ) {
320
 
321
  // if no class for add-on
322
- $checked_plugin = $this->get_registered_plugin( $class );
323
  if ( $addon && empty( $checked_plugin ) ) {
324
  continue;
325
  }
@@ -329,91 +356,33 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
329
  continue;
330
  }
331
 
332
- $dependent_plugin = $tribe_plugins->get_plugin_by_class( $class );
 
 
 
 
 
 
 
333
 
334
- $pue = $this->get_pue_from_class( $dependent_plugin['class'] );
335
- $has_pue_notice = $pue ? tribe( 'pue.notices' )->has_notice( $pue->pue_install_key ) : false;
336
-
337
- $this->admin_messages[ $plugin['class'] ]->add_required_plugin(
338
- $dependent_plugin['short_name'],
339
- $dependent_plugin['thickbox_url'],
340
- $is_registered,
341
- $version,
342
- $addon,
343
- $has_pue_notice
344
- );
345
  $failed_dependency++;
346
  }
347
 
348
  return $failed_dependency;
349
  }
350
 
351
- /**
352
- * Gets the Tribe__PUE__Checker instance of a given plugin based on the class.
353
- *
354
- * @since 4.9.12
355
- *
356
- * @param string $class Which plugin main class we are looking for.
357
- *
358
- * @return Tribe__PUE__Checker
359
- */
360
- public function get_pue_from_class( $class ) {
361
- if ( ! is_string( $class ) ) {
362
- return false;
363
- }
364
-
365
- // If class doesnt exist the plugin doesnt exist.
366
- if ( ! class_exists( $class ) ) {
367
- return false;
368
- }
369
-
370
- /**
371
- * These callbacks are only required to prevent fatals.
372
- * Only happen for plugin that use PUE.
373
- */
374
- $callback_map = [
375
- 'Tribe__Events__Pro__Main' => function() {
376
- $pue_reflection = new ReflectionClass( Tribe__Events__Pro__PUE::class );
377
- $values = $pue_reflection->getStaticProperties();
378
- $values['plugin_file'] = EVENTS_CALENDAR_PRO_FILE;
379
- return $values;
380
- },
381
- 'Tribe__Events__Filterbar__View' => function() {
382
- $pue_reflection = new ReflectionClass( Tribe__Events__Filterbar__PUE::class );
383
- $values = $pue_reflection->getStaticProperties();
384
- $values['plugin_file'] = TRIBE_EVENTS_FILTERBAR_FILE;
385
- return $values;
386
- },
387
- 'Tribe__Events__Tickets__Eventbrite__Main' => function() {
388
- $pue_reflection = new ReflectionClass( Tribe__Events__Tickets__Eventbrite__PUE::class );
389
- $values = $pue_reflection->getStaticProperties();
390
- $values['plugin_file'] = EVENTBRITE_PLUGIN_FILE;
391
- return $values;
392
- },
393
- ];
394
-
395
- // Bail when class is not mapped.
396
- if ( ! isset( $callback_map[ $class ] ) ) {
397
- return false;
398
- }
399
-
400
- // Use the callback to get the returns without fatals
401
- $values = $callback_map[ $class ]();
402
- $pue_instance = new Tribe__PUE__Checker( $values['update_url'], $values['pue_slug'], [], plugin_basename( $values['plugin_file'] ) );
403
-
404
- return $pue_instance;
405
- }
406
-
407
  /**
408
  * Register a Plugin
409
  *
410
  * @since 4.9
411
  *
412
- * @param string $file_path Full file path to the base plugin file.
413
- * @param string $main_class The Main/base class for this plugin.
414
- * @param string $version The plugin version.
415
- * @param array $classes_req Any Main class files/tribe plugins required for this to run.
416
- * @param array $dependencies an array of dependencies to check.
417
  */
418
  public function register_plugin( $file_path, $main_class, $version, $classes_req = array(), $dependencies = array() ) {
419
  /**
@@ -446,20 +415,9 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
446
  if ( ! empty( $classes_req ) && ! $this->has_requisite_plugins( $classes_req ) ) {
447
  $tribe_plugins = new Tribe__Plugins();
448
  foreach ( $classes_req as $class => $plugin_version ) {
449
- $plugin = $tribe_plugins->get_plugin_by_class( $class );
450
-
451
- $is_active = $this->is_plugin_version( $class, $plugin_version );
452
- $pue = $this->get_pue_from_class( $plugin['class'] );
453
- $has_pue_notice = $pue ? tribe( 'pue.notices' )->has_notice( $pue->pue_install_key ) : false;
454
-
455
- $this->admin_messages[ $main_class ]->add_required_plugin(
456
- $plugin['short_name'],
457
- $plugin['thickbox_url'],
458
- $is_active,
459
- $plugin_version,
460
- false,
461
- $has_pue_notice
462
- );
463
  }
464
  }
465
 
@@ -475,7 +433,11 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
475
  *
476
  * @since 4.9
477
  *
 
478
  * @param string $main_class The Main/base class for this plugin
 
 
 
479
  *
480
  * @return bool Indicates if plugin should continue initialization
481
  */
@@ -483,25 +445,25 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
483
 
484
  $parent_dependencies = $co_dependencies = $addon_dependencies = 0;
485
 
486
- // Check if plugin is registered, if not return false.
487
  $plugin = $this->get_registered_plugin( $main_class );
488
  if ( empty( $plugin ) ) {
489
  return false;
490
  }
491
 
492
- // Check parent dependencies in add-on.
493
  if ( ! empty( $plugin['dependencies']['parent-dependencies'] ) ) {
494
  $parent_dependencies = $this->has_valid_dependencies( $plugin, $plugin['dependencies']['parent-dependencies'] );
495
  }
496
- // Check co-dependencies in add-on.
497
  if ( ! empty( $plugin['dependencies']['co-dependencies'] ) ) {
498
  $co_dependencies = $this->has_valid_dependencies( $plugin, $plugin['dependencies']['co-dependencies'] );
499
  }
500
 
501
- // Check add-on dependencies from parent.
502
  $addon_dependencies = $this->check_addon_dependencies( $main_class );
503
 
504
- // If good then we set as active plugin and continue to load.
505
  if ( ! $parent_dependencies && ! $co_dependencies && ! $addon_dependencies ) {
506
  $this->add_active_plugin( $main_class, $plugin['version'], $plugin['path'] );
507
 
@@ -509,6 +471,7 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
509
  }
510
 
511
  return false;
 
512
  }
513
 
514
  /**
@@ -516,38 +479,25 @@ if ( ! class_exists( 'Tribe__Dependency' ) ) {
516
  *
517
  * @since 4.9
518
  *
519
- * @param string $main_class A string of the main class for the plugin being checked.
520
  *
521
- * @return bool Returns false if any dependency is invalid.
522
  */
523
  protected function check_addon_dependencies( $main_class ) {
 
 
 
524
  foreach ( $this->registered_plugins as $registered ) {
525
  if ( empty( $registered['dependencies']['addon-dependencies'][ $main_class ] ) ) {
526
  continue;
527
  }
528
 
529
- $dependencies = [ $main_class => $registered['dependencies']['addon-dependencies'][ $main_class ] ];
530
- $check = $this->has_valid_dependencies( $registered, $dependencies, true );
531
-
532
- // A value of `true` or `0` indicates there are no failing checks. So here we check for ints gt 0.
533
- if ( is_int( $check ) && $check > 0 ) {
534
- return true;
535
- }
536
  }
537
 
538
- return false;
539
  }
540
 
541
- /**
542
- * Static Singleton Factory Method
543
- *
544
- * @deprecated 4.9.12 We shouldn't be handling singletons internally.
545
- *
546
- * @return self
547
- */
548
- public static function instance() {
549
- return tribe( self::class );
550
- }
551
  }
552
 
553
- }
4
 
5
  if ( ! class_exists( 'Tribe__Dependency' ) ) {
6
  /**
7
+ * Tracks which tribe plugins are currently activated
8
  */
9
  class Tribe__Dependency {
10
 
17
  * 'path' => 'Path to the main plugin/bootstrap file' (optional)
18
  * )
19
  */
20
+ protected $active_plugins = array();
21
 
22
  /**
23
  * A multidimensional array of active tribe plugins in the following format
29
  * 'dependencies' => 'A multidimensional of dependencies' (optional)
30
  * )
31
  */
32
+ protected $registered_plugins = array();
33
 
34
  /**
35
  * An array of class Tribe__Admin__Notice__Plugin_Download per plugin
37
  * @since 4.9
38
  *
39
  */
40
+ protected $admin_messages = array();
41
+
42
+ /**
43
+ * Static Singleton Holder
44
+ *
45
+ * @var self
46
+ */
47
+ private static $instance;
48
+
49
+
50
+ /**
51
+ * Static Singleton Factory Method
52
+ *
53
+ * @return self
54
+ */
55
+ public static function instance() {
56
+ if ( ! self::$instance ) {
57
+ self::$instance = new self;
58
+ }
59
+ return self::$instance;
60
+ }
61
 
62
  /**
63
  * Adds a plugin to the active list
64
  *
65
  * @since 4.9
66
  *
67
+ * @param string $main_class Main/base class for this plugin
68
+ * @param null|string $version Version number of plugin
69
+ * @param null|string $path Path to the main plugin/bootstrap file
70
+ * @param array $dependencies An array of dependencies for a plugin
71
  */
72
  public function add_registered_plugin( $main_class, $version = null, $path = null, $dependencies = array() ) {
73
+
74
  $plugin = array(
75
  'class' => $main_class,
76
  'version' => $version,
83
  if ( $path ) {
84
  $this->admin_messages[ $main_class ] = new Tribe__Admin__Notice__Plugin_Download( $path );
85
  }
86
+
87
  }
88
 
89
  /**
105
  * @param string $path Path to the main plugin/bootstrap file
106
  */
107
  public function add_active_plugin( $main_class, $version = null, $path = null ) {
108
+
109
  $plugin = array(
110
  'class' => $main_class,
111
  'version' => $version,
251
  * @return bool
252
  */
253
  public function is_plugin_version( $main_class, $version, $compare = '>=' ) {
254
+
255
  //active plugin check to see if the correct version is active
256
  if ( ! $this->is_plugin_active( $main_class ) ) {
257
  return false;
274
  * @return bool
275
  */
276
  public function is_plugin_version_registered( $main_class, $version, $compare = '>=' ) {
277
+
278
  //registered plugin check if addon as it tests if it might load
279
  if ( ! $this->is_plugin_registered( $main_class ) ) {
280
  return false;
293
  * @return bool
294
  */
295
  public function has_requisite_plugins( $plugins_required = array() ) {
296
+
297
  foreach ( $plugins_required as $class => $version ) {
298
  // Return false if the plugin is not set or is a lesser version
299
  if ( ! $this->is_plugin_active( $class ) ) {
322
  }
323
 
324
  /**
325
+ * Gets all dependencies or single class requirements
326
+ * if parent, co, add does not exist use array as is
327
+ * if they do exist check each one in turn
328
  *
329
  * @since 4.9
330
  *
331
+ * @param array $plugin An array of data for given registered plugin
332
+ * @param array $dependencies An array of dependencies for a plugin
333
+ * @param bool $addon Indicates if the plugin is an add-on for The Events Calendar or Event Tickets
334
  *
335
+ * @return bool returns false if any dependency is invalid
336
  */
337
  public function has_valid_dependencies( $plugin, $dependencies = array(), $addon = false ) {
338
+
339
  if ( empty( $dependencies ) ) {
340
  return true;
341
  }
342
 
343
  $failed_dependency = 0;
344
+ $tribe_plugins = new Tribe__Plugins();
 
345
 
346
  foreach ( $dependencies as $class => $version ) {
347
 
348
  // if no class for add-on
349
+ $checked_plugin = $this->get_registered_plugin( $class );
350
  if ( $addon && empty( $checked_plugin ) ) {
351
  continue;
352
  }
356
  continue;
357
  }
358
 
359
+ if ( $class === $checked_plugin['class'] ) {
360
+ /*
361
+ * If the required plugin class is the same we're checking we clear the version to keep the message
362
+ * clear and redirect users to the latest version download link in place of providing a wrong
363
+ * version number.
364
+ */
365
+ $version = '';
366
+ }
367
 
368
+ $dependent_plugin = $tribe_plugins->get_plugin_by_class( $class );
369
+ $this->admin_messages[ $plugin['class'] ]->add_required_plugin( $dependent_plugin['short_name'], $dependent_plugin['thickbox_url'], $is_registered, $version, $addon );
 
 
 
 
 
 
 
 
 
370
  $failed_dependency++;
371
  }
372
 
373
  return $failed_dependency;
374
  }
375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  /**
377
  * Register a Plugin
378
  *
379
  * @since 4.9
380
  *
381
+ * @param $file_path
382
+ * @param $main_class
383
+ * @param $version
384
+ * @param array $classes_req
385
+ * @param array $dependencies
386
  */
387
  public function register_plugin( $file_path, $main_class, $version, $classes_req = array(), $dependencies = array() ) {
388
  /**
415
  if ( ! empty( $classes_req ) && ! $this->has_requisite_plugins( $classes_req ) ) {
416
  $tribe_plugins = new Tribe__Plugins();
417
  foreach ( $classes_req as $class => $plugin_version ) {
418
+ $plugin = $tribe_plugins->get_plugin_by_class( $class );
419
+ $is_active = $this->is_plugin_version( $class, $plugin_version );
420
+ $this->admin_messages[ $main_class ]->add_required_plugin( $plugin['short_name'], $plugin['thickbox_url'], $is_active, $plugin_version );
 
 
 
 
 
 
 
 
 
 
 
421
  }
422
  }
423
 
433
  *
434
  * @since 4.9
435
  *
436
+ * @param string $file_path Full file path to the base plugin file
437
  * @param string $main_class The Main/base class for this plugin
438
+ * @param string $version The version
439
+ * @param array $classes_req Any Main class files/tribe plugins required for this to run
440
+ * @param array $dependencies an array of dependencies to check
441
  *
442
  * @return bool Indicates if plugin should continue initialization
443
  */
445
 
446
  $parent_dependencies = $co_dependencies = $addon_dependencies = 0;
447
 
448
+ //check if plugin is registered, if not return false
449
  $plugin = $this->get_registered_plugin( $main_class );
450
  if ( empty( $plugin ) ) {
451
  return false;
452
  }
453
 
454
+ // check parent dependencies in add-on
455
  if ( ! empty( $plugin['dependencies']['parent-dependencies'] ) ) {
456
  $parent_dependencies = $this->has_valid_dependencies( $plugin, $plugin['dependencies']['parent-dependencies'] );
457
  }
458
+ //check co-dependencies in add-on
459
  if ( ! empty( $plugin['dependencies']['co-dependencies'] ) ) {
460
  $co_dependencies = $this->has_valid_dependencies( $plugin, $plugin['dependencies']['co-dependencies'] );
461
  }
462
 
463
+ //check add-on dependencies from parent
464
  $addon_dependencies = $this->check_addon_dependencies( $main_class );
465
 
466
+ //if good then we set as active plugin and continue to load
467
  if ( ! $parent_dependencies && ! $co_dependencies && ! $addon_dependencies ) {
468
  $this->add_active_plugin( $main_class, $plugin['version'], $plugin['path'] );
469
 
471
  }
472
 
473
  return false;
474
+
475
  }
476
 
477
  /**
479
  *
480
  * @since 4.9
481
  *
482
+ * @param string $main_class a string of the main class for the plugin being checked
483
  *
484
+ * @return bool returns false if any dependency is invalid
485
  */
486
  protected function check_addon_dependencies( $main_class ) {
487
+
488
+ $addon_dependencies = 0;
489
+
490
  foreach ( $this->registered_plugins as $registered ) {
491
  if ( empty( $registered['dependencies']['addon-dependencies'][ $main_class ] ) ) {
492
  continue;
493
  }
494
 
495
+ $addon_dependencies = $this->has_valid_dependencies( $registered, $registered['dependencies']['addon-dependencies'], true );
 
 
 
 
 
 
496
  }
497
 
498
+ return $addon_dependencies;
499
  }
500
 
 
 
 
 
 
 
 
 
 
 
501
  }
502
 
503
+ }
common/src/Tribe/Dialog/View.php DELETED
@@ -1,526 +0,0 @@
1
- <?php
2
-
3
- namespace Tribe\Dialog;
4
-
5
- /**
6
- * Class View
7
- *
8
- * @since 4.10.0
9
- */
10
- class View extends \Tribe__Template {
11
- /**
12
- * Where in the themes we will look for templates.
13
- *
14
- * @since 4.10.0
15
- *
16
- * @var string
17
- */
18
- public $template_namespace = 'dialogs';
19
-
20
- /**
21
- * View constructor
22
- *
23
- * @since 4.10.0
24
- */
25
- public function __construct() {
26
- $this->set_template_origin( \Tribe__Main::instance() );
27
- $this->set_template_folder( 'src/views/dialog' );
28
-
29
- // Configures this templating class to extract variables.
30
- $this->set_template_context_extract( true );
31
-
32
- // Uses the public folders.
33
- $this->set_template_folder_lookup( true );
34
- }
35
-
36
- /**
37
- * Public wrapper for build method.
38
- * Contains all the logic/validation checks.
39
- *
40
- * @since 4.10.0
41
- *
42
- * @param string $content Content as an HTML string.
43
- * @param array $args {
44
- * List of arguments to override dialog template.
45
- *
46
- * @type string $button_id The ID for the trigger button (optional).
47
- * @type array $button_classes Any desired classes for the trigger button (optional).
48
- * @type boolean $button_disabled Should the button be disabled (optional).
49
- * @type string $button_text The text for the dialog trigger button ("Open the dialog window").
50
- * @type string $button_type The type for the trigger button (optional).
51
- * @type string $button_value The value for the trigger button (optional).
52
- * @type string $close_event The dialog close event hook name (`tribe_dialog_close_dialog`).
53
- * @type string $content_classes The dialog content classes ("tribe-dialog__content").
54
- * @type array $context Any additional context data you need to expose to this file (optional).
55
- * @type string $id The unique ID for this dialog (`uniqid()`).
56
- * @type string $show_event The dialog event show hook name (`tribe_dialog_show_dialog`).
57
- * @type string $template The dialog template name (dialog).
58
- * @type string $title The dialog title (optional).
59
- * @type string $trigger_classes Classes for the dialog trigger ("tribe_dialog_trigger").
60
- *
61
- * Dialog script option overrides.
62
- *
63
- * @type string $append_target The dialog will be inserted after the button, you could supply a selector string here to override (optional).
64
- * @type boolean $body_lock Whether to lock the body while dialog open (false).
65
- * @type string $close_button_aria_label Aria label for the close button ("Close this dialog window").
66
- * @type string $close_button_classes Classes for the close button ("tribe-dialog__close-button").
67
- * @type string $content_wrapper_classes Dialog content wrapper classes. This wrapper includes the close button ("tribe-dialog__wrapper").
68
- * @type string $effect CSS effect on open. none or fade (optional).
69
- * @type string $effect_easing A css easing string to apply ("ease-in-out").
70
- * @type int $effect_speed CSS effect speed in milliseconds (optional).
71
- * @type string $overlay_classes The dialog overlay classes ("tribe-dialog__overlay").
72
- * @type boolean $overlay_click_closes If clicking the overlay closes the dialog (false).
73
- * @type string $wrapper_classes The wrapper class for the dialog ("tribe-dialog").
74
- * }
75
- * @param string $id The unique ID for this dialog. Gets prepended to the data attributes. Generated if not passed (`uniqid()`).
76
- * @param boolean $echo Whether to echo the script or to return it (default: true).
77
- *
78
- * @return string An HTML string of the dialog.
79
- */
80
- public function render_dialog( $content, $args = [], $id = null, $echo = true ) {
81
- // Check for content to be passed.
82
- if ( empty( $content ) ) {
83
- return '';
84
- }
85
-
86
- // Generate an ID if we weren't passed one.
87
- if ( is_null( $id ) ) {
88
- $id = \uniqid();
89
- }
90
-
91
- /** @var \Tribe__Assets $assets */
92
- $assets = tribe( 'assets' );
93
- $assets->enqueue_group( 'tribe-dialog' );
94
-
95
- $html = $this->build_dialog( $content, $id, $args );
96
-
97
- if ( ! $echo ) {
98
- return $html;
99
- }
100
-
101
- echo $html;
102
- }
103
-
104
- /**
105
- * Syntactic sugar for `render_dialog()` to make creating modals easier.
106
- * Adds sensible defaults for modals.
107
- *
108
- * @since 4.10.0
109
- *
110
- * @param string $content Content as an HTML string.
111
- * @param array $args {
112
- * List of arguments to override dialog template.
113
- *
114
- * @type string $button_id The ID for the trigger button (optional).
115
- * @type array $button_classes Any desired classes for the trigger button (optional).
116
- * @type boolean $button_disabled Should the button be disabled (optional).
117
- * @type string $button_text The text for the dialog trigger button ("Open the modal window").
118
- * @type string $button_type The type for the trigger button (optional).
119
- * @type string $button_value The value for the trigger button (optional).
120
- * @type string $close_event The dialog close event hook name (`tribe_dialog_close_modal`).
121
- * @type string $content_classes The dialog content classes ("tribe-dialog__content tribe-modal__content").
122
- * @type string $title_classes The dialog title classes ("tribe-dialog__title tribe-modal__title").
123
- * @type array $context Any additional context data you need to expose to this file (optional).
124
- * @type string $id The unique ID for this dialog (`uniqid()`).
125
- * @type string $show_event The dialog event hook name (`tribe_dialog_show_modal`).
126
- * @type string $template The dialog template name (modal).
127
- * @type string $title The dialog title (optional).
128
- * @type string $trigger_classes Classes for the dialog trigger ("tribe_dialog_trigger").
129
- *
130
- * Dialog script option overrides.
131
- *
132
- * @type string $append_target The dialog will be inserted after the button, you could supply a selector string here to override ("body").
133
- * @type boolean $body_lock Whether to lock the body while dialog open (true).
134
- * @type string $close_button_aria_label Aria label for the close button ("Close this modal window").
135
- * @type string $close_button_classes Classes for the close button ("tribe-dialog__close-button tribe-modal__close-button").
136
- * @type string $content_wrapper_classes Dialog content wrapper classes. This wrapper includes the close button ("tribe-dialog__wrapper tribe-modal__wrapper").
137
- * @type string $effect CSS effect on open. none or fade ("fade").
138
- * @type string $effect_easing A css easing string to apply ("ease-in-out").
139
- * @type int $effect_speed CSS effect speed in milliseconds (300).
140
- * @type string $overlay_classes The dialog overlay classes ("tribe-dialog__overlay tribe-modal__overlay").
141
- * @type boolean $overlay_click_closes If clicking the overlay closes the dialog (true).
142
- * @type string $wrapper_classes The wrapper class for the dialog ("tribe-dialog").
143
- * }
144
- * @param string $id The unique ID for this dialog. Gets prepended to the data attributes. Generated if not passed (`uniqid()`).
145
- * @param boolean $echo Whether to echo the script or to return it (default: true).
146
- *
147
- * @return string An HTML string of the dialog.
148
- */
149
- public function render_modal( $content, $args = [], $id = null, $echo = true ) {
150
- $default_args = [
151
- 'append_target' => '',
152
- 'body_lock' => true,
153
- 'button_text' => __( 'Open the modal window', 'tribe-common' ),
154
- 'close_button_aria_label' => __( 'Close this modal window', 'tribe-common' ),
155
- 'close_button_classes' => 'tribe-dialog__close-button tribe-modal__close-button',
156
- 'close_event' => 'tribe_dialog_close_modal',
157
- 'content_classes' => 'tribe-dialog__content tribe-modal__content',
158
- 'content_wrapper_classes' => 'tribe-dialog__wrapper tribe-modal__wrapper',
159
- 'effect' => 'fade',
160
- 'effect_speed' => 300,
161
- 'overlay_classes' => 'tribe-dialog__overlay tribe-modal__overlay',
162
- 'overlay_click_closes' => true,
163
- 'show_event' => 'tribe_dialog_show_modal',
164
- 'template' => 'modal',
165
- 'title_classes' => [ 'tribe-dialog__title', 'tribe-modal__title' ],
166
- ];
167
-
168
- $args = wp_parse_args( $args, $default_args );
169
-
170
- $this->render_dialog( $content, $args, $id, $echo );
171
- }
172
-
173
- /**
174
- * Syntactic sugar for `render_dialog()` to make creating custom confirmation dialogs easier.
175
- * Adds sensible defaults for confirmation dialogs.
176
- *
177
- * @since 4.10.0
178
- *
179
- * @param string $content Content as an HTML string.
180
- * @param array $args {
181
- * List of arguments to override dialog template.
182
- *
183
- * @type string $button_id The ID for the trigger button (optional).
184
- * @type array $button_classes Any desired classes for the trigger button (optional).
185
- * @type boolean $button_disabled Should the button be disabled (optional).
186
- * @type string $button_text The text for the dialog trigger button ("Open the dialog window").
187
- * @type string $button_type The type for the trigger button (optional).
188
- * @type string $button_value The value for the trigger button (optional).
189
- * @type string $cancel_button_text Text for the "Cancel" button ("Cancel").
190
- * @type string $content_classes The dialog content classes ("tribe-dialog__content tribe-confirm__content").
191
- * @type string $continue_button_text Text for the "Continue" button ("Confirm").
192
- * @type array $context Any additional context data you need to expose to this file (optional).
193
- * @type string $id The unique ID for this dialog (`uniqid()`).
194
- * @type string $template The dialog template name (confirm).
195
- * @type string $title The dialog title (optional).
196
- * @type string $trigger_classes Classes for the dialog trigger ("tribe_dialog_trigger").
197
- *
198
- * Dialog script option overrides.
199
- *
200
- * @type string $append_target The dialog will be inserted after the button, you could supply a selector string here to override (optional).
201
- * @type boolean $body_lock Whether to lock the body while dialog open (true).
202
- * @type string $close_button_aria_label Aria label for the close button (optional).
203
- * @type string $close_button_classes Classes for the close button ("tribe-dialog__close-button--hidden").
204
- * @type string $close_event The dialog close event hook name (`tribe_dialog_close_confirm`).
205
- * @type string $content_wrapper_classes Dialog content wrapper classes. This wrapper includes the close button ("tribe-dialog__wrapper tribe-confirm__wrapper").
206
- * @type string $effect CSS effect on open. none or fade (optional).
207
- * @type string $effect_easing A css easing string to apply ("ease-in-out").
208
- * @type int $effect_speed CSS effect speed in milliseconds (optional).
209
- * @type string $overlay_classes The dialog overlay classes ("tribe-dialog__overlay tribe-confirm__overlay").
210
- * @type boolean $overlay_click_closes If clicking the overlay closes the dialog (false).
211
- * @type string $show_event The dialog event hook name (`tribe_dialog_show_confirm`).
212
- * @type string $wrapper_classes The wrapper class for the dialog ("tribe-dialog").
213
- * }
214
- * @param string $id The unique ID for this dialog. Gets prepended to the data attributes. Generated if not passed (`uniqid()`).
215
- * @param boolean $echo Whether to echo the script or to return it (default: true).
216
- *
217
- * @return string An HTML string of the dialog.
218
- */
219
- public function render_confirm( $content, $args = [], $id = null, $echo = true ) {
220
- $default_args = [
221
- 'body_lock' => true,
222
- 'cancel_button_text' => __( 'Cancel', 'tribe-common' ),
223
- 'continue_button_text' => __( 'Confirm', 'tribe-common' ),
224
- 'close_button_aria_label' => '',
225
- 'close_button_classes' => 'tribe-dialog__close-button--hidden',
226
- 'close_event' => 'tribe_dialog_close_confirm',
227
- 'content_classes' => 'tribe-dialog__content tribe-confirm__content',
228
- 'content_wrapper_classes' => 'tribe-dialog__wrapper tribe-confirm__wrapper',
229
- 'overlay_classes' => 'tribe-dialog__overlay tribe-confirm__overlay',
230
- 'show_event' => 'tribe_dialog_show_confirm',
231
- 'template' => 'confirm',
232
- 'title_classes' => [ 'tribe-dialog__title', 'tribe-confirm__title' ],
233
- ];
234
-
235
- $args = wp_parse_args( $args, $default_args );
236
-
237
- $this->render_dialog( $content, $args, $id, $echo );
238
- }
239
-
240
- /**
241
- * Syntactic sugar for `render_dialog()` to make creating custom alerts easier.
242
- * Adds sensible defaults for alerts.
243
- *
244
- * @since 4.10.0
245
- *
246
- * @param string $content Content as an HTML string.
247
- * @param array $args {
248
- * List of arguments to override dialog template.
249
- *
250
- * @type string $alert_button_text Text for the "OK" button ("OK").
251
- * @type string $button_id The ID for the trigger button (optional).
252
- * @type array $button_classes Any desired classes for the trigger button (optional).
253
- * @type boolean $button_disabled Should the button be disabled (optional).
254
- * @type string $button_text The text for the dialog trigger button ("Open the dialog window").
255
- * @type string $button_type The type for the trigger button (optional).
256
- * @type string $button_value The value for the trigger button (optional).
257
- * @type string $content_classes The dialog content classes ("tribe-dialog__content tribe-alert__content").
258
- * @type string $title_classes The dialog title classes ("tribe-dialog__title tribe-alert__title").
259
- * @type array $context Any additional context data you need to expose to this file (optional).
260
- * @type string $id The unique ID for this dialog (`uniqid()`).
261
- * @type string $template The dialog template name (alert).
262
- * @type string $title The dialog title (optional).
263
- * @type string $trigger_classes Classes for the dialog trigger ("tribe_dialog_trigger").
264
- *
265
- * Dialog script option overrides.
266
- *
267
- * @type string $append_target The dialog will be inserted after the button, you could supply a selector string here to override (optional).
268
- * @type boolean $body_lock Whether to lock the body while dialog open (true).
269
- * @type string $close_button_aria_label Aria label for the close button (optional).
270
- * @type string $close_button_classes Classes for the close button ("tribe-dialog__close-button--hidden").
271
- * @type string $close_event The dialog close event hook name (`tribe_dialog_close_alert`).
272
- * @type string $content_wrapper_classes Dialog content wrapper classes. This wrapper includes the close button ("tribe-dialog__wrapper tribe-alert__wrapper").
273
- * @type string $effect CSS effect on open. none or fade (optional).
274
- * @type string $effect_easing A css easing string to apply ("ease-in-out").
275
- * @type int $effect_speed CSS effect speed in milliseconds (optional).
276
- * @type string $overlay_classes The dialog overlay classes ("tribe-dialog__overlay tribe-alert__overlay").
277
- * @type boolean $overlay_click_closes If clicking the overlay closes the dialog (false).
278
- * @type string $show_event The dialog event hook name (`tribe_dialog_show_alert`).
279
- * @type string $wrapper_classes The wrapper class for the dialog ("tribe-dialog").
280
- * }
281
- * @param string $id The unique ID for this dialog. Gets prepended to the data attributes. Generated if not passed (`uniqid()`).
282
- * @param boolean $echo Whether to echo the script or to return it (default: true).
283
- *
284
- * @return string An HTML string of the dialog.
285
- */
286
- public function render_alert( $content, $args = [], $id = null, $echo = true ) {
287
- $default_args = [
288
- 'alert_button_text' => __( 'OK', 'tribe-common' ),
289
- 'body_lock' => true,
290
- 'close_button_aria_label' => '',
291
- 'close_button_classes' => 'tribe-dialog__close-button--hidden',
292
- 'close_event' => 'tribe_dialog_close_alert',
293
- 'content_classes' => 'tribe-dialog__content tribe-alert__content',
294
- 'content_wrapper_classes' => 'tribe-dialog__wrapper tribe-alert__wrapper',
295
- 'overlay_classes' => 'tribe-dialog__overlay tribe-alert__overlay',
296
- 'show_event' => 'tribe_dialog_show_alert',
297
- 'template' => 'alert',
298
- 'title_classes' => [ 'tribe-dialog__title', 'tribe-alert__title' ],
299
- ];
300
-
301
- $args = wp_parse_args( $args, $default_args );
302
-
303
- $this->render_dialog( $content, $args, $id, $echo );
304
- }
305
-
306
- /**
307
- * Factory method for dialog HTML
308
- *
309
- * @since 4.10.0
310
- *
311
- * @param string $content HTML dialog content.
312
- * @param string $id The unique ID for this dialog (`uniqid()`) Gets prepended to the data attributes.
313
- * @param array $args {
314
- * List of arguments to override dialog template.
315
- *
316
- * @type string $button_id The ID for the trigger button (optional).
317
- * @type array $button_classes Any desired classes for the trigger button (optional).
318
- * @type boolean $button_disabled Should the button be disabled (optional).
319
- * @type string $button_text The text for the dialog trigger button ("Open the dialog window").
320
- * @type string $button_type The type for the trigger button (optional).
321
- * @type string $button_value The value for the trigger button (optional).
322
- * @type string $close_event The dialog event hook name (`tribe_dialog_close_dialog`).
323
- * @type string $content_classes The dialog content classes ("tribe-dialog__content").
324
- * @type string $title_classes The dialog title classes ("tribe-dialog__title").
325
- * @type array $context Any additional context data you need to expose to this file (optional).
326
- * @type string $id The unique ID for this dialog (`uniqid()`).
327
- * @type string $show_event The dialog event hook name (`tribe_dialog_show_dialog`).
328
- * @type string $template The dialog template name (dialog).
329
- * @type string $title The dialog title (optional).
330
- * @type string $trigger_classes Classes for the dialog trigger ("tribe_dialog_trigger").
331
- *
332
- * Dialog script option overrides.
333
- *
334
- * @type string $append_target The dialog will be inserted after the button, you could supply a selector string here to override (optional).
335
- * @type boolean $body_lock Whether to lock the body while dialog open (false).
336
- * @type string $close_button_aria_label Aria label for the close button ("Close this dialog window").
337
- * @type string $close_button_classes Classes for the close button ("tribe-dialog__close-button").
338
- * @type string $content_wrapper_classes Dialog content wrapper classes. This wrapper includes the close button ("tribe-dialog__wrapper").
339
- * @type string $effect CSS effect on open. none or fade (optional).
340
- * @type string $effect_easing A css easing string to apply ("ease-in-out").
341
- * @type int $effect_speed CSS effect speed in milliseconds (optional).
342
- * @type string $overlay_classes The dialog overlay classes ("tribe-dialog__overlay").
343
- * @type boolean $overlay_click_closes If clicking the overlay closes the dialog (false).
344
- * @type string $wrapper_classes The wrapper class for the dialog ("tribe-dialog").
345
- * }
346
- *
347
- * @return string An HTML string of the dialog.
348
- */
349
- private function build_dialog( $content, $id, $args ) {
350
- $default_args = [
351
- 'button_classes' => '',
352
- 'button_disabled' => false,
353
- 'button_id' => '',
354
- 'button_name' => '',
355
- 'button_text' => __( 'Open the dialog window', 'tribe-common' ),
356
- 'button_type' => '',
357
- 'button_value' => '',
358
- 'close_event' => 'tribe_dialog_close_dialog',
359
- 'content_classes' => 'tribe-dialog__content',
360
- 'context' => '',
361
- 'show_event' => 'tribe_dialog_show_dialog',
362
- 'template' => 'dialog',
363
- 'title_classes' => 'tribe-dialog__title',
364
- 'title' => '',
365
- 'trigger_classes' => 'tribe_dialog_trigger',
366
- // Dialog script options.
367
- 'append_target' => '', // The dialog will be inserted after the button, you could supply a selector string here to override.
368
- 'body_lock' => false, // Lock the body while dialog open?
369
- 'close_button_aria_label' => __( 'Close this dialog window', 'tribe-common' ), // Aria label for close button.
370
- 'close_button_classes' => 'tribe-dialog__close-button', // Classes for close button.
371
- 'content_wrapper_classes' => 'tribe-dialog__wrapper', // Dialog content classes.
372
- 'effect' => 'none', // None or fade (for now).
373
- 'effect_speed' => 0, // Effect speed in milliseconds.
374
- 'effect_easing' => 'ease-in-out', // A css easing string.
375
- 'overlay_classes' => 'tribe-dialog__overlay', // Overlay classes.
376
- 'overlay_click_closes' => false, // Clicking overlay closes dialog.
377
- 'wrapper_classes' => 'tribe-dialog', // The wrapper class for the dialog.
378
- ];
379
-
380
- $args = wp_parse_args( $args, $default_args );
381
-
382
- $args[ 'content' ] = $content;
383
- $args[ 'id' ] = $id;
384
-
385
- /**
386
- * Allow us to filter the dialog arguments.
387
- *
388
- * @since 4.10.0
389
- *
390
- * @param array $args The dialog arguments.
391
- * @param string $content HTML content string.
392
- */
393
- $args = apply_filters( 'tribe_dialog_args', $args, $content );
394
-
395
- $template = $args[ 'template' ];
396
- /**
397
- * Allow us to filter the dialog template name.
398
- *
399
- * @since 4.10.0
400
- *
401
- * @param string $template The dialog template name.
402
- * @param array $args The dialog arguments.
403
- */
404
- $template_name = apply_filters( 'tribe_dialog_template', $template, $args );
405
-
406
- ob_start();
407
-
408
- $this->template( $template_name, $args, true );
409
-
410
- $this->get_dialog_script( $args );
411
-
412
- $html = ob_get_clean();
413
-
414
- /**
415
- * Allow us to filter the dialog output (HTML string).
416
- *
417
- * @since 4.10.0
418
- *
419
- * @param string $html The dialog HTML string.
420
- * @param array $args The dialog arguments.
421
- */
422
- return apply_filters( 'tribe_dialog_html', $html, $args );
423
- }
424
-
425
- /**
426
- * Get dialog <script> to be rendered.
427
- *
428
- * @since 4.10.0
429
- *
430
- * @param array $args List of arguments for the dialog script. See \Tribe\Dialog\View->build_dialog().
431
- * @param boolean $echo Whether to echo the script or to return it (default: true).
432
- *
433
- * @return string|void The dialog <script> HTML or nothing if $echo is true.
434
- */
435
- public function get_dialog_script( $args, $echo = true ) {
436
- $args = [
437
- 'appendTarget' => $args[ 'append_target' ],
438
- 'bodyLock' => $args[ 'body_lock' ],
439
- 'closeButtonAriaLabel' => $args[ 'close_button_aria_label' ],
440
- 'closeButtonClasses' => $args[ 'close_button_classes' ],
441
- 'closeEvent' => $args[ 'close_event' ],
442
- 'contentClasses' => $args[ 'content_wrapper_classes' ],
443
- 'effect' => $args[ 'effect' ],
444
- 'effectEasing' => $args[ 'effect_easing' ],
445
- 'effectSpeed' => $args[ 'effect_speed' ],
446
- 'id' => $args[ 'id' ],
447
- 'overlayClasses' => $args[ 'overlay_classes' ],
448
- 'overlayClickCloses' => $args[ 'overlay_click_closes' ],
449
- 'showEvent' => $args[ 'show_event' ],
450
- 'closeEvent' => $args[ 'close_event' ],
451
- 'template' => $args[ 'template' ],
452
- 'wrapperClasses' => esc_attr( $args[ 'wrapper_classes' ] ),
453
- ];
454
-
455
- /**
456
- * Allows for modifying the arguments before they are passed to the dialog script.
457
- *
458
- * @since 4.10.0
459
- *
460
- * @param array $args List of arguments to override dialog script. See \Tribe\Dialog\View->build_dialog().
461
- */
462
- $args = apply_filters( 'tribe_dialog_script_args', $args );
463
-
464
- // Escape all argument values.
465
- $args = array_map( 'esc_html', $args );
466
-
467
- $args[ 'trigger' ] = "[data-js='" . esc_attr( 'trigger-dialog-' . $args[ 'id' ] ) . "' ]";
468
-
469
- ob_start();
470
- ?>
471
- <script>
472
- var tribe = tribe || {};
473
- tribe.dialogs = tribe.dialogs || [];
474
- tribe.dialogs.dialogs = tribe.dialogs.dialogs || [];
475
-
476
- tribe.dialogs.dialogs.push( <?php echo json_encode( $args ); ?> );
477
-
478
- <?php
479
- /**
480
- * Allows for injecting additional scripts (button actions, etc).
481
- *
482
- * @since 4.10.0
483
- *
484
- * @param array $args List of arguments to override dialog script. See \Tribe\Dialog\View->build_dialog().
485
- */
486
- do_action( 'tribe_dialog_additional_scripts', $args );
487
-
488
- /**
489
- * Allows for injecting additional scripts (button actions, etc) by template.
490
- *
491
- * @since 4.10.0
492
- *
493
- * @param array $args List of arguments to override dialog script. See \Tribe\Dialog\View->build_dialog().
494
- */
495
- do_action( 'tribe_dialog_additional_scripts_' . $args[ 'template' ], $args );
496
-
497
- /**
498
- * Allows for injecting additional scripts (button actions, etc) by dialog ID.
499
- *
500
- * @since 4.10.0
501
- *
502
- * @param array $args List of arguments to override dialog script. See \Tribe\Dialog\View->build_dialog().
503
- */
504
- do_action( 'tribe_dialog_additional_scripts_' . $args[ 'id' ], $args );
505
- ?>
506
- </script>
507
- <?php
508
- $html = ob_get_clean();
509
-
510
- /**
511
- * Allows for modifying the HTML before it is echoed or returned.
512
- *
513
- * @since 4.10.0
514
- *
515
- * @param array $args List of arguments to override dialog script. See \Tribe\Dialog\View->build_dialog().
516
- */
517
- $html = apply_filters( 'tribe_dialog_script_html', $html );
518
-
519
- if ( $echo ) {
520
- echo $html;
521
- return;
522
- }
523
-
524
- return $html;
525
- }
526
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Editor.php CHANGED
@@ -27,22 +27,10 @@ class Tribe__Editor {
27
  * @return bool
28
  */
29
  public function should_load_blocks() {
30
- $gutenberg = $this->is_gutenberg_active() || $this->is_wp_version();
31
- $blocks = $this->is_blocks_editor_active();
32
- $classic = $this->is_classic_plugin_active() || $this->is_classic_option_active();
33
-
34
- $should_load_blocks = $gutenberg && $blocks && ! $classic;
35
-
36
- /**
37
- * Filters whether the Blocks Editor should be activated or not.
38
- *
39
- * @since 4.12.0
40
- *
41
- * @param bool $should_load_blocks Whether the blocks editor should be activated or not.
42
- */
43
- $should_load_blocks = (bool) apply_filters( 'tribe_editor_should_load_blocks', $should_load_blocks );
44
-
45
- return $should_load_blocks;
46
  }
47
 
48
  /**
@@ -173,15 +161,7 @@ class Tribe__Editor {
173
  * @return bool
174
  */
175
  public function is_classic_plugin_active() {
176
- $is_plugin_active = function_exists( 'classic_editor_replace' ) || class_exists( 'Classic_Editor' );
177
- /**
178
- * Filter to change the output of calling: `is_classic_plugin_active`
179
- *
180
- * @since 4.9.12
181
- *
182
- * @param $is_plugin_active bool Value that indicates if the plugin is active or not.
183
- */
184
- return apply_filters( 'tribe_is_classic_editor_plugin_active', $is_plugin_active );
185
  }
186
 
187
  /**
@@ -222,35 +202,4 @@ class Tribe__Editor {
222
 
223
  return $is_classic_editor_request || $disabled_by_plugin || $disabled_by_filter;
224
  }
225
-
226
- /**
227
- * Whether the events are being served using Blocks or the Classical Editor.
228
- *
229
- * @since 4.12.0
230
- *
231
- * @return bool True if using Blocks. False if using the Classical Editor.
232
- */
233
- public function is_events_using_blocks() {
234
- /**
235
- * Whether the event is being served through blocks
236
- * or the classical editor.
237
- *
238
- * @since 4.12.0
239
- *
240
- * @param bool $is_using_blocks True if using blocks. False if using the classical editor.
241
- */
242
- $is_using_blocks = apply_filters( 'tribe_is_using_blocks', null );
243
-
244
- // Early bail: The filter was overridden to return either true or false.
245
- if ( null !== $is_using_blocks ) {
246
- return $is_using_blocks;
247
- }
248
-
249
- // Early bail: The site itself is not using blocks.
250
- if ( ! $this->should_load_blocks() ) {
251
- return false;
252
- }
253
-
254
- return tribe_is_truthy( tribe_get_option( 'toggle_blocks_editor' ) );
255
- }
256
  }
27
  * @return bool
28
  */
29
  public function should_load_blocks() {
30
+ return (
31
+ $this->is_gutenberg_active() || $this->is_wp_version()
32
+ )
33
+ && $this->is_blocks_editor_active();
 
 
 
 
 
 
 
 
 
 
 
 
34
  }
35
 
36
  /**
161
  * @return bool
162
  */
163
  public function is_classic_plugin_active() {
164
+ return function_exists( 'classic_editor_replace' ) || class_exists( 'Classic_Editor' );
 
 
 
 
 
 
 
 
165
  }
166
 
167
  /**
202
 
203
  return $is_classic_editor_request || $disabled_by_plugin || $disabled_by_filter;
204
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  }
common/src/Tribe/Editor/Blocks/Abstract.php CHANGED
@@ -38,7 +38,7 @@ implements Tribe__Editor__Blocks__Interface {
38
  return $this->namespace;
39
  }
40
 
41
- /**
42
  * Return the block attributes
43
  *
44
  * @since 4.8
@@ -69,7 +69,7 @@ implements Tribe__Editor__Blocks__Interface {
69
  return $attributes;
70
  }
71
 
72
- /**
73
  * Return the block default attributes
74
  *
75
  * @since 4.8
@@ -203,39 +203,5 @@ implements Tribe__Editor__Blocks__Interface {
203
  */
204
  public function hook() {
205
  }
206
-
207
- /**
208
- * Returns the block data for the block editor.
209
- *
210
- * @since 4.12.0
211
- *
212
- * @return array<string,mixed> The block editor data.
213
- */
214
- public function block_data() {
215
- $block_data = [
216
- 'id' => $this->slug(),
217
- ];
218
-
219
- /**
220
- * Filters the block data.
221
- *
222
- * @since 4.12.0
223
- *
224
- * @param array $block_data The block data.
225
- * @param object $this The current object.
226
- */
227
- $block_data = apply_filters( 'tribe_block_block_data', $block_data, $this );
228
-
229
- /**
230
- * Filters the block data for the block.
231
- *
232
- * @since 4.12.0
233
- *
234
- * @param array $block_data The block data.
235
- * @param object $this The current object.
236
- */
237
- $block_data = apply_filters( 'tribe_block_block_data_' . $this->slug(), $block_data, $this );
238
-
239
- return $block_data;
240
- }
241
  }
 
38
  return $this->namespace;
39
  }
40
 
41
+ /*
42
  * Return the block attributes
43
  *
44
  * @since 4.8
69
  return $attributes;
70
  }
71
 
72
+ /*
73
  * Return the block default attributes
74
  *
75
  * @since 4.8
203
  */
204
  public function hook() {
205
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  }
207
+
common/src/Tribe/Editor/Configuration.php CHANGED
@@ -39,7 +39,6 @@ class Tribe__Editor__Configuration implements Tribe__Editor__Configuration_Inter
39
  'countries' => tribe( 'languages.locations' )->get_countries(),
40
  'usStates' => Tribe__View_Helpers::loadStates(),
41
  ),
42
- 'blocks' => [],
43
  );
44
 
45
  /**
39
  'countries' => tribe( 'languages.locations' )->get_countries(),
40
  'usStates' => Tribe__View_Helpers::loadStates(),
41
  ),
 
42
  );
43
 
44
  /**
common/src/Tribe/Extension.php CHANGED
@@ -160,54 +160,25 @@ abstract class Tribe__Extension {
160
  * Checks if the extension has permission to run, if so runs init() in child class
161
  */
162
  final public function register() {
163
- $extension_file = $this->get_plugin_file();
164
- $extension_class_name = $this->get( 'class' );
165
- $extension_version = $this->get_version();
166
- $plugins_required = $this->get( 'requires', [] );
167
-
168
  tribe_register_plugin(
169
- $extension_file,
170
- $extension_class_name,
171
- $extension_version,
172
- $plugins_required
173
  );
174
 
175
  $dependency = Tribe__Dependency::instance();
176
 
177
  // check requisite plugins are active for this extension
178
- $is_plugin_authorized = $dependency->has_requisite_plugins( $plugins_required );
179
-
180
- /**
181
- * Explicitly disallow an extension, such as a core plugin having absorbed/replaced its functionality.
182
- *
183
- * @since 4.12.2
184
- *
185
- * @param bool $is_disallowed    False by default.
186
- * @param string $extension_class_name This extension's class name string
187
- * (without initial forward slash for namespaced classes).
188
- * @param Tribe__Extension $this_instance This extension class' instance.
189
- */
190
- $is_disallowed = (bool) apply_filters( 'tribe_extension_is_disallowed', false, $extension_class_name, $this );
191
-
192
- if ( $is_disallowed ) {
193
- if (
194
- is_admin()
195
- && current_user_can( 'activate_plugins' )
196
- ) {
197
- tribe_notice( 'tribe_extension_is_disallowed', [ $this, 'notice_disallowed' ], [ 'type' => 'error' ] );
198
- }
199
-
200
- deactivate_plugins( $extension_file, true );
201
-
202
- return;
203
- }
204
 
205
  if ( $is_plugin_authorized ) {
206
  $this->init();
207
 
208
- // Add extension as active to dependency checker.
209
- $dependency->add_active_plugin( $extension_class_name, $extension_version, $extension_file );
210
  }
 
211
  }
212
 
213
  /**
@@ -258,7 +229,7 @@ abstract class Tribe__Extension {
258
  }
259
 
260
  /**
261
- * Gets the action/hook for the extensions' init().
262
  *
263
  * @return string Action/hook
264
  */
@@ -407,25 +378,6 @@ abstract class Tribe__Extension {
407
  );
408
  }
409
 
410
- /**
411
- * Gets the error message about being explicitly disallowed.
412
- *
413
- * @since 4.12.2
414
- *
415
- * @return string Notice text.
416
- */
417
- public function notice_disallowed() {
418
- return sprintf(
419
- '<p><strong>%1$s:</strong> %2$s</p>',
420
- $this->get_name(),
421
- esc_html_x(
422
- "This extension has been programmatically disallowed. The most common reason is due to another Modern Tribe plugin having absorbed or replaced this extension's functionality. This extension plugin has been deactivated, and you should likely delete it.",
423
- 'extension disallowed',
424
- 'tribe-common'
425
- )
426
- );
427
- }
428
-
429
  /**
430
  * Prevent cloning the singleton with 'clone' operator
431
  *
160
  * Checks if the extension has permission to run, if so runs init() in child class
161
  */
162
  final public function register() {
 
 
 
 
 
163
  tribe_register_plugin(
164
+ $this->get_plugin_file(),
165
+ $this->get( 'class' ),
166
+ $this->get_version(),
167
+ $this->get( 'requires', array() )
168
  );
169
 
170
  $dependency = Tribe__Dependency::instance();
171
 
172
  // check requisite plugins are active for this extension
173
+ $is_plugin_authorized = $dependency->has_requisite_plugins( $this->get( 'requires', array() ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  if ( $is_plugin_authorized ) {
176
  $this->init();
177
 
178
+ //add extension as active to dependency checker
179
+ $dependency->add_active_plugin( $this->get( 'class' ), $this->get_version(), $this->get_plugin_file() );
180
  }
181
+
182
  }
183
 
184
  /**
229
  }
230
 
231
  /**
232
+ * Get's the action/hook for the extensions init()
233
  *
234
  * @return string Action/hook
235
  */
378
  );
379
  }
380
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
  /**
382
  * Prevent cloning the singleton with 'clone' operator
383
  *
common/src/Tribe/Feature_Detection.php CHANGED
@@ -6,8 +6,6 @@
6
  * @since 4.7.23
7
  */
8
 
9
- use Tribe__Utils__Array as Arr;
10
-
11
  /**
12
  * Class Tribe__Feature_Detection
13
  *
@@ -22,17 +20,6 @@ class Tribe__Feature_Detection {
22
  */
23
  public static $transient = 'tribe_feature_detection';
24
 
25
- /**
26
- * A set of example byte sizes of result sets.
27
- *
28
- * @since 4.10.2
29
- *
30
- * @var array
31
- */
32
- public static $example_size = [
33
- 'post_result' => 6000,
34
- ];
35
-
36
  /**
37
  * The name of the option that will be used to indicate a feature detection is running.
38
  *
@@ -162,97 +149,4 @@ class Tribe__Feature_Detection {
162
 
163
  return ! empty( $lock_option );
164
  }
165
-
166
- /**
167
- * Returns the value of the `max_allowed_packet` MYSQL variable, if set, or a default value.
168
- *
169
- * @since 4.10.2
170
- *
171
- * @return int The byte size of the `max_allowed_packet` MYSQL variable.
172
- */
173
- public function get_mysql_max_packet_size() {
174
- /**
175
- * Filters the value of the `max_allowed_packet` variable before it's read from the database.
176
- *
177
- * If the value returned from this filter is not `null`, then it will be assumed to be the value.
178
- *
179
- * @since 4.10.2
180
- *
181
- * @param int $mysql_max_packet_size The value of the `max_allowed_packet` variable, initially `null`.
182
- */
183
- $mysql_max_packet_size = apply_filters( 'tribe_max_allowed_packet_size', null );
184
-
185
- if ( null !== $mysql_max_packet_size ) {
186
- return absint( $mysql_max_packet_size );
187
- }
188
-
189
- /** @var Tribe__Cache $cache */
190
- $cache = tribe( 'cache' );
191
-
192
- $cached = $cache->get( 'max_allowed_packet' );
193
-
194
- if ( false !== $cached ) {
195
- return $cached;
196
- }
197
-
198
- global $wpdb;
199
- $mysql_max_packet_size = $wpdb->get_var( "SHOW VARIABLES LIKE 'max_allowed_packet'", 1 );
200
- // At min set it to 2 MBs.
201
- $mysql_max_packet_size = absint( max( absint( $mysql_max_packet_size ), 2097152 ) );
202
-
203
- $cache->set( 'max_allowed_packet', $mysql_max_packet_size, WEEK_IN_SECONDS );
204
-
205
- return $mysql_max_packet_size;
206
- }
207
-
208
- /**
209
- * Returns the suggested SQL LIMIT value, based on the `max_allowed_packet` size and example string length.
210
- *
211
- * This is useful to size "reasonable" LIMITs when dealing with either very long queries or potentially long
212
- * result sets.
213
- *
214
- * @since 4.10.2
215
- *
216
- * @param string $example_string The example string.
217
- *
218
- * @return int The suggested LIMIT value.
219
- */
220
- public function mysql_limit_for_string( $example_string ) {
221
- $byte_size = function_exists( 'mb_strlen' )
222
- ? mb_strlen( $example_string )
223
- : strlen( $example_string );
224
-
225
- return $this->mysql_limit_for_size( $byte_size );
226
- }
227
-
228
- /**
229
- * Returns the SQL LIMIT for a byte size, in relation to the `max_allowed_packet` value.
230
- *
231
- * @since 4.10.2
232
- *
233
- * @param int $byte_size The byte size to check.
234
- *
235
- * @return int The SQL LIMIT value.
236
- */
237
- public function mysql_limit_for_size( $byte_size ) {
238
- return absint( floor( $this->get_mysql_max_packet_size() / $byte_size ) * 0.8 );
239
- }
240
-
241
- /**
242
- * Provides the SQL LIMIT value, in relation to the `max_allowed_packet` value, for a pre-existing example.
243
- *
244
- * Defaults to the complete post result example string if the example is not found.
245
- *
246
- * @since 4.10.2
247
- *
248
- * @param string $example The name of the example to return. See the `Tribe__Feature_Detection::$example_sizes`
249
- * prop for the available examples. Defaults to the `post_result` one.
250
- *
251
- * @return int The SQL LIMIT value for the example.
252
- */
253
- public function mysql_limit_for_example( $example ) {
254
- $example_size = Arr::get( static::$example_size, $example, static::$example_size['post_result'] );
255
-
256
- return $this->mysql_limit_for_size( $example_size );
257
- }
258
  }
6
  * @since 4.7.23
7
  */
8
 
 
 
9
  /**
10
  * Class Tribe__Feature_Detection
11
  *
20
  */
21
  public static $transient = 'tribe_feature_detection';
22
 
 
 
 
 
 
 
 
 
 
 
 
23
  /**
24
  * The name of the option that will be used to indicate a feature detection is running.
25
  *
149
 
150
  return ! empty( $lock_option );
151
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  }
common/src/Tribe/Field.php CHANGED
@@ -89,7 +89,6 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
89
  'can_be_empty' => false,
90
  'clear_after' => true,
91
  'tooltip_first' => false,
92
- 'allow_clear' => false,
93
  );
94
 
95
  // a list of valid field types, to prevent screwy behavior
@@ -188,7 +187,6 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
188
  $can_be_empty = (bool) $args['can_be_empty'];
189
  $clear_after = (bool) $args['clear_after'];
190
  $tooltip_first = (bool) $args['tooltip_first'];
191
- $allow_clear = (bool) $args['allow_clear'];
192
 
193
  // set the ID
194
  $this->id = apply_filters( 'tribe_field_id', $id );
@@ -523,15 +521,8 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
523
  $field .= $this->do_field_div_start();
524
  if ( is_array( $this->options ) ) {
525
  foreach ( $this->options as $option_id => $title ) {
526
- $field_id = sprintf(
527
- '%1$s-%2$s',
528
- sanitize_html_class( trim( $this->id ) ),
529
- sanitize_html_class( trim( $option_id ) )
530
- );
531
-
532
  $field .= '<label title="' . esc_attr( strip_tags( $title ) ) . '">';
533
  $field .= '<input type="radio"';
534
- $field .= ' id="tribe-field-' . esc_attr( $field_id ) . '"';
535
  $field .= $this->do_field_name();
536
  $field .= ' value="' . esc_attr( $option_id ) . '" ' . checked( $this->value, $option_id, false ) . '/>';
537
  $field .= $title;
@@ -617,9 +608,6 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
617
  $field .= $this->do_field_name();
618
  $field .= " id='{$this->id}-select'";
619
  $field .= " class='tribe-dropdown'";
620
- if ( empty( $this->allow_clear ) ) {
621
- $field .= " data-prevent-clear='true'";
622
- }
623
  $field .= '>';
624
  foreach ( $this->options as $option_id => $title ) {
625
  $field .= '<option value="' . esc_attr( $option_id ) . '"';
89
  'can_be_empty' => false,
90
  'clear_after' => true,
91
  'tooltip_first' => false,
 
92
  );
93
 
94
  // a list of valid field types, to prevent screwy behavior
187
  $can_be_empty = (bool) $args['can_be_empty'];
188
  $clear_after = (bool) $args['clear_after'];
189
  $tooltip_first = (bool) $args['tooltip_first'];
 
190
 
191
  // set the ID
192
  $this->id = apply_filters( 'tribe_field_id', $id );
521
  $field .= $this->do_field_div_start();
522
  if ( is_array( $this->options ) ) {
523
  foreach ( $this->options as $option_id => $title ) {
 
 
 
 
 
 
524
  $field .= '<label title="' . esc_attr( strip_tags( $title ) ) . '">';
525
  $field .= '<input type="radio"';
 
526
  $field .= $this->do_field_name();
527
  $field .= ' value="' . esc_attr( $option_id ) . '" ' . checked( $this->value, $option_id, false ) . '/>';
528
  $field .= $title;
608
  $field .= $this->do_field_name();
609
  $field .= " id='{$this->id}-select'";
610
  $field .= " class='tribe-dropdown'";
 
 
 
611
  $field .= '>';
612
  foreach ( $this->options as $option_id => $title ) {
613
  $field .= '<option value="' . esc_attr( $option_id ) . '"';
common/src/Tribe/Freemius.php CHANGED
@@ -15,9 +15,8 @@ class Tribe__Freemius {
15
  private $instances = [];
16
 
17
  /**
18
- * Load the vendor files for Freemius vendor.
19
- *
20
- * Freemius class should only be loaded once since it will be registered as a Singleton.
21
  *
22
  * @since 4.9.5
23
  */
15
  private $instances = [];
16
 
17
  /**
18
+ * Loading of the vendor files for Freemius vendor
19
+ * Freemius class should only be loaded once since it will be registred as a Singleton
 
20
  *
21
  * @since 4.9.5
22
  */
common/src/Tribe/Image/Uploader.php CHANGED
@@ -29,14 +29,13 @@ class Tribe__Image__Uploader {
29
  */
30
  public static function reset_cache() {
31
  self::$attachment_guids_cache = false;
32
- self::$original_urls_cache = false;
33
  }
34
 
35
  /**
36
  * Uploads a file and creates the media attachment or simply returns the attachment ID if existing.
37
  *
38
- * @return int|bool The attachment post ID if the uploading and attachment is successful or the ID refers to an
39
- * attachment;
40
  * `false` otherwise.
41
  */
42
  public function upload_and_get_attachment_id() {
@@ -44,66 +43,18 @@ class Tribe__Image__Uploader {
44
  return false;
45
  }
46
 
47
- $existing = false;
48
-
49
  if ( is_string( $this->featured_image ) && ! is_numeric( $this->featured_image ) ) {
50
- // Assume image exists in the local file system.
51
- $id = $this->get_attachment_ID_from_url( $this->featured_image );
52
- if ( ! $id ) {
53
- $id = $this->upload_file( $this->featured_image );
54
- $id = $this->maybe_retry_upload( $id );
55
- }
56
- $existing = (bool) $id;
57
  } elseif ( $post = get_post( $this->featured_image ) ) {
58
  $id = $post && 'attachment' === $post->post_type ? $this->featured_image : false;
59
  } else {
60
  $id = false;
61
  }
62
 
63
- do_action(
64
- 'tribe_log',
65
- 'debug',
66
- __CLASS__,
67
- [
68
- 'featured_image' => $this->featured_image,
69
- 'exists' => $existing,
70
- 'id' => $id,
71
- ]
72
- );
73
-
74
  return $id;
75
  }
76
 
77
- /**
78
- * Retry to upload an image after it failed as was provided, try to decode the URL as in some cases the
79
- * original URL might be encoded HTML components such as: "&" and some CDNs does not handle well different URLs
80
- * as they were provided so we try to recreate the original URL where it might be required.
81
- *
82
- * @since 4.11.5
83
- *
84
- * @param int|bool $id The id of the attachment if was uploaded correctly, false otherwise.
85
- *
86
- * @return int The ID of the attachment after the upload retry.
87
- */
88
- protected function maybe_retry_upload( $id ) {
89
- if ( $id ) {
90
- do_action( 'tribe_log', 'debug', __CLASS__, [ 'message' => "ID: {$id} is already a valid one." ] );
91
-
92
- return $id;
93
- }
94
-
95
- $decoded = esc_url_raw( html_entity_decode( $this->featured_image ) );
96
-
97
- do_action( 'tribe_log', 'debug', __CLASS__, [
98
- 'message' => 'Retry upload decoding the URL of the image',
99
- 'url' => $this->featured_image,
100
- 'decoded' => $decoded,
101
- ] );
102
-
103
- // Maybe the URL was encoded and we need to convert it to a valid URL.
104
- return $this->upload_file( $decoded );
105
- }
106
-
107
  /**
108
  * @param string $file_url
109
  *
@@ -113,147 +64,105 @@ class Tribe__Image__Uploader {
113
  /**
114
  * Allow plugins to enable local URL uploads, mainly used for testing.
115
  *
116
- * @since 4.9.5
117
- *
118
  * @param bool $allow_local_urls Whether to allow local URLs.
119
  * @param string $file_url File URL.
 
 
120
  */
121
  $allow_local_urls = apply_filters( 'tribe_image_uploader_local_urls', false, $file_url );
122
 
123
- if ( ! $allow_local_urls && ! filter_var( $file_url, FILTER_VALIDATE_URL ) ) {
124
  return false;
125
  }
126
 
127
- // These files need to be included as dependencies
128
- require_once( ABSPATH . 'wp-admin/includes/image.php' );
129
- require_once( ABSPATH . 'wp-admin/includes/file.php' );
130
- require_once( ABSPATH . 'wp-admin/includes/media.php' );
131
-
132
- $is_local = false;
133
- // This is a local file no need to fetch it from the wire.
134
- if ( $allow_local_urls && file_exists( $file_url ) ) {
135
- $file = $file_url;
136
- $is_local = true;
137
- } else {
138
- /**
139
- * Some CDN services will append query arguments to the image URL; removing
140
- * them now has the potential of blocking the image fetching completely so we
141
- * let them be here.
142
- */
143
- $file = download_url( $file_url );
144
- if ( is_wp_error( $file ) ) {
145
- do_action( 'tribe_log', 'error', __CLASS__, [
146
- 'message' => $file->get_error_message(),
147
- 'url' => $file_url,
148
- 'error' => $file,
149
- ] );
150
-
151
- return false;
152
- }
153
- }
154
-
155
- // Upload file into WP and leave WP handle the resize and such.
156
- $attachment_id = media_handle_sideload( [
157
- 'name' => $this->create_file_name( $file ),
158
- 'tmp_name' => $file,
159
- 'post_mime_type' => 'image',
160
- ] );
161
 
162
- // Remove the temporary file as is no longer required at this point.
163
- if ( ! $is_local && file_exists( $file ) ) {
164
- @unlink( $file );
165
- }
 
 
 
 
 
 
166
 
167
- if ( is_wp_error( $attachment_id ) ) {
168
- do_action( 'tribe_log', 'error', __CLASS__, [
169
- 'message' => $attachment_id->get_error_message(),
170
- 'url' => $file_url,
171
- 'error' => $attachment_id,
172
- ] );
173
 
174
  return false;
175
  }
176
 
177
- update_post_meta( $attachment_id, '_tribe_importer_original_url', $file_url );
178
 
179
- $this->maybe_init_attachment_guids_cache();
180
- $this->maybe_init_attachment_original_urls_cache();
 
181
 
182
- $attachment_post = get_post( $attachment_id );
183
- // Only update the cache if is a valid attachment.
184
- if ( $attachment_post instanceof WP_Post ) {
185
- self::$attachment_guids_cache[ $attachment_post->guid ] = $attachment_id;
186
- self::$original_urls_cache[ $file_url ] = $attachment_id;
187
  }
188
 
189
- return $attachment_id;
190
- }
191
-
192
- /**
193
- * WordPress requires to have an extension in all all files as uses `wp_check_filetype` which uses the extension
194
- * of the file to define if a file is valid or not, in this case the extension might not be present in some URLs of
195
- * attachments or media files, in those cases we try to guess the right extension using the mime of the file as
196
- * an alternative, if the $filename is a path we can verify the mime type using native WP functions.
197
- *
198
- * @since 4.11.5
199
- *
200
- * @param string $filename The name of the file or URL.
201
- *
202
- * @return string Returned a file name with an extension if is not already part of the file name.
203
- */
204
- protected function create_file_name( $filename ) {
205
- /**
206
  * We use the path basename only here to provided WordPress with a good filename
207
  * that will allow it to correctly detect and validate the extension.
208
  */
209
- $path = wp_parse_url( $filename, PHP_URL_PATH );
 
210
 
211
- $name = basename( $path );
212
- $properties = wp_check_filetype( $name );
 
213
 
214
- // Type can be defined from the name use that one instead.
215
- if ( ! empty( $properties['type'] ) ) {
216
- return $name;
217
  }
218
 
219
- // This is not a file that exists on the system, use the name instead.
220
- if ( ! file_exists( $filename ) ) {
221
- return $name;
 
 
 
 
 
222
  }
223
 
224
- $mime = wp_get_image_mime( $filename );
 
 
 
 
 
 
225
 
226
- // There's no mime defined for the file use the plain name instead.
227
- if ( $mime === '' ) {
228
- return $name;
229
- }
230
 
231
- // create an array with the mimes as the keys and extensions as values.
232
- $mime_to_extensions = array_flip( wp_get_mime_types() );
233
 
234
- // No mime was found for the file on the array of allowed mime types, fallback to the name.
235
- if ( ! isset( $mime_to_extensions[ $mime ] ) ) {
236
- return $name;
237
- }
238
 
239
- // If there are more than one extension just ose one.
240
- $parts = explode( '|', $mime_to_extensions[ $mime ] );
 
 
 
241
 
242
- // Create a new name with extension.
243
- return implode( '.', [ $name, reset( $parts ) ] );
244
  }
245
 
246
  protected function get_attachment_ID_from_url( $featured_image ) {
247
  $this->maybe_init_attachment_guids_cache();
248
  $this->maybe_init_attachment_original_urls_cache();
249
 
250
- $guids_cache = self::$attachment_guids_cache;
251
  $original_urls_cache = self::$original_urls_cache;
252
  if ( isset( $guids_cache[ $featured_image ] ) ) {
253
  return $guids_cache[ $featured_image ];
254
- }
255
-
256
- if ( isset( $original_urls_cache[ $featured_image ] ) ) {
257
  return $original_urls_cache[ $featured_image ];
258
  }
259
 
@@ -267,11 +176,11 @@ class Tribe__Image__Uploader {
267
  $guids = $wpdb->get_results( "SELECT ID, guid FROM $wpdb->posts where post_type = 'attachment'" );
268
 
269
  if ( $guids ) {
270
- $keys = wp_list_pluck( $guids, 'guid' );
271
- $values = wp_list_pluck( $guids, 'ID' );
272
  self::$attachment_guids_cache = array_combine( $keys, $values );
273
  } else {
274
- self::$attachment_guids_cache = [];
275
  }
276
  }
277
  }
@@ -289,11 +198,11 @@ class Tribe__Image__Uploader {
289
  " );
290
 
291
  if ( $original_urls ) {
292
- $keys = wp_list_pluck( $original_urls, 'meta_value' );
293
- $values = wp_list_pluck( $original_urls, 'ID' );
294
  self::$original_urls_cache = array_combine( $keys, $values );
295
  } else {
296
- self::$original_urls_cache = [];
297
  }
298
  }
299
  }
@@ -305,7 +214,7 @@ class Tribe__Image__Uploader {
305
  * @since 4.7.22
306
  *
307
  * @param string $unused_error_code The error numeric code.
308
- * @param string $message The error message.
309
  *
310
  * @throws RuntimeException To pass the error as an exception to
311
  * the handler.
29
  */
30
  public static function reset_cache() {
31
  self::$attachment_guids_cache = false;
32
+ self::$original_urls_cache = false;
33
  }
34
 
35
  /**
36
  * Uploads a file and creates the media attachment or simply returns the attachment ID if existing.
37
  *
38
+ * @return int|bool The attachment post ID if the uploading and attachment is successful or the ID refers to an attachment;
 
39
  * `false` otherwise.
40
  */
41
  public function upload_and_get_attachment_id() {
43
  return false;
44
  }
45
 
 
 
46
  if ( is_string( $this->featured_image ) && ! is_numeric( $this->featured_image ) ) {
47
+ $existing = $this->get_attachment_ID_from_url( $this->featured_image );
48
+ $id = $existing ? $existing : $this->upload_file( $this->featured_image );
 
 
 
 
 
49
  } elseif ( $post = get_post( $this->featured_image ) ) {
50
  $id = $post && 'attachment' === $post->post_type ? $this->featured_image : false;
51
  } else {
52
  $id = false;
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
55
  return $id;
56
  }
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  /**
59
  * @param string $file_url
60
  *
64
  /**
65
  * Allow plugins to enable local URL uploads, mainly used for testing.
66
  *
 
 
67
  * @param bool $allow_local_urls Whether to allow local URLs.
68
  * @param string $file_url File URL.
69
+ *
70
+ * @since 4.9.5
71
  */
72
  $allow_local_urls = apply_filters( 'tribe_image_uploader_local_urls', false, $file_url );
73
 
74
+ if ( ! filter_var( $file_url, FILTER_VALIDATE_URL ) && ! $allow_local_urls ) {
75
  return false;
76
  }
77
 
78
+ /*
79
+ * Since `file_get_contents` would fail silently we set an explicit
80
+ * error handler to catch the content of error.s.
81
+ */
82
+ set_error_handler( array( $this, 'handle_error' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
+ /*
85
+ * Some CDN services will append query arguments to the image URL; removing
86
+ * them now has the potential of blocking the image fetching completely so we
87
+ * let them be here.
88
+ */
89
+ try {
90
+ $contents = file_get_contents( $file_url );
91
+ } catch ( Exception $e ) {
92
+ $message = sprintf( 'Could not upload image file "%s": with message "%s"', $file_url, $e->getMessage() );
93
+ tribe( 'logger' )->log_error( $message, 'Image Uploader' );
94
 
95
+ restore_error_handler();
 
 
 
 
 
96
 
97
  return false;
98
  }
99
 
100
+ restore_error_handler();
101
 
102
+ if ( false === $contents ) {
103
+ $message = sprintf( 'Could not upload image file "%s": failed getting the contents.', $file_url );
104
+ tribe( 'logger' )->log_error( $message, 'Image Uploader' );
105
 
106
+ return false;
 
 
 
 
107
  }
108
 
109
+ /*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  * We use the path basename only here to provided WordPress with a good filename
111
  * that will allow it to correctly detect and validate the extension.
112
  */
113
+ $path = parse_url( $file_url, PHP_URL_PATH );
114
+ $upload = wp_upload_bits( basename( $path ), null, $contents );
115
 
116
+ if ( isset( $upload['error'] ) && $upload['error'] ) {
117
+ $message = sprintf( 'Could not upload image file "%s" with message "%s"', $file_url, $upload['error'] );
118
+ tribe( 'logger' )->log_error( $message, 'Image Uploader' );
119
 
120
+ return false;
 
 
121
  }
122
 
123
+ $type = '';
124
+ if ( ! empty( $upload['type'] ) ) {
125
+ $type = $upload['type'];
126
+ } else {
127
+ $mime = wp_check_filetype( $upload['file'] );
128
+ if ( $mime ) {
129
+ $type = $mime['type'];
130
+ }
131
  }
132
 
133
+ $attachment = array(
134
+ 'post_title' => basename( $upload['file'] ),
135
+ 'post_content' => '',
136
+ 'post_type' => 'attachment',
137
+ 'post_mime_type' => $type,
138
+ 'guid' => $upload['url'],
139
+ );
140
 
141
+ $id = wp_insert_attachment( $attachment, $upload['file'] );
 
 
 
142
 
143
+ require_once( ABSPATH . 'wp-admin/includes/image.php' );
 
144
 
145
+ wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
146
+ update_post_meta( $id, '_tribe_importer_original_url', $file_url );
 
 
147
 
148
+ $this->maybe_init_attachment_guids_cache();
149
+ $this->maybe_init_attachment_original_urls_cache();
150
+
151
+ self::$attachment_guids_cache[ get_post( $id )->guid ] = $id;
152
+ self::$original_urls_cache[ $file_url ] = $id;
153
 
154
+ return $id;
 
155
  }
156
 
157
  protected function get_attachment_ID_from_url( $featured_image ) {
158
  $this->maybe_init_attachment_guids_cache();
159
  $this->maybe_init_attachment_original_urls_cache();
160
 
161
+ $guids_cache = self::$attachment_guids_cache;
162
  $original_urls_cache = self::$original_urls_cache;
163
  if ( isset( $guids_cache[ $featured_image ] ) ) {
164
  return $guids_cache[ $featured_image ];
165
+ } elseif ( isset( $original_urls_cache[ $featured_image ] ) ) {
 
 
166
  return $original_urls_cache[ $featured_image ];
167
  }
168
 
176
  $guids = $wpdb->get_results( "SELECT ID, guid FROM $wpdb->posts where post_type = 'attachment'" );
177
 
178
  if ( $guids ) {
179
+ $keys = wp_list_pluck( $guids, 'guid' );
180
+ $values = wp_list_pluck( $guids, 'ID' );
181
  self::$attachment_guids_cache = array_combine( $keys, $values );
182
  } else {
183
+ self::$attachment_guids_cache = array();
184
  }
185
  }
186
  }
198
  " );
199
 
200
  if ( $original_urls ) {
201
+ $keys = wp_list_pluck( $original_urls, 'meta_value' );
202
+ $values = wp_list_pluck( $original_urls, 'ID' );
203
  self::$original_urls_cache = array_combine( $keys, $values );
204
  } else {
205
+ self::$original_urls_cache = array();
206
  }
207
  }
208
  }
214
  * @since 4.7.22
215
  *
216
  * @param string $unused_error_code The error numeric code.
217
+ * @param string $message The error message.
218
  *
219
  * @throws RuntimeException To pass the error as an exception to
220
  * the handler.
common/src/Tribe/Languages/Locations.php CHANGED
@@ -135,7 +135,7 @@ class Tribe__Languages__Locations {
135
  'TF' => esc_html__( 'French Southern Territories', 'tribe-common' ),
136
  'GA' => esc_html__( 'Gabon', 'tribe-common' ),
137
  'GM' => esc_html__( 'Gambia', 'tribe-common' ),
138
- 'GE' => esc_html_x( 'Georgia', 'The country', 'tribe-common' ),
139
  'DE' => esc_html__( 'Germany', 'tribe-common' ),
140
  'GH' => esc_html__( 'Ghana', 'tribe-common' ),
141
  'GI' => esc_html__( 'Gibraltar', 'tribe-common' ),
@@ -182,6 +182,7 @@ class Tribe__Languages__Locations {
182
  'LT' => esc_html__( 'Lithuania', 'tribe-common' ),
183
  'LU' => esc_html__( 'Luxembourg', 'tribe-common' ),
184
  'MO' => esc_html__( 'Macau', 'tribe-common' ),
 
185
  'MG' => esc_html__( 'Madagascar', 'tribe-common' ),
186
  'MW' => esc_html__( 'Malawi', 'tribe-common' ),
187
  'MY' => esc_html__( 'Malaysia', 'tribe-common' ),
@@ -214,7 +215,6 @@ class Tribe__Languages__Locations {
214
  'NG' => esc_html__( 'Nigeria', 'tribe-common' ),
215
  'NU' => esc_html__( 'Niue', 'tribe-common' ),
216
  'NF' => esc_html__( 'Norfolk Island', 'tribe-common' ),
217
- 'MK' => esc_html__( 'North Macedonia', 'tribe-common' ),
218
  'MP' => esc_html__( 'Northern Mariana Islands', 'tribe-common' ),
219
  'NO' => esc_html__( 'Norway', 'tribe-common' ),
220
  'OM' => esc_html__( 'Oman', 'tribe-common' ),
@@ -331,7 +331,7 @@ class Tribe__Languages__Locations {
331
  'DE' => esc_html__( 'Delaware', 'tribe-common' ),
332
  'DC' => esc_html__( 'District of Columbia', 'tribe-common' ),
333
  'FL' => esc_html__( 'Florida', 'tribe-common' ),
334
- 'GA' => esc_html_x( 'Georgia', 'The US state Georgia','tribe-common' ),
335
  'HI' => esc_html__( 'Hawaii', 'tribe-common' ),
336
  'ID' => esc_html__( 'Idaho', 'tribe-common' ),
337
  'IL' => esc_html__( 'Illinois', 'tribe-common' ),
135
  'TF' => esc_html__( 'French Southern Territories', 'tribe-common' ),
136
  'GA' => esc_html__( 'Gabon', 'tribe-common' ),
137
  'GM' => esc_html__( 'Gambia', 'tribe-common' ),
138
+ 'GE' => esc_html__( 'Georgia', 'tribe-common' ),
139
  'DE' => esc_html__( 'Germany', 'tribe-common' ),
140
  'GH' => esc_html__( 'Ghana', 'tribe-common' ),
141
  'GI' => esc_html__( 'Gibraltar', 'tribe-common' ),
182
  'LT' => esc_html__( 'Lithuania', 'tribe-common' ),
183
  'LU' => esc_html__( 'Luxembourg', 'tribe-common' ),
184
  'MO' => esc_html__( 'Macau', 'tribe-common' ),
185
+ 'MK' => esc_html__( 'Macedonia', 'tribe-common' ),
186
  'MG' => esc_html__( 'Madagascar', 'tribe-common' ),
187
  'MW' => esc_html__( 'Malawi', 'tribe-common' ),
188
  'MY' => esc_html__( 'Malaysia', 'tribe-common' ),
215
  'NG' => esc_html__( 'Nigeria', 'tribe-common' ),
216
  'NU' => esc_html__( 'Niue', 'tribe-common' ),
217
  'NF' => esc_html__( 'Norfolk Island', 'tribe-common' ),
 
218
  'MP' => esc_html__( 'Northern Mariana Islands', 'tribe-common' ),
219
  'NO' => esc_html__( 'Norway', 'tribe-common' ),
220
  'OM' => esc_html__( 'Oman', 'tribe-common' ),
331
  'DE' => esc_html__( 'Delaware', 'tribe-common' ),
332
  'DC' => esc_html__( 'District of Columbia', 'tribe-common' ),
333
  'FL' => esc_html__( 'Florida', 'tribe-common' ),
334
+ 'GA' => esc_html__( 'Georgia', 'tribe-common' ),
335
  'HI' => esc_html__( 'Hawaii', 'tribe-common' ),
336
  'ID' => esc_html__( 'Idaho', 'tribe-common' ),
337
  'IL' => esc_html__( 'Illinois', 'tribe-common' ),
common/src/Tribe/Log.php CHANGED
@@ -282,9 +282,6 @@ class Tribe__Log {
282
  public function set_current_logger( $engine ) {
283
  $available_engines = $this->get_logging_engines();
284
 
285
- // Make sure to de-duplicate the slashes on class names.
286
- $engine = str_replace( '\\\\', '\\', $engine );
287
-
288
  if ( ! isset( $available_engines[ $engine ] ) ) {
289
  throw new Exception( sprintf( __( 'Cannot set %s as the current logging engine', 'tribe-common' ), $engine ) );
290
  }
282
  public function set_current_logger( $engine ) {
283
  $available_engines = $this->get_logging_engines();
284
 
 
 
 
285
  if ( ! isset( $available_engines[ $engine ] ) ) {
286
  throw new Exception( sprintf( __( 'Cannot set %s as the current logging engine', 'tribe-common' ), $engine ) );
287
  }
common/src/Tribe/Log/Action_Logger.php DELETED
@@ -1,125 +0,0 @@
1
- <?php
2
- /**
3
- * Hooks the `tribe_log` action based logger under the existing one for back-compatibility.
4
- *
5
- * @since 4.9.16
6
- *
7
- * @package Tribe\Log
8
- */
9
-
10
- namespace Tribe\Log;
11
-
12
- use Monolog\Logger;
13
- use Tribe__Log;
14
-
15
- /**
16
- * Class Action_Logger
17
- *
18
- * @since 4.9.16
19
- *
20
- * @package Tribe\Log
21
- */
22
- class Action_Logger implements \Tribe__Log__Logger {
23
-
24
- /**
25
- * {@inheritDoc}
26
- *
27
- * @since 4.9.16
28
- */
29
- public function is_available() {
30
- return true;
31
- }
32
-
33
- /**
34
- * {@inheritDoc}
35
- *
36
- * @since 4.9.16
37
- */
38
- public function get_name() {
39
- return __( 'Action-based Logger', 'tribe-common' );
40
- }
41
-
42
- /**
43
- * {@inheritDoc}
44
- *
45
- * @since 4.9.16
46
- */
47
- public function log( $entry, $type = Tribe__Log::DEBUG, $src = '' ) {
48
- $message = empty( $src ) ? $entry : $src . ': ' . $entry;
49
-
50
- do_action( 'tribe_log', $this->translate_log_level( $type ), $message );
51
- }
52
-
53
- /**
54
- * Translates the log types used by `Tribe__Log` to those used by Monolog.
55
- *
56
- * @since 4.9.16
57
- *
58
- * @param string $type The `Tribe__Log` log type.
59
- *
60
- * @return int The Monolog equivalent of the current level.
61
- */
62
- protected function translate_log_level( $type ) {
63
- switch ( $type ) {
64
- case Tribe__Log::DEBUG:
65
- return Logger::DEBUG;
66
- case Tribe__Log::ERROR:
67
- return Logger::ERROR;
68
- case Tribe__Log::WARNING:
69
- return Logger::WARNING;
70
- case Tribe__Log::SUCCESS:
71
- default:
72
- return Logger::INFO;
73
- }
74
- }
75
-
76
- /**
77
- * {@inheritDoc}
78
- *
79
- * @since 4.9.16
80
- */
81
- public function retrieve( $limit = 0, array $args = array() ) {
82
- return [
83
- [
84
- 'message' => __(
85
- 'The Action Logger will dispatch any logging message using the "tribe_log" action writing, by ' .
86
- 'default, to the PHP error log.',
87
- 'tribe-common' )
88
- ],
89
- ];
90
- }
91
-
92
- /**
93
- * {@inheritDoc}
94
- *
95
- * @since 4.9.16
96
- */
97
- public function list_available_logs() {
98
- return [];
99
- }
100
-
101
- /**
102
- * Changes the Monolog logger channel to the specified one.
103
- *
104
- * @since 4.9.16
105
- *
106
- * @param string $log_identifier The channel to switch to.
107
- * @param bool $create Unused by this class.
108
- *
109
- * @return bool The exit status of the channel change.
110
- *
111
- * @uses \Tribe\Log\Monolog_Logger::set_channel().
112
- */
113
- public function use_log( $log_identifier, $create = false ) {
114
- return tribe( 'monolog' )->set_global_channel( $log_identifier );
115
- }
116
-
117
- /**
118
- * {@inheritDoc}
119
- *
120
- * @since 4.9.16
121
- */
122
- public function cleanup() {
123
- return true;
124
- }
125
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Log/Canonical_Formatter.php DELETED
@@ -1,101 +0,0 @@
1
- <?php
2
- /**
3
- * ${CARET}
4
- *
5
- * @since 4.9.16
6
- *
7
- * @package Tribe\Log
8
- */
9
-
10
-
11
- namespace Tribe\Log;
12
-
13
-
14
- use Monolog\Formatter\LineFormatter;
15
-
16
- class Canonical_Formatter extends LineFormatter {
17
-
18
- /**
19
- * Formats a log record.
20
- *
21
- * @since 4.9.16
22
- *
23
- * @param array $record A record to format.
24
- *
25
- * @return mixed The formatted record.
26
- */
27
- public function format( array $record ) {
28
- $has_context = ! empty( $record['context'] );
29
-
30
- if ( $has_context ) {
31
- $record['message'] = $this->format_record_message( $record );
32
-
33
- $this->format = 'tribe-canonical-line channel=%channel% %message%';
34
- } else {
35
- // Fall-back on a standard format if the message does not have a context.
36
- $this->format = 'tribe.%channel%.%level_name%: %message%';
37
- }
38
-
39
- return parent::format( $record );
40
- }
41
-
42
- /**
43
- * Formats the record to the canonical format.
44
- *
45
- * @since 4.9.16
46
- *
47
- * @param array $record The record to process.
48
- *
49
- * @return string The formatted message, as built from the record context and message, in the format `<key>=<value>`.
50
- */
51
- protected function format_record_message( array $record ) {
52
- $message = [];
53
- $extra = [];
54
-
55
- $extra['level'] = isset( $record['level_name'] ) ? strtolower( $record['level_name'] ) : 'debug';
56
-
57
- if ( ! empty( $record['message'] ) ) {
58
- // Use the message as the source.
59
- $extra['source'] = $this->escape_quotes( $record['message'] );
60
- }
61
-
62
- $context = $record['context'];
63
- $context = array_merge( $extra, $context );
64
-
65
- foreach ( $context as $key => $value ) {
66
- $escape = false;
67
-
68
- if ( is_bool( $value ) ) {
69
- $value = $value ? 'true' : 'false';
70
- } elseif ( ! is_scalar( $value ) ) {
71
- $value = json_encode( $value );
72
- if ( false === $value ) {
73
- $value = 'malformed';
74
- } else {
75
- $escape = true;
76
- }
77
- }
78
-
79
- if ( $escape || ( is_string( $value ) && preg_match( '~[\\\\/\\s]+~', $value ) ) ) {
80
- $value = '"' . $this->escape_quotes( $value ) . '"';
81
- }
82
-
83
- $message[] = "{$key}={$value}";
84
- }
85
-
86
- return implode( ' ', $message );
87
- }
88
-
89
- /**
90
- * Escapes the double quotes in a string.
91
- *
92
- * @since 4.9.16
93
- *
94
- * @param string $string The string to escape the quotes in.
95
- *
96
- * @return string The string, with the quotes escaped.
97
- */
98
- protected function escape_quotes( $string ) {
99
- return str_replace( '"', '\\"', $string ) ;
100
- }
101
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Log/File_Logger.php CHANGED
@@ -27,7 +27,7 @@ class Tribe__Log__File_Logger implements Tribe__Log__Logger {
27
  *
28
  * @var string $log_dir
29
  */
30
- $this->log_dir = apply_filters( 'tribe_file_logger_directory', get_temp_dir() );
31
  }
32
 
33
  /**
27
  *
28
  * @var string $log_dir
29
  */
30
+ $this->log_dir = apply_filters( 'tribe_file_logger_directory', sys_get_temp_dir() );
31
  }
32
 
33
  /**
common/src/Tribe/Log/Monolog_Logger.php DELETED
@@ -1,54 +0,0 @@
1
- <?php
2
- /**
3
- * An extension of the base Monolog logger to add our need to replace the instance, and global, loggers.
4
- *
5
- * @since 4.9.16
6
- *
7
- * @package Tribe\Log
8
- */
9
-
10
- namespace Tribe\Log;
11
-
12
- use Monolog\Logger;
13
-
14
- /**
15
- * Class Monolog_Logger
16
- *
17
- * @since 4.9.16
18
- *
19
- * @package Tribe\Log
20
- */
21
- class Monolog_Logger extends Logger {
22
- /**
23
- * @since 4.9.16
24
- */
25
- const DEFAULT_CHANNEL = 'default';
26
-
27
- /**
28
- * Resets the global channel to the default one.
29
- *
30
- * @since 4.9.16
31
- *
32
- * @return bool Whether the channel reset
33
- */
34
- public function reset_global_channel() {
35
- return $this->set_global_channel( static::DEFAULT_CHANNEL );
36
- }
37
-
38
- /**
39
- * Clones this logger and replaces it in the `tribe` container.
40
- *
41
- * @since 4.9.16
42
- *
43
- * @param string $channel The new logger name, also referred to as "channel" (hence the method name).
44
- *
45
- * @return bool Whether the channel change was successful or not.
46
- */
47
- public function set_global_channel( $channel ) {
48
- $new = $this->withName( $channel );
49
- tribe_register( Logger::class, $new );
50
- tribe_register( 'monolog', $new );
51
-
52
- return $channel === tribe( 'monolog' )->getName();
53
- }
54
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Log/README.md DELETED
@@ -1,154 +0,0 @@
1
- # Monolog-based logging
2
-
3
- We've introduced a [Monolog based](https://github.com/Seldaek/monolog) logger in our common libraries.
4
- You can find more information about all the possibilities this opens [on the library documentaion](https://seldaek.github.io/monolog/), but this document will serve as an introduction to the essentials of its day to day use.
5
-
6
- ## When should I log?
7
-
8
- Whenever you feel you might have to debug this in the future and could use that information.
9
-
10
- > Pro tip: if you, as a developer, find yourself using `var_dump` and `error_log` a lot, then you should log instead. Someone, someday, will have your same issue.
11
-
12
- Worried about "spamming" the logs? [Read here](#logging-levels--or-stuff-does-not-appear-in-the-log).
13
-
14
- ## This will deprecate the old logger, but not yet
15
-
16
- At first we're not replacing the "old" logger with this new one, we're just asking you **to stop using the old logger** in your code from now on and use the new, Monolog-based, one.
17
- The old logger still offers file-based logging and connections to the UI the new logger is not yet offering; the current implementation will allow us, in the future, to log **everything** with the Monolog-based logger, but, currently, intercepting log messages from the "old" logger requires manual activation, see the following section.
18
-
19
- To be clear: this is what we mean by "old" or "legacy" logger:
20
-
21
- ```php
22
- <?php
23
- tribe( 'logger' )->log_debug( 'Some debug information', 'The source' );
24
- tribe( 'logger' )->log( 'Some information', Tribe__Log::DEBUG, 'The source' );
25
- ```
26
-
27
- ### Intercepting legacy logger logs with the new Monolog logger
28
-
29
- The Monolog-based logger will handle logging coming from the legacy logger only if explicitly told so.
30
-
31
- You can activate this function with this code:
32
-
33
- ```php
34
- <?php
35
- add_filter( 'tribe_log_use_action_logger', '__return_true' );
36
- ```
37
-
38
- Once this is in your code any call to the legacy logger wil be redirected to the new one.
39
-
40
- ## Using the logger
41
-
42
- The new logger listens on the `tribe_log` action.
43
- By default it will log to the `default` channel (see [Monolog documentation for more information about channels](https://seldaek.github.io/monolog/doc/01-usage.html#leveraging-channels)).
44
-
45
- So the code below will log a **debug** to the **default** channel with a source of **ea_client**:
46
-
47
- ```php
48
- <?php
49
- do_action(
50
- 'tribe_log',
51
- 'debug',
52
- 'ea_client',
53
- [ 'action' => 'updated', 'post_id' => $id, 'origin' => $origin ]
54
- );
55
- ```
56
-
57
- The logger listening on the action will consume three parameters:
58
-
59
- 1. `level` - `debug` in the example; is the level of the log; available levels, in increasing value of urgency are: `debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency`. Use each level wisely.
60
- 2. `source` - `ea_client` in this example; is the source of the log; this is a human-readable value; consistency is king here.
61
- 3. `context` - the array in this example; this is an associative array that will be logged to define the context of the log. Think of this as something that will provide the details required to unpack what **was** happening when the log entry was created. Provide enough context to make it clear, but avoid bloating it.
62
-
63
- ## Where are my logs?
64
-
65
- The initial implementation of the new logger will write, by default, to the **PHP error** log.
66
-
67
- We're using Monolog to allow us, and third parties, to "attach" and "deatach" loggers as required.
68
- By default we're formatting logs using canonical lines( read more [here](https://brandur.org/logfmt) and [here](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write/)) to make our log entries both human-readable and machine parsable (e.g. by a tool like [this](https://www.npmjs.com/package/logfmt)).
69
-
70
- The output format of the example above would be this:
71
-
72
- ```
73
- [22-Aug-2019 15:50:42 UTC] tribe-canonical-line channel=default level=debug source=ea_client action=updated post_id=23 origin=ical
74
- ```
75
-
76
- What about legacy logs?
77
- Their format would not be formatted to the canonical line style:
78
-
79
- ```
80
- [22-Aug-2019 16:03:33 UTC] tribe.default.DEBUG: The source: debug information
81
- ```
82
-
83
- ### Logging levels ( or "stuff does not appear in the log")
84
-
85
- By default we're only logging Warnings and above.
86
- This means all your `debug` level logs are being ignored.
87
-
88
- In production we do not want to fill people logs with pointless information, but you can control the level of logging: any log equal or above the specified level will be logged.
89
-
90
- You can control the logging level with the `tribe_log_level` filter:
91
-
92
- ```php
93
- <?php
94
- add_filter(
95
- 'tribe_log_level',
96
- static function () {
97
- // Only log errors or above.
98
- return Monolog\Logger::ERROR;
99
- }
100
- );
101
- ```
102
-
103
- ### Logging channels
104
-
105
- The default logging channel is `default`, you've seen that in the example log lines above.
106
-
107
- But how can I change the channel?
108
-
109
- Easy:
110
-
111
- ```php
112
- <?php
113
- tribe( 'monolog' )->set_global_channel( 'my_channel' );
114
- do_action( 'tribe_log', 'debug', 'my_source', [ 'foo' => 'bar' ] );
115
- tribe( 'logger' )->log_debug( 'Some debug information', 'My source' );
116
- ```
117
-
118
- You can do the same using the legacy logger, [if enabled][0527-0003]:
119
-
120
- ```php
121
- <?php
122
- tribe( 'logger' )->use_log( 'my_channel' );
123
- ```
124
-
125
- Any log produced after the call will log to the `my_channel` channel; this will apply to the legacy logger too ([if redirected][0527-0003]):
126
-
127
- ```
128
- [22-Aug-2019 15:50:42 UTC] tribe-canonical-line channel=default level=debug source=ea_client action=updated post_id=23 origin=ical
129
- [22-Aug-2019 15:51:13 UTC] tribe-canonical-line channel=my_channel level=debug source=my_source foo=bar
130
- [22-Aug-2019 16:03:33 UTC] tribe.my_channel.DEBUG: My source: Some debug information
131
- ```
132
-
133
- ## I want to use this right now to debug my code
134
-
135
- Copy and paste this in a plugin, or must-use plugin.
136
- If you're using a plugin remember to activate it.
137
-
138
- ```php
139
- <?php
140
- /**
141
- * Plugin Name: Modern Tribe Logger Control
142
- * Plugin Description: Control the behavior of Modern Tribe Monolog-based logger.
143
- */
144
- add_filter(
145
- 'tribe_log_level',
146
- static function () {
147
- // Control the min level of logging.
148
- return Monolog\Logger::DEBUG;
149
- }
150
- );
151
-
152
- // Redirect legacy logger calls.
153
- add_filter( 'tribe_log_use_action_logger', '__return_true' );
154
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Log/Service_Provider.php DELETED
@@ -1,148 +0,0 @@
1
- <?php
2
- /**
3
- * ${CARET}
4
- *
5
- * @since 4.9.16
6
- *
7
- * @package Tribe\Log
8
- */
9
-
10
-
11
- namespace Tribe\Log;
12
-
13
-
14
- use Monolog\Handler\ErrorLogHandler;
15
- use Monolog\Logger;
16
-
17
- class Service_Provider extends \tad_DI52_ServiceProvider {
18
-
19
- /**
20
- * Binds and sets up implementations.
21
- *
22
- * @since 4.9.16
23
- */
24
- public function register() {
25
- $this->container->singleton( Logger::class, [ $this, 'build_logger' ] );
26
- $this->container->singleton( 'monolog',
27
- function () {
28
- return $this->container->make( Logger::class );
29
- }
30
- );
31
-
32
- add_action( 'tribe_log', [ $this, 'dispatch_log' ], 10, 3 );
33
-
34
- /**
35
- * Filters whether to make the Action Logger available as logger or not.
36
- *
37
- * @since 4.9.16
38
- *
39
- * @param bool $use_action_logger Whether to allow logging messages from the \Tribe\Log\Logger class using the
40
- * `tribe_log` action or not.
41
- */
42
- $use_action_logger = apply_filters( 'tribe_log_use_action_logger', false );
43
-
44
- if ( $use_action_logger ) {
45
- add_filter( 'tribe_common_logging_engines', [ $this, 'add_logging_engine' ] );
46
- }
47
- }
48
-
49
- /**
50
- * Builds and returns the Monolog Logger instance that will listen to the `tribe_log` action.
51
- *
52
- * To avoid the over-head introduced by filtering the filters are applied here, only once, when the instance is
53
- * first built. Any later call will use the singleton instance stored in the container.
54
- *
55
- * @since 4.9.16
56
- *
57
- * @return Logger
58
- */
59
- public function build_logger() {
60
- /**
61
- * Filters the level of the messages that will be logged.
62
- *
63
- * The threshold is inclusive of the level; it default to log any warning and above.
64
- *
65
- * @since 4.9.16
66
- *
67
- * @param int The threshold level; if the level of a message is this level or above, then it will be logged.
68
- *
69
- * @see \Monolog\Logger for possible levels.
70
- */
71
- $level_threshold = apply_filters( 'tribe_log_level', Logger::WARNING );
72
-
73
- $error_log_handler = new ErrorLogHandler( null, $level_threshold );
74
-
75
- /**
76
- * Filters whether to use canonical format for the logs or not.
77
- *
78
- * @since 4.9.16
79
- *
80
- * @param bool $use_canonical_format Whether to use canonical format for the logs or not; defaults to `true`.
81
- */
82
- $use_canonical_format = apply_filters( 'tribe_log_canonical', true );
83
-
84
- if ( $use_canonical_format ) {
85
- $error_log_handler->setFormatter( new Canonical_Formatter() );
86
- }
87
-
88
- $handlers = [
89
- 'default' => $error_log_handler
90
- ];
91
-
92
- /**
93
- * Filters the list of handlers that will handle dispatched log messages.
94
- *
95
- * All handlers should implement the `\Monolog\Handler\HandlerInterface`.
96
- *
97
- * @since 4.9.16
98
- *
99
- * @param array $handlers An array of default log handlers.
100
- */
101
- $handlers = apply_filters( 'tribe_log_handlers', $handlers );
102
-
103
- // Monolog will log to stderr when no handlers are set.
104
- $logger = new Monolog_Logger( Monolog_Logger::DEFAULT_CHANNEL );
105
-
106
- $logger->setHandlers( $handlers );
107
-
108
- return $logger;
109
- }
110
-
111
- /**
112
- * Dispatch a message of a specific level.
113
- *
114
- * Available levels are: `debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency`.
115
- *
116
- * @since 4.9.16
117
- *
118
- * @param string|int $level Either the log level or the log pretty name, see long description.
119
- * @param string $message The message to log.
120
- * @param array $context An array of values to define the context.
121
- *
122
- * @see \Monolog\Logger for the log level constants and names.
123
- */
124
- public function dispatch_log( $level = 'debug', $message = '', array $context = [] ) {
125
- // Goes from something like `debug` to `100`.
126
- $level = is_numeric( $level ) ? $level : Logger::toMonologLevel( $level );
127
-
128
- /** @var Logger $logger */
129
- $logger = $this->container->make( Logger::class );
130
-
131
- $logger->log( $level, $message, $context );
132
- }
133
-
134
- /**
135
- * Makes the action-based logging engine available in the backend.
136
- *
137
- * @since 4.9.16
138
- *
139
- * @param array $logging_engines An array of available logging engines.
140
- *
141
- * @return array The updated array of logging engines.
142
- */
143
- public function add_logging_engine( array $logging_engines = [] ) {
144
- $logging_engines[ Action_Logger::class ] = new Action_Logger();
145
-
146
- return $logging_engines;
147
- }
148
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Main.php CHANGED
@@ -17,7 +17,7 @@ class Tribe__Main {
17
  const OPTIONNAME = 'tribe_events_calendar_options';
18
  const OPTIONNAMENETWORK = 'tribe_events_calendar_network_options';
19
 
20
- const VERSION = '4.12.3';
21
 
22
  const FEED_URL = 'https://theeventscalendar.com/feed/';
23
 
@@ -55,6 +55,7 @@ class Tribe__Main {
55
  /**
56
  * Constructor for Common Class
57
  *
 
58
  * We are using a `public` constructor here for backwards compatibility.
59
  *
60
  * The way our code used to work we would have `new Tribe__Main()` called directly
@@ -85,10 +86,8 @@ class Tribe__Main {
85
  $parent_plugin_dir = trailingslashit( plugin_basename( $this->plugin_path ) );
86
  $this->plugin_url = plugins_url( $parent_plugin_dir === $this->plugin_dir ? $this->plugin_dir : $parent_plugin_dir );
87
 
88
- $this->promoter_connector();
89
-
90
- add_action( 'plugins_loaded', [ $this, 'plugins_loaded' ], 1 );
91
- add_action( 'tribe_common_loaded', [ $this, 'tribe_common_app_store' ], 10 );
92
  }
93
 
94
  /**
@@ -96,6 +95,8 @@ class Tribe__Main {
96
  */
97
  public function plugins_loaded() {
98
 
 
 
99
  $this->init_autoloading();
100
 
101
  $this->bind_implementations();
@@ -127,7 +128,7 @@ class Tribe__Main {
127
 
128
  $autoloader = Tribe__Autoloader::instance();
129
 
130
- $prefixes = [ 'Tribe__' => dirname( __FILE__ ) ];
131
  $autoloader->register_prefixes( $prefixes );
132
 
133
  foreach ( glob( $this->plugin_path . 'src/deprecated/*.php' ) as $file ) {
@@ -166,8 +167,6 @@ class Tribe__Main {
166
  require_once $this->plugin_path . 'src/functions/multibyte.php';
167
  require_once $this->plugin_path . 'src/functions/template-tags/general.php';
168
  require_once $this->plugin_path . 'src/functions/template-tags/date.php';
169
- require_once $this->plugin_path . 'src/functions/template-tags/html.php';
170
- require_once $this->plugin_path . 'src/functions/template-tags/post.php';
171
 
172
  Tribe__Debug::instance();
173
  tribe( 'assets' );
@@ -192,8 +191,8 @@ class Tribe__Main {
192
  [ 'tribe-query-string', 'utils/query-string.js' ],
193
  [ 'tribe-clipboard', 'vendor/clipboard/clipboard.js' ],
194
  [ 'datatables', 'vendor/datatables/datatables.js', [ 'jquery' ] ],
195
- [ 'tribe-select2', 'vendor/tribe-selectWoo/dist/js/selectWoo.full.js', [ 'jquery' ] ],
196
- [ 'tribe-select2-css', 'vendor/tribe-selectWoo/dist/css/selectWoo.css' ],
197
  [ 'tribe-utils-camelcase', 'utils-camelcase.js', [ 'underscore' ] ],
198
  [ 'tribe-moment', 'vendor/momentjs/moment.js' ],
199
  [ 'tribe-tooltipster', 'vendor/tooltipster/tooltipster.bundle.js', [ 'jquery' ] ],
@@ -214,8 +213,8 @@ class Tribe__Main {
214
  tribe_assets(
215
  $this,
216
  [
217
- [ 'tribe-common-skeleton-style', 'common-skeleton.css' ],
218
- [ 'tribe-common-full-style', 'common-full.css', [ 'tribe-common-skeleton-style' ] ],
219
  ],
220
  null
221
  );
@@ -223,80 +222,34 @@ class Tribe__Main {
223
  // These ones will be enqueued on `admin_enqueue_scripts` if the conditional method on filter is met
224
  tribe_assets(
225
  $this,
226
- [
227
- [ 'tribe-ui', 'tribe-ui.css' ],
228
- [ 'tribe-buttonset', 'buttonset.js', [ 'jquery', 'underscore' ] ],
229
- [ 'tribe-common-admin', 'tribe-common-admin.css', [ 'tribe-dependency-style', 'tribe-bumpdown-css', 'tribe-buttonset-style', 'tribe-select2-css' ] ],
230
- [ 'tribe-validation', 'validation.js', [ 'jquery', 'underscore', 'tribe-common', 'tribe-utils-camelcase', 'tribe-tooltipster' ] ],
231
- [ 'tribe-validation-style', 'validation.css', [ 'tribe-tooltipster-css' ] ],
232
- [ 'tribe-dependency', 'dependency.js', [ 'jquery', 'underscore', 'tribe-common' ] ],
233
- [ 'tribe-dependency-style', 'dependency.css', [ 'tribe-select2-css' ] ],
234
- [ 'tribe-pue-notices', 'pue-notices.js', [ 'jquery' ] ],
235
- [ 'tribe-datepicker', 'datepicker.css' ],
236
- ],
237
  'admin_enqueue_scripts',
238
- [
239
- 'conditionals' => [ $this, 'should_load_common_admin_css' ],
240
  'priority' => 5,
241
- ]
242
  );
243
 
244
  tribe_asset(
245
  $this,
246
  'tribe-common',
247
  'tribe-common.js',
248
- [],
249
  'admin_enqueue_scripts',
250
- [
251
  'priority' => 0,
252
- ]
253
- );
254
-
255
- tribe_asset(
256
- $this,
257
- 'tribe-admin-url-fragment-scroll',
258
- 'admin/url-fragment-scroll.js',
259
- [ 'tribe-common' ],
260
- 'admin_enqueue_scripts',
261
- [
262
- 'conditionals' => [ $this, 'should_load_common_admin_css' ],
263
- 'priority' => 5,
264
- ]
265
  );
266
-
267
- tribe( Tribe__Admin__Help_Page::class )->register_assets();
268
- }
269
-
270
- /**
271
- * Load Common's text domain, then fire the hook for other plugins to do the same.
272
- *
273
- * Make sure this fires on 'init', per WordPress best practices.
274
- *
275
- * @since 4.12.0
276
- *
277
- * @return bool
278
- */
279
- public function hook_load_text_domain() {
280
- $loaded = $this->load_text_domain(
281
- 'tribe-common',
282
- basename( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ) . '/common/lang/'
283
- );
284
-
285
- /**
286
- * After attempting (hopefully successfully) to load Common's text domain.
287
- *
288
- * Load other plugin text domains on this hook, but make sure they're setup on this hook prior to 'init'.
289
- *
290
- * @since 4.12.0
291
- *
292
- * @param bool $loaded Whether or not Common's text domain was loaded.
293
- *
294
- * @return bool
295
- */
296
- do_action( 'tribe_load_text_domains', $loaded );
297
-
298
- return $loaded;
299
- }
300
 
301
  /**
302
  * Load All localization data create by `asset.data`
@@ -308,11 +261,11 @@ class Tribe__Main {
308
  public function load_localize_data() {
309
  $datepicker_months = array_values( Tribe__Date_Utils::get_localized_months_full() );
310
 
311
- tribe( 'asset.data' )->add( 'tribe_l10n_datatables', [
312
- 'aria' => [
313
  'sort_ascending' => __( ': activate to sort column ascending', 'tribe-common' ),
314
  'sort_descending' => __( ': activate to sort column descending', 'tribe-common' ),
315
- ],
316
  'length_menu' => __( 'Show _MENU_ entries', 'tribe-common' ),
317
  'empty_table' => __( 'No data available in table', 'tribe-common' ),
318
  'info' => __( 'Showing _START_ to _END_ of _TOTAL_ entries', 'tribe-common' ),
@@ -323,19 +276,19 @@ class Tribe__Main {
323
  'all_selected_text' => __( 'All items on this page were selected. ', 'tribe-common' ),
324
  'select_all_link' => __( 'Select all pages', 'tribe-common' ),
325
  'clear_selection' => __( 'Clear Selection.', 'tribe-common' ),
326
- 'pagination' => [
327
  'all' => __( 'All', 'tribe-common' ),
328
  'next' => __( 'Next', 'tribe-common' ),
329
  'previous' => __( 'Previous', 'tribe-common' ),
330
- ],
331
- 'select' => [
332
- 'rows' => [
333
  0 => '',
334
  '_' => __( ': Selected %d rows', 'tribe-common' ),
335
  1 => __( ': Selected 1 row', 'tribe-common' ),
336
- ],
337
- ],
338
- 'datepicker' => [
339
  'dayNames' => Tribe__Date_Utils::get_localized_weekdays_full(),
340
  'dayNamesShort' => Tribe__Date_Utils::get_localized_weekdays_short(),
341
  'dayNamesMin' => Tribe__Date_Utils::get_localized_weekdays_initial(),
@@ -348,38 +301,35 @@ class Tribe__Main {
348
  'closeText' => esc_html__( 'Done', 'the-events-calendar' ),
349
  'today' => esc_html__( 'Today', 'the-events-calendar' ),
350
  'clear' => esc_html__( 'Clear', 'the-events-calendar' ),
351
- ],
352
- ] );
 
 
 
 
 
 
 
353
  }
354
 
355
  /**
356
  * Adds core hooks
357
  */
358
  public function add_hooks() {
359
- add_action( 'plugins_loaded', [ 'Tribe__App_Shop', 'instance' ] );
360
- add_action( 'plugins_loaded', [ $this, 'tribe_plugins_loaded' ], PHP_INT_MAX );
361
 
362
  // Register for the assets to be available everywhere
363
- add_action( 'tribe_common_loaded', [ $this, 'load_assets' ], 1 );
364
- add_action( 'init', [ $this, 'hook_load_text_domain' ] );
365
- add_action( 'init', [ $this, 'load_localize_data' ] );
366
- add_action( 'plugins_loaded', [ 'Tribe__Admin__Notices', 'instance' ], 1 );
367
- add_action( 'admin_enqueue_scripts', [ $this, 'store_admin_notices' ] );
368
-
369
- add_filter( 'body_class', [ $this, 'add_js_class' ] );
370
- add_action( 'wp_footer', [ $this, 'toggle_js_class' ] );
371
  }
372
 
373
- /**
374
- * Adds `tribe-no-js` class to all pages when common is active.
375
- *
376
- * @since 4.3.4
377
- *
378
- * @param array|string $classes Previous classes on body.
379
- *
380
- * @return array All classes that will be printed on `<body>`.
381
- */
382
- public function add_js_class( $classes = [] ) {
383
  if ( ! is_array( $classes ) ) {
384
  $classes = explode( ' ', $classes );
385
  }
@@ -389,13 +339,6 @@ class Tribe__Main {
389
  return array_filter( array_unique( $classes ) );
390
  }
391
 
392
- /**
393
- * Removes `tribe-no-js` and replaces with `tribe-js` when the Javascript of the page is enabled.
394
- *
395
- * @since 4.3.4
396
- *
397
- * @return void This method only prints HTML to the screen no return.
398
- */
399
  public function toggle_js_class() {
400
  ?>
401
  <script>
@@ -415,7 +358,7 @@ class Tribe__Main {
415
  *
416
  * @since 4.5.7
417
  *
418
- * @return bool Whether we should load Common Admin CSS or not.
419
  */
420
  public function should_load_common_admin_css() {
421
  $helper = Tribe__Admin__Helpers::instance();
@@ -435,15 +378,13 @@ class Tribe__Main {
435
 
436
  /**
437
  * A Helper method to load text domain
438
- * First it tries to load the wp-content/languages translation then if falls to the try to load $dir language files.
439
- *
440
- * @since 4.0.1 Introduced.
441
- * @since 4.2 Included $domain and $dir params.
442
  *
443
- * @param string $domain The text domain that will be loaded.
444
- * @param string|false $dir What directory should be used to try to load if the default doesn't work.
445
  *
446
- * @return bool If it was able to load the text domain.
447
  */
448
  public function load_text_domain( $domain, $dir = false ) {
449
  // Added safety just in case this runs twice...
@@ -455,13 +396,13 @@ class Tribe__Main {
455
  $plugin_rel_path = WP_LANG_DIR . '/plugins/';
456
 
457
  /**
458
- * Allows users to filter the file location for a given text domain..
459
  * Be careful when using this filter, it will apply across the whole plugin suite.
460
  *
461
- * @param string $plugin_rel_path The relative path for the language files.
462
- * @param string $domain Which plugin domain we are trying to load.
463
- * @param string $locale Which Language we will load.
464
- * @param string|bool $dir If there was a custom directory passed on the method call.
465
  */
466
  $plugin_rel_path = apply_filters( 'tribe_load_text_domain', $plugin_rel_path, $domain, $locale, $dir );
467
 
@@ -475,21 +416,11 @@ class Tribe__Main {
475
  }
476
 
477
  /**
478
- * Returns the post types registered by Tribe plugins.
479
- *
480
- * @since 4.0.1 Introduced the method.
481
- *
482
- * @return array Slugs for all Post Types registered.
483
  */
484
  public static function get_post_types() {
485
- /**
486
- * We default the post type array to empty in tribe-common. Plugins like TEC add to it.
487
- *
488
- * @since 4.0.1
489
- *
490
- * @param array Slugs for all Post Types registered.
491
- */
492
- return apply_filters( 'tribe_post_types', [] );
493
  }
494
 
495
  /**
@@ -500,6 +431,7 @@ class Tribe__Main {
500
  * @param $insert_array
501
  *
502
  * @return array
 
503
  */
504
  public static function array_insert_after_key( $key, $source_array, $insert_array ) {
505
  if ( array_key_exists( $key, $source_array ) ) {
@@ -551,6 +483,26 @@ class Tribe__Main {
551
  return $candidate_post instanceof WP_Post ? $candidate_post->ID : false;
552
  }
553
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  /**
555
  * Adds a hook
556
  *
@@ -568,7 +520,6 @@ class Tribe__Main {
568
  */
569
  public function tribe_plugins_loaded() {
570
  tribe( 'admin.notice.php.version' );
571
- tribe( 'cache' );
572
  tribe_singleton( 'feature-detection', 'Tribe__Feature_Detection' );
573
  tribe_register_provider( 'Tribe__Service_Providers__Processes' );
574
 
@@ -586,26 +537,22 @@ class Tribe__Main {
586
 
587
  /**
588
  * Registers the slug bound to the implementations in the container.
589
- *
590
- * @since 4.4
591
- *
592
- * @return void Implementation of components loader doesnt return anything.
593
  */
594
  public function bind_implementations() {
595
  tribe_singleton( 'settings.manager', 'Tribe__Settings_Manager' );
596
- tribe_singleton( 'settings', 'Tribe__Settings', [ 'hook' ] );
597
- tribe_singleton( 'ajax.dropdown', 'Tribe__Ajax__Dropdown', [ 'hook' ] );
598
  tribe_singleton( 'assets', 'Tribe__Assets' );
599
- tribe_singleton( 'assets.pipeline', 'Tribe__Assets_Pipeline', [ 'hook' ] );
600
- tribe_singleton( 'asset.data', 'Tribe__Asset__Data', [ 'hook' ] );
601
  tribe_singleton( 'admin.helpers', 'Tribe__Admin__Helpers' );
602
- tribe_singleton( 'tracker', 'Tribe__Tracker', [ 'hook' ] );
603
- tribe_singleton( 'chunker', 'Tribe__Meta__Chunker', [ 'set_post_types', 'hook' ] );
604
- tribe_singleton( 'cache', 'Tribe__Cache', [ 'hook' ] );
605
  tribe_singleton( 'languages.locations', 'Tribe__Languages__Locations' );
606
  tribe_singleton( 'plugins.api', new Tribe__Plugins_API );
607
  tribe_singleton( 'logger', 'Tribe__Log' );
608
- tribe_singleton( 'cost-utils', [ 'Tribe__Cost_Utils', 'instance' ] );
609
  tribe_singleton( 'post-duplicate.strategy-factory', 'Tribe__Duplicate__Strategy_Factory' );
610
  tribe_singleton( 'post-duplicate', 'Tribe__Duplicate__Post' );
611
  tribe_singleton( 'context', 'Tribe__Context' );
@@ -613,45 +560,18 @@ class Tribe__Main {
613
  tribe_singleton( 'db', 'Tribe__Db' );
614
  tribe_singleton( 'freemius', 'Tribe__Freemius' );
615
 
616
- tribe_singleton( Tribe__Dependency::class, Tribe__Dependency::class );
617
-
618
  tribe_singleton( 'callback', 'Tribe__Utils__Callback' );
619
  tribe_singleton( 'pue.notices', 'Tribe__PUE__Notices' );
620
 
621
- tribe_singleton( Tribe__Admin__Help_Page::class, Tribe__Admin__Help_Page::class );
622
-
623
- tribe_singleton( 'admin.notice.php.version', 'Tribe__Admin__Notice__Php_Version', [ 'hook' ] );
624
- tribe_singleton( 'admin.notice.marketing', 'Tribe__Admin__Notice__Marketing', [ 'hook' ] );
625
-
626
- tribe_register_provider( Tribe__Editor__Provider::class );
627
- tribe_register_provider( Tribe__Service_Providers__Debug_Bar::class );
628
- tribe_register_provider( Tribe__Service_Providers__Promoter::class );
629
- tribe_register_provider( Tribe\Service_Providers\Tooltip::class );
630
- tribe_register_provider( Tribe\Service_Providers\Dialog::class );
631
- tribe_register_provider( Tribe\Service_Providers\PUE::class );
632
- tribe_register_provider( Tribe\Service_Providers\Shortcodes::class );
633
- tribe_register_provider( Tribe\Log\Service_Provider::class );
634
- }
635
-
636
- /**
637
- * Create the Promoter connector singleton early to allow hook into the filters early.
638
- *
639
- * Add a filter to determine_current_user during the setup of common library.
640
- *
641
- * @since 4.9.20
642
- *
643
- * @return void Internal method without any return.
644
- */
645
- public function promoter_connector() {
646
- tribe_singleton( 'promoter.connector', 'Tribe__Promoter__Connector' );
647
 
648
- add_filter(
649
- 'determine_current_user',
650
- tribe_callback( 'promoter.connector', 'authenticate_user_with_connector' )
651
- );
652
  }
653
 
654
-
655
  /************************
656
  * *
657
  * Deprecated Methods *
@@ -659,26 +579,6 @@ class Tribe__Main {
659
  ************************/
660
  // @codingStandardsIgnoreStart
661
 
662
- /**
663
- * Helper function to indicate whether the current execution context is AJAX
664
- *
665
- * This method exists to allow us test code that behaves differently depending on the execution
666
- * context.
667
- *
668
- * @since 4.0
669
- *
670
- * @todo Add warning with '_deprecated_function'
671
- *
672
- * @param bool $doing_ajax An injectable status to override the `DOING_AJAX` check.
673
- *
674
- * @deprecated 4.7.12
675
- *
676
- * @return boolean
677
- */
678
- public function doing_ajax( $doing_ajax = null ) {
679
- return tribe( 'context' )->doing_ajax( $doing_ajax );
680
- }
681
-
682
  /**
683
  * Manages PUE license key notifications.
684
  *
17
  const OPTIONNAME = 'tribe_events_calendar_options';
18
  const OPTIONNAMENETWORK = 'tribe_events_calendar_network_options';
19
 
20
+ const VERSION = '4.9.11.2';
21
 
22
  const FEED_URL = 'https://theeventscalendar.com/feed/';
23
 
55
  /**
56
  * Constructor for Common Class
57
  *
58
+ * @access public
59
  * We are using a `public` constructor here for backwards compatibility.
60
  *
61
  * The way our code used to work we would have `new Tribe__Main()` called directly
86
  $parent_plugin_dir = trailingslashit( plugin_basename( $this->plugin_path ) );
87
  $this->plugin_url = plugins_url( $parent_plugin_dir === $this->plugin_dir ? $this->plugin_dir : $parent_plugin_dir );
88
 
89
+ add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ), 1 );
90
+ add_action( 'tribe_common_loaded', array( $this, 'tribe_common_app_store' ), 10 );
 
 
91
  }
92
 
93
  /**
95
  */
96
  public function plugins_loaded() {
97
 
98
+ $this->load_text_domain( 'tribe-common', basename( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ) . '/common/lang/' );
99
+
100
  $this->init_autoloading();
101
 
102
  $this->bind_implementations();
128
 
129
  $autoloader = Tribe__Autoloader::instance();
130
 
131
+ $prefixes = array( 'Tribe__' => dirname( __FILE__ ) );
132
  $autoloader->register_prefixes( $prefixes );
133
 
134
  foreach ( glob( $this->plugin_path . 'src/deprecated/*.php' ) as $file ) {
167
  require_once $this->plugin_path . 'src/functions/multibyte.php';
168
  require_once $this->plugin_path . 'src/functions/template-tags/general.php';
169
  require_once $this->plugin_path . 'src/functions/template-tags/date.php';
 
 
170
 
171
  Tribe__Debug::instance();
172
  tribe( 'assets' );
191
  [ 'tribe-query-string', 'utils/query-string.js' ],
192
  [ 'tribe-clipboard', 'vendor/clipboard/clipboard.js' ],
193
  [ 'datatables', 'vendor/datatables/datatables.js', [ 'jquery' ] ],
194
+ [ 'tribe-select2', 'vendor/tribe-select2/select2.js', [ 'jquery' ] ],
195
+ [ 'tribe-select2-css', 'vendor/tribe-select2/select2.css' ],
196
  [ 'tribe-utils-camelcase', 'utils-camelcase.js', [ 'underscore' ] ],
197
  [ 'tribe-moment', 'vendor/momentjs/moment.js' ],
198
  [ 'tribe-tooltipster', 'vendor/tooltipster/tooltipster.bundle.js', [ 'jquery' ] ],
213
  tribe_assets(
214
  $this,
215
  [
216
+ [ 'tribe-reset-style', 'reset.css' ],
217
+ [ 'tribe-common-style', 'common.css', [ 'tribe-reset-style' ] ],
218
  ],
219
  null
220
  );
222
  // These ones will be enqueued on `admin_enqueue_scripts` if the conditional method on filter is met
223
  tribe_assets(
224
  $this,
225
+ array(
226
+ array( 'tribe-buttonset', 'buttonset.js', array( 'jquery', 'underscore' ) ),
227
+ array( 'tribe-common-admin', 'tribe-common-admin.css', array( 'tribe-dependency-style', 'tribe-bumpdown-css', 'tribe-buttonset-style', 'tribe-select2-css' ) ),
228
+ array( 'tribe-validation', 'validation.js', array( 'jquery', 'underscore', 'tribe-common', 'tribe-utils-camelcase', 'tribe-tooltipster' ) ),
229
+ array( 'tribe-validation-style', 'validation.css', array( 'tribe-tooltipster-css' ) ),
230
+ array( 'tribe-dependency', 'dependency.js', array( 'jquery', 'underscore', 'tribe-common' ) ),
231
+ array( 'tribe-dependency-style', 'dependency.css', array( 'tribe-select2-css' ) ),
232
+ array( 'tribe-pue-notices', 'pue-notices.js', array( 'jquery' ) ),
233
+ array( 'tribe-datepicker', 'datepicker.css' ),
234
+ ),
 
235
  'admin_enqueue_scripts',
236
+ array(
237
+ 'conditionals' => array( $this, 'should_load_common_admin_css' ),
238
  'priority' => 5,
239
+ )
240
  );
241
 
242
  tribe_asset(
243
  $this,
244
  'tribe-common',
245
  'tribe-common.js',
246
+ array( 'tribe-clipboard' ),
247
  'admin_enqueue_scripts',
248
+ array(
249
  'priority' => 0,
250
+ )
 
 
 
 
 
 
 
 
 
 
 
 
251
  );
252
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
  /**
255
  * Load All localization data create by `asset.data`
261
  public function load_localize_data() {
262
  $datepicker_months = array_values( Tribe__Date_Utils::get_localized_months_full() );
263
 
264
+ tribe( 'asset.data' )->add( 'tribe_l10n_datatables', array(
265
+ 'aria' => array(
266
  'sort_ascending' => __( ': activate to sort column ascending', 'tribe-common' ),
267
  'sort_descending' => __( ': activate to sort column descending', 'tribe-common' ),
268
+ ),
269
  'length_menu' => __( 'Show _MENU_ entries', 'tribe-common' ),
270
  'empty_table' => __( 'No data available in table', 'tribe-common' ),
271
  'info' => __( 'Showing _START_ to _END_ of _TOTAL_ entries', 'tribe-common' ),
276
  'all_selected_text' => __( 'All items on this page were selected. ', 'tribe-common' ),
277
  'select_all_link' => __( 'Select all pages', 'tribe-common' ),
278
  'clear_selection' => __( 'Clear Selection.', 'tribe-common' ),
279
+ 'pagination' => array(
280
  'all' => __( 'All', 'tribe-common' ),
281
  'next' => __( 'Next', 'tribe-common' ),
282
  'previous' => __( 'Previous', 'tribe-common' ),
283
+ ),
284
+ 'select' => array(
285
+ 'rows' => array(
286
  0 => '',
287
  '_' => __( ': Selected %d rows', 'tribe-common' ),
288
  1 => __( ': Selected 1 row', 'tribe-common' ),
289
+ ),
290
+ ),
291
+ 'datepicker' => array(
292
  'dayNames' => Tribe__Date_Utils::get_localized_weekdays_full(),
293
  'dayNamesShort' => Tribe__Date_Utils::get_localized_weekdays_short(),
294
  'dayNamesMin' => Tribe__Date_Utils::get_localized_weekdays_initial(),
301
  'closeText' => esc_html__( 'Done', 'the-events-calendar' ),
302
  'today' => esc_html__( 'Today', 'the-events-calendar' ),
303
  'clear' => esc_html__( 'Clear', 'the-events-calendar' ),
304
+ ),
305
+ ) );
306
+
307
+ tribe( 'asset.data' )->add( 'tribe_system_info', array(
308
+ 'sysinfo_optin_nonce' => wp_create_nonce( 'sysinfo_optin_nonce' ),
309
+ 'clipboard_btn_text' => __( 'Copy to clipboard', 'tribe-common' ),
310
+ 'clipboard_copied_text' => __( 'System info copied', 'tribe-common' ),
311
+ 'clipboard_fail_text' => __( 'Press "Cmd + C" to copy', 'tribe-common' ),
312
+ ) );
313
  }
314
 
315
  /**
316
  * Adds core hooks
317
  */
318
  public function add_hooks() {
319
+ add_action( 'plugins_loaded', array( 'Tribe__App_Shop', 'instance' ) );
320
+ add_action( 'plugins_loaded', array( $this, 'tribe_plugins_loaded' ), PHP_INT_MAX );
321
 
322
  // Register for the assets to be available everywhere
323
+ add_action( 'tribe_common_loaded', array( $this, 'load_assets' ), 1 );
324
+ add_action( 'init', array( $this, 'load_localize_data' ) );
325
+ add_action( 'plugins_loaded', array( 'Tribe__Admin__Notices', 'instance' ), 1 );
326
+ add_action( 'admin_enqueue_scripts', array( $this, 'store_admin_notices' ) );
327
+
328
+ add_filter( 'body_class', array( $this, 'add_js_class' ) );
329
+ add_action( 'wp_footer', array( $this, 'toggle_js_class' ) );
 
330
  }
331
 
332
+ public function add_js_class( $classes = array() ) {
 
 
 
 
 
 
 
 
 
333
  if ( ! is_array( $classes ) ) {
334
  $classes = explode( ' ', $classes );
335
  }
339
  return array_filter( array_unique( $classes ) );
340
  }
341
 
 
 
 
 
 
 
 
342
  public function toggle_js_class() {
343
  ?>
344
  <script>
358
  *
359
  * @since 4.5.7
360
  *
361
+ * @return bool
362
  */
363
  public function should_load_common_admin_css() {
364
  $helper = Tribe__Admin__Helpers::instance();
378
 
379
  /**
380
  * A Helper method to load text domain
381
+ * First it tries to load the wp-content/languages translation then if falls to the
382
+ * try to load $dir language files
 
 
383
  *
384
+ * @param string $domain The text domain that will be loaded
385
+ * @param string $dir What directory should be used to try to load if the default doenst work
386
  *
387
+ * @return bool If it was able to load the text domain
388
  */
389
  public function load_text_domain( $domain, $dir = false ) {
390
  // Added safety just in case this runs twice...
396
  $plugin_rel_path = WP_LANG_DIR . '/plugins/';
397
 
398
  /**
399
+ * Allows users to filter the file location for a given text domain
400
  * Be careful when using this filter, it will apply across the whole plugin suite.
401
  *
402
+ * @param string $plugin_rel_path The relative path for the language files
403
+ * @param string $domain Which plugin domain we are trying to load
404
+ * @param string $locale Which Language we will load
405
+ * @param string|bool $dir If there was a custom directory passed on the method call
406
  */
407
  $plugin_rel_path = apply_filters( 'tribe_load_text_domain', $plugin_rel_path, $domain, $locale, $dir );
408
 
416
  }
417
 
418
  /**
419
+ * Returns the post types registered by Tribe plugins
 
 
 
 
420
  */
421
  public static function get_post_types() {
422
+ // we default the post type array to empty in tribe-common. Plugins like TEC add to it
423
+ return apply_filters( 'tribe_post_types', array() );
 
 
 
 
 
 
424
  }
425
 
426
  /**
431
  * @param $insert_array
432
  *
433
  * @return array
434
+ *
435
  */
436
  public static function array_insert_after_key( $key, $source_array, $insert_array ) {
437
  if ( array_key_exists( $key, $source_array ) ) {
483
  return $candidate_post instanceof WP_Post ? $candidate_post->ID : false;
484
  }
485
 
486
+ /**
487
+ * Helper function to indicate whether the current execution context is AJAX
488
+ *
489
+ * This method exists to allow us test code that behaves differently depending on the execution
490
+ * context.
491
+ *
492
+ * @since 4.0
493
+ *
494
+ * @todo Add warning with '_deprecated_function'
495
+ *
496
+ * @param bool $doing_ajax An injectable status to override the `DOING_AJAX` check.
497
+ *
498
+ * @deprecated 4.7.12
499
+ *
500
+ * @return boolean
501
+ */
502
+ public function doing_ajax( $doing_ajax = null ) {
503
+ return tribe( 'context' )->doing_ajax( $doing_ajax );
504
+ }
505
+
506
  /**
507
  * Adds a hook
508
  *
520
  */
521
  public function tribe_plugins_loaded() {
522
  tribe( 'admin.notice.php.version' );
 
523
  tribe_singleton( 'feature-detection', 'Tribe__Feature_Detection' );
524
  tribe_register_provider( 'Tribe__Service_Providers__Processes' );
525
 
537
 
538
  /**
539
  * Registers the slug bound to the implementations in the container.
 
 
 
 
540
  */
541
  public function bind_implementations() {
542
  tribe_singleton( 'settings.manager', 'Tribe__Settings_Manager' );
543
+ tribe_singleton( 'settings', 'Tribe__Settings', array( 'hook' ) );
544
+ tribe_singleton( 'ajax.dropdown', 'Tribe__Ajax__Dropdown', array( 'hook' ) );
545
  tribe_singleton( 'assets', 'Tribe__Assets' );
546
+ tribe_singleton( 'assets.pipeline', 'Tribe__Assets_Pipeline', array( 'hook' ) );
547
+ tribe_singleton( 'asset.data', 'Tribe__Asset__Data', array( 'hook' ) );
548
  tribe_singleton( 'admin.helpers', 'Tribe__Admin__Helpers' );
549
+ tribe_singleton( 'tracker', 'Tribe__Tracker', array( 'hook' ) );
550
+ tribe_singleton( 'chunker', 'Tribe__Meta__Chunker', array( 'set_post_types', 'hook' ) );
551
+ tribe_singleton( 'cache', 'Tribe__Cache' );
552
  tribe_singleton( 'languages.locations', 'Tribe__Languages__Locations' );
553
  tribe_singleton( 'plugins.api', new Tribe__Plugins_API );
554
  tribe_singleton( 'logger', 'Tribe__Log' );
555
+ tribe_singleton( 'cost-utils', array( 'Tribe__Cost_Utils', 'instance' ) );
556
  tribe_singleton( 'post-duplicate.strategy-factory', 'Tribe__Duplicate__Strategy_Factory' );
557
  tribe_singleton( 'post-duplicate', 'Tribe__Duplicate__Post' );
558
  tribe_singleton( 'context', 'Tribe__Context' );
560
  tribe_singleton( 'db', 'Tribe__Db' );
561
  tribe_singleton( 'freemius', 'Tribe__Freemius' );
562
 
 
 
563
  tribe_singleton( 'callback', 'Tribe__Utils__Callback' );
564
  tribe_singleton( 'pue.notices', 'Tribe__PUE__Notices' );
565
 
566
+ tribe_singleton( 'admin.notice.php.version', 'Tribe__Admin__Notice__Php_Version', array( 'hook' ) );
567
+ tribe_singleton( 'admin.notice.marketing', 'Tribe__Admin__Notice__Marketing', array( 'hook' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
 
569
+ tribe_register_provider( 'Tribe__Editor__Provider' );
570
+ tribe_register_provider( 'Tribe__Service_Providers__Debug_Bar' );
571
+ tribe_register_provider( 'Tribe__Service_Providers__Promoter_Connector' );
572
+ tribe_register_provider( 'Tribe__Service_Providers__Tooltip' );
573
  }
574
 
 
575
  /************************
576
  * *
577
  * Deprecated Methods *
579
  ************************/
580
  // @codingStandardsIgnoreStart
581
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
  /**
583
  * Manages PUE license key notifications.
584
  *
common/src/Tribe/Models/Post_Types/Base.php DELETED
@@ -1,206 +0,0 @@
1
- <?php
2
- /**
3
- * The base, abstract, class modeling a post.
4
- *
5
- * @since 4.9.18
6
- *
7
- * @package Tribe\Models\Post_Types
8
- */
9
-
10
-
11
- namespace Tribe\Models\Post_Types;
12
-
13
- use Tribe__Cache as Cache;
14
- use Tribe__Cache_Listener as Cache_Listener;
15
-
16
- /**
17
- * Class Base
18
- *
19
- * @since 4.9.18
20
- *
21
- * @package Tribe\Models\Post_Types
22
- */
23
- abstract class Base {
24
- /**
25
- * The post object base for this post type instance.
26
- *
27
- * @since 4.9.18
28
- *
29
- * @var \WP_Post
30
- */
31
- protected $post;
32
-
33
- /**
34
- * Builds, and returns, a post type model from a given post.
35
- *
36
- * @since 4.9.18
37
- *
38
- * @param \WP_Post|int $post The post ID or post object.
39
- *
40
- * @return Base|Nothing Either the built Post Type model, or a Nothing model if the post does not exist.
41
- */
42
- public static function from_post( $post ) {
43
- $post = get_post( $post );
44
-
45
- if ( ! $post instanceof \WP_Post ) {
46
- return new Nothing();
47
- }
48
-
49
- $instance = new static;
50
- $instance->post = $post;
51
-
52
- return $instance;
53
- }
54
-
55
- /**
56
- * Returns the slug that will be prefixed to the cache key for the model.
57
- *
58
- * @since 4.9.18
59
- *
60
- * @return string The slug that will be prefixed to the cache key for the model.
61
- */
62
- abstract protected function get_cache_slug();
63
-
64
- /**
65
- * Returns the cached model properties for the specified filter, if any.
66
- *
67
- * @since 4.9.18
68
- *
69
- * @param string $filter Type of filter to apply, used here as the stored post values might change.
70
- *
71
- * @return array|false An array of model properties, or `false` if not found.
72
- */
73
- protected function get_cached_properties( $filter ) {
74
- $cache_slug = $this->get_cache_slug();
75
-
76
- if ( empty( $cache_slug ) ) {
77
- return false;
78
- }
79
-
80
- // Cache by post ID and filter.
81
- $cache_key = $cache_slug . '_' . $this->post->ID . '_' . $filter;
82
-
83
- return ( new Cache() )->get( $cache_key, Cache_Listener::TRIGGER_SAVE_POST );
84
- }
85
-
86
- /**
87
- * Builds and returns the properties for the model.
88
- *
89
- * In this method child classes should also implement any caching trigger mechanism, if any.
90
- *
91
- * @since 4.9.18
92
- *
93
- * @param string $filter The type of filter to build the properties for.
94
- *
95
- * @return array An array of built properties.
96
- */
97
- abstract protected function build_properties( $filter );
98
-
99
- /**
100
- * Returns an array of the model properties.
101
- *
102
- * @since 4.9.18
103
- *
104
- * @param string $filter The type of filter to get the properties for.
105
- *
106
- * @return array The model properties. This value might be cached.
107
- */
108
- protected function get_properties( $filter ) {
109
- $cached = $this->get_cached_properties( $filter);
110
-
111
- if ( false !== $cached ) {
112
- return $cached;
113
- }
114
-
115
- $props = $this->build_properties( $filter );
116
-
117
- $cache_slug = $this->get_cache_slug();
118
-
119
- /**
120
- * Filters the array of properties that will be used to decorate the post object handled by the class.
121
- *
122
- * @since 4.9.18
123
- *
124
- * @param array $props An associative array of all the properties that will be set on the "decorated" post
125
- * object.
126
- * @param \WP_Post $post The post object handled by the class.
127
- */
128
- $props = apply_filters( "tribe_post_type_{$cache_slug}_properties", $props, $this->post );
129
-
130
- return $props;
131
- }
132
-
133
- /**
134
- * Returns the WP_Post version of this model.
135
- *
136
- * @since 4.9.18
137
- *
138
- * @param string $output The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to a WP_Post
139
- * object,an associative array, or a numeric array, respectively.
140
- * @param string $filter Type of filter to apply. Accepts 'raw', 'edit', 'db', or 'display' and other formats
141
- * supported by the specific type implementation.
142
- *
143
- * @return \WP_Post|array|null The post object version of this post type model or `null` if the post is not valid.
144
- */
145
- public function to_post( $output = OBJECT, $filter = 'raw' ) {
146
- $properties = $this->get_properties( $filter );
147
-
148
- // Clone the post to avoid side effects.
149
- $post = clone $this->post;
150
-
151
- // And decorate the clone with the properties.
152
- foreach ( $properties as $key => $value ) {
153
- $post->{$key} = $value;
154
- }
155
-
156
- switch ( $output ) {
157
- case ARRAY_A:
158
- return (array) $post;
159
- case ARRAY_N:
160
- return array_values( (array) $post );
161
- case OBJECT:
162
- default;
163
- return $post;
164
- }
165
- }
166
-
167
- /**
168
- * Returns the closure that should be used to cache the post type model when, and if, caching it is required.
169
- *
170
- * @since 4.9.18
171
- *
172
- * @param string $filter The kind of filter applied to the model.
173
- *
174
- * @return callable The closure, or callable, that should be used to cache this model when, and if, required.
175
- */
176
- protected function get_caching_callback( $filter ) {
177
- $cache_slug = $this->get_cache_slug();
178
-
179
- if ( empty( $cache_slug ) ) {
180
- return '__return_true';
181
- }
182
-
183
- $callback = null;
184
-
185
- if ( wp_using_ext_object_cache() ) {
186
- /*
187
- * If any real caching is in place , then define a function to cache this event when, and if, one of the
188
- * lazy properties is loaded.
189
- * Cache by post ID and filter.
190
- */
191
- $cache_key = $cache_slug . '_' . $this->post->ID . '_' . $filter;
192
- $cache = new Cache();
193
- $callback = function () use ( $cache, $cache_key, $filter ) {
194
- $properties = $this->get_properties( $filter );
195
-
196
- /*
197
- * Cache without expiration, but only until a post of the types managed by The Events Calendar is
198
- * updated or created.
199
- */
200
- $cache->set( $cache_key, $properties, 0, Cache_Listener::TRIGGER_SAVE_POST );
201
- };
202
- }
203
-
204
- return $callback;
205
- }
206
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Models/Post_Types/Nothing.php DELETED
@@ -1,44 +0,0 @@
1
- <?php
2
- /**
3
- * Models a non existing post.
4
- *
5
- * The reason for this class existence is to allow method chaining to happen without errors and to return a consistent
6
- * model type from methods.
7
- *
8
- * @since 4.9.18
9
- *
10
- * @package Tribe\Models\Post_Types
11
- */
12
-
13
- namespace Tribe\Models\Post_Types;
14
-
15
- /**
16
- * Class Nothing
17
- *
18
- * @since 4.9.18
19
- *
20
- * @package Tribe\Models\Post_Types
21
- */
22
- class Nothing extends Base {
23
-
24
- /**
25
- * {@inheritDoc}
26
- */
27
- protected function get_cache_slug() {
28
- return '';
29
- }
30
-
31
- /**
32
- * {@inheritDoc}
33
- */
34
- protected function build_properties( $filter ) {
35
- return [];
36
- }
37
-
38
- /**
39
- * {@inheritDoc}
40
- */
41
- public function to_post( $output = OBJECT, $filter = 'raw' ) {
42
- return null;
43
- }
44
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/PUE/Checker.php CHANGED
@@ -61,7 +61,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
61
  *
62
  * @var array
63
  */
64
- private $download_query = [];
65
 
66
  /**
67
  * The context in which this license key is used. May be 'component'
@@ -138,14 +138,14 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
138
  *
139
  * @var array
140
  */
141
- private static $stats = [];
142
 
143
  /**
144
  * Full Stats
145
  *
146
  * @var array
147
  */
148
- private static $stats_full = [];
149
 
150
  /**
151
  * Class constructor.
@@ -166,7 +166,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
166
  * }
167
  * @param string $plugin_file fully qualified path to the main plugin file.
168
  */
169
- public function __construct( $pue_update_url, $slug = '', $options = [], $plugin_file = '' ) {
170
  $this->set_slug( $slug );
171
  $this->set_plugin_file( $plugin_file );
172
  $this->set_options( $options );
@@ -180,30 +180,30 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
180
  */
181
  public function hooks() {
182
  // Override requests for plugin information
183
- add_filter( 'plugins_api', [ $this, 'inject_info' ], 10, 3 );
184
 
185
  // Check for updates when the WP updates are checked and inject our update if needed.
186
  // Only add filter if the TRIBE_DISABLE_PUE constant is not set as true and where
187
  // the context is not 'service'
188
  if ( ( ! defined( 'TRIBE_DISABLE_PUE' ) || true !== TRIBE_DISABLE_PUE ) && 'service' !== $this->context ) {
189
- add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_for_updates' ] );
190
  }
191
 
192
- add_filter( 'tribe_licensable_addons', [ $this, 'build_addon_list' ] );
193
- add_action( 'tribe_license_fields', [ $this, 'do_license_key_fields' ] );
194
- add_action( 'tribe_settings_after_content_tab_licenses', [ $this, 'do_license_key_javascript' ] );
195
- add_action( 'tribe_settings_success_message', [ $this, 'do_license_key_success_message' ], 10, 2 );
196
- add_action( 'load-plugins.php', [ $this, 'remove_default_inline_update_msg' ], 50 );
197
 
198
  // Key validation
199
- add_filter( 'tribe_settings_save_field_value', [ $this, 'check_for_api_key_error' ], 10, 3 );
200
- add_action( 'wp_ajax_pue-validate-key_' . $this->get_slug(), [ $this, 'ajax_validate_key' ] );
201
- add_filter( 'tribe-pue-install-keys', [ $this, 'return_install_key' ] );
202
- add_action( 'admin_enqueue_scripts', [ $this, 'maybe_display_json_error_on_plugins_page' ], 1 );
203
- add_action( 'admin_init', [ $this, 'general_notifications' ] );
204
 
205
  // Package name
206
- add_filter( 'upgrader_pre_download', [ Tribe__PUE__Package_Handler::instance(), 'filter_upgrader_pre_download' ], 5, 3 );
207
  }
208
 
209
  /********************** Getter / Setter Functions **********************/
@@ -319,16 +319,16 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
319
  *
320
  * @param array $options
321
  */
322
- private function set_options( $options = [] ) {
323
 
324
  $options = wp_parse_args(
325
- $options, [
326
  'pue_option_name' => 'external_updates-' . $this->get_slug(),
327
  'apikey' => '',
328
  'check_period' => 12,
329
  'context' => 'component',
330
  'plugin_name' => '',
331
- ]
332
  );
333
 
334
  $this->pue_option_name = $options['pue_option_name'];
@@ -343,7 +343,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
343
  *
344
  * @param array $download_query
345
  */
346
- private function set_download_query( $download_query = [] ) {
347
 
348
  if ( ! empty( $download_query ) ) {
349
  $this->download_query = $download_query;
@@ -392,7 +392,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
392
  *
393
  * @param array $validate_query
394
  */
395
- private function set_validate_query( $validate_query = [] ) {
396
 
397
  if ( ! empty( $validate_query ) ) {
398
  $this->validate_query = $validate_query;
@@ -472,7 +472,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
472
  *
473
  * @return array list of addons
474
  */
475
- public function build_addon_list( $addons = [] ) {
476
  $addons[] = $this->get_plugin_name();
477
 
478
  return $addons;
@@ -487,12 +487,12 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
487
  */
488
  public function do_license_key_fields( $fields ) {
489
  // common fields whether licenses should be hidden or not
490
- $to_insert = [
491
- $this->pue_install_key . '-heading' => [
492
  'type' => 'heading',
493
  'label' => $this->get_plugin_name(),
494
- ],
495
- ];
496
 
497
  $no_license_tooltip = esc_html__( 'A valid license key is required for support and updates', 'tribe-common' );
498
  if ( 'event-aggregator' === $this->get_slug() ) {
@@ -505,7 +505,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
505
 
506
  // we want to inject the following license settings at the end of the licenses tab
507
  if ( $this->should_show_network_editable_license() ) {
508
- $to_insert[ $this->pue_install_key ] = [
509
  'type' => 'license_key',
510
  'size' => 'large',
511
  'validation_type' => 'license_key',
@@ -514,9 +514,9 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
514
  'tooltip' => $no_license_tooltip,
515
  'parent_option' => false,
516
  'network_option' => true,
517
- ];
518
  } elseif ( $this->should_show_subsite_editable_license() ) {
519
- $to_insert[ $this->pue_install_key ] = [
520
  'type' => 'license_key',
521
  'size' => 'large',
522
  'validation_type' => 'license_key',
@@ -525,28 +525,28 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
525
  'tooltip' => $no_license_tooltip,
526
  'parent_option' => false,
527
  'network_option' => false,
528
- ];
529
  } elseif ( $this->should_show_overrideable_license() ) {
530
- $to_insert[ $this->pue_install_key . '-state' ] = [
531
  'type' => 'html',
532
  'label' => sprintf( esc_attr__( 'License Key Status:', 'tribe-common' ) ),
533
- 'label_attributes' => [ 'style' => 'width:auto;' ],
534
  'html' => sprintf( '<p>%s</p>', $this->get_network_license_state_string() ),
535
- ];
536
 
537
  $override_id = $this->pue_install_key . '-override';
538
 
539
- $to_insert[ $override_id ] = [
540
  'type' => 'checkbox_bool',
541
  'label' => esc_html__( 'Override network license key', 'tribe-common' ),
542
  'tooltip' => esc_html__( 'Check this box if you wish to override the network license key with your own', 'tribe-common' ),
543
  'default' => false,
544
  'validation_type' => 'boolean',
545
  'parent_option' => false,
546
- 'attributes' => [ 'id' => $override_id . '-field' ],
547
- ];
548
 
549
- $to_insert[ $this->pue_install_key ] = [
550
  'type' => 'license_key',
551
  'size' => 'large',
552
  'validation_type' => 'license_key',
@@ -555,18 +555,18 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
555
  'parent_option' => false,
556
  'network_option' => false,
557
  'class' => 'tribe-dependent',
558
- 'fieldset_attributes' => [
559
  'data-depends' => '#' . $override_id . '-field',
560
  'data-condition-checked' => true,
561
- ],
562
- ];
563
  } else {
564
- $to_insert[ $this->pue_install_key . '-state' ] = [
565
  'type' => 'html',
566
  'label' => sprintf( esc_attr__( 'License Key Status:', 'tribe-common' ) ),
567
- 'label_attributes' => [ 'style' => 'width:auto;' ],
568
  'html' => sprintf( '<p>%s</p>', $this->get_network_license_state_string() ),
569
- ];
570
  }
571
 
572
  $fields = self::array_insert_after_key( 'tribe-form-content-start', $fields, $to_insert );
@@ -663,16 +663,16 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
663
 
664
  global $wpdb;
665
 
666
- $stats = [
667
- 'versions' => [
668
  'wp' => sanitize_text_field( $GLOBALS['wp_version'] ),
669
- ],
670
- 'network' => [
671
  'multisite' => 0,
672
  'network_activated' => 0,
673
  'active_sites' => 1,
674
- ],
675
- ];
676
 
677
  if ( is_multisite() ) {
678
  $sql_count = "
@@ -728,18 +728,18 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
728
  }
729
  }
730
 
731
- $stats['versions'] = [
732
  'wp' => sanitize_text_field( $GLOBALS['wp_version'] ),
733
  'php' => sanitize_text_field( phpversion() ),
734
  'mysql' => sanitize_text_field( $wpdb->db_version() ),
735
- ];
736
 
737
- $stats['theme'] = [
738
  'name' => sanitize_text_field( $theme->get( 'Name' ) ),
739
  'version' => sanitize_text_field( $theme->get( 'Version' ) ),
740
  'stylesheet' => sanitize_text_field( $theme->get_stylesheet() ),
741
  'template' => sanitize_text_field( $theme->get_template() ),
742
- ];
743
 
744
  $stats['site_language'] = sanitize_text_field( get_locale() );
745
  $stats['user_language'] = sanitize_text_field( get_user_locale() );
@@ -747,13 +747,13 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
747
  $stats['wp_debug'] = (int) ( defined( 'WP_DEBUG' ) && WP_DEBUG );
748
  $stats['site_timezone'] = sanitize_text_field( $timezone );
749
 
750
- $stats['totals'] = [
751
  'all_post_types' => (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$wpdb->posts}`" ),
752
  'events' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE post_type = %s", 'tribe_events' ) ),
753
  'venues' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE post_type = %s", 'tribe_venue' ) ),
754
  'organizers' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE post_type = %s", 'tribe_organizer' ) ),
755
  'event_categories' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->term_taxonomy}` WHERE taxonomy = %s", 'tribe_events_cat' ) ),
756
- ];
757
 
758
  self::$stats_full = $stats;
759
 
@@ -841,15 +841,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
841
  $class_name = $autoloader->get_prefix_by_slug( $this->get_slug() );
842
 
843
  if ( $class_name ) {
844
- $is_namespaced = false !== strpos( $class_name, '\\' );
845
-
846
- if ( $is_namespaced ) {
847
- // Handle class prefixes like Tribe\Plugin\.
848
- $class_name .= 'PUE\Helper';
849
- } else {
850
- // Handle class prefixes like Tribe__Plugin__.
851
- $class_name .= 'PUE__Helper';
852
- }
853
 
854
  if ( constant( $class_name . '::DATA' ) ) {
855
  $license_key = constant( $class_name . '::DATA' );
@@ -900,7 +892,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
900
  * @return array An associative array containing the license status response.
901
  */
902
  public function validate_key( $key, $network = false ) {
903
- $response = [];
904
  $response['status'] = 0;
905
 
906
  if ( ! $key ) {
@@ -985,23 +977,22 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
985
  * Echo JSON results for key validation
986
  */
987
  public function ajax_validate_key() {
 
988
  $key = isset( $_POST['key'] ) ? wp_unslash( $_POST['key'] ) : null;
989
  $nonce = isset( $_POST['_wpnonce'] ) ? wp_unslash( $_POST['_wpnonce'] ) : null;
990
 
991
- if (
992
- empty( $nonce )
993
- || false === wp_verify_nonce( $nonce, 'pue-validate-key_' . $this->get_slug() )
994
- ) {
995
- $response = [
996
  'status' => 0,
997
  'message' => __( 'Please refresh the page and try your request again.', 'tribe-common' ),
998
- ];
999
  } else {
1000
  $response = $this->validate_key( $key );
1001
  }
1002
 
1003
  echo json_encode( $response );
1004
  exit;
 
1005
  }
1006
 
1007
  /**
@@ -1091,7 +1082,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1091
  }
1092
 
1093
  $state = $this->get_state();
1094
- $messages = [];
1095
  $plugin_updates = get_plugin_updates();
1096
  $update_available = isset( $plugin_updates[ $this->plugin_file ] );
1097
 
@@ -1141,12 +1132,12 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1141
  $message_row_html
1142
  );
1143
 
1144
- $this->plugin_notice = [
1145
  'slug' => $this->plugin_file,
1146
  'message_row_html' => $message_row_html,
1147
- ];
1148
 
1149
- add_filter( 'tribe_plugin_notices', [ $this, 'add_notice_to_plugin_notices' ] );
1150
 
1151
  }
1152
 
@@ -1243,7 +1234,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1243
  *
1244
  * @return string $plugin_info
1245
  */
1246
- public function request_info( $query_args = [] ) {
1247
  $query_args = apply_filters( 'tribe_puc_request_info_query_args-' . $this->get_slug(), $query_args );
1248
 
1249
  // Cache the API call so it only needs to be made once per plugin per page load.
@@ -1265,13 +1256,13 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1265
  }
1266
 
1267
  //Various options for the wp_remote_get() call. Plugins can filter these, too.
1268
- $options = [
1269
  'body' => $query_args,
1270
  'timeout' => 15, //seconds
1271
- 'headers' => [
1272
  'Accept' => 'application/json',
1273
- ],
1274
- ];
1275
  $options = apply_filters( 'tribe_puc_request_info_options-' . $this->get_slug(), $options );
1276
 
1277
  $url = sprintf( '%s/api/plugins/v2/license/validate', $this->get_pue_update_url() );
@@ -1335,7 +1326,6 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1335
  if ( isset( $plugin_info->api_invalid ) ) {
1336
  $plugin_info = Tribe__PUE__Utility::from_plugin_info( $plugin_info );
1337
  $plugin_info->license_error = $this->get_api_message( $plugin_info );
1338
-
1339
  return $plugin_info;
1340
  }
1341
 
@@ -1343,7 +1333,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1343
  $this->update_key( $plugin_info->new_install_key );
1344
  }
1345
 
1346
- // Need to correct the download url so it contains the custom user data (e.g. api and any other parameters).
1347
  $download_query = $this->get_download_query();
1348
 
1349
  if ( ! empty( $download_query ) ) {
@@ -1424,7 +1414,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1424
  *
1425
  * @return array
1426
  */
1427
- public function check_for_updates( $updates = [], $force_recheck = false ) {
1428
  $state = $this->get_state( $force_recheck );
1429
 
1430
  $state->lastCheck = time();
@@ -1435,12 +1425,12 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1435
 
1436
  $state->update = $this->request_update();
1437
 
1438
- // If a null update was returned, skip to the end of the function.
1439
  if ( null !== $state->update ) {
1440
- // Is there an update to insert?
1441
  if ( version_compare( $state->update->version, $this->get_installed_version(), '>' ) ) {
1442
  if ( empty( $updates ) ) {
1443
- $updates = (object) [ 'response' => [] ];
1444
  }
1445
 
1446
  $updates->response[ $this->get_plugin_file() ] = $state->update->to_wp_format();
@@ -1474,7 +1464,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1474
  }
1475
 
1476
  if ( 'service' !== $this->context ) {
1477
- $this->check_for_updates( [], true );
1478
  }
1479
 
1480
  $network_option = false;
@@ -1614,7 +1604,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1614
  * @return array $keys
1615
  *
1616
  */
1617
- public function return_install_key( $keys = [] ) {
1618
  $key = $this->get_key();
1619
 
1620
  if ( ! empty( $key ) ) {
@@ -1665,9 +1655,9 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1665
  return false;
1666
  }
1667
 
1668
- $map = [
1669
  'event-aggregator/event-aggregator.php' => 'the-events-calendar/the-events-calendar.php',
1670
- ];
1671
 
1672
  $plugin_file = $this->get_plugin_file();
1673
 
@@ -1692,11 +1682,11 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
1692
  * @return string The localized state string.
1693
  */
1694
  protected function get_network_license_state_string() {
1695
- $states = [
1696
  'licensed' => esc_html__( 'A valid license has been entered by your network administrator.', 'tribe-common' ),
1697
  'not-licensed' => esc_html__( 'No license entered. Consult your network administrator.', 'tribe-common' ),
1698
  'expired' => esc_html__( 'Expired license. Consult your network administrator.', 'tribe-common' ),
1699
- ];
1700
 
1701
  $response = $this->validate_key( $this->get_key( 'network' ), true );
1702
 
61
  *
62
  * @var array
63
  */
64
+ private $download_query = array();
65
 
66
  /**
67
  * The context in which this license key is used. May be 'component'
138
  *
139
  * @var array
140
  */
141
+ private static $stats = array();
142
 
143
  /**
144
  * Full Stats
145
  *
146
  * @var array
147
  */
148
+ private static $stats_full = array();
149
 
150
  /**
151
  * Class constructor.
166
  * }
167
  * @param string $plugin_file fully qualified path to the main plugin file.
168
  */
169
+ public function __construct( $pue_update_url, $slug = '', $options = array(), $plugin_file = '' ) {
170
  $this->set_slug( $slug );
171
  $this->set_plugin_file( $plugin_file );
172
  $this->set_options( $options );
180
  */
181
  public function hooks() {
182
  // Override requests for plugin information
183
+ add_filter( 'plugins_api', array( $this, 'inject_info' ), 10, 3 );
184
 
185
  // Check for updates when the WP updates are checked and inject our update if needed.
186
  // Only add filter if the TRIBE_DISABLE_PUE constant is not set as true and where
187
  // the context is not 'service'
188
  if ( ( ! defined( 'TRIBE_DISABLE_PUE' ) || true !== TRIBE_DISABLE_PUE ) && 'service' !== $this->context ) {
189
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_for_updates' ) );
190
  }
191
 
192
+ add_filter( 'tribe_licensable_addons', array( $this, 'build_addon_list' ) );
193
+ add_action( 'tribe_license_fields', array( $this, 'do_license_key_fields' ) );
194
+ add_action( 'tribe_settings_after_content_tab_licenses', array( $this, 'do_license_key_javascript' ) );
195
+ add_action( 'tribe_settings_success_message', array( $this, 'do_license_key_success_message' ), 10, 2 );
196
+ add_action( 'load-plugins.php', array( $this, 'remove_default_inline_update_msg' ), 50 );
197
 
198
  // Key validation
199
+ add_filter( 'tribe_settings_save_field_value', array( $this, 'check_for_api_key_error' ), 10, 3 );
200
+ add_action( 'wp_ajax_pue-validate-key_' . $this->get_slug(), array( $this, 'ajax_validate_key' ) );
201
+ add_filter( 'tribe-pue-install-keys', array( $this, 'return_install_key' ) );
202
+ add_action( 'admin_enqueue_scripts', array( $this, 'maybe_display_json_error_on_plugins_page' ), 1 );
203
+ add_action( 'admin_init', array( $this, 'general_notifications' ) );
204
 
205
  // Package name
206
+ add_filter( 'upgrader_pre_download', array( Tribe__PUE__Package_Handler::instance(), 'filter_upgrader_pre_download' ), 5, 3 );
207
  }
208
 
209
  /********************** Getter / Setter Functions **********************/
319
  *
320
  * @param array $options
321
  */
322
+ private function set_options( $options = array() ) {
323
 
324
  $options = wp_parse_args(
325
+ $options, array(
326
  'pue_option_name' => 'external_updates-' . $this->get_slug(),
327
  'apikey' => '',
328
  'check_period' => 12,
329
  'context' => 'component',
330
  'plugin_name' => '',
331
+ )
332
  );
333
 
334
  $this->pue_option_name = $options['pue_option_name'];
343
  *
344
  * @param array $download_query
345
  */
346
+ private function set_download_query( $download_query = array() ) {
347
 
348
  if ( ! empty( $download_query ) ) {
349
  $this->download_query = $download_query;
392
  *
393
  * @param array $validate_query
394
  */
395
+ private function set_validate_query( $validate_query = array() ) {
396
 
397
  if ( ! empty( $validate_query ) ) {
398
  $this->validate_query = $validate_query;
472
  *
473
  * @return array list of addons
474
  */
475
+ public function build_addon_list( $addons = array() ) {
476
  $addons[] = $this->get_plugin_name();
477
 
478
  return $addons;
487
  */
488
  public function do_license_key_fields( $fields ) {
489
  // common fields whether licenses should be hidden or not
490
+ $to_insert = array(
491
+ $this->pue_install_key . '-heading' => array(
492
  'type' => 'heading',
493
  'label' => $this->get_plugin_name(),
494
+ ),
495
+ );
496
 
497
  $no_license_tooltip = esc_html__( 'A valid license key is required for support and updates', 'tribe-common' );
498
  if ( 'event-aggregator' === $this->get_slug() ) {
505
 
506
  // we want to inject the following license settings at the end of the licenses tab
507
  if ( $this->should_show_network_editable_license() ) {
508
+ $to_insert[ $this->pue_install_key ] = array(
509
  'type' => 'license_key',
510
  'size' => 'large',
511
  'validation_type' => 'license_key',
514
  'tooltip' => $no_license_tooltip,
515
  'parent_option' => false,
516
  'network_option' => true,
517
+ );
518
  } elseif ( $this->should_show_subsite_editable_license() ) {
519
+ $to_insert[ $this->pue_install_key ] = array(
520
  'type' => 'license_key',
521
  'size' => 'large',
522
  'validation_type' => 'license_key',
525
  'tooltip' => $no_license_tooltip,
526
  'parent_option' => false,
527
  'network_option' => false,
528
+ );
529
  } elseif ( $this->should_show_overrideable_license() ) {
530
+ $to_insert[ $this->pue_install_key . '-state' ] = array(
531
  'type' => 'html',
532
  'label' => sprintf( esc_attr__( 'License Key Status:', 'tribe-common' ) ),
533
+ 'label_attributes' => array( 'style' => 'width:auto;' ),
534
  'html' => sprintf( '<p>%s</p>', $this->get_network_license_state_string() ),
535
+ );
536
 
537
  $override_id = $this->pue_install_key . '-override';
538
 
539
+ $to_insert[ $override_id ] = array(
540
  'type' => 'checkbox_bool',
541
  'label' => esc_html__( 'Override network license key', 'tribe-common' ),
542
  'tooltip' => esc_html__( 'Check this box if you wish to override the network license key with your own', 'tribe-common' ),
543
  'default' => false,
544
  'validation_type' => 'boolean',
545
  'parent_option' => false,
546
+ 'attributes' => array( 'id' => $override_id . '-field' ),
547
+ );
548
 
549
+ $to_insert[ $this->pue_install_key ] = array(
550
  'type' => 'license_key',
551
  'size' => 'large',
552
  'validation_type' => 'license_key',
555
  'parent_option' => false,
556
  'network_option' => false,
557
  'class' => 'tribe-dependent',
558
+ 'fieldset_attributes' => array(
559
  'data-depends' => '#' . $override_id . '-field',
560
  'data-condition-checked' => true,
561
+ ),
562
+ );
563
  } else {
564
+ $to_insert[ $this->pue_install_key . '-state' ] = array(
565
  'type' => 'html',
566
  'label' => sprintf( esc_attr__( 'License Key Status:', 'tribe-common' ) ),
567
+ 'label_attributes' => array( 'style' => 'width:auto;' ),
568
  'html' => sprintf( '<p>%s</p>', $this->get_network_license_state_string() ),
569
+ );
570
  }
571
 
572
  $fields = self::array_insert_after_key( 'tribe-form-content-start', $fields, $to_insert );
663
 
664
  global $wpdb;
665
 
666
+ $stats = array(
667
+ 'versions' => array(
668
  'wp' => sanitize_text_field( $GLOBALS['wp_version'] ),
669
+ ),
670
+ 'network' => array(
671
  'multisite' => 0,
672
  'network_activated' => 0,
673
  'active_sites' => 1,
674
+ ),
675
+ );
676
 
677
  if ( is_multisite() ) {
678
  $sql_count = "
728
  }
729
  }
730
 
731
+ $stats['versions'] = array(
732
  'wp' => sanitize_text_field( $GLOBALS['wp_version'] ),
733
  'php' => sanitize_text_field( phpversion() ),
734
  'mysql' => sanitize_text_field( $wpdb->db_version() ),
735
+ );
736
 
737
+ $stats['theme'] = array(
738
  'name' => sanitize_text_field( $theme->get( 'Name' ) ),
739
  'version' => sanitize_text_field( $theme->get( 'Version' ) ),
740
  'stylesheet' => sanitize_text_field( $theme->get_stylesheet() ),
741
  'template' => sanitize_text_field( $theme->get_template() ),
742
+ );
743
 
744
  $stats['site_language'] = sanitize_text_field( get_locale() );
745
  $stats['user_language'] = sanitize_text_field( get_user_locale() );
747
  $stats['wp_debug'] = (int) ( defined( 'WP_DEBUG' ) && WP_DEBUG );
748
  $stats['site_timezone'] = sanitize_text_field( $timezone );
749
 
750
+ $stats['totals'] = array(
751
  'all_post_types' => (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$wpdb->posts}`" ),
752
  'events' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE post_type = %s", 'tribe_events' ) ),
753
  'venues' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE post_type = %s", 'tribe_venue' ) ),
754
  'organizers' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE post_type = %s", 'tribe_organizer' ) ),
755
  'event_categories' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->term_taxonomy}` WHERE taxonomy = %s", 'tribe_events_cat' ) ),
756
+ );
757
 
758
  self::$stats_full = $stats;
759
 
841
  $class_name = $autoloader->get_prefix_by_slug( $this->get_slug() );
842
 
843
  if ( $class_name ) {
844
+ $class_name .= 'PUE__Helper';
 
 
 
 
 
 
 
 
845
 
846
  if ( constant( $class_name . '::DATA' ) ) {
847
  $license_key = constant( $class_name . '::DATA' );
892
  * @return array An associative array containing the license status response.
893
  */
894
  public function validate_key( $key, $network = false ) {
895
+ $response = array();
896
  $response['status'] = 0;
897
 
898
  if ( ! $key ) {
977
  * Echo JSON results for key validation
978
  */
979
  public function ajax_validate_key() {
980
+
981
  $key = isset( $_POST['key'] ) ? wp_unslash( $_POST['key'] ) : null;
982
  $nonce = isset( $_POST['_wpnonce'] ) ? wp_unslash( $_POST['_wpnonce'] ) : null;
983
 
984
+ if ( empty( $nonce ) || false === wp_verify_nonce( $nonce, 'pue-validate-key_' . $this->get_slug() ) ) {
985
+ $response = array(
 
 
 
986
  'status' => 0,
987
  'message' => __( 'Please refresh the page and try your request again.', 'tribe-common' ),
988
+ );
989
  } else {
990
  $response = $this->validate_key( $key );
991
  }
992
 
993
  echo json_encode( $response );
994
  exit;
995
+
996
  }
997
 
998
  /**
1082
  }
1083
 
1084
  $state = $this->get_state();
1085
+ $messages = array();
1086
  $plugin_updates = get_plugin_updates();
1087
  $update_available = isset( $plugin_updates[ $this->plugin_file ] );
1088
 
1132
  $message_row_html
1133
  );
1134
 
1135
+ $this->plugin_notice = array(
1136
  'slug' => $this->plugin_file,
1137
  'message_row_html' => $message_row_html,
1138
+ );
1139
 
1140
+ add_filter( 'tribe_plugin_notices', array( $this, 'add_notice_to_plugin_notices' ) );
1141
 
1142
  }
1143
 
1234
  *
1235
  * @return string $plugin_info
1236
  */
1237
+ public function request_info( $query_args = array() ) {
1238
  $query_args = apply_filters( 'tribe_puc_request_info_query_args-' . $this->get_slug(), $query_args );
1239
 
1240
  // Cache the API call so it only needs to be made once per plugin per page load.
1256
  }
1257
 
1258
  //Various options for the wp_remote_get() call. Plugins can filter these, too.
1259
+ $options = array(
1260
  'body' => $query_args,
1261
  'timeout' => 15, //seconds
1262
+ 'headers' => array(
1263
  'Accept' => 'application/json',
1264
+ ),
1265
+ );
1266
  $options = apply_filters( 'tribe_puc_request_info_options-' . $this->get_slug(), $options );
1267
 
1268
  $url = sprintf( '%s/api/plugins/v2/license/validate', $this->get_pue_update_url() );
1326
  if ( isset( $plugin_info->api_invalid ) ) {
1327
  $plugin_info = Tribe__PUE__Utility::from_plugin_info( $plugin_info );
1328
  $plugin_info->license_error = $this->get_api_message( $plugin_info );
 
1329
  return $plugin_info;
1330
  }
1331
 
1333
  $this->update_key( $plugin_info->new_install_key );
1334
  }
1335
 
1336
+ //need to correct the download url so it contains the custom user data (i.e. api and any other paramaters)
1337
  $download_query = $this->get_download_query();
1338
 
1339
  if ( ! empty( $download_query ) ) {
1414
  *
1415
  * @return array
1416
  */
1417
+ public function check_for_updates( $updates = array(), $force_recheck = false ) {
1418
  $state = $this->get_state( $force_recheck );
1419
 
1420
  $state->lastCheck = time();
1425
 
1426
  $state->update = $this->request_update();
1427
 
1428
+ // If a null update was returned, skip the end of the function.
1429
  if ( null !== $state->update ) {
1430
+ //Is there an update to insert?
1431
  if ( version_compare( $state->update->version, $this->get_installed_version(), '>' ) ) {
1432
  if ( empty( $updates ) ) {
1433
+ $updates = (object) array( 'response' => array() );
1434
  }
1435
 
1436
  $updates->response[ $this->get_plugin_file() ] = $state->update->to_wp_format();
1464
  }
1465
 
1466
  if ( 'service' !== $this->context ) {
1467
+ $this->check_for_updates( array(), true );
1468
  }
1469
 
1470
  $network_option = false;
1604
  * @return array $keys
1605
  *
1606
  */
1607
+ public function return_install_key( $keys = array() ) {
1608
  $key = $this->get_key();
1609
 
1610
  if ( ! empty( $key ) ) {
1655
  return false;
1656
  }
1657
 
1658
+ $map = array(
1659
  'event-aggregator/event-aggregator.php' => 'the-events-calendar/the-events-calendar.php',
1660
+ );
1661
 
1662
  $plugin_file = $this->get_plugin_file();
1663
 
1682
  * @return string The localized state string.
1683
  */
1684
  protected function get_network_license_state_string() {
1685
+ $states = array(
1686
  'licensed' => esc_html__( 'A valid license has been entered by your network administrator.', 'tribe-common' ),
1687
  'not-licensed' => esc_html__( 'No license entered. Consult your network administrator.', 'tribe-common' ),
1688
  'expired' => esc_html__( 'Expired license. Consult your network administrator.', 'tribe-common' ),
1689
+ );
1690
 
1691
  $response = $this->validate_key( $this->get_key( 'network' ), true );
1692
 
common/src/Tribe/PUE/Notices.php CHANGED
@@ -14,17 +14,6 @@ class Tribe__PUE__Notices {
14
  protected $saved_notices = array();
15
  protected $notices = array();
16
 
17
- protected $plugin_names = [
18
- 'pue_install_key_event_tickets_plus' => 'Event Tickets Plus',
19
- 'pue_install_key_events_community' => 'The Events Calendar: Community Events',
20
- 'pue_install_key_events_community_tickets' => 'The Events Calendar: Community Events Tickets',
21
- 'pue_install_key_image_widget_plus' => 'Image Widget Plus',
22
- 'pue_install_key_tribe_eventbrite' => 'The Events Calendar: Eventbrite Tickets',
23
- 'pue_install_key_tribe_filterbar' => 'The Events Calendar: Filter Bar',
24
- 'pue_install_key_event_aggregator' => 'Event Aggregator',
25
- 'pue_install_key_events_calendar_pro' => 'The Events Calendar PRO',
26
- ];
27
-
28
  /**
29
  * Sets up license key related admin notices.
30
  */
@@ -144,11 +133,6 @@ class Tribe__PUE__Notices {
144
  * @return boolean
145
  */
146
  public function has_notice( $plugin_name, $notice_type = null ) {
147
- // If we match a pue key we use that value
148
- if ( isset( $this->plugin_names[ $plugin_name ] ) ) {
149
- $plugin_name = $this->plugin_names[ $plugin_name ];
150
- }
151
-
152
  if ( $notice_type ) {
153
  return ! empty( $this->notices[ $notice_type ][ $plugin_name ] );
154
  }
@@ -237,10 +221,21 @@ class Tribe__PUE__Notices {
237
 
238
  $empty_keys = $wpdb->get_results( $sql, ARRAY_N );
239
 
 
 
 
 
 
 
 
 
 
 
 
240
  $formatted_empty_keys = array();
241
  foreach ( $empty_keys as $empty_key ) {
242
  $empty_key = Tribe__Utils__Array::get( $empty_key, array( 0 ) );
243
- $formatted_empty_keys[] = Tribe__Utils__Array::get( $this->plugin_names, $empty_key );
244
  }
245
 
246
  return $formatted_empty_keys;
@@ -387,7 +382,7 @@ class Tribe__PUE__Notices {
387
  $html =
388
  '<div class="api-check">
389
  <div class="tribe-mascot">
390
- <img src="' . $mascot . '" style="max-height: 150px; max-width: 150px; height: 100%; width: auto;"/>
391
  </div>
392
  <div class="notice-content">' . $inner_html . '</div>
393
  </div>';
@@ -406,61 +401,6 @@ class Tribe__PUE__Notices {
406
  );
407
  }
408
 
409
- /**
410
- * Transforms a list of plugins into human readable string.
411
- *
412
- * Examples of output:
413
- *
414
- * # One name
415
- * "Ticket Pro"
416
- *
417
- * # Two names
418
- * "Ticket Pro and Calendar Legend"
419
- *
420
- * # Three names
421
- * "Ticket Pro, Calendar Legend and Date Stars"
422
- *
423
- *
424
- * @since 4.9.12
425
- *
426
- * @param array|string $plugins Array of plugin classes.
427
- *
428
- * @return string|false
429
- */
430
- public function get_formatted_plugin_names_from_classes( $plugins ) {
431
- $plugin_list = [];
432
-
433
- foreach ( (array) $plugins as $class_name ) {
434
- $pue = tribe( Tribe__Dependency::class )->get_pue_from_class( $class_name );
435
-
436
- if ( ! $pue ) {
437
- continue;
438
- }
439
-
440
- if ( ! isset( $this->plugin_names[ $pue->pue_install_key ] ) ) {
441
- continue;
442
- }
443
-
444
- $plugin_list[] = $this->plugin_names[ $pue->pue_install_key ];
445
- }
446
-
447
- $num_plugins = count( $plugin_list );
448
-
449
- if ( 0 === $num_plugins ) {
450
- return false;
451
- }
452
-
453
- if ( 1 === $num_plugins ) {
454
- $html = current( $plugin_list );
455
- } elseif ( 1 < $num_plugins ) {
456
- $all_but_last = join( ', ', array_slice( $plugin_list, 0, count( $plugin_list ) - 1 ) );
457
- $last = current( array_slice( $plugin_list, count( $plugin_list ) - 1, 1 ) );
458
- $html = sprintf( _x( '%1$s and %2$s', 'formatted plugin list', 'tribe-common' ), $all_but_last, $last );
459
- }
460
-
461
- return '<span class="plugin-list">' . $html . '</span>';
462
- }
463
-
464
  /**
465
  * Transforms the array referenced by group into a human readable,
466
  * comma delimited list.
@@ -479,7 +419,7 @@ class Tribe__PUE__Notices {
479
  * # Fallback
480
  * "Unknown Plugin(s)"
481
  *
482
- * @param string $group
483
  *
484
  * @return string
485
  */
14
  protected $saved_notices = array();
15
  protected $notices = array();
16
 
 
 
 
 
 
 
 
 
 
 
 
17
  /**
18
  * Sets up license key related admin notices.
19
  */
133
  * @return boolean
134
  */
135
  public function has_notice( $plugin_name, $notice_type = null ) {
 
 
 
 
 
136
  if ( $notice_type ) {
137
  return ! empty( $this->notices[ $notice_type ][ $plugin_name ] );
138
  }
221
 
222
  $empty_keys = $wpdb->get_results( $sql, ARRAY_N );
223
 
224
+ $plugin_names = array(
225
+ 'pue_install_key_event_tickets_plus' => 'Event Tickets Plus',
226
+ 'pue_install_key_events_community' => 'The Events Calendar: Community Events',
227
+ 'pue_install_key_events_community_tickets' => 'The Events Calendar: Community Events Tickets',
228
+ 'pue_install_key_image_widget_plus' => 'Image Widget Plus',
229
+ 'pue_install_key_tribe_eventbrite' => 'The Events Calendar: Eventbrite Tickets',
230
+ 'pue_install_key_tribe_filterbar' => 'The Events Calendar: Filter Bar',
231
+ 'pue_install_key_event_aggregator' => 'Event Aggregator',
232
+ 'pue_install_key_events_calendar_pro' => 'The Events Calendar PRO',
233
+ );
234
+
235
  $formatted_empty_keys = array();
236
  foreach ( $empty_keys as $empty_key ) {
237
  $empty_key = Tribe__Utils__Array::get( $empty_key, array( 0 ) );
238
+ $formatted_empty_keys[] = Tribe__Utils__Array::get( $plugin_names, $empty_key );
239
  }
240
 
241
  return $formatted_empty_keys;
382
  $html =
383
  '<div class="api-check">
384
  <div class="tribe-mascot">
385
+ <img src="' . $mascot . '"/>
386
  </div>
387
  <div class="notice-content">' . $inner_html . '</div>
388
  </div>';
401
  );
402
  }
403
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  /**
405
  * Transforms the array referenced by group into a human readable,
406
  * comma delimited list.
419
  * # Fallback
420
  * "Unknown Plugin(s)"
421
  *
422
+ * @param string $group
423
  *
424
  * @return string
425
  */
common/src/Tribe/PUE/Update_Prevention.php DELETED
@@ -1,199 +0,0 @@
1
- <?php
2
- namespace Tribe\PUE;
3
-
4
- use WP_Error;
5
- use Tribe__Dependency as Dependency;
6
-
7
- /**
8
- * Class Update_Prevention engine for a plugin with invalid/empty keys with
9
- * unmet dependencies on Core or Event Tickets.
10
- *
11
- * @package Tribe\PUE;
12
- */
13
- class Update_Prevention {
14
-
15
- /**
16
- * Fetches the dependencies based on a regular expression search of the Plugin_Register.php
17
- * file that we use to prevent problems with mismatched version on our plugins.
18
- *
19
- * @since 4.9.12
20
- *
21
- * @param string $content Contents of the file in question.
22
- *
23
- * @return array Named array with [ class_name => version ] or empty if it didnt find it.
24
- */
25
- public function get_dependencies( $content ) {
26
- $regex = "/'(?<plugin>[^']*)'(?:[^']*)'(?<version>[^']*)',/";
27
-
28
- if ( ! preg_match_all( $regex, $content, $matches ) ) {
29
- return [];
30
- }
31
-
32
- $dependencies = array_combine( $matches['plugin'], $matches['version'] );
33
-
34
- return $dependencies;
35
- }
36
-
37
- /**
38
- * Checks for the list of constants associate with plugin to make sure we are dealing
39
- * with a plugin owned by Modern Tribe.
40
- *
41
- * @since 4.9.12
42
- *
43
- * @param string $plugin Plugin file partial path, folder and main php file.
44
- *
45
- * @return bool
46
- */
47
- public function is_tribe_plugin( $plugin ) {
48
- $path_constants_list = [
49
- // The Events Calendar
50
- 'TRIBE_EVENTS_FILE',
51
-
52
- // Events Pro
53
- 'EVENTS_CALENDAR_PRO_FILE',
54
-
55
- // Filter bar
56
- 'TRIBE_EVENTS_FILTERBAR_FILE',
57
-
58
- // Eventbrite Tickets
59
- 'EVENTBRITE_PLUGIN_FILE',
60
- ];
61
-
62
- foreach ( $path_constants_list as $constant_name ) {
63
- if ( ! defined( $constant_name ) ) {
64
- continue;
65
- }
66
-
67
- if ( false === strpos( constant( $constant_name ), $plugin ) ) {
68
- continue;
69
- }
70
-
71
- return true;
72
- }
73
-
74
- return false;
75
- }
76
-
77
- /**
78
- * Filters the source file location for the upgrade package for the PUE Update_Prevention engine.
79
- *
80
- * @since 4.9.12
81
- *
82
- * @param string $source File source location.
83
- * @param string $remote_source Remote file source location.
84
- * @param WP_Upgrader $upgrader WP_Upgrader instance.
85
- * @param array $extra Extra arguments passed to hooked filters.
86
- */
87
- public function filter_upgrader_source_selection( $source, $remote_source, $upgrader, $extras ) {
88
- if ( ! isset( $extras['plugin'] ) ) {
89
- return $source;
90
- }
91
-
92
- $plugin = $extras['plugin'];
93
-
94
- // Bail if we are not dealing with a plugin we own.
95
- if ( ! $this->is_tribe_plugin( $plugin ) ) {
96
- return $source;
97
- }
98
-
99
- $register_path = $source . '/src/Tribe/Plugin_Register.php';
100
-
101
- // Bail when the Plugin Register file doesnt exist.
102
- if ( ! file_exists( $register_path ) ) {
103
- return $source;
104
- }
105
-
106
- $register_contents = file_get_contents( $register_path );
107
-
108
- $dependencies = $this->get_dependencies( $register_contents );
109
- $incompatible_plugins = [];
110
-
111
- foreach ( $dependencies as $class_name => $required_version ) {
112
- // Skip inactive plugin checks.
113
- if ( ! class_exists( $class_name ) ) {
114
- continue;
115
- }
116
-
117
- $constant_name = $class_name . '::VERSION';
118
-
119
- // Skip if we cant find the version constant.
120
- if ( ! defined( $constant_name ) ) {
121
- continue;
122
- }
123
-
124
- $current_version = constant( $constant_name );
125
-
126
- // Skip when the version is equal or higher than the required.
127
- if ( version_compare( $current_version, $required_version, '>=' ) ) {
128
- continue;
129
- }
130
-
131
- $pue = tribe( Dependency::class )->get_pue_from_class( $class_name );
132
- $has_pue_notice = $pue ? tribe( 'pue.notices' )->has_notice( $pue->pue_install_key ) : false;
133
-
134
- // Only throw warning for customers with notices of invalid/expired licenses.
135
- if ( ! $has_pue_notice ) {
136
- continue;
137
- }
138
-
139
- // Flag that we should prevent the Update
140
- $incompatible_plugins[ $class_name ] = $required_version;
141
- }
142
-
143
- // Bail when there are no incompatible plugins.
144
- if ( empty( $incompatible_plugins ) ) {
145
- return $source;
146
- }
147
-
148
- /**
149
- * Filter the if we should prevent the update.
150
- *
151
- * @since 4.9.12
152
- *
153
- * @param bool $should_revent Flag false to skip the prevention.
154
- * @param array $incompatible_plugins Which plugins were incompatible with new version of the plugin.
155
- * @param string $source File source location.
156
- * @param string $remote_source Remote file source location.
157
- * @param WP_Upgrader $upgrader WP_Upgrader instance.
158
- * @param array $extra Extra arguments passed to hooked filters.
159
- */
160
- $should_prevent_update = apply_filters(
161
- 'tribe_pue_should_prevent_update_without_license',
162
- true,
163
- $incompatible_plugins,
164
- $source,
165
- $remote_source,
166
- $upgrader,
167
- $extras
168
- );
169
-
170
- // Bail if the filter above returns anything but true.
171
- if ( true !== $should_prevent_update ) {
172
- return $source;
173
- }
174
-
175
- $full_plugin_path = $remote_source . '/' . $plugin;
176
- $plugin_data = get_plugin_data( $full_plugin_path );
177
-
178
- $plugins_classes = array_keys( $incompatible_plugins );
179
- $plugins_list_html = tribe( 'pue.notices' )->get_formatted_plugin_names_from_classes( $plugins_classes );
180
-
181
- $link_read_more = '<a href="http://m.tri.be/1aev" target="_blank">' . esc_html__( 'Read more', 'tribe-common' ) . '.</a>';
182
-
183
- $message = sprintf(
184
- esc_html__( 'Your update failed due to an incompatibility between the version (%1$s) of the %2$s you tried to update to and the version of %3$s that you are using. %4$s', 'tribe-common' ),
185
- esc_html( $plugin_data['Version'] ),
186
- esc_html( $plugin_data['Name'] ),
187
- $plugins_list_html,
188
- $link_read_more
189
- );
190
-
191
- $error = new WP_Error(
192
- 'tribe-updater-failed-prevention',
193
- $message,
194
- []
195
- );
196
-
197
- return $error;
198
- }
199
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Plugins.php CHANGED
@@ -9,71 +9,71 @@ if ( ! class_exists( 'Tribe__Plugins' ) ) {
9
  class Tribe__Plugins {
10
 
11
  /**
12
- * A list of tribe plugin's details in this array format:
13
  *
14
- * [
15
  * 'short_name' => Common name for the plugin, used in places such as WP Admin messages
16
  * 'class' => Main plugin class
17
  * 'thickbox_url' => Download or purchase URL for plugin from within /wp-admin/ thickbox
18
- * ]
19
  */
20
- private $tribe_plugins = [
21
- [
22
  'short_name' => 'Event Tickets',
23
  'class' => 'Tribe__Tickets__Main',
24
  'thickbox_url' => 'plugin-install.php?tab=plugin-information&plugin=event-tickets&TB_iframe=true',
25
- ],
26
- [
27
  'short_name' => 'Event Tickets Plus',
28
  'class' => 'Tribe__Tickets_Plus__Main',
29
- 'thickbox_url' => 'https://theeventscalendar.com/product/wordpress-event-tickets-plus/?TB_iframe=true',
30
- ],
31
- [
32
  'short_name' => 'The Events Calendar',
33
  'class' => 'Tribe__Events__Main',
34
  'thickbox_url' => 'plugin-install.php?tab=plugin-information&plugin=the-events-calendar&TB_iframe=true',
35
- ],
36
- [
37
  'short_name' => 'Events Calendar Pro',
38
  'class' => 'Tribe__Events__Pro__Main',
39
- 'thickbox_url' => 'https://theeventscalendar.com/product/wordpress-events-calendar-pro/?TB_iframe=true',
40
- ],
41
- [
42
  'short_name' => 'Community Events',
43
  'class' => 'Tribe__Events__Community__Main',
44
- 'thickbox_url' => 'https://theeventscalendar.com/product/wordpress-community-events/?TB_iframe=true',
45
- ],
46
- [
47
  'short_name' => 'Community Tickets',
48
  'class' => 'Tribe__Events__Community__Tickets__Main',
49
- 'thickbox_url' => 'https://theeventscalendar.com/product/community-tickets/?TB_iframe=true',
50
- ],
51
- [
52
  'short_name' => 'Filter Bar',
53
  'class' => 'Tribe__Events__Filterbar__View',
54
- 'thickbox_url' => 'https://theeventscalendar.com/product/wordpress-events-filterbar/?TB_iframe=true',
55
- ],
56
- [
57
  'short_name' => 'Facebook Events',
58
  'class' => 'Tribe__Events__Facebook__Importer',
59
- 'thickbox_url' => 'https://theeventscalendar.com/product/facebook-events/?TB_iframe=true',
60
- ],
61
- [
62
  'short_name' => 'iCal Importer',
63
  'class' => 'Tribe__Events__Ical_Importer__Main',
64
- 'thickbox_url' => 'https://theeventscalendar.com/product/ical-importer/?TB_iframe=true',
65
- ],
66
- [
67
  'short_name' => 'Eventbrite Tickets',
68
  'class' => 'Tribe__Events__Tickets__Eventbrite__Main',
69
- 'thickbox_url' => 'https://theeventscalendar.com/product/wordpress-eventbrite-tickets/?TB_iframe=true',
70
- ],
71
- [
72
  'short_name' => 'Advanced Post Manager',
73
  'class' => 'Tribe_APM',
74
  'thickbox_url' => 'plugin-install.php?tab=plugin-information&plugin=advanced-post-manager&TB_iframe=true',
75
- ],
76
- ];
77
 
78
  /**
79
  * Searches the plugin list for key/value pair and return the full details for that plugin
@@ -84,7 +84,7 @@ if ( ! class_exists( 'Tribe__Plugins' ) ) {
84
  * @return array|null
85
  */
86
  public function get_plugin_by_key( $search_key, $search_val ) {
87
- foreach ( $this->get_list() as $plugin ) {
88
  if ( isset( $plugin[ $search_key ] ) && $plugin[ $search_key ] === $search_val ) {
89
  return $plugin;
90
  }
@@ -131,37 +131,5 @@ if ( ! class_exists( 'Tribe__Plugins' ) ) {
131
  return apply_filters( 'tribe_plugins_get_list', $this->tribe_plugins );
132
  }
133
 
134
- /**
135
- * Checks if given plugin is active. Usually a Modern Tribe plugin.
136
- *
137
- * @param string $plugin_name The name of the plugin. Each plugin defines their name upon hooking on the filter.
138
- *
139
- * @since 4.12.1
140
- *
141
- * @return bool True if plugin is active. False if plugin is not active.
142
- */
143
- public static function is_active( $plugin_name ) {
144
- if ( ! did_action( "plugins_loaded" ) ) {
145
- _doing_it_wrong(
146
- __METHOD__,
147
- __( 'Using this function before "plugins_loaded" action has fired can return unreliable results.', 'tribe-common' ),
148
- 'TBD'
149
- );
150
- }
151
-
152
- /**
153
- * Filters the array that each Tribe plugin overrides to
154
- * set itself as active when this function is called.
155
- *
156
- * @example [ 'the-events-calendar' => true, 'event-tickets' => true ]
157
- *
158
- * @since 4.12.1
159
- *
160
- * @return array Plugin slugs as keys and bool as value for whether it's active or not.
161
- */
162
- $plugins = apply_filters( 'tribe_active_plugins', [] );
163
-
164
- return isset( $plugins[ $plugin_name ] ) && tribe_is_truthy( $plugins[ $plugin_name ] );
165
- }
166
  }
167
  }
9
  class Tribe__Plugins {
10
 
11
  /**
12
+ * A list of tribe plugin's details in this format:
13
  *
14
+ * array(
15
  * 'short_name' => Common name for the plugin, used in places such as WP Admin messages
16
  * 'class' => Main plugin class
17
  * 'thickbox_url' => Download or purchase URL for plugin from within /wp-admin/ thickbox
18
+ * )
19
  */
20
+ private $tribe_plugins = array(
21
+ array(
22
  'short_name' => 'Event Tickets',
23
  'class' => 'Tribe__Tickets__Main',
24
  'thickbox_url' => 'plugin-install.php?tab=plugin-information&plugin=event-tickets&TB_iframe=true',
25
+ ),
26
+ array(
27
  'short_name' => 'Event Tickets Plus',
28
  'class' => 'Tribe__Tickets_Plus__Main',
29
+ 'thickbox_url' => '//theeventscalendar.com/product/wordpress-event-tickets-plus/?TB_iframe=true',
30
+ ),
31
+ array(
32
  'short_name' => 'The Events Calendar',
33
  'class' => 'Tribe__Events__Main',
34
  'thickbox_url' => 'plugin-install.php?tab=plugin-information&plugin=the-events-calendar&TB_iframe=true',
35
+ ),
36
+ array(
37
  'short_name' => 'Events Calendar Pro',
38
  'class' => 'Tribe__Events__Pro__Main',
39
+ 'thickbox_url' => '//theeventscalendar.com/product/wordpress-events-calendar-pro/?TB_iframe=true',
40
+ ),
41
+ array(
42
  'short_name' => 'Community Events',
43
  'class' => 'Tribe__Events__Community__Main',
44
+ 'thickbox_url' => '//theeventscalendar.com/product/wordpress-community-events/?TB_iframe=true',
45
+ ),
46
+ array(
47
  'short_name' => 'Community Tickets',
48
  'class' => 'Tribe__Events__Community__Tickets__Main',
49
+ 'thickbox_url' => '//theeventscalendar.com/product/community-tickets/?TB_iframe=true',
50
+ ),
51
+ array(
52
  'short_name' => 'Filter Bar',
53
  'class' => 'Tribe__Events__Filterbar__View',
54
+ 'thickbox_url' => '//theeventscalendar.com/product/wordpress-events-filterbar/?TB_iframe=true',
55
+ ),
56
+ array(
57
  'short_name' => 'Facebook Events',
58
  'class' => 'Tribe__Events__Facebook__Importer',
59
+ 'thickbox_url' => '//theeventscalendar.com/product/facebook-events/?TB_iframe=true',
60
+ ),
61
+ array(
62
  'short_name' => 'iCal Importer',
63
  'class' => 'Tribe__Events__Ical_Importer__Main',
64
+ 'thickbox_url' => '//theeventscalendar.com/product/ical-importer/?TB_iframe=true',
65
+ ),
66
+ array(
67
  'short_name' => 'Eventbrite Tickets',
68
  'class' => 'Tribe__Events__Tickets__Eventbrite__Main',
69
+ 'thickbox_url' => '//theeventscalendar.com/product/wordpress-eventbrite-tickets/?TB_iframe=true',
70
+ ),
71
+ array(
72
  'short_name' => 'Advanced Post Manager',
73
  'class' => 'Tribe_APM',
74
  'thickbox_url' => 'plugin-install.php?tab=plugin-information&plugin=advanced-post-manager&TB_iframe=true',
75
+ ),
76
+ );
77
 
78
  /**
79
  * Searches the plugin list for key/value pair and return the full details for that plugin
84
  * @return array|null
85
  */
86
  public function get_plugin_by_key( $search_key, $search_val ) {
87
+ foreach ( $this->tribe_plugins as $plugin ) {
88
  if ( isset( $plugin[ $search_key ] ) && $plugin[ $search_key ] === $search_val ) {
89
  return $plugin;
90
  }
131
  return apply_filters( 'tribe_plugins_get_list', $this->tribe_plugins );
132
  }
133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  }
135
  }
common/src/Tribe/Plugins_API.php CHANGED
@@ -24,206 +24,121 @@ class Tribe__Plugins_API {
24
  'the-events-calendar' => array(
25
  'title' => __( 'The Events Calendar', 'tribe-common' ),
26
  'slug' => 'the-events-calendar',
27
- 'link' => 'https://m.tri.be/1ai-',
28
- 'description' => __( 'Our flagship free calendar', 'tribe-common' ),
29
- 'features' => [
30
- __( 'Customizable', 'tribe-common' ),
31
- __( 'Import & export events', 'tribe-common' ),
32
- __( 'Timezone support', 'tribe-common' ),
33
- __( 'Multiple views', 'tribe-common' ),
34
- ],
35
- 'image' => 'images/shop/calendar.jpg',
36
- 'logo' => 'images/logo/the-events-calendar.svg',
37
  'is_installed' => class_exists( 'Tribe__Events__Main' ),
38
- 'free' => true,
39
- 'active_installs' => 800000,
40
  ),
41
  'event-aggregator' => array(
42
  'title' => __( 'Event Aggregator', 'tribe-common' ),
43
  'slug' => 'event-aggregator',
44
- 'link' => 'https://m.tri.be/1aj0',
45
- 'description' => __( 'Automated imports for your calendar', 'tribe-common' ),
46
- 'features' => [
47
- __( 'Schedule automated imports', 'tribe-common' ),
48
- __( 'Customizable', 'tribe-common' ),
49
- __( 'Works with Google Calendar, Meetup, and more', 'tribe-common' ),
50
- __( 'Refine by date, location, or keyword', 'tribe-common' ),
51
- ],
52
- 'image' => 'images/shop/aggregator.jpg',
53
- 'logo' => 'images/logo/event-aggregator.svg',
54
  'is_installed' => class_exists( 'Tribe__Events__Aggregator' ) && Tribe__Events__Aggregator::is_service_active(),
55
- 'free' => false,
56
  'active_installs' => 20000,
57
  ),
58
  'events-calendar-pro' => array(
59
  'title' => __( 'Events Calendar PRO', 'tribe-common' ),
60
  'slug' => 'events-calendar-pro',
61
- 'link' => 'https://m.tri.be/1ai-',
62
- 'description' => __( 'Power up your calendar with Pro', 'tribe-common' ),
63
- 'features' => [
64
- __( 'Premium support', 'tribe-common' ),
65
- __( 'Recurring events', 'tribe-common' ),
66
- __( 'Additional views', 'tribe-common' ),
67
- __( 'Shortcodes', 'tribe-common' ),
68
- ],
69
- 'image' => 'images/shop/pro.jpg',
70
- 'logo' => 'images/logo/events-calendar-pro.svg',
71
  'is_installed' => class_exists( 'Tribe__Events__Pro__Main' ),
72
- 'free' => false,
73
  'active_installs' => 100000,
74
  ),
75
  'event-tickets' => array(
76
  'title' => __( 'Event Tickets', 'tribe-common' ),
77
  'slug' => 'event-tickets',
78
- 'link' => 'https://m.tri.be/1aj1',
79
- 'description' => __( 'Manage ticketing and RSVPs', 'tribe-common' ),
80
- 'features' => [
81
- __( 'Add tickets and RSVP to any post', 'tribe-common' ),
82
- __( 'Paypal integration', 'tribe-common' ),
83
- __( 'Attendee reports', 'tribe-common' ),
84
- __( 'Customizable ticket template', 'tribe-common' ),
85
- ],
86
- 'image' => 'images/shop/tickets.jpg',
87
- 'logo' => 'images/logo/event-tickets.svg',
88
  'is_installed' => class_exists( 'Tribe__Tickets__Main' ),
89
- 'free' => true,
90
  'active_installs' => 20000,
91
  ),
92
  'event-tickets-plus' => array(
93
  'title' => __( 'Event Tickets Plus', 'tribe-common' ),
94
  'slug' => 'event-tickets-plus',
95
- 'link' => 'http://m.tri.be/1aj1',
96
- 'description' => __( 'Monetize your events', 'tribe-common' ),
97
- 'features' => [
98
- __( 'Custom registration fields', 'tribe-common' ),
99
- __( 'WooCommerce compatibility', 'tribe-common' ),
100
- __( 'Ticket scanning with mobile app', 'tribe-common' ),
101
- __( 'Custom attendee registration fields', 'tribe-common' ),
102
- ],
103
- 'image' => 'images/shop/tickets-plus.jpg',
104
- 'logo' => 'images/logo/event-tickets-plus.svg',
105
  'is_installed' => class_exists( 'Tribe__Tickets_Plus__Main' ),
106
- 'free' => false,
107
  'active_installs' => 10000,
108
  ),
109
  'promoter' => array(
110
  'title' => __( 'Promoter', 'tribe-common' ),
111
  'slug' => 'promoter',
112
- 'link' => 'https://m.tri.be/1acy',
113
- 'description' => __( 'An email marketing solution for events and the people running them', 'tribe-common' ),
114
- 'features' => [
115
- __( 'Automate email touchpoints', 'tribe-common' ),
116
- __( 'Customize email templates', 'tribe-common' ),
117
- __( 'Streamline your email process', 'tribe-common' ),
118
- __( 'Segment your attendee lists', 'tribe-common' ),
119
- ],
120
- 'image' => 'images/shop/promoter.jpg',
121
- 'logo' => 'images/logo/promoter.svg',
122
- 'is_installed' => tribe( 'promoter.pue' )->has_license_key(),
123
- 'free' => false,
124
  'active_installs' => 1000,
125
  ),
126
  'tribe-filterbar' => array(
127
  'title' => __( 'Filter Bar', 'tribe-common' ),
128
  'slug' => 'tribe-filterbar',
129
- 'link' => 'https://m.tri.be/19o6',
130
- 'description' => __( 'Help users find exactly the right event', 'tribe-common' ),
131
- 'features' => [
132
- __( 'Configurable set of filters', 'tribe-common' ),
133
- __( 'Horizontal or vertical', 'tribe-common' ),
134
- __( 'Filter category, price, and more', 'tribe-common' ),
135
- __( 'Filter distance (for Events Calendar Pro)', 'tribe-common' ),
136
- ],
137
- 'image' => 'images/shop/filter-bar.jpg',
138
- 'logo' => 'images/logo/filterbar.svg',
139
  'is_installed' => class_exists( 'Tribe__Events__Filterbar__View' ),
140
- 'free' => false,
141
  'active_installs' => 20000,
142
  ),
143
  'events-community' => array(
144
  'title' => __( 'Community Events', 'tribe-common' ),
145
  'slug' => 'events-community',
146
- 'link' => 'https://m.tri.be/19o7',
147
- 'description' => __( 'Users submit events to your calendar', 'tribe-common' ),
148
- 'features' => [
149
- __( 'Publishing Control', 'tribe-common' ),
150
- __( 'Event Submission Form', 'tribe-common' ),
151
- __( 'Registered User Settings', 'tribe-common' ),
152
- __( 'Email notifications', 'tribe-common' ),
153
- ],
154
- 'image' => 'images/shop/community.jpg',
155
- 'logo' => 'images/logo/community-events.svg',
156
  'is_installed' => class_exists( 'Tribe__Events__Community__Main' ),
157
- 'free' => false,
158
  'active_installs' => 20000,
159
  ),
160
  'events-community-tickets' => array(
161
  'title' => __( 'Community Tickets', 'tribe-common' ),
162
  'slug' => 'events-community-tickets',
163
- 'link' => 'https://m.tri.be/19o8',
164
- 'description' => __( 'Run your own events marketplace', 'tribe-common' ),
165
- 'features' => [
166
- __( 'Users submit events and sell tickets', 'tribe-common' ),
167
- __( 'Split commission with users', 'tribe-common' ),
168
- __( 'No admin access required', 'tribe-common' ), /* code review: fail this */
169
- __( 'Sales reporting', 'tribe-common' ),
170
- ],
171
- 'requires' => _x( 'Event Tickets Plus and Community Events', 'Names of required plugins for Community Tickets', 'tribe-common' ),
172
- 'image' => 'images/shop/community-tickets.jpg',
173
- 'logo' => 'images/logo/community-tickets.svg',
174
  'is_installed' => class_exists( 'Tribe__Events__Community__Tickets__Main' ),
175
- 'free' => false,
176
  'active_installs' => 10000,
177
  ),
178
  'tribe-eventbrite' => array(
179
  'title' => __( 'Eventbrite Tickets', 'tribe-common' ),
180
  'slug' => 'tribe-eventbrite',
181
- 'link' => 'https://m.tri.be/19o9',
182
- 'description' => __( 'Unite the power of TEC with the ticketing of Eventbrite', 'tribe-common' ),
183
- 'features' => [
184
- __( 'Manage tickets from WordPress', 'tribe-common' ),
185
- __( 'Ticket availability automatically updates', 'tribe-common' ),
186
- __( 'Integrated with your events on Eventbrite', 'tribe-common' ),
187
- __( 'Automatically import your events', 'tribe-common' ),
188
- ],
189
- 'image' => 'images/shop/eventbrite.jpg',
190
- 'logo' => 'images/logo/eventbrite-tickets.svg',
191
  'is_installed' => class_exists( 'Tribe__Events__Tickets__Eventbrite__Main' ),
192
- 'free' => false,
193
  'active_installs' => 20000,
194
  ),
195
  'image-widget-plus' => array(
196
  'title' => __( 'Image Widget Plus', 'tribe-common' ),
197
  'slug' => 'image-widget-plus',
198
- 'link' => 'https://m.tri.be/19nv',
199
- 'description' => __( 'Beautiful display options for your favorite photos.', 'tribe-common' ),
200
- 'features' => [
201
- __( 'Multi-Image Support', 'tribe-common' ),
202
- __( 'Lightbox', 'tribe-common' ),
203
- __( 'Slideshow', 'tribe-common' ),
204
- __( 'Random Images', 'tribe-common' ),
205
- ],
206
- 'image' => 'images/shop/image-widget-plus.jpg',
207
- 'logo' => 'images/logo/image-widget-plus.svg',
208
  'is_installed' => class_exists( 'Tribe__Image__Plus__Main' ),
209
- 'free' => false,
210
- 'active_installs' => 2500,
211
- ),
212
- 'events-virtual' => array(
213
- 'title' => __( 'Virtual Events', 'tribe-common' ),
214
- 'slug' => 'events-virtual',
215
- 'link' => 'http://m.tri.be/19nv', /* CODE REVIEW: this link needs updating */
216
- 'description' => __( '[ADD DESCRIPTION]', 'tribe-common' ), /* CODE REVIEW: this desciption needs updating */
217
- 'features' => [
218
- __( 'Feature', 'tribe-common' ),
219
- __( 'Feature', 'tribe-common' ),
220
- __( 'Feature', 'tribe-common' ),
221
- __( 'Feature', 'tribe-common' ),
222
- ],
223
- 'image' => 'images/shop/virtual-events.jpg',
224
- 'logo' => 'images/logo/virtual-events.svg',
225
- 'is_installed' => defined( 'EVENTS_VIRTUAL_FILE' ),
226
- 'free' => false,
227
  'active_installs' => 2500,
228
  ),
229
  );
24
  'the-events-calendar' => array(
25
  'title' => __( 'The Events Calendar', 'tribe-common' ),
26
  'slug' => 'the-events-calendar',
27
+ 'link' => null,
28
+ 'description' => __( 'Create an events calendar and manage it with ease. The Events Calendar plugin provides professional-level quality and features backed by a team you can trust.', 'tribe-common' ),
29
+ 'image' => 'https://ps.w.org/the-events-calendar/assets/icon-128x128.png?rev=1342379',
 
 
 
 
 
 
 
30
  'is_installed' => class_exists( 'Tribe__Events__Main' ),
31
+ 'active_installs' => 500000,
 
32
  ),
33
  'event-aggregator' => array(
34
  'title' => __( 'Event Aggregator', 'tribe-common' ),
35
  'slug' => 'event-aggregator',
36
+ 'link' => 'https://theeventscalendar.com/product/event-aggregator/?utm_campaign=in-app&utm_source=addonspage&utm_medium=event-aggregator&utm_content=appstoreembedded-1',
37
+ 'description' => __( 'Event Aggregator adds massive import functionality to your calendar. Before you know it, you’ll be importing events from Meetup, Eventbrite, Google Calendar, iCalendar, and other URLs with ease. Schedule imports to run automatically behind-the-scenes or run them manually when you’re ready. Go ahead and import to your heart’s content—Event Aggregator hooks you up with a central dashboard in the admin to make managing your imports a breeze.', 'tribe-common' ),
38
+ 'image' => 'images/app-shop-ical.jpg',
 
 
 
 
 
 
 
39
  'is_installed' => class_exists( 'Tribe__Events__Aggregator' ) && Tribe__Events__Aggregator::is_service_active(),
 
40
  'active_installs' => 20000,
41
  ),
42
  'events-calendar-pro' => array(
43
  'title' => __( 'Events Calendar PRO', 'tribe-common' ),
44
  'slug' => 'events-calendar-pro',
45
+ 'link' => 'https://theeventscalendar.com/product/wordpress-events-calendar-pro/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-events-calendar-pro&utm_content=appstoreembedded-1',
46
+ 'buy-now' => 'http://m.tri.be/19o4',
47
+ 'description' => sprintf(
48
+ __( 'The Events Calendar PRO is a paid Add-On to our open source WordPress plugin %1$sThe Events Calendar%2$s. PRO offers a whole host of calendar features including recurring events, custom event attributes, saved venues and organizers, venue pages, advanced event admin and lots more.', 'tribe-common' ),
49
+ '<a href="http://m.tri.be/18vc">',
50
+ '</a>'
51
+ ),
52
+ 'image' => 'images/app-shop-pro.jpg',
 
 
53
  'is_installed' => class_exists( 'Tribe__Events__Pro__Main' ),
 
54
  'active_installs' => 100000,
55
  ),
56
  'event-tickets' => array(
57
  'title' => __( 'Event Tickets', 'tribe-common' ),
58
  'slug' => 'event-tickets',
59
+ 'link' => null,
60
+ 'description' => __( 'Event Tickets provides a simple way for visitors to RSVP to your events. As a standalone plugin, it enables you to add RSVP functionality to posts or pages. When paired with The Events Calendar, you can add that same RSVP functionality directly to your event listings.', 'tribe-common' ),
61
+ 'image' => 'https://ps.w.org/event-tickets/assets/icon-128x128.png?rev=1299138',
 
 
 
 
 
 
 
62
  'is_installed' => class_exists( 'Tribe__Tickets__Main' ),
 
63
  'active_installs' => 20000,
64
  ),
65
  'event-tickets-plus' => array(
66
  'title' => __( 'Event Tickets Plus', 'tribe-common' ),
67
  'slug' => 'event-tickets-plus',
68
+ 'link' => 'https://theeventscalendar.com/product/wordpress-event-tickets-plus/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-event-tickets-plus&utm_content=appstoreembedded-1',
69
+ 'buy-now' => 'http://m.tri.be/19o5',
70
+ 'description' => sprintf(
71
+ __( 'Event Tickets Plus allows you to sell tickets to your events using WooCommerce, Easy Digital Downloads, or our built in Tribe Commerce tool. Add tickets to your posts and pages, or add %1$sThe Events Calendar%2$s and sell tickets from your event listings. Create custom registration forms, manage attendees, use custom capacity options, and more. Guest check in is easy with QR codes and our custom scanning app.', 'tribe-common' ),
72
+ '<a href="http://m.tri.be/18vc">',
73
+ '</a>'
74
+ ),
75
+ 'image' => 'images/app-shop-tickets-plus.jpg',
 
 
76
  'is_installed' => class_exists( 'Tribe__Tickets_Plus__Main' ),
 
77
  'active_installs' => 10000,
78
  ),
79
  'promoter' => array(
80
  'title' => __( 'Promoter', 'tribe-common' ),
81
  'slug' => 'promoter',
82
+ 'link' => 'https://theeventscalendar.com/product/promoter/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-events-promoter&utm_content=appstoreembedded-1',
83
+ 'buy-now' => 'http://m.tri.be/1acy',
84
+ 'description' => __( 'With Promoter, you’ll connect with your community via email through every stage of your event, bolster event attendance, and manage notifications more efficiently than ever. Increase event attendance and engagement by automatically sending reminders for on-sale dates, event times and more.', 'tribe-common' ),
85
+ 'image' => 'images/app-shop-promoter.jpg',
86
+ 'is_installed' => false,
 
 
 
 
 
 
 
87
  'active_installs' => 1000,
88
  ),
89
  'tribe-filterbar' => array(
90
  'title' => __( 'Filter Bar', 'tribe-common' ),
91
  'slug' => 'tribe-filterbar',
92
+ 'link' => 'https://theeventscalendar.com/product/wordpress-events-filterbar/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-events-filterbar&utm_content=appstoreembedded-1',
93
+ 'buy-now' => 'http://m.tri.be/19o6',
94
+ 'description' => __( 'It is awesome that your calendar is <em>THE PLACE</em> to get hooked up with prime choice ways to spend time. You have more events than Jabba the Hutt has rolls. Too bad visitors are hiring a personal assistant to go through all the choices. Ever wish you could just filter the calendar to only show events in walking distance, on a weekend, that are free? BOOM. Now you can. Introducing… the Filter Bar.', 'tribe-common' ),
95
+ 'image' => 'images/app-shop-filter-bar.jpg',
 
 
 
 
 
 
96
  'is_installed' => class_exists( 'Tribe__Events__Filterbar__View' ),
 
97
  'active_installs' => 20000,
98
  ),
99
  'events-community' => array(
100
  'title' => __( 'Community Events', 'tribe-common' ),
101
  'slug' => 'events-community',
102
+ 'link' => 'https://theeventscalendar.com/product/wordpress-community-events/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-community-events&utm_content=appstoreembedded-1',
103
+ 'buy-now' => 'http://m.tri.be/19o7',
104
+ 'description' => __( 'Accept user-submitted events on your site! With Community Events, you can accept public submissions or require account sign-on. Settings give you the options to save as a draft or publish automatically, enable categories and tags, and choose whether users can edit/manage their own events or simply submit. Best of all - setup is easy! Just activate, configure the options, and off you go.', 'tribe-common' ),
105
+ 'image' => 'images/app-shop-community.jpg',
 
 
 
 
 
 
106
  'is_installed' => class_exists( 'Tribe__Events__Community__Main' ),
 
107
  'active_installs' => 20000,
108
  ),
109
  'events-community-tickets' => array(
110
  'title' => __( 'Community Tickets', 'tribe-common' ),
111
  'slug' => 'events-community-tickets',
112
+ 'link' => 'https://theeventscalendar.com/product/community-tickets/?utm_campaign=in-app&utm_source=addonspage&utm_medium=community-tickets&utm_content=appstoreembedded-1',
113
+ 'buy-now' => 'http://m.tri.be/19o8',
114
+ 'description' => __( 'Enable Community Events organizers to offer tickets to their events. You can set flexible payment and fee options. They can even check-in attendees to their events! All of this managed from the front-end of your site without ever needing to grant access to your admin', 'tribe-common' ),
115
+ 'requires' => _x( 'Event Tickets Plus and Community Events', 'Names of required plugins for Community Tickets', 'tribe-common' ),
116
+ 'image' => 'images/app-shop-community-tickets.jpg',
 
 
 
 
 
 
117
  'is_installed' => class_exists( 'Tribe__Events__Community__Tickets__Main' ),
 
118
  'active_installs' => 10000,
119
  ),
120
  'tribe-eventbrite' => array(
121
  'title' => __( 'Eventbrite Tickets', 'tribe-common' ),
122
  'slug' => 'tribe-eventbrite',
123
+ 'link' => 'https://theeventscalendar.com/product/wordpress-eventbrite-tickets/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-eventbrite-tickets&utm_content=appstoreembedded-1',
124
+ 'buy-now' => 'http://m.tri.be/19o9',
125
+ 'description' => sprintf(
126
+ __( 'The Eventbrite Tickets add-on allows you to create & sell tickets through The Events Calendar using the power of %1$sEventbrite%2$s. Whether you’re creating your ticket on the WordPress dashboard or importing the details of an already-existing event from %1$sEventbrite.com%2$s, this add-on brings the power of the Eventbrite API to your calendar.', 'tribe-common' ),
127
+ '<a href="http://www.eventbrite.com/r/etp">',
128
+ '</a>'
129
+ ),
130
+ 'image' => 'images/app-shop-eventbrite.jpg',
 
 
131
  'is_installed' => class_exists( 'Tribe__Events__Tickets__Eventbrite__Main' ),
 
132
  'active_installs' => 20000,
133
  ),
134
  'image-widget-plus' => array(
135
  'title' => __( 'Image Widget Plus', 'tribe-common' ),
136
  'slug' => 'image-widget-plus',
137
+ 'link' => 'http://m.tri.be/19nv',
138
+ 'buy-now' => 'http://m.tri.be/19oa',
139
+ 'description' => __( 'Take your image widgets to the next level with Image Widget Plus! We\'ve taken the simple functionality of our basic Image Widget and amped it up with several popular feature requests - multiple image support, slideshow, lightbox, and random image - all backed by a full year of premium support.', 'tribe-common' ),
140
+ 'image' => 'images/app-shop-image-widget-plus.jpg',
 
 
 
 
 
 
141
  'is_installed' => class_exists( 'Tribe__Image__Plus__Main' ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  'active_installs' => 2500,
143
  ),
144
  );
common/src/Tribe/Post_Transient.php CHANGED
@@ -29,7 +29,6 @@ class Tribe__Post_Transient {
29
  * @param int $post_id The Post ID, can also be a WP_Post
30
  * @param string $transient Post Meta to Fetch
31
  *
32
- * @return mixed Value stored on the Post Transient.
33
  */
34
  public function get( $post_id, $transient ) {
35
  global $_wp_using_ext_object_cache;
@@ -98,11 +97,10 @@ class Tribe__Post_Transient {
98
  *
99
  * @since 4.1
100
  *
101
- * @param int $post_id The Post ID, can also be a WP_Post.
102
- * @param string $transient Post Meta to Delete.
103
- * @param string $value Only delete if the value Matches.
104
  *
105
- * @return boolean If we were able to delete the transient.
106
  */
107
  public function delete( $post_id, $transient, $value = null ) {
108
  global $_wp_using_ext_object_cache;
@@ -151,16 +149,15 @@ class Tribe__Post_Transient {
151
  }
152
 
153
  /**
154
- * Sets a new value for the Transient.
155
  *
156
  * @since 4.1
157
  *
158
- * @param int $post_id The Post ID, can also be a WP_Post.
159
- * @param string $transient Post Meta to set.
160
- * @param string $value Only delete if the value Matches.
161
- * @param int $expiration How long this transient will be valid, in seconds.
162
  *
163
- * @return int|false Meta ID on success, false on failure.
164
  */
165
  public function set( $post_id, $transient, $value, $expiration = 0 ) {
166
  global $_wp_using_ext_object_cache;
@@ -211,4 +208,6 @@ class Tribe__Post_Transient {
211
 
212
  return $result;
213
  }
 
 
214
  }
29
  * @param int $post_id The Post ID, can also be a WP_Post
30
  * @param string $transient Post Meta to Fetch
31
  *
 
32
  */
33
  public function get( $post_id, $transient ) {
34
  global $_wp_using_ext_object_cache;
97
  *
98
  * @since 4.1
99
  *
100
+ * @param int $post_id The Post ID, can also be a WP_Post
101
+ * @param string $transient Post Meta to Delete
102
+ * @param string $value Only delete if the value Matches
103
  *
 
104
  */
105
  public function delete( $post_id, $transient, $value = null ) {
106
  global $_wp_using_ext_object_cache;
149
  }
150
 
151
  /**
152
+ * Sets a new value for the Transient
153
  *
154
  * @since 4.1
155
  *
156
+ * @param int $post_id The Post ID, can also be a WP_Post
157
+ * @param string $transient Post Meta to set
158
+ * @param string $value Only delete if the value Matches
159
+ * @param int $expiration How long this transient will be valid, in seconds
160
  *
 
161
  */
162
  public function set( $post_id, $transient, $value, $expiration = 0 ) {
163
  global $_wp_using_ext_object_cache;
208
 
209
  return $result;
210
  }
211
+
212
+
213
  }
common/src/Tribe/Process/Handler.php CHANGED
@@ -147,22 +147,6 @@ abstract class Tribe__Process__Handler {
147
 
148
  check_ajax_referer( $this->identifier, 'nonce' );
149
 
150
- // Let's make sure to hydrate date from the request if not set.
151
- if ( count( array_filter( $data_source ) ) < 1 ) {
152
- $data_source = $_POST;
153
- }
154
-
155
- do_action(
156
- 'tribe_log',
157
- 'debug',
158
- $this->identifier,
159
- [
160
- 'action' => 'async_handling',
161
- 'data_source' => $data_source,
162
- 'payload' => $_POST,
163
- ]
164
- );
165
-
166
  $this->handle( $data_source );
167
 
168
  wp_die();
@@ -175,12 +159,6 @@ abstract class Tribe__Process__Handler {
175
  */
176
  wp_clear_scheduled_hook( $this->healthcheck_cron_hook_id, [ $data_source ] );
177
 
178
- do_action(
179
- 'tribe_log',
180
- 'debug',
181
- $this->identifier,
182
- array_merge( [ 'action' => 'cron_handling' ], $data_source ) );
183
-
184
  $this->handle( $data_source );
185
  }
186
 
@@ -198,8 +176,6 @@ abstract class Tribe__Process__Handler {
198
  ( defined( 'TRIBE_NO_ASYNC' ) && true === TRIBE_NO_ASYNC )
199
  || true === (bool) getenv( 'TRIBE_NO_ASYNC' )
200
  ) {
201
- do_action( 'tribe_log', 'debug', $this->identifier, [ 'action' => 'sync_handle', 'data' => $this->data ] );
202
-
203
  return $this->sync_handle( $this->data );
204
  }
205
 
@@ -207,8 +183,6 @@ abstract class Tribe__Process__Handler {
207
  $url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
208
  $args = $this->get_post_args();
209
 
210
- do_action( 'tribe_log', 'debug', $this->identifier, [ 'action' => 'async_dispatch', 'data' => $this->data ] );
211
-
212
  return wp_remote_post( esc_url_raw( $url ), $args );
213
  }
214
 
@@ -227,21 +201,7 @@ abstract class Tribe__Process__Handler {
227
  $class = get_class( $this );
228
  $src = call_user_func( [ $class, 'action' ] );
229
  $logger->log( 'Could not schedule event for cron-based handling', Tribe__Log::ERROR, $src );
230
-
231
- do_action(
232
- 'tribe_log',
233
- 'error',
234
- $this->identifier,
235
- [ 'action' => 'schedule_cron', 'data' => $this->data ]
236
- );
237
  }
238
-
239
- do_action(
240
- 'tribe_log',
241
- 'debug',
242
- $this->identifier,
243
- [ 'action' => 'schedule_cron', 'data' => $this->data ]
244
- );
245
  }
246
 
247
  return true;
147
 
148
  check_ajax_referer( $this->identifier, 'nonce' );
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  $this->handle( $data_source );
151
 
152
  wp_die();
159
  */
160
  wp_clear_scheduled_hook( $this->healthcheck_cron_hook_id, [ $data_source ] );
161
 
 
 
 
 
 
 
162
  $this->handle( $data_source );
163
  }
164
 
176
  ( defined( 'TRIBE_NO_ASYNC' ) && true === TRIBE_NO_ASYNC )
177
  || true === (bool) getenv( 'TRIBE_NO_ASYNC' )
178
  ) {
 
 
179
  return $this->sync_handle( $this->data );
180
  }
181
 
183
  $url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
184
  $args = $this->get_post_args();
185
 
 
 
186
  return wp_remote_post( esc_url_raw( $url ), $args );
187
  }
188
 
201
  $class = get_class( $this );
202
  $src = call_user_func( [ $class, 'action' ] );
203
  $logger->log( 'Could not schedule event for cron-based handling', Tribe__Log::ERROR, $src );
 
 
 
 
 
 
 
204
  }
 
 
 
 
 
 
 
205
  }
206
 
207
  return true;
common/src/Tribe/Process/Post_Thumbnail_Setter.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  /**
4
  * Class Tribe__Process__Post_Thumbnail_Setter
 
5
  *
6
  * Handles upload and setting of a post thumbnail in an async process.
7
  * Example usage:
@@ -41,14 +42,7 @@ class Tribe__Process__Post_Thumbnail_Setter extends Tribe__Process__Handler {
41
  throw new InvalidArgumentException( 'Post ID and featured image should be set before trying to dispatch.' );
42
  }
43
 
44
- $data = [
45
- 'post_id' => $this->post_id,
46
- 'post_thumbnail' => trim( $this->post_thumbnail ),
47
- ];
48
-
49
- $this->data( $data );
50
-
51
- do_action( 'tribe_log', 'debug', $this->identifier, $data );
52
 
53
  return parent::dispatch();
54
  }
@@ -85,11 +79,10 @@ class Tribe__Process__Post_Thumbnail_Setter extends Tribe__Process__Handler {
85
  *
86
  * @since 4.7.12
87
  *
88
- * @param array|null $data_source An optional source of data.
89
- *
90
  * @see Tribe__Process__Post_Thumbnail_Setter::sync_handle()
91
  *
92
- * @see tribe_upload_image()
93
  */
94
  protected function handle( array $data_source = null ) {
95
  $this->sync_handle( $data_source );
@@ -99,77 +92,40 @@ class Tribe__Process__Post_Thumbnail_Setter extends Tribe__Process__Handler {
99
  * {@inheritdoc}
100
  */
101
  public function sync_handle( array $data_source = null ) {
102
- do_action( 'tribe_log', 'debug', $this->identifier, [ 'status' => 'handling request' ] );
 
 
 
 
103
 
104
  $data_source = isset( $data_source ) ? $data_source : $_POST;
105
 
106
  if ( ! isset( $data_source['post_id'], $data_source['post_thumbnail'] ) ) {
107
- do_action( 'tribe_log', 'error', $this->identifier, [ 'data' => $data_source, ] );
108
-
109
  return 0;
110
  }
111
 
112
  $id = filter_var( $data_source['post_id'], FILTER_SANITIZE_NUMBER_INT );
113
  $post_thumbnail = filter_var( $data_source['post_thumbnail'], FILTER_SANITIZE_STRING );
114
 
115
- do_action( 'tribe_log', 'debug', $this->identifier, [
116
- 'status' => 'fetching thumbnail',
117
- 'post_thumbnail' => $post_thumbnail,
118
- 'post_id' => $id,
119
- ] );
120
 
121
  $thumbnail_id = tribe_upload_image( $post_thumbnail );
122
 
123
  if ( false === $thumbnail_id ) {
124
- do_action(
125
- 'tribe_log',
126
- 'error',
127
- $this->identifier,
128
- [
129
- 'action' => 'fetch',
130
- 'post_thumbnail' => $post_thumbnail,
131
- 'post_id' => $id,
132
- 'status' => 'could not fetch',
133
- ]
134
- );
135
 
136
  return 0;
137
  }
138
 
139
- $set = true;
140
- if ( (int) get_post_thumbnail_id( $id ) !== (int) $thumbnail_id ) {
141
- $set = set_post_thumbnail( $id, $thumbnail_id );
142
- }
143
 
144
  if ( false === $set ) {
145
- do_action(
146
- 'tribe_log',
147
- 'error',
148
- $this->identifier,
149
- [
150
- 'action' => 'set',
151
- 'post_thumbnail' => $post_thumbnail,
152
- 'attachment_id' => $thumbnail_id,
153
- 'post_id' => $id,
154
- 'status' => 'unable to set thumbnail',
155
- ]
156
- );
157
 
158
  return $thumbnail_id;
159
  }
160
 
161
- do_action(
162
- 'tribe_log',
163
- 'debug',
164
- $this->identifier,
165
- [
166
- 'action' => 'set',
167
- 'post_thumbnail' => $post_thumbnail,
168
- 'attachment_id' => $thumbnail_id,
169
- 'post_id' => $id,
170
- 'status' => 'completed - attachment created and linked to the post',
171
- ]
172
- );
173
 
174
  return $thumbnail_id;
175
  }
2
 
3
  /**
4
  * Class Tribe__Process__Post_Thumbnail_Setter
5
+
6
  *
7
  * Handles upload and setting of a post thumbnail in an async process.
8
  * Example usage:
42
  throw new InvalidArgumentException( 'Post ID and featured image should be set before trying to dispatch.' );
43
  }
44
 
45
+ $this->data( array( 'post_id' => $this->post_id, 'post_thumbnail' => trim( $this->post_thumbnail ) ) );
 
 
 
 
 
 
 
46
 
47
  return parent::dispatch();
48
  }
79
  *
80
  * @since 4.7.12
81
  *
82
+ * @see tribe_upload_image()
 
83
  * @see Tribe__Process__Post_Thumbnail_Setter::sync_handle()
84
  *
85
+ * @param array|null $data_source An optional source of data.
86
  */
87
  protected function handle( array $data_source = null ) {
88
  $this->sync_handle( $data_source );
92
  * {@inheritdoc}
93
  */
94
  public function sync_handle( array $data_source = null ) {
95
+ /** @var Tribe__Log $logger */
96
+ $logger = tribe( 'logger' );
97
+ $log_src = 'Featured image setter';
98
+
99
+ $logger->log_debug( "(ID: {$this->identifier}) - handling request.", $log_src );
100
 
101
  $data_source = isset( $data_source ) ? $data_source : $_POST;
102
 
103
  if ( ! isset( $data_source['post_id'], $data_source['post_thumbnail'] ) ) {
 
 
104
  return 0;
105
  }
106
 
107
  $id = filter_var( $data_source['post_id'], FILTER_SANITIZE_NUMBER_INT );
108
  $post_thumbnail = filter_var( $data_source['post_thumbnail'], FILTER_SANITIZE_STRING );
109
 
110
+ $logger->log_debug( "(ID: {$this->identifier}) - fetching {$post_thumbnail} for post {$id}", $log_src );
 
 
 
 
111
 
112
  $thumbnail_id = tribe_upload_image( $post_thumbnail );
113
 
114
  if ( false === $thumbnail_id ) {
115
+ $logger->log_debug( "(ID: {$this->identifier}) - could not fetch {$post_thumbnail} for post {$id}, done.", $log_src );
 
 
 
 
 
 
 
 
 
 
116
 
117
  return 0;
118
  }
119
 
120
+ $set = set_post_thumbnail( $id, $thumbnail_id );
 
 
 
121
 
122
  if ( false === $set ) {
123
+ $logger->log_debug( "(ID: {$this->identifier}) - fetched {$post_thumbnail}, created attachment with ID {$thumbnail_id}, unable to set thumbnail for post {$id}, done.", $log_src );
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  return $thumbnail_id;
126
  }
127
 
128
+ $logger->log_debug( "(ID: {$this->identifier}) - fetched {$post_thumbnail}, created attachment with ID {$thumbnail_id}, set thumbnail for post {$id}, done.", $log_src );
 
 
 
 
 
 
 
 
 
 
 
129
 
130
  return $thumbnail_id;
131
  }
common/src/Tribe/Promoter/Auth.php CHANGED
@@ -23,19 +23,6 @@ class Tribe__Promoter__Auth {
23
  $this->connector = $connector;
24
  }
25
 
26
- /**
27
- * Add an update the KEY used for promoter during the connection.
28
- *
29
- * @since 4.9.12
30
- *
31
- * @param $secret_key
32
- *
33
- * @return string
34
- */
35
- public function filter_promoter_secret_key( $secret_key ) {
36
- return empty( $secret_key ) ? $this->generate_secret_key() : $secret_key;
37
- }
38
-
39
  /**
40
  * Authorize the request with the Promoter Connector.
41
  *
@@ -53,62 +40,18 @@ class Tribe__Promoter__Auth {
53
  }
54
 
55
  /**
56
- * Grab the WP constant and store it as the auth key, if none exists or is it empty
57
- * it creates a dynamic one.
58
- *
59
- * @since 4.9.12
60
  *
61
  * @return string The secret key.
62
  *
63
  * @since 4.9
64
  */
65
- public function generate_secret_key() {
66
- $key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
67
-
68
- if ( empty( $key ) ) {
69
- $key = $this->generate_key();
70
- }
71
 
72
  update_option( 'tribe_promoter_auth_key', $key );
73
 
74
  return $key;
75
  }
76
 
77
- /**
78
- * Create a custom key to be usead as tribe_promoter_auth_key
79
- *
80
- * @since 4.9.12
81
- *
82
- * @return string
83
- */
84
- private function generate_key() {
85
- $base = bin2hex( $this->get_random_byes() );
86
- $to_hash = sprintf( '%s%s%s', get_bloginfo( 'name' ), get_bloginfo( 'url' ), uniqid() );
87
- return $base . hash( 'md5', $to_hash );
88
- }
89
-
90
- /**
91
- * Add function to get a random set of bytes to be used as Token
92
- *
93
- * @since 4.9.12
94
- *
95
- * @param int $length
96
- *
97
- * @return string
98
- */
99
- private function get_random_byes( $length = 16 ) {
100
- if ( function_exists( 'random_bytes' ) ) {
101
- try {
102
- return random_bytes( $length );
103
- } catch ( Exception $e ) {
104
- return uniqid();
105
- }
106
- }
107
-
108
- if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {
109
- return openssl_random_pseudo_bytes( $length );
110
- }
111
-
112
- return uniqid();
113
- }
114
  }
23
  $this->connector = $connector;
24
  }
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  /**
27
  * Authorize the request with the Promoter Connector.
28
  *
40
  }
41
 
42
  /**
43
+ * Grab the WP constant and store it as the auth key.
 
 
 
44
  *
45
  * @return string The secret key.
46
  *
47
  * @since 4.9
48
  */
49
+ private function generate_secret_key() {
50
+ $key = AUTH_KEY;
 
 
 
 
51
 
52
  update_option( 'tribe_promoter_auth_key', $key );
53
 
54
  return $key;
55
  }
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
common/src/Tribe/Promoter/Connector.php CHANGED
@@ -1,9 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * Custom class for communicating with the Promoter Auth Connector the class is created
5
- * early in the process and many functions that come from utils are not available during the
6
- * execution of the methods from this class
7
  *
8
  * @since 4.9
9
  */
@@ -26,11 +24,13 @@ class Tribe__Promoter__Connector {
26
  * @since 4.9
27
  */
28
  public function base_url() {
 
 
29
  if ( defined( 'TRIBE_PROMOTER_AUTH_CONNECTOR_URL' ) ) {
30
- return TRIBE_PROMOTER_AUTH_CONNECTOR_URL;
31
  }
32
 
33
- return 'https://us-central1-promoter-auth-connector.cloudfunctions.net/promoterConnector/';
34
  }
35
 
36
  /**
@@ -48,18 +48,22 @@ class Tribe__Promoter__Connector {
48
  public function authorize_with_connector( $user_id, $secret_key, $promoter_key, $license_key ) {
49
  $url = $this->base_url() . 'connect';
50
 
51
- $payload = [
52
  'clientSecret' => $secret_key,
53
- 'licenseKey' => $license_key,
54
- 'userId' => $user_id,
55
- ];
 
 
56
 
57
  $token = \Firebase\JWT\JWT::encode( $payload, $promoter_key );
58
 
59
- $response = $this->make_call( $url, [
60
- 'body' => [ 'token' => $token ],
61
  'sslverify' => false,
62
- ] );
 
 
63
 
64
  return (bool) $response;
65
  }
@@ -76,13 +80,7 @@ class Tribe__Promoter__Connector {
76
  public function authenticate_user_with_connector( $user_id ) {
77
  $this->authorized = false;
78
 
79
- // If user is already authenticated no need to move forward (wp-admin) and others.
80
- if ( ! empty( $user_id ) ) {
81
- $this->authorized = true;
82
- return $user_id;
83
- }
84
-
85
- $token = $this->get_token();
86
 
87
  if ( empty( $token ) ) {
88
  return $user_id;
@@ -90,10 +88,12 @@ class Tribe__Promoter__Connector {
90
 
91
  $url = $this->base_url() . 'connect/auth';
92
 
93
- $response = $this->make_call( $url, [
94
- 'body' => [ 'token' => $token ],
95
  'sslverify' => false,
96
- ] );
 
 
97
 
98
  if ( ! $response ) {
99
  return $user_id;
@@ -104,63 +104,6 @@ class Tribe__Promoter__Connector {
104
  return $response;
105
  }
106
 
107
- /**
108
- * Get the token either from a request or a header
109
- *
110
- * @since 4.9.20
111
- *
112
- * @return mixed
113
- */
114
- protected function get_token() {
115
- $request_token = $this->get_token_from_request();
116
-
117
- return ( $request_token )
118
- ? sanitize_text_field( $request_token )
119
- : $this->get_token_from_headers();
120
- }
121
-
122
- /**
123
- * Get the token from a Request variable if present, otherwise fallback to `null`
124
- *
125
- * @since 4.9.20
126
- *
127
- * @return mixed
128
- */
129
- protected function get_token_from_request() {
130
- // Used in favor of tribe_get_request_var as at this point tribe_get_request_var is not defined.
131
- return \Tribe__Utils__Array::get_in_any(
132
- [ $_GET, $_POST, $_REQUEST ],
133
- 'tribe_promoter_auth_token'
134
- );
135
- }
136
-
137
- /**
138
- * Get the token directly from a Bearer Authentication Header, for hosts that
139
- * does not support large Query strings
140
- *
141
- * @since 4.9.20
142
- *
143
- * @return mixed
144
- */
145
- protected function get_token_from_headers() {
146
- $headers = [
147
- 'HTTP_AUTHORIZATION',
148
- 'REDIRECT_HTTP_AUTHORIZATION',
149
- ];
150
-
151
- foreach ( $headers as $header ) {
152
- if ( empty( $_SERVER[ $header ] ) ) {
153
- continue;
154
- }
155
-
156
- list( $token ) = sscanf( $_SERVER[ $header ], 'Bearer %s' );
157
-
158
- if ( $token ) {
159
- return sanitize_text_field( $token );
160
- }
161
- }
162
- }
163
-
164
  /**
165
  * Notify the Promoter app of changes within this system.
166
  *
@@ -171,7 +114,7 @@ class Tribe__Promoter__Connector {
171
  public function notify_promoter_of_changes( $post_id ) {
172
  $post_type = get_post_type( $post_id );
173
 
174
- if ( ! in_array( $post_type, [ 'tribe_events', 'tribe_tickets' ], true ) ) {
175
  return;
176
  }
177
 
@@ -184,47 +127,29 @@ class Tribe__Promoter__Connector {
184
  }
185
 
186
  $license_key = $license_info['key'];
187
- $secret_key = $this->get_secret_key();
188
 
189
  if ( empty( $secret_key ) ) {
190
  return;
191
  }
192
 
193
- $payload = [
194
  'licenseKey' => $license_key,
195
- 'sourceId' => $post_id instanceof WP_Post ? $post_id->ID : $post_id,
196
- ];
197
 
198
  $token = \Firebase\JWT\JWT::encode( $payload, $secret_key );
199
 
200
  $url = $this->base_url() . 'connect/notify';
201
 
202
- $args = [
203
- 'body' => [ 'token' => $token ],
204
  'sslverify' => false,
205
- ];
206
 
207
  $this->make_call( $url, $args );
208
  }
209
 
210
- /**
211
- * Get the value for the option `tribe_promoter_auth_key`
212
- *
213
- * @since 4.9.12
214
- *
215
- * @return mixed
216
- */
217
- protected function get_secret_key() {
218
- $secret_key = get_option( 'tribe_promoter_auth_key' );
219
-
220
- /**
221
- * @since 4.9.12
222
- *
223
- * @param string $secret_key
224
- */
225
- return apply_filters( 'tribe_promoter_secret_key', $secret_key );
226
- }
227
-
228
  /**
229
  * Make the call to the remote endpoint.
230
  *
@@ -240,7 +165,15 @@ class Tribe__Promoter__Connector {
240
  $code = wp_remote_retrieve_response_code( $response );
241
  $body = wp_remote_retrieve_body( $response );
242
 
243
- if ( is_wp_error( $response ) || $code > 299 ) {
 
 
 
 
 
 
 
 
244
  return false;
245
  }
246
 
@@ -257,4 +190,5 @@ class Tribe__Promoter__Connector {
257
  public function is_user_authorized() {
258
  return $this->authorized;
259
  }
 
260
  }
1
  <?php
2
 
3
  /**
4
+ * Custom class for communicating with the Promoter Auth Connector
 
 
5
  *
6
  * @since 4.9
7
  */
24
  * @since 4.9
25
  */
26
  public function base_url() {
27
+ $url = 'https://us-central1-promoter-auth-connector.cloudfunctions.net/promoterConnector/';
28
+
29
  if ( defined( 'TRIBE_PROMOTER_AUTH_CONNECTOR_URL' ) ) {
30
+ $url = TRIBE_PROMOTER_AUTH_CONNECTOR_URL;
31
  }
32
 
33
+ return $url;
34
  }
35
 
36
  /**
48
  public function authorize_with_connector( $user_id, $secret_key, $promoter_key, $license_key ) {
49
  $url = $this->base_url() . 'connect';
50
 
51
+ $payload = array(
52
  'clientSecret' => $secret_key,
53
+ 'licenseKey' => $license_key,
54
+ 'userId' => $user_id,
55
+ );
56
+
57
+ tribe( 'logger' )->log( $url );
58
 
59
  $token = \Firebase\JWT\JWT::encode( $payload, $promoter_key );
60
 
61
+ $args = array(
62
+ 'body' => array( 'token' => $token ),
63
  'sslverify' => false,
64
+ );
65
+
66
+ $response = $this->make_call( $url, $args );
67
 
68
  return (bool) $response;
69
  }
80
  public function authenticate_user_with_connector( $user_id ) {
81
  $this->authorized = false;
82
 
83
+ $token = tribe_get_request_var( 'tribe_promoter_auth_token' );
 
 
 
 
 
 
84
 
85
  if ( empty( $token ) ) {
86
  return $user_id;
88
 
89
  $url = $this->base_url() . 'connect/auth';
90
 
91
+ $args = array(
92
+ 'body' => array( 'token' => $token ),
93
  'sslverify' => false,
94
+ );
95
+
96
+ $response = $this->make_call( $url, $args );
97
 
98
  if ( ! $response ) {
99
  return $user_id;
104
  return $response;
105
  }
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  /**
108
  * Notify the Promoter app of changes within this system.
109
  *
114
  public function notify_promoter_of_changes( $post_id ) {
115
  $post_type = get_post_type( $post_id );
116
 
117
+ if ( ! in_array( $post_type, array( 'tribe_events', 'tribe_tickets' ), true ) ) {
118
  return;
119
  }
120
 
127
  }
128
 
129
  $license_key = $license_info['key'];
130
+ $secret_key = get_option( 'tribe_promoter_auth_key' );
131
 
132
  if ( empty( $secret_key ) ) {
133
  return;
134
  }
135
 
136
+ $payload = array(
137
  'licenseKey' => $license_key,
138
+ 'sourceId' => $post_id,
139
+ );
140
 
141
  $token = \Firebase\JWT\JWT::encode( $payload, $secret_key );
142
 
143
  $url = $this->base_url() . 'connect/notify';
144
 
145
+ $args = array(
146
+ 'body' => array( 'token' => $token ),
147
  'sslverify' => false,
148
+ );
149
 
150
  $this->make_call( $url, $args );
151
  }
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  /**
154
  * Make the call to the remote endpoint.
155
  *
165
  $code = wp_remote_retrieve_response_code( $response );
166
  $body = wp_remote_retrieve_body( $response );
167
 
168
+ if ( is_wp_error( $response ) ) {
169
+ tribe( 'logger' )->log( $response->get_error_message() );
170
+
171
+ return false;
172
+ }
173
+
174
+ if ( $code > 299 ) {
175
+ tribe( 'logger' )->log( $body, 0 );
176
+
177
  return false;
178
  }
179
 
190
  public function is_user_authorized() {
191
  return $this->authorized;
192
  }
193
+
194
  }
common/src/Tribe/Repository.php CHANGED
@@ -5,8 +5,6 @@ use Tribe__Utils__Array as Arr;
5
  abstract class Tribe__Repository
6
  implements Tribe__Repository__Interface {
7
 
8
- const MAX_NUMBER_OF_POSTS_PER_PAGE = 99999999999;
9
-
10
  /**
11
  * @var array An array of keys that cannot be updated on this repository.
12
  */
@@ -561,10 +559,6 @@ abstract class Tribe__Repository
561
  public function build_query( $use_query_builder = true ) {
562
  $query = null;
563
 
564
- if ( array_key_exists( 'void_query', $this->query_args ) && false !== $this->query_args['void_query'] ) {
565
- $this->void_query = true;
566
- }
567
-
568
  // We'll let the query builder decide if the query has to be rebuilt or not.
569
  if ( $use_query_builder && null !== $this->query_builder ) {
570
  $query = $this->build_query_with_builder();
@@ -839,10 +833,6 @@ abstract class Tribe__Repository
839
  if ( ! empty( $query->request ) ) {
840
  $ids = $this->get_ids();
841
 
842
- if ( empty( $ids ) ) {
843
- return null;
844
- }
845
-
846
  return $return_id ? reset( $ids ) : $this->format_item( reset( $ids ) );
847
  }
848
 
@@ -903,10 +893,6 @@ abstract class Tribe__Repository
903
  if ( ! empty( $query->request ) ) {
904
  $ids = $this->get_ids();
905
 
906
- if ( empty( $ids ) ) {
907
- return null;
908
- }
909
-
910
  return $return_id ? end( $ids ) : $this->format_item( end( $ids ) );
911
  }
912
 
@@ -1074,9 +1060,7 @@ abstract class Tribe__Repository
1074
  * {@inheritdoc}
1075
  */
1076
  public function by( $key, $value = null ) {
1077
- if ( $this->void_query || ( 'void_query' === $key && false !== $value ) ) {
1078
- $this->void_query = true;
1079
-
1080
  // No point in doing more computations if the query is void.
1081
  return $this;
1082
  }
@@ -1360,24 +1344,16 @@ abstract class Tribe__Repository
1360
  $query = $this->get_query();
1361
 
1362
  // The request property will be set during the `get_posts` method and empty before it.
1363
- if ( empty( $query->request ) ) {
1364
- $query->set( 'fields', 'ids' );
1365
-
1366
- return $query->get_posts();
1367
- }
1368
-
1369
- return array_map(
1370
- static function ( $post ) {
1371
  if ( is_int( $post ) ) {
1372
  return $post;
1373
  }
1374
  $post_arr = (array) $post;
1375
 
1376
  return Arr::get( $post_arr, 'ID', Arr::get( $post_arr, 'id', 0 ) );
1377
- },
1378
- $query->posts
1379
- );
1380
-
1381
  } catch ( Tribe__Repository__Void_Query_Exception $e ) {
1382
  /*
1383
  * Extending classes might use this method to run sub-queries
@@ -1385,6 +1361,10 @@ abstract class Tribe__Repository
1385
  */
1386
  return array();
1387
  }
 
 
 
 
1388
  }
1389
 
1390
  /**
@@ -1564,9 +1544,9 @@ abstract class Tribe__Repository
1564
  * Filters the query to only return posts that are related, via a meta key, to posts
1565
  * that satisfy a condition.
1566
  *
1567
- * @param string|array $meta_keys One or more `meta_keys` relating the queried post type(s)
1568
  * to another post type.
1569
- * @param string $compare The SQL comparison operator.
1570
  * @param string $field One (a column in the `posts` table) that should match
1571
  * the comparison criteria; required if the comparison operator is not `EXISTS` or
1572
  * `NOT EXISTS`.
@@ -1583,10 +1563,9 @@ abstract class Tribe__Repository
1583
  if ( empty( $field ) || empty( $values ) ) {
1584
  throw Tribe__Repository__Usage_Error::because_this_comparison_operator_requires_fields_and_values( $meta_keys, $compare, $this );
1585
  }
 
1586
  }
1587
 
1588
- $field = esc_sql( $field );
1589
-
1590
  /** @var wpdb $wpdb */
1591
  global $wpdb;
1592
  $p = $this->sql_slug( 'meta_related_post', $compare, $meta_keys );
@@ -1613,90 +1592,6 @@ abstract class Tribe__Repository
1613
  return $this;
1614
  }
1615
 
1616
- /**
1617
- * Filters the query to only return posts that are related, via a meta key, to posts
1618
- * that satisfy a condition.
1619
- *
1620
- * @since 4.10.3
1621
- *
1622
- * @throws Tribe__Repository__Usage_Error If the comparison operator requires and no value provided.
1623
- *
1624
- * @param string|array $meta_keys One or more `meta_keys` relating the queried post type(s)
1625
- * to another post type.
1626
- * @param string $compare The SQL comparison operator.
1627
- * @param string $meta_field One (a column in the `postmeta` table) that should match
1628
- * the comparison criteria; required if the comparison operator is not `EXISTS` or
1629
- * `NOT EXISTS`.
1630
- * @param string|array $meta_values One or more values the post field(s) should be compared to;
1631
- * required if the comparison operator is not `EXISTS` or `NOT EXISTS`.
1632
- * @param boolean $or_not_exists Whether or not to also include a clause to check if value IS NULL.
1633
- * Example with this as true: `value = X OR value IS NULL`.
1634
- *
1635
- * @return $this
1636
- */
1637
- public function where_meta_related_by_meta( $meta_keys, $compare, $meta_field = null, $meta_values = null, $or_not_exists = false ) {
1638
- $meta_keys = Tribe__Utils__Array::list_to_array( $meta_keys );
1639
-
1640
- if ( ! in_array( $compare, array( 'EXISTS', 'NOT EXISTS' ), true ) ) {
1641
- if ( empty( $meta_field ) || empty( $meta_values ) ) {
1642
- throw Tribe__Repository__Usage_Error::because_this_comparison_operator_requires_fields_and_values( $meta_keys, $compare, $this );
1643
- }
1644
- }
1645
-
1646
- $meta_field = esc_sql( $meta_field );
1647
-
1648
- /** @var wpdb $wpdb */
1649
- global $wpdb;
1650
-
1651
- $pm = $this->sql_slug( 'post_meta_related_post_meta', $compare, $meta_keys );
1652
- $pmm = $this->sql_slug( 'meta_post_meta_related_post_meta', $compare, $meta_keys );
1653
-
1654
- $this->filter_query->join( "LEFT JOIN {$wpdb->postmeta} {$pm} ON {$pm}.post_id = {$wpdb->posts}.ID" );
1655
- $this->filter_query->join( "
1656
- LEFT JOIN {$wpdb->postmeta} {$pmm}
1657
- ON {$pmm}.post_id = {$pm}.meta_value
1658
- AND {$pmm}.meta_key = '{$meta_field}'
1659
- " );
1660
-
1661
- $keys_in = $this->prepare_interval( $meta_keys );
1662
-
1663
- if ( 'EXISTS' === $compare ) {
1664
- $this->filter_query->where( "
1665
- {$pm}.meta_key IN {$keys_in}
1666
- AND {$pmm}.meta_id IS NOT NULL
1667
- " );
1668
- } elseif ( 'NOT EXISTS' === $compare ) {
1669
- $this->filter_query->where( "
1670
- {$pm}.meta_key IN {$keys_in}
1671
- AND {$pmm}.meta_id IS NULL
1672
- " );
1673
- } else {
1674
- if ( in_array( $compare, static::$multi_value_keys, true ) ) {
1675
- $meta_values = $this->prepare_interval( $meta_values );
1676
- } else {
1677
- $meta_values = $this->prepare_value( $meta_values );
1678
- }
1679
-
1680
- $clause = "{$pmm}.meta_value {$compare} {$meta_values}";
1681
-
1682
- if ( $or_not_exists ) {
1683
- $clause = "
1684
- (
1685
- {$clause}
1686
- OR {$pmm}.meta_id IS NULL
1687
- )
1688
- ";
1689
- }
1690
-
1691
- $this->filter_query->where( "
1692
- {$pm}.meta_key IN {$keys_in}
1693
- AND {$clause}
1694
- " );
1695
- }
1696
-
1697
- return $this;
1698
- }
1699
-
1700
  /**
1701
  * Builds a fenced group of WHERE clauses that will be used with OR logic.
1702
  *
@@ -2884,7 +2779,7 @@ abstract class Tribe__Repository
2884
 
2885
  $created = call_user_func( $this->get_create_callback( $postarr ), $postarr );
2886
 
2887
- $post = $this->format_item( $created );
2888
 
2889
  return $post instanceof WP_Post && $post->ID === $created ? $post : false;
2890
  }
@@ -3169,38 +3064,20 @@ abstract class Tribe__Repository
3169
  */
3170
  $query_args = apply_filters( "tribe_repository_{$this->filter_name}_query_args", $query_args, $query, $this );
3171
 
3172
- /**
3173
- * Provides a last-ditch effort to override the filtered offset.
3174
- *
3175
- * This should only be used if doing creating pagination for performance purposes.
3176
- *
3177
- * @since 4.11.0
3178
- *
3179
- * @param null|int $filtered_offset Offset parameter setting.
3180
- * @param array $query_args List of query arguments.
3181
- */
3182
- $filtered_offset = apply_filters( 'tribe_repository_query_arg_offset_override', null, $query_args );
3183
-
3184
- if ( $filtered_offset || isset( $query_args['offset'] ) ) {
3185
  $per_page = (int) Tribe__Utils__Array::get( $query_args, 'posts_per_page', get_option( 'posts_per_page' ) );
 
3186
 
3187
- if ( $filtered_offset ) {
3188
- $query_args['offset'] = $filtered_offset;
3189
- } elseif ( isset( $query_args['offset'] ) ) {
3190
- $offset = absint( $query_args['offset'] );
3191
- $page = (int) Tribe__Utils__Array::get( $query_args, 'paged', 1 );
3192
-
3193
- $real_offset = $per_page === -1 ? $offset : ( $per_page * ( $page - 1 ) ) + $offset;
3194
- $query_args['offset'] = $real_offset;
3195
-
3196
- /**
3197
- * Unset the `offset` query argument to avoid applying it multiple times when this method
3198
- * is used, on the same repository, more than once.
3199
- */
3200
- unset( $this->query_args['offset'] );
3201
- }
3202
 
3203
- $query_args['posts_per_page'] = $per_page === -1 ? self::MAX_NUMBER_OF_POSTS_PER_PAGE : $per_page;
 
 
 
 
3204
  }
3205
 
3206
  foreach ( $query_args as $key => $value ) {
@@ -3720,13 +3597,4 @@ abstract class Tribe__Repository
3720
 
3721
  return $prev;
3722
  }
3723
-
3724
- /**
3725
- * {@inheritDoc}
3726
- */
3727
- public function void_query( $void_query = true ) {
3728
- $this->void_query = (bool) $void_query;
3729
-
3730
- return $this;
3731
- }
3732
  }
5
  abstract class Tribe__Repository
6
  implements Tribe__Repository__Interface {
7
 
 
 
8
  /**
9
  * @var array An array of keys that cannot be updated on this repository.
10
  */
559
  public function build_query( $use_query_builder = true ) {
560
  $query = null;
561
 
 
 
 
 
562
  // We'll let the query builder decide if the query has to be rebuilt or not.
563
  if ( $use_query_builder && null !== $this->query_builder ) {
564
  $query = $this->build_query_with_builder();
833
  if ( ! empty( $query->request ) ) {
834
  $ids = $this->get_ids();
835
 
 
 
 
 
836
  return $return_id ? reset( $ids ) : $this->format_item( reset( $ids ) );
837
  }
838
 
893
  if ( ! empty( $query->request ) ) {
894
  $ids = $this->get_ids();
895
 
 
 
 
 
896
  return $return_id ? end( $ids ) : $this->format_item( end( $ids ) );
897
  }
898
 
1060
  * {@inheritdoc}
1061
  */
1062
  public function by( $key, $value = null ) {
1063
+ if ( $this->void_query ) {
 
 
1064
  // No point in doing more computations if the query is void.
1065
  return $this;
1066
  }
1344
  $query = $this->get_query();
1345
 
1346
  // The request property will be set during the `get_posts` method and empty before it.
1347
+ if ( ! empty( $query->request ) ) {
1348
+ return array_map( static function ( $post ) {
 
 
 
 
 
 
1349
  if ( is_int( $post ) ) {
1350
  return $post;
1351
  }
1352
  $post_arr = (array) $post;
1353
 
1354
  return Arr::get( $post_arr, 'ID', Arr::get( $post_arr, 'id', 0 ) );
1355
+ }, $query->posts );
1356
+ }
 
 
1357
  } catch ( Tribe__Repository__Void_Query_Exception $e ) {
1358
  /*
1359
  * Extending classes might use this method to run sub-queries
1361
  */
1362
  return array();
1363
  }
1364
+
1365
+ $query->set( 'fields', 'ids' );
1366
+
1367
+ return $query->get_posts();
1368
  }
1369
 
1370
  /**
1544
  * Filters the query to only return posts that are related, via a meta key, to posts
1545
  * that satisfy a condition.
1546
  *
1547
+ * @param string|array $meta_keys One ore more `meta_keys` relating the queried post type(s)
1548
  * to another post type.
1549
+ * @param string $compare The SQL compoarison operator.
1550
  * @param string $field One (a column in the `posts` table) that should match
1551
  * the comparison criteria; required if the comparison operator is not `EXISTS` or
1552
  * `NOT EXISTS`.
1563
  if ( empty( $field ) || empty( $values ) ) {
1564
  throw Tribe__Repository__Usage_Error::because_this_comparison_operator_requires_fields_and_values( $meta_keys, $compare, $this );
1565
  }
1566
+ $field = esc_sql( $field );
1567
  }
1568
 
 
 
1569
  /** @var wpdb $wpdb */
1570
  global $wpdb;
1571
  $p = $this->sql_slug( 'meta_related_post', $compare, $meta_keys );
1592
  return $this;
1593
  }
1594
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1595
  /**
1596
  * Builds a fenced group of WHERE clauses that will be used with OR logic.
1597
  *
2779
 
2780
  $created = call_user_func( $this->get_create_callback( $postarr ), $postarr );
2781
 
2782
+ $post = get_post( $created );
2783
 
2784
  return $post instanceof WP_Post && $post->ID === $created ? $post : false;
2785
  }
3064
  */
3065
  $query_args = apply_filters( "tribe_repository_{$this->filter_name}_query_args", $query_args, $query, $this );
3066
 
3067
+ if ( isset( $query_args['offset'] ) ) {
3068
+ $offset = absint( $query_args['offset'] );
 
 
 
 
 
 
 
 
 
 
 
3069
  $per_page = (int) Tribe__Utils__Array::get( $query_args, 'posts_per_page', get_option( 'posts_per_page' ) );
3070
+ $page = (int) Tribe__Utils__Array::get( $query_args, 'paged', 1 );
3071
 
3072
+ $real_offset = $per_page === - 1 ? $offset : ( $per_page * ( $page - 1 ) ) + $offset;
3073
+ $query_args['offset'] = $real_offset;
3074
+ $query_args['posts_per_page'] = $per_page === - 1 ? 99999999999 : $per_page;
 
 
 
 
 
 
 
 
 
 
 
 
3075
 
3076
+ /**
3077
+ * Unset the `offset` query argument to avoid applying it multiple times when this method
3078
+ * is used, on the same repository, more than once.
3079
+ */
3080
+ unset( $this->query_args['offset'] );
3081
  }
3082
 
3083
  foreach ( $query_args as $key => $value ) {
3597
 
3598
  return $prev;
3599
  }
 
 
 
 
 
 
 
 
 
3600
  }
common/src/Tribe/Repository/Core_Read_Interface.php DELETED
@@ -1,402 +0,0 @@
1
- <?php
2
- /**
3
- * The "core" read interface for repositories.
4
- *
5
- * This interface is the minimal one a repository should implement to be called such.
6
- *
7
- * @since 4.10.2
8
- *
9
- * @package Tribe\Repository
10
- */
11
-
12
- namespace Tribe\Repository;
13
-
14
- use Tribe__Repository__Read_Interface;
15
- use WP_Post;
16
-
17
- /**
18
- * Class Core_Read_Interface
19
- *
20
- * @since 4.10.2
21
- *
22
- * @package Tribe\Repository
23
- */
24
- interface Core_Read_Interface {
25
- /**
26
- * Batch filter application method.
27
- *
28
- * This is the same as calling `by` multiple times with different arguments.
29
- *
30
- * @since 4.7.19
31
- *
32
- * @param array $args An associative array of arguments to filter
33
- * the posts by in the shape [ <key>, <value> ]. * * @return Tribe__Repository__Read_Interface
34
- */
35
- public function by_args( array $args );
36
-
37
- /**
38
- * Applies a filter to the query.
39
- *
40
- * While the signature only shows 2 arguments additional arguments will be passed
41
- * to the schema filters.
42
- *
43
- * @since 4.7.19
44
- *
45
- * @param string $key
46
- * @param mixed $value
47
- * @param mixed ...$args Additional, optional, call arguments that will be passed to
48
- * the schema.
49
- *
50
- * @return Tribe__Repository__Read_Interface
51
- */
52
- public function by( $key, $value = null );
53
-
54
- /**
55
- * Just an alias of the `by` method to allow for easier reading.
56
- *
57
- * @since 4.7.19
58
- *
59
- * @param string $key
60
- * @param mixed $value
61
- *
62
- * @return Tribe__Repository__Read_Interface
63
- */
64
- public function where( $key, $value = null );
65
-
66
- /**
67
- * Sets the page of posts to fetch.
68
- *
69
- * Mind that this implementation does not support a `by( 'page', 2 )`
70
- * filter to force more readable code.
71
- *
72
- * @since 4.7.19
73
- *
74
- * @param int $page
75
- *
76
- * @return Tribe__Repository__Read_Interface
77
- */
78
- public function page( $page );
79
-
80
- /**
81
- * Sets the number of posts to retrieve per page.
82
- *
83
- * Mind that this implementation does not support a `by( 'per_page', 5 )`
84
- * filter to force more readable code; by default posts per page is set to
85
- * the pagination defaults for the post type.
86
- *
87
- * @param int $per_page
88
- *
89
- * @return Tribe__Repository__Read_Interface
90
- */
91
- public function per_page( $per_page );
92
-
93
- /**
94
- * Returns the number of posts found matching the query.
95
- *
96
- * Mind that this value ignores the offset returning the
97
- * number of results if limits where not applied.
98
- *
99
- * @since 4.7.19
100
- *
101
- * @return int
102
- */
103
- public function found();
104
-
105
- /**
106
- * Returns all posts matching the query.
107
- *
108
- * Mind that "all" means "all the posts matching all the filters" so pagination applies.
109
- *
110
- * @return array
111
- */
112
- public function all();
113
-
114
- /**
115
- * Sets the offset on the query.
116
- *
117
- * Mind that this implementation does not support a `by( 'offset', 2 )`
118
- * filter to force more readable code.
119
- *
120
- * @since 4.7.19
121
- *
122
- * @param int $offset
123
- * @param bool $increment Whether to increment the offset by the value
124
- * or replace it.
125
- *
126
- * @return Tribe__Repository__Read_Interface
127
- */
128
- public function offset( $offset, $increment = false );
129
-
130
- /**
131
- * Sets the order on the query.
132
- *
133
- * Mind that this implementation does not support a `by( 'order', 2 )`
134
- * filter to force more readable code.
135
- *
136
- * @since 4.7.19
137
- *
138
- * @param string $order
139
- *
140
- * @return Tribe__Repository__Read_Interface
141
- */
142
- public function order( $order = 'ASC' );
143
-
144
- /**
145
- * Sets the order criteria results should be fetched by.
146
- *
147
- * Mind that this implementation does not support a `by( 'order_by', 'title' )`
148
- * filter to force more readable code.
149
- *
150
- * @since 4.7.19
151
- *
152
- * @param string $order_by The post field, custom field or alias key to order posts by.
153
- * @param string $order The order direction; optional; shortcut for the `order` method; defaults
154
- * to `DESC`.
155
- *
156
- * @return Tribe__Repository__Read_Interface
157
- */
158
- public function order_by( $order_by, $order = 'DESC' );
159
-
160
- /**
161
- * Sets the fields that should be returned by the query.
162
- *
163
- * Mind that this implementation does not support a `by( 'fields', 'ids' )`
164
- * filter to force more readable code.
165
- *
166
- * @since 4.7.19
167
- *
168
- * @param string $fields
169
- *
170
- * @return Tribe__Repository__Read_Interface
171
- */
172
- public function fields( $fields );
173
-
174
- /**
175
- * Sugar method to set the `post__in` argument.
176
- *
177
- * Successive calls will stack, not replace each one.
178
- *
179
- * @since 4.7.19
180
- *
181
- * @param array|int $post_ids
182
- *
183
- * @return Tribe__Repository__Read_Interface
184
- */
185
- public function in( $post_ids );
186
-
187
- /**
188
- * Sugar method to set the `post__not_in` argument.
189
- *
190
- * Successive calls will stack, not replace each one.
191
- *
192
- * @since 4.7.19
193
- *
194
- * @param array|int $post_ids
195
- *
196
- * @return Tribe__Repository__Read_Interface
197
- */
198
- public function not_in( $post_ids );
199
-
200
- /**
201
- * Sugar method to set the `post_parent__in` argument.
202
- *
203
- * Successive calls will stack, not replace each one.
204
- *
205
- * @since 4.7.19
206
- *
207
- * @param array|int $post_id
208
- *
209
- * @return Tribe__Repository__Read_Interface
210
- */
211
- public function parent( $post_id );
212
-
213
- /**
214
- * Sugar method to set the `post_parent__in` argument.
215
- *
216
- * Successive calls will stack, not replace each one.
217
- *
218
- * @since 4.7.19
219
- *
220
- * @param array $post_ids
221
- *
222
- * @return Tribe__Repository__Read_Interface
223
- */
224
- public function parent_in( $post_ids );
225
-
226
- /**
227
- * Sugar method to set the `post_parent__not_in` argument.
228
- *
229
- * Successive calls will stack, not replace each one.
230
- *
231
- * @since 4.7.19
232
- *
233
- * @param array $post_ids
234
- *
235
- * @return Tribe__Repository__Read_Interface
236
- */
237
- public function parent_not_in( $post_ids );
238
-
239
- /**
240
- * Sugar method to set the `s` argument.
241
- *
242
- * Successive calls will replace the search string.
243
- * This is the default WordPress searh, to search by title,
244
- * content or excerpt only use the `title`, `content`, `excerpt` filters.
245
- *
246
- * @param $search
247
- *
248
- * @return Tribe__Repository__Read_Interface
249
- */
250
- public function search( $search );
251
-
252
- /**
253
- * Returns the number of posts found matching the query in the current page.
254
- *
255
- * While the `found` method will return the number of posts found
256
- * across all pages this method will only return the number of
257
- * posts found in the current page.
258
- * Differently from the `found` method this method will apply the
259
- * offset if set.
260
- *
261
- * @since 4.7.19
262
- *
263
- * @return int
264
- */
265
- public function count();
266
-
267
- /**
268
- * Returns the first post of the page matching the current query.
269
- *
270
- * If, by default or because set with the `per_page` method, all
271
- * posts matching the query should be returned then this will be
272
- * the first post of all those matching the query.
273
- *
274
- * @since 4.7.19
275
- *
276
- * @return WP_Post|mixed|null
277
- *
278
- * @see Tribe__Repository__Read_Interface::per_page()
279
- */
280
- public function first();
281
-
282
- /**
283
- * Returns the last post of the page matching the current query.
284
- *
285
- * If, by default or because set with the `per_page` method, all
286
- * posts matching the query should be returned then this will be
287
- * the last post of all those matching the query.
288
- *
289
- * @since 4.7.19
290
- *
291
- * @return WP_Post|mixed|null
292
- *
293
- * @see Tribe__Repository__Read_Interface::per_page()
294
- */
295
- public function last();
296
-
297
- /**
298
- * Returns the nth post (1-based) of the page matching the current query.
299
- *
300
- * Being 1-based the second post can be fetched using `nth( 2 )`.
301
- * If, by default or because set with the `per_page` method, all
302
- * posts matching the query should be returned then this will be
303
- * the nth post of all those matching the query.
304
- *
305
- * @since 4.7.19
306
- *
307
- * @param int $n
308
- *
309
- * @return WP_Post|mixed|null
310
- *
311
- * @see Tribe__Repository__Read_Interface::per_page()
312
- */
313
- public function nth( $n );
314
-
315
- /**
316
- * Returns the first n posts of the page matching the current query.
317
- *
318
- * If, by default or because set with the `per_page` method, all
319
- * posts matching the query should be returned then this method will
320
- * return the first n posts of all those matching the query.
321
- *
322
- * @since 4.7.19
323
- *
324
- * @return array An array of posts matching the query.
325
- *
326
- * @see Tribe__Repository__Read_Interface::per_page()
327
- */
328
- public function take( $n );
329
-
330
- /**
331
- * Plucks a field from all results and returns it.
332
- *
333
- * This method will implicitly build and use a `WP_List_Util` instance on the return
334
- * value of a call to the `all` method.
335
- *
336
- * @since 4.9.5
337
- *
338
- * @param string $field The field to pluck from each result.
339
- *
340
- * @return array An array of the plucked results.
341
- *
342
- * @see \wp_list_pluck()
343
- */
344
- public function pluck( $field );
345
-
346
- /**
347
- * Filters the results according to the specified criteria.
348
- *
349
- * This method will implicitly build and use a `WP_List_Util` instance on the return
350
- * value of a call to the `all` method.
351
- *
352
- * @since 4.9.5
353
- *
354
- * @param array $args Optional. An array of key => value arguments to match
355
- * against each object. Default empty array.
356
- * @param string $operator Optional. The logical operation to perform. 'AND' means
357
- * all elements from the array must match. 'OR' means only
358
- * one element needs to match. 'NOT' means no elements may
359
- * match. Default 'AND'.
360
- *
361
- * @return array An array of the filtered results.
362
- *
363
- * @see \wp_list_filter()
364
- */
365
- public function filter( $args = array(), $operator = 'AND' );
366
-
367
- /**
368
- * Sorts the results according to the specified criteria.
369
- *
370
- * This method will implicitly build and use a `WP_List_Util` instance on the return
371
- * value of a call to the `all` method.
372
- *
373
- * @since 4.9.5
374
- *
375
- * @param string|array $orderby Optional. Either the field name to order by or an array
376
- * of multiple orderby fields as $orderby => $order.
377
- * @param string $order Optional. Either 'ASC' or 'DESC'. Only used if $orderby
378
- * is a string.
379
- * @param bool $preserve_keys Optional. Whether to preserve keys. Default false.
380
- *
381
- * @return array An array of the sorted results.
382
- *
383
- * @see \wp_list_sort()
384
- */
385
- public function sort( $orderby = array(), $order = 'ASC', $preserve_keys = false );
386
-
387
- /**
388
- * Builds a collection on the result of the `all()` method call.
389
- *
390
- * @since 4.9.5
391
- *
392
- * @return \Tribe__Utils__Post_Collection
393
- */
394
- public function collect();
395
-
396
- /**
397
- * Gets the ids of the posts matching the query.
398
- *
399
- * @return array An array containing the post IDs to update.
400
- */
401
- public function get_ids();
402
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Repository/Decorator.php CHANGED
@@ -676,13 +676,4 @@ abstract class Tribe__Repository__Decorator implements Tribe__Repository__Interf
676
 
677
  return $this;
678
  }
679
-
680
- /**
681
- * {@inheritDoc}
682
- */
683
- public function void_query( $void_query = true ) {
684
- $this->decorated->void_query( $void_query );
685
-
686
- return $this;
687
- }
688
  }
676
 
677
  return $this;
678
  }
 
 
 
 
 
 
 
 
 
679
  }
common/src/Tribe/Repository/Filter_Validation.php DELETED
@@ -1,61 +0,0 @@
1
- <?php
2
- /**
3
- * Provides methods to validate repository filters.
4
- *
5
- * @since 4.10.2
6
- *
7
- * @package Tribe\Repository
8
- */
9
-
10
- namespace Tribe\Repository;
11
-
12
- use Tribe__Repository__Usage_Error as Usage_Error;
13
- use Tribe__Utils__Array as Arr;
14
-
15
- trait Filter_Validation {
16
- /**
17
- * Checks the passed arguments to make sure they are the correct number and nature.
18
- *
19
- * This method requires the class using it to define a `static::$filter_args_map` property in the shape:
20
- * ```
21
- * [
22
- * <filter> => [ <arg_name> => <arg_validation_callback> ]
23
- * ]
24
- * ```
25
- *
26
- * @since 4.10.2
27
- *
28
- * @param string $filter The name of the filter currently validating.
29
- * @param array $call_args The current filter call args, usually `func_get_args()`.
30
- *
31
- * @throws Usage_Error If there is a definition for the filter and the argument count or nature is not correct.
32
- */
33
- protected function ensure_args_for_filter( $filter, array $call_args ) {
34
- $map = isset( static::$filter_args_map ) ? static::$filter_args_map : false;
35
-
36
- if ( empty( $map ) ) {
37
- return;
38
- }
39
-
40
- $required_args = Arr::get( $filter, $map, false );
41
-
42
- if ( false === $required_args ) {
43
- return;
44
- }
45
-
46
- if ( count( $required_args ) !== count( $call_args ) ) {
47
- throw Usage_Error::because_filter_requires_args( $filter, array_keys( $required_args ) );
48
- }
49
-
50
- $iterator = new \MultipleIterator();
51
- $iterator->attachIterator( new \ArrayIterator( array_keys( $required_args ) ) );
52
- $iterator->attachIterator( new \ArrayIterator( array_values( $required_args ) ) );
53
- $iterator->attachIterator( new \ArrayIterator( $call_args ) );
54
-
55
- foreach ( $required_args as list( $arg_name, $validator, $input ) ) {
56
- if ( empty( $validator( $input ) ) ) {
57
- throw Usage_Error::because_filter_arg_is_not_valid( $filter, $arg_name );
58
- }
59
- }
60
- }
61
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Repository/Interface.php CHANGED
@@ -186,6 +186,13 @@ interface Tribe__Repository__Interface
186
  */
187
  public function by_related_to_between( $by_meta_keys, $min, $max, $keys = null, $values = null );
188
 
 
 
 
 
 
 
 
189
  /**
190
  * Adds an entry to the repository filter schema.
191
  *
@@ -309,15 +316,4 @@ interface Tribe__Repository__Interface
309
  * @return \Tribe__Repository__Interface The repository instance, for chaining.
310
  */
311
  public function set_found_rows( $found_rows );
312
-
313
- /**
314
- * Voids the repositories queries preventing the repository from running any query.
315
- *
316
- * @since 4.9.14
317
- *
318
- * @param bool $void Whether to void the repository queries or not.
319
- *
320
- * @return Tribe__Repository__Interface $this The repository instance.
321
- */
322
- public function void_query( $void_query = true );
323
  }
186
  */
187
  public function by_related_to_between( $by_meta_keys, $min, $max, $keys = null, $values = null );
188
 
189
+ /**
190
+ * Gets the ids of the posts matching the query.
191
+ *
192
+ * @return array An array containing the post IDs to update.
193
+ */
194
+ public function get_ids();
195
+
196
  /**
197
  * Adds an entry to the repository filter schema.
198
  *
316
  * @return \Tribe__Repository__Interface The repository instance, for chaining.
317
  */
318
  public function set_found_rows( $found_rows );
 
 
 
 
 
 
 
 
 
 
 
319
  }
common/src/Tribe/Repository/Query_Filters.php CHANGED
@@ -7,13 +7,6 @@
7
  */
8
  class Tribe__Repository__Query_Filters {
9
 
10
- /**
11
- * Indicates something has to happen "after" something else. The specific meaning is contextual.
12
- *
13
- * @since 4.9.21
14
- */
15
- CONST AFTER = 'after:';
16
-
17
  /**
18
  * @var array
19
  */
@@ -28,15 +21,6 @@ class Tribe__Repository__Query_Filters {
28
  'where' => array(),
29
  );
30
 
31
- /**
32
- * An array of the filters that can be set and unset by id.
33
- *
34
- * @since 4.9.14
35
- *
36
- * @var array
37
- */
38
- protected static $identifiable_filters = [ 'fields', 'join', 'where', 'orderby' ];
39
-
40
  /**
41
  * @var array
42
  */
@@ -642,29 +626,14 @@ class Tribe__Repository__Query_Filters {
642
  * Add a custom WHERE clause to the query.
643
  *
644
  * @since 4.7.19
645
- * @since 4.9.14 Added the `$id` and `$override` parameters.
646
  *
647
  * @param string $where_clause
648
- * @param null|string $id Optional WHERE ID to prevent duplicating clauses.
649
- * @param boolean $override Whether to override the clause if a WHERE by the same ID exists or not.
650
  */
651
- public function where( $where_clause, $id = null, $override =false ) {
652
  if ( $this->buffer_where_clauses ) {
653
- if ( $id ) {
654
- if ( $override || ! isset( $this->buffered_where_clauses[ $id ] ) ) {
655
- $this->buffered_where_clauses[ $id ] = $where_clause;
656
- }
657
- } else {
658
- $this->buffered_where_clauses[] = '(' . $where_clause . ')';
659
- }
660
  } else {
661
- if ( $id ) {
662
- if ( $override || ! isset( $this->query_vars['where'][ $id ] ) ) {
663
- $this->query_vars['where'][ $id ] = '(' . $where_clause . ')';
664
- }
665
- } else {
666
- $this->query_vars['where'][] = '(' . $where_clause . ')';
667
- }
668
 
669
  if ( ! has_filter( 'posts_where', array( $this, 'filter_posts_where' ) ) ) {
670
  add_filter( 'posts_where', array( $this, 'filter_posts_where' ), 10, 2 );
@@ -683,7 +652,7 @@ class Tribe__Repository__Query_Filters {
683
  */
684
  public function join( $join_clause, $id = null, $override = false ) {
685
  if ( $id ) {
686
- if ( $override || ! isset( $this->query_vars['join'][ $id ] ) ) {
687
  $this->query_vars['join'][ $id ] = $join_clause;
688
  }
689
  } else {
@@ -699,44 +668,11 @@ class Tribe__Repository__Query_Filters {
699
  * Add a custom ORDER BY to the query.
700
  *
701
  * @since 4.9.5
702
- * @since 4.9.14 Added the `$id` and `$override` parameters.
703
- * @since 4.9.21 Added the `$order` and `$after` parameters.
704
- *
705
- * @param string|array $orderby The order by criteria; this argument can be specified in array form to specify
706
- * multiple order by clauses and orders associated to each,
707
- * e.g. `[ '_meta_1' => 'ASC', '_meta_2' => 'DESC' ]`. If a simple array is
708
- * passed, then the order will be set to the default one for each entry.
709
- * This arguments supports the same formats of the `WP_Query` `orderby` argument.
710
- * @param null|string $id Optional ORDER ID to prevent duplicating order-by clauses.
711
- * @param boolean $override Whether to override the clause if another by the same ID exists.
712
- * @param bool $after Whether to append the order by clause to the ones managed by WordPress or not.
713
- * Defaults to `false`,to prepend them to the ones managed by WordPress.
714
  */
715
- public function orderby( $orderby, $id = null, $override = false, $after = false ) {
716
- $orderby_key = $after ? static::AFTER . 'orderby' : 'orderby';
717
- $entries = [];
718
-
719
- foreach ( (array) $orderby as $key => $value ) {
720
- /*
721
- * As WordPress does, we support "simple" entries, like `[ 'menu_order', 'post_date' ]` and entries in the
722
- * shape `[ 'menu_order' => 'ASC', 'post_date' => 'DESC' ]`.
723
- */
724
- $the_orderby = is_numeric( $key ) ? $value : $key;
725
- $the_order = is_numeric( $key ) ? 'DESC' : $value;
726
-
727
- $entries[] = [ $the_orderby, $the_order ];
728
- }
729
-
730
- $id = $id ?: 'default';
731
-
732
- // Use the `$id` parameter to allow later method calls to replace values set in previous calls.
733
- if ( $id ) {
734
- if ( $override || ! isset( $this->query_vars[ $orderby_key ][ $id ] ) ) {
735
- $this->query_vars[ $orderby_key ][ $id ] = $entries;
736
- }
737
- } else {
738
- $this->query_vars[ $orderby_key ][ $id ] = array_merge( $this->query_vars[ $orderby_key ][ $id ], $entries );
739
- }
740
 
741
  if ( ! has_filter( 'posts_orderby', array( $this, 'filter_posts_orderby' ) ) ) {
742
  add_filter( 'posts_orderby', array( $this, 'filter_posts_orderby' ), 10, 2 );
@@ -747,20 +683,11 @@ class Tribe__Repository__Query_Filters {
747
  * Add custom select fields to the query.
748
  *
749
  * @since 4.9.5
750
- * @since 4.9.14 Added the `$id` and `$override` parameters.
751
  *
752
- * @param string $field The field to add to the result.
753
- * @param null|string $id Optional ORDER ID to prevent duplicating order-by clauses..
754
- * @param boolean $override Whether to override the clause if another by the same ID exists.
755
  */
756
- public function fields( $field, $id = null, $override = false ) {
757
- if ( $id ) {
758
- if ( $override || ! isset( $this->query_vars['fields'][ $id ] ) ) {
759
- $this->query_vars['fields'][ $id ] = $field;
760
- }
761
- } else {
762
- $this->query_vars['fields'][] = $field;
763
- }
764
 
765
  if ( ! has_filter( 'posts_fields', array( $this, 'filter_posts_fields' ) ) ) {
766
  add_filter( 'posts_fields', array( $this, 'filter_posts_fields' ), 10, 2 );
@@ -973,46 +900,23 @@ class Tribe__Repository__Query_Filters {
973
  *
974
  * @since 4.9.5
975
  *
976
- * @param string $orderby The `ORDER BY` clause of the query being filtered.
977
- * @param WP_Query $query The query object currently being filtered.
978
  *
979
- * @return string The filtered `ORDER BY` clause.
980
  */
981
  public function filter_posts_orderby( $orderby, WP_Query $query ) {
982
  if ( $query !== $this->current_query ) {
983
  return $orderby;
984
  }
985
 
986
- if ( empty( $this->query_vars['orderby'] ) && empty( $this->query_vars[ static::AFTER . 'orderby' ] ) ) {
987
  return $orderby;
988
  }
989
 
990
- $frags = [ $orderby ];
991
 
992
- /*
993
- * Entries will be set, from the `orderby` method, to the `[ [ <orderby>, <order> ], [ <orderby>, <order> ] ]`
994
- * format.
995
- */
996
- $build_entry = static function ( $entries ) {
997
- $buffer = [];
998
-
999
- foreach ( $entries as list( $orderby, $order ) ) {
1000
- $buffer[] = sprintf( '%s %s', $orderby, $order );
1001
- }
1002
-
1003
- return implode( ', ', $buffer );
1004
- };
1005
-
1006
- if ( ! empty( $this->query_vars['orderby'] ) ) {
1007
- $before = implode( ', ', array_map( $build_entry, $this->query_vars['orderby'] ) );
1008
- $frags = [ $before, $orderby ];
1009
- }
1010
-
1011
- if ( ! empty( $this->query_vars[ static::AFTER . 'orderby' ] ) ) {
1012
- $frags[] = implode( ', ', array_map( $build_entry, $this->query_vars[ static::AFTER . 'orderby' ] ) );
1013
- }
1014
-
1015
- return implode( ', ', $frags );
1016
  }
1017
 
1018
  /**
@@ -1093,47 +997,4 @@ class Tribe__Repository__Query_Filters {
1093
 
1094
  return $this->last_request;
1095
  }
1096
-
1097
- /**
1098
- * Returns the fields, join, where and orderby clauses for an id.
1099
- *
1100
- * @since 4.9.14
1101
- *
1102
- * @param string $id The identifier of the group to remove.
1103
- *
1104
- * @return array An associative array of identifiable filters and their values, if any.
1105
- *
1106
- * @see Tribe__Repository__Query_Filters::$identifiable_filters
1107
- */
1108
- public function get_filters_by_id( $id ) {
1109
- $entries = [];
1110
-
1111
- foreach ( static::$identifiable_filters as $key ) {
1112
- if ( empty( $this->query_vars[ $key ][ $id ] ) ) {
1113
- continue;
1114
- }
1115
- $entries[ $key ] = $this->query_vars[ $key ][ $id ];
1116
- }
1117
-
1118
- return $entries;
1119
- }
1120
-
1121
- /**
1122
- * Removes fields, join, where and orderby clauses for an id.
1123
- *
1124
- * @since 4.9.14
1125
- *
1126
- * @param string $id The identifier of the group to remove.
1127
- */
1128
- public function remove_filters_by_id( $id ) {
1129
- array_walk(
1130
- $this->query_vars,
1131
- static function ( array &$filters, $key ) use ( $id ) {
1132
- if ( ! in_array( $key, static::$identifiable_filters, true ) ) {
1133
- return;
1134
- }
1135
- unset( $filters[ $id ] );
1136
- }
1137
- );
1138
- }
1139
  }
7
  */
8
  class Tribe__Repository__Query_Filters {
9
 
 
 
 
 
 
 
 
10
  /**
11
  * @var array
12
  */
21
  'where' => array(),
22
  );
23
 
 
 
 
 
 
 
 
 
 
24
  /**
25
  * @var array
26
  */
626
  * Add a custom WHERE clause to the query.
627
  *
628
  * @since 4.7.19
 
629
  *
630
  * @param string $where_clause
 
 
631
  */
632
+ public function where( $where_clause ) {
633
  if ( $this->buffer_where_clauses ) {
634
+ $this->buffered_where_clauses[] = '(' . $where_clause . ')';
 
 
 
 
 
 
635
  } else {
636
+ $this->query_vars['where'][] = '(' . $where_clause . ')';
 
 
 
 
 
 
637
 
638
  if ( ! has_filter( 'posts_where', array( $this, 'filter_posts_where' ) ) ) {
639
  add_filter( 'posts_where', array( $this, 'filter_posts_where' ), 10, 2 );
652
  */
653
  public function join( $join_clause, $id = null, $override = false ) {
654
  if ( $id ) {
655
+ if ( ! isset( $this->query_vars['join'][ $id ] ) ) {
656
  $this->query_vars['join'][ $id ] = $join_clause;
657
  }
658
  } else {
668
  * Add a custom ORDER BY to the query.
669
  *
670
  * @since 4.9.5
671
+ *
672
+ * @param string $orderby
 
 
 
 
 
 
 
 
 
 
673
  */
674
+ public function orderby( $orderby ) {
675
+ $this->query_vars['orderby'][] = $orderby;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
676
 
677
  if ( ! has_filter( 'posts_orderby', array( $this, 'filter_posts_orderby' ) ) ) {
678
  add_filter( 'posts_orderby', array( $this, 'filter_posts_orderby' ), 10, 2 );
683
  * Add custom select fields to the query.
684
  *
685
  * @since 4.9.5
 
686
  *
687
+ * @param string $field
 
 
688
  */
689
+ public function fields( $field ) {
690
+ $this->query_vars['fields'][] = $field;
 
 
 
 
 
 
691
 
692
  if ( ! has_filter( 'posts_fields', array( $this, 'filter_posts_fields' ) ) ) {
693
  add_filter( 'posts_fields', array( $this, 'filter_posts_fields' ), 10, 2 );
900
  *
901
  * @since 4.9.5
902
  *
903
+ * @param string $orderby
904
+ * @param WP_Query $query
905
  *
906
+ * @return string
907
  */
908
  public function filter_posts_orderby( $orderby, WP_Query $query ) {
909
  if ( $query !== $this->current_query ) {
910
  return $orderby;
911
  }
912
 
913
+ if ( empty( $this->query_vars['orderby'] ) ) {
914
  return $orderby;
915
  }
916
 
917
+ $order = $query->get( 'order', 'ASC' );
918
 
919
+ return implode( ' ' . $order . ', ', $this->query_vars['orderby'] ) . ' ' . $order . ', ' . $orderby;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
920
  }
921
 
922
  /**
997
 
998
  return $this->last_request;
999
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1000
  }
common/src/Tribe/Repository/Read_Interface.php CHANGED
@@ -1,14 +1,166 @@
1
  <?php
2
 
3
- use Tribe\Repository\Core_Read_Interface;
4
-
5
  /**
6
  * Interface Tribe__Repository__Read_Interface
7
  *
8
- *
9
  * @since 4.7.19
10
  */
11
- interface Tribe__Repository__Read_Interface extends Tribe__Repository__Setter_Interface, Core_Read_Interface {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  /**
13
  * Sets the permission that should be used to get the posts.
14
  *
@@ -21,6 +173,162 @@ interface Tribe__Repository__Read_Interface extends Tribe__Repository__Setter_In
21
  */
22
  public function permission( $permission );
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  /**
25
  * Fetches a single instance of the post type handled by the repository by
26
  * the primary key.
@@ -170,4 +478,70 @@ interface Tribe__Repository__Read_Interface extends Tribe__Repository__Setter_In
170
  * @return WP_Query A query object ready to return, and operate, on the posts.
171
  */
172
  public function get_query_for_posts( array $posts );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
1
  <?php
2
 
 
 
3
  /**
4
  * Interface Tribe__Repository__Read_Interface
5
  *
 
6
  * @since 4.7.19
7
  */
8
+ interface Tribe__Repository__Read_Interface extends Tribe__Repository__Setter_Interface {
9
+ /**
10
+ * Batch filter application method.
11
+ *
12
+ * This is the same as calling `by` multiple times with different arguments.
13
+ *
14
+ * @since 4.7.19
15
+ *
16
+ * @param array $args An associative array of arguments to filter
17
+ * the posts by in the shape [ <key>, <value> ]. * * @return Tribe__Repository__Read_Interface */
18
+ public function by_args( array $args );
19
+
20
+ /**
21
+ * Batch filter application method.
22
+ *
23
+ * This is the same as calling `where` multiple times with different arguments.
24
+ *
25
+ * T
26
+
27
+ /**
28
+ * Applies a filter to the query.
29
+ *
30
+ * While the signature only shows 2 arguments additional arguments will be passed
31
+ * to the schema filters.
32
+ *
33
+ * @since 4.7.19
34
+ *
35
+ * @param string $key
36
+ * @param mixed $value
37
+ * @param mixed ...$args Additional, optional, call arguments that will be passed to
38
+ * the schema.
39
+ *
40
+ * @return Tribe__Repository__Read_Interface
41
+ */
42
+ public function by( $key, $value = null );
43
+
44
+ /**
45
+ * Just an alias of the `by` method to allow for easier reading.
46
+ *
47
+ * @since 4.7.19
48
+ *
49
+ * @param string $key
50
+ * @param mixed $value
51
+ *
52
+ * @return Tribe__Repository__Read_Interface
53
+ */
54
+ public function where( $key, $value = null );
55
+
56
+ /**
57
+ * Sets the page of posts to fetch.
58
+ *
59
+ * Mind that this implementation does not support a `by( 'page', 2 )`
60
+ * filter to force more readable code.
61
+ *
62
+ * @since 4.7.19
63
+ *
64
+ * @param int $page
65
+ *
66
+ * @return Tribe__Repository__Read_Interface
67
+ */
68
+ public function page( $page );
69
+
70
+ /**
71
+ * Sets the number of posts to retrieve per page.
72
+ *
73
+ * Mind that this implementation does not support a `by( 'per_page', 5 )`
74
+ * filter to force more readable code; by default posts per page is set to
75
+ * the pagination defaults for the post type.
76
+ *
77
+ * @param int $per_page
78
+ *
79
+ * @return Tribe__Repository__Read_Interface
80
+ */
81
+ public function per_page( $per_page );
82
+
83
+ /**
84
+ * Returns the number of posts found matching the query.
85
+ *
86
+ * Mind that this value ignores the offset returning the
87
+ * number of results if limits where not applied.
88
+ *
89
+ * @since 4.7.19
90
+ *
91
+ * @return int
92
+ */
93
+ public function found();
94
+
95
+ /**
96
+ * Returns all posts matching the query.
97
+ *
98
+ * Mind that "all" means "all the posts matching all the filters" so pagination applies.
99
+ *
100
+ * @return array
101
+ */
102
+ public function all();
103
+
104
+ /**
105
+ * Sets the offset on the query.
106
+ *
107
+ * Mind that this implementation does not support a `by( 'offset', 2 )`
108
+ * filter to force more readable code.
109
+ *
110
+ * @since 4.7.19
111
+ *
112
+ * @param int $offset
113
+ * @param bool $increment Whether to increment the offset by the value
114
+ * or replace it.
115
+ *
116
+ * @return Tribe__Repository__Read_Interface
117
+ */
118
+ public function offset( $offset, $increment = false );
119
+
120
+ /**
121
+ * Sets the order on the query.
122
+ *
123
+ * Mind that this implementation does not support a `by( 'order', 2 )`
124
+ * filter to force more readable code.
125
+ *
126
+ * @since 4.7.19
127
+ *
128
+ * @param string $order
129
+ *
130
+ * @return Tribe__Repository__Read_Interface
131
+ */
132
+ public function order( $order = 'ASC' );
133
+
134
+ /**
135
+ * Sets the order criteria results should be fetched by.
136
+ *
137
+ * Mind that this implementation does not support a `by( 'order_by', 'title' )`
138
+ * filter to force more readable code.
139
+ *
140
+ * @since 4.7.19
141
+ *
142
+ * @param string $order_by The post field, custom field or alias key to order posts by.
143
+ * @param string $order The order direction; optional; shortcut for the `order` method; defaults
144
+ * to `DESC`.
145
+ *
146
+ * @return Tribe__Repository__Read_Interface
147
+ */
148
+ public function order_by( $order_by, $order = 'DESC' );
149
+
150
+ /**
151
+ * Sets the fields that should be returned by the query.
152
+ *
153
+ * Mind that this implementation does not support a `by( 'fields', 'ids' )`
154
+ * filter to force more readable code.
155
+ *
156
+ * @since 4.7.19
157
+ *
158
+ * @param string $fields
159
+ *
160
+ * @return Tribe__Repository__Read_Interface
161
+ */
162
+ public function fields( $fields );
163
+
164
  /**
165
  * Sets the permission that should be used to get the posts.
166
  *
173
  */
174
  public function permission( $permission );
175
 
176
+ /**
177
+ * Sugar method to set the `post__in` argument.
178
+ *
179
+ * Successive calls will stack, not replace each one.
180
+ *
181
+ * @since 4.7.19
182
+ *
183
+ * @param array|int $post_ids
184
+ *
185
+ * @return Tribe__Repository__Read_Interface
186
+ */
187
+ public function in( $post_ids );
188
+
189
+ /**
190
+ * Sugar method to set the `post__not_in` argument.
191
+ *
192
+ * Successive calls will stack, not replace each one.
193
+ *
194
+ * @since 4.7.19
195
+ *
196
+ * @param array|int $post_ids
197
+ *
198
+ * @return Tribe__Repository__Read_Interface
199
+ */
200
+ public function not_in( $post_ids );
201
+
202
+ /**
203
+ * Sugar method to set the `post_parent__in` argument.
204
+ *
205
+ * Successive calls will stack, not replace each one.
206
+ *
207
+ * @since 4.7.19
208
+ *
209
+ * @param array|int $post_id
210
+ *
211
+ * @return Tribe__Repository__Read_Interface
212
+ */
213
+ public function parent( $post_id );
214
+
215
+ /**
216
+ * Sugar method to set the `post_parent__in` argument.
217
+ *
218
+ * Successive calls will stack, not replace each one.
219
+ *
220
+ * @since 4.7.19
221
+ *
222
+ * @param array $post_ids
223
+ *
224
+ * @return Tribe__Repository__Read_Interface
225
+ */
226
+ public function parent_in( $post_ids );
227
+
228
+ /**
229
+ * Sugar method to set the `post_parent__not_in` argument.
230
+ *
231
+ * Successive calls will stack, not replace each one.
232
+ *
233
+ * @since 4.7.19
234
+ *
235
+ * @param array $post_ids
236
+ *
237
+ * @return Tribe__Repository__Read_Interface
238
+ */
239
+ public function parent_not_in( $post_ids );
240
+
241
+ /**
242
+ * Sugar method to set the `s` argument.
243
+ *
244
+ * Successive calls will replace the search string.
245
+ * This is the default WordPress searh, to search by title,
246
+ * content or excerpt only use the `title`, `content`, `excerpt` filters.
247
+ *
248
+ * @param $search
249
+ *
250
+ * @return Tribe__Repository__Read_Interface
251
+ */
252
+ public function search( $search );
253
+
254
+ /**
255
+ * Returns the number of posts found matching the query in the current page.
256
+ *
257
+ * While the `found` method will return the number of posts found
258
+ * across all pages this method will only return the number of
259
+ * posts found in the current page.
260
+ * Differently from the `found` method this method will apply the
261
+ * offset if set.
262
+ *
263
+ * @since 4.7.19
264
+ *
265
+ * @return int
266
+ */
267
+ public function count();
268
+
269
+ /**
270
+ * Returns the first post of the page matching the current query.
271
+ *
272
+ * If, by default or because set with the `per_page` method, all
273
+ * posts matching the query should be returned then this will be
274
+ * the first post of all those matching the query.
275
+ *
276
+ * @since 4.7.19
277
+ *
278
+ * @return WP_Post|mixed|null
279
+ *
280
+ * @see Tribe__Repository__Read_Interface::per_page()
281
+ */
282
+ public function first();
283
+
284
+ /**
285
+ * Returns the last post of the page matching the current query.
286
+ *
287
+ * If, by default or because set with the `per_page` method, all
288
+ * posts matching the query should be returned then this will be
289
+ * the last post of all those matching the query.
290
+ *
291
+ * @since 4.7.19
292
+ *
293
+ * @return WP_Post|mixed|null
294
+ *
295
+ * @see Tribe__Repository__Read_Interface::per_page()
296
+ */
297
+ public function last();
298
+
299
+ /**
300
+ * Returns the nth post (1-based) of the page matching the current query.
301
+ *
302
+ * Being 1-based the second post can be fetched using `nth( 2 )`.
303
+ * If, by default or because set with the `per_page` method, all
304
+ * posts matching the query should be returned then this will be
305
+ * the nth post of all those matching the query.
306
+ *
307
+ * @since 4.7.19
308
+ *
309
+ * @param int $n
310
+ *
311
+ * @return WP_Post|mixed|null
312
+ *
313
+ * @see Tribe__Repository__Read_Interface::per_page()
314
+ */
315
+ public function nth( $n );
316
+
317
+ /**
318
+ * Returns the first n posts of the page matching the current query.
319
+ *
320
+ * If, by default or because set with the `per_page` method, all
321
+ * posts matching the query should be returned then this method will
322
+ * return the first n posts of all those matching the query.
323
+ *
324
+ * @since 4.7.19
325
+ *
326
+ * @return array An array of posts matching the query.
327
+ *
328
+ * @see Tribe__Repository__Read_Interface::per_page()
329
+ */
330
+ public function take( $n );
331
+
332
  /**
333
  * Fetches a single instance of the post type handled by the repository by
334
  * the primary key.
478
  * @return WP_Query A query object ready to return, and operate, on the posts.
479
  */
480
  public function get_query_for_posts( array $posts );
481
+
482
+ /**
483
+ * Plucks a field from all results and returns it.
484
+ *
485
+ * This method will implicitly build and use a `WP_List_Util` instance on the return
486
+ * value of a call to the `all` method.
487
+ *
488
+ * @since 4.9.5
489
+ *
490
+ * @param string $field The field to pluck from each result.
491
+ *
492
+ * @return array An array of the plucked results.
493
+ *
494
+ * @see \wp_list_pluck()
495
+ */
496
+ public function pluck( $field );
497
+
498
+ /**
499
+ * Filters the results according to the specified criteria.
500
+ *
501
+ * This method will implicitly build and use a `WP_List_Util` instance on the return
502
+ * value of a call to the `all` method.
503
+ *
504
+ * @since 4.9.5
505
+ *
506
+ * @param array $args Optional. An array of key => value arguments to match
507
+ * against each object. Default empty array.
508
+ * @param string $operator Optional. The logical operation to perform. 'AND' means
509
+ * all elements from the array must match. 'OR' means only
510
+ * one element needs to match. 'NOT' means no elements may
511
+ * match. Default 'AND'.
512
+ *
513
+ * @return array An array of the filtered results.
514
+ *
515
+ * @see \wp_list_filter()
516
+ */
517
+ public function filter( $args = array(), $operator = 'AND' );
518
+
519
+ /**
520
+ * Sorts the results according to the specified criteria.
521
+ *
522
+ * This method will implicitly build and use a `WP_List_Util` instance on the return
523
+ * value of a call to the `all` method.
524
+ *
525
+ * @since 4.9.5
526
+ *
527
+ * @param string|array $orderby Optional. Either the field name to order by or an array
528
+ * of multiple orderby fields as $orderby => $order.
529
+ * @param string $order Optional. Either 'ASC' or 'DESC'. Only used if $orderby
530
+ * is a string.
531
+ * @param bool $preserve_keys Optional. Whether to preserve keys. Default false.
532
+ *
533
+ * @return array An array of the sorted results.
534
+ *
535
+ * @see \wp_list_sort()
536
+ */
537
+ public function sort( $orderby = array(), $order = 'ASC', $preserve_keys = false );
538
+
539
+ /**
540
+ * Builds a collection on the result of the `all()` method call.
541
+ *
542
+ * @since 4.9.5
543
+ *
544
+ * @return \Tribe__Utils__Post_Collection
545
+ */
546
+ public function collect();
547
  }
common/src/Tribe/Repository/Usage_Error.php CHANGED
@@ -244,45 +244,4 @@ class Tribe__Repository__Usage_Error extends Exception {
244
  public static function because_query_cannot_be_set_after_it_ran() {
245
  return new self( "You are trying to set the repository query after it ran!" );
246
  }
247
-
248
- /**
249
- * Indicates the client code is trying to call a filter without the correct number of req. parameters.
250
- *
251
- * @since 4.10.2
252
- *
253
- * @param string $filter The called filter.
254
- * @param array $required_args The human-readable name of the required arguments.
255
- *
256
- * @return static A ready to throw instance of the class.
257
- */
258
- public static function because_filter_requires_args( $filter, array $required_args ) {
259
- return new static(
260
- sprintf(
261
- 'The "%s" filter requires %d arguments: %s',
262
- $filter,
263
- count( $required_args ),
264
- implode( ', ', $required_args )
265
- )
266
- );
267
- }
268
-
269
- /**
270
- * Indicates the client code is trying to call a filter with an invalid parameter.
271
- *
272
- * @since 4.10.2
273
- *
274
- * @param string $filter The called filter.
275
- * @param string $arg_name The human-readable name of the parameter.
276
- *
277
- * @return static A ready to throw instance of the class.
278
- */
279
- public static function because_filter_arg_is_not_valid( $filter, $arg_name ) {
280
- return new static(
281
- sprintf(
282
- 'The "%s" filter "%s" argument is not valid.',
283
- $filter,
284
- $arg_name
285
- )
286
- );
287
- }
288
  }
244
  public static function because_query_cannot_be_set_after_it_ran() {
245
  return new self( "You are trying to set the repository query after it ran!" );
246
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
common/src/Tribe/Rewrite.php CHANGED
@@ -26,14 +26,6 @@ class Tribe__Rewrite {
26
  * @var static
27
  */
28
  public static $instance;
29
- /**
30
- * A delimiter used to separate a localized matcher from its base in the format `<loc_matcher><delim><base>`.
31
- *
32
- * @since 4.11.5
33
- *
34
- * @var string
35
- */
36
- protected static $localized_matcher_delimiter = '~';
37
 
38
  /**
39
  * WP_Rewrite Instance
@@ -186,26 +178,11 @@ class Tribe__Rewrite {
186
 
187
  // Remove percent Placeholders on all items
188
  add_filter( 'rewrite_rules_array', array( $this, 'remove_percent_placeholders' ), 25 );
189
-
190
- add_action( 'shutdown', [ $this, 'dump_cache' ] );
191
  }
192
 
193
  protected function remove_hooks() {
194
  remove_filter( 'generate_rewrite_rules', array( $this, 'filter_generate' ) );
195
  remove_filter( 'rewrite_rules_array', array( $this, 'remove_percent_placeholders' ), 25 );
196
-
197
- remove_action( 'shutdown', [ $this, 'dump_cache' ] );
198
- }
199
-
200
- /**
201
- * Determines if we have plain permalink.
202
- *
203
- * @since 4.11.2
204
- *
205
- * @return bool If we use plain permalink or not.
206
- */
207
- public static function is_plain_permalink() {
208
- return tribe_context()->is( 'plain_permalink' );
209
  }
210
 
211
  /**
@@ -426,11 +403,8 @@ class Tribe__Rewrite {
426
  $query = (string) parse_url( $url, PHP_URL_QUERY );
427
  wp_parse_str( $query, $query_vars );
428
 
429
- // Drop any query var that is not a scalar; it should not be handled.
430
- $query_vars = array_filter( $query_vars, 'is_scalar' );
431
-
432
  if ( isset( $query_vars['paged'] ) && 1 === (int) $query_vars['paged'] ) {
433
- // Remove the `paged` query var if it's 1.
434
  unset( $query_vars['paged'] );
435
  }
436
 
@@ -438,16 +412,10 @@ class Tribe__Rewrite {
438
 
439
  $our_rules = $this->get_handled_rewrite_rules();
440
  $handled_query_vars = $this->get_rules_query_vars( $our_rules );
441
- $handled_post_types = $this->get_post_types();
442
 
443
  if (
444
- // The rules we handle should not be empty.
445
  empty( $our_rules )
446
- || ! (
447
- // Supported post types should be either keys or values, of the `post_type` argument, in the query vars.
448
- count( array_intersect_key( array_flip( $handled_post_types ), $query_vars ) )
449
- || in_array( Arr::get( $query_vars, 'post_type', 'post' ), $handled_post_types, true )
450
- )
451
  ) {
452
  $wp_canonical = redirect_canonical( $canonical_url, false );
453
  if ( empty( $wp_canonical ) ) {
@@ -494,7 +462,7 @@ class Tribe__Rewrite {
494
  continue;
495
  }
496
 
497
- $replace = array_map( function ( $localized_matcher ) use ( $matched_vars ) {
498
  if ( ! is_array( $localized_matcher ) ) {
499
  // For the dates.
500
  return isset( $matched_vars[ $localized_matcher ] )
@@ -502,49 +470,20 @@ class Tribe__Rewrite {
502
  : '';
503
  }
504
 
505
- $query_var = $localized_matcher['query_var'];
506
- $query_vars = [ $query_var ];
507
-
508
- if ( $query_var === 'name' ) {
509
- $query_vars = array_merge( $query_vars, $this->get_post_types() );
510
- }
511
-
512
- if ( ! array_intersect( array_keys( $matched_vars ), $query_vars ) ) {
513
  return '';
514
  }
515
 
516
- if ( isset( $localized_matcher['localized_slug'] ) ) {
517
- // If available, then return the localized slug instead of inferring it as we do below.
518
- return $localized_matcher['localized_slug'];
519
- }
520
-
521
  /*
522
  * We use `end` as, by default, the localized version of the slug in the current language will be at the
523
  * end of the array.
 
524
  */
525
  return end( $localized_matcher['localized_slugs'] );
526
  }, $localized_matchers );
527
 
528
  // Include dynamic matchers now.
529
  $replace = array_merge( $dynamic_matchers, $replace );
530
-
531
- /*
532
- * Prune from the replacements the empty values. This will resolve conflicts (e.g. single and archive w/
533
- * same slug) as no two can be true at the same time.
534
- * Remove the `<delim><base>` prefix added to localized matchers, if any.
535
- */
536
- $replace = array_filter( $replace );
537
- $replace = array_combine(
538
- array_map( static function ( $key ) {
539
- return preg_replace(
540
- '/' . preg_quote( Tribe__Rewrite::$localized_matcher_delimiter ) . '\\w*$/',
541
- '',
542
- $key
543
- );
544
- }, array_keys( $replace ) ),
545
- $replace
546
- );
547
-
548
  $replaced = str_replace( array_keys( $replace ), $replace, $link_template );
549
 
550
  // Remove trailing chars.
@@ -600,45 +539,21 @@ class Tribe__Rewrite {
600
  * @return array An array of rewrite rules handled by the implementation in the shape `[ <regex> => <path> ]`.
601
  */
602
  protected function get_handled_rewrite_rules() {
603
- static $cache_var_name = __METHOD__;
604
-
605
- $our_rules = tribe_get_var( $cache_var_name, null );
606
-
607
  // We need to make sure we are have WP_Rewrite setup
608
  if ( ! $this->rewrite ) {
609
  $this->setup();
610
  }
611
 
612
- $all_rules = isset( $this->rewrite->rules ) ? (array) $this->rewrite->rules : [];
613
-
614
- if ( null === $our_rules ) {
615
- // While this is specific to The Events Calendar we're handling a small enough post type base to keep it here.
616
- $pattern = '/post_type=tribe_(events|venue|organizer)/';
617
- // Reverse the rules to try and match the most complex first.
618
- $our_rules = array_filter( $all_rules,
619
- static function ( $rule_query_string ) use ( $pattern ) {
620
- return preg_match( $pattern, $rule_query_string );
621
- }
622
- );
623
-
624
- tribe_set_var( $cache_var_name, $our_rules );
625
- }
626
-
627
- /**
628
- * Filters the list of rewrite rules handled by our code to add or remove some as required.
629
- *
630
- * @since 4.9.18
631
- *
632
- * @param array $our_rules An array of rewrite rules handled by our code, in the shape
633
- * `[ <rewrite_rule_regex_pattern> => <query_string> ]`.
634
- * E.g. `[ '(?:events)/(?:list)/?$' => 'index.php?post_type=tribe_events&eventDisplay=list' ]`.
635
- * @param array<string,string> All the current rewrite rules, before any filtering is applied; these have the
636
- * same `<pattern => rewrite >` format as the previous argument, which is the
637
- * format used by WordPress rewrite rules.
638
- */
639
- $our_rules = apply_filters( 'tribe_rewrite_handled_rewrite_rules', $our_rules, $all_rules );
640
 
641
- return $our_rules;
642
  }
643
 
644
  /**
@@ -649,25 +564,13 @@ class Tribe__Rewrite {
649
  * @return array A map of localized regex matchers in the shape `[ <localized_regex> => <query_var> ]`.
650
  */
651
  protected function get_localized_matchers() {
652
- static $cache_var_name = __METHOD__;
653
-
654
  $bases = (array) $this->get_bases();
655
-
656
  $query_var_map = $this->get_matcher_to_query_var_map();
657
 
658
- $localized_matchers = tribe_get_var( $cache_var_name, [] );
659
-
660
  foreach ( $bases as $base => $localized_matcher ) {
661
- // Use the base too to allow possible conflicts if the slugs are the same for single and archive.
662
- $localized_matcher_key = $localized_matcher . static::$localized_matcher_delimiter . $base;
663
-
664
- if ( isset( $localized_matchers[ $localized_matcher_key ] ) ) {
665
- continue;
666
- }
667
-
668
  if ( isset( $query_var_map[ $base ] ) ) {
669
- $localized_matchers[ $localized_matcher_key ] = [
670
- 'base' => $base,
671
  'query_var' => $query_var_map[ $base ],
672
  'en_slug' => $base,
673
  'localized_slugs' => [ $base ],
@@ -677,7 +580,7 @@ class Tribe__Rewrite {
677
  if ( ! empty( $buffer['slugs'] ) ) {
678
  $slugs = explode( '|', $buffer['slugs'] );
679
 
680
- $localized_matchers[ $localized_matcher_key ]['localized_slugs'] = array_map(
681
  static function ( $localized_slug ) {
682
  return str_replace( '\-', '-', $localized_slug );
683
  },
@@ -685,18 +588,16 @@ class Tribe__Rewrite {
685
  );
686
 
687
  // The English version is the first.
688
- $localized_matchers[ $localized_matcher_key ]['en_slug'] = reset( $slugs );
689
  }
690
  }
691
  }
692
 
693
- tribe_set_var( $cache_var_name, $localized_matchers );
694
-
695
  return $localized_matchers;
696
  }
697
 
698
  /**
699
- * Returns a map relating localized matcher slugs to the corresponding query var.
700
  *
701
  * @since 4.9.11
702
  *
@@ -718,33 +619,13 @@ class Tribe__Rewrite {
718
  * @return array A list of all the query vars handled in the rules.
719
  */
720
  protected function get_rules_query_vars( array $rules ) {
721
- static $cache_var_name = __METHOD__;
722
-
723
- $cached_rules = tribe_get_var( $cache_var_name, [] );
724
- $cache_key = md5( json_encode( $rules ) );
725
-
726
- if ( ! isset( $cached_rules[ $cache_key ] ) ) {
727
- $cached_rules[ $cache_key ] = array_unique(
728
- array_filter(
729
- array_merge(
730
- [],
731
- ...array_values(
732
- array_map(
733
- static function ( $rule_string ) {
734
- wp_parse_str( parse_url( $rule_string, PHP_URL_QUERY ), $vars );
735
- return array_keys( $vars );
736
- },
737
- $rules
738
- )
739
- )
740
- )
741
- )
742
- );
743
-
744
- tribe_set_var( $cache_var_name, $cached_rules );
745
- }
746
 
747
- return $cached_rules[ $cache_key ];
 
 
748
  }
749
 
750
  /**
@@ -759,24 +640,16 @@ class Tribe__Rewrite {
759
  protected function get_dynamic_matchers( array $query_vars ) {
760
  $bases = (array) $this->get_bases();
761
  $dynamic_matchers = [];
762
-
763
- /*
764
- * In some instance we use the `page` (w/o `d`) to paginate a dynamic archive.
765
- * Let's support that too.
766
- * It's important to add `page` after `paged` to try and match the longest (`paged`) first.
767
- */
768
- foreach ( [ 'paged', 'page' ] as $page_var ) {
769
- if ( isset( $query_vars[ $page_var ] ) ) {
770
- $page_regex = $bases['page'];
771
- preg_match( '/^\(\?:(?<slugs>[^\\)]+)\)/', $page_regex, $matches );
772
- if ( isset( $matches['slugs'] ) ) {
773
- $slugs = explode( '|', $matches['slugs'] );
774
- // The localized version is the last.
775
- $localized_slug = end( $slugs );
776
- // We use two different regular expressions to read pages, let's add both.
777
- $dynamic_matchers["{$page_regex}/(\d+)"] = "{$localized_slug}/{$query_vars[$page_var]}";
778
- $dynamic_matchers["{$page_regex}/([0-9]{1,})"] = "{$localized_slug}/{$query_vars[$page_var]}";
779
- }
780
  }
781
  }
782
 
@@ -809,8 +682,6 @@ class Tribe__Rewrite {
809
  * Returns a list of post types supported by the implementation.
810
  *
811
  * @since 4.9.11
812
- *
813
- * @return array<string> An array of post types supported and handled by the rewrite implementation.
814
  */
815
  protected function get_post_types() {
816
  throw new BadMethodCallException( 'Method get_post_types should be implemented by extending classes.' );
@@ -1006,23 +877,11 @@ class Tribe__Rewrite {
1006
  }
1007
  }
1008
 
1009
- /*
1010
- * If we have both the `name` query var and the post type one, then let's remove the `name` one.
1011
- */
1012
- if ( array_intersect( array_keys( $query_vars ), $this->get_post_types() ) ) {
1013
- unset( $query_vars['name'] );
1014
- }
1015
-
1016
  if ( ! empty( $url_query_vars ) ) {
1017
  // If the URL did have query vars keep them if not overridden by our resolution.
1018
  $query_vars = array_merge( $url_query_vars, $query_vars );
1019
  }
1020
 
1021
- // Prune the query vars to drop the empty `page` or `paged` ones.
1022
- $query_vars = array_filter( $query_vars, static function ( $value, $key ) {
1023
- return ! in_array( $key, [ 'paged', 'page' ] ) || (int) $value !== 0;
1024
- }, ARRAY_FILTER_USE_BOTH );
1025
-
1026
  /**
1027
  * Filters the array of parsed query variables after the class logic has been applied to it.
1028
  *
@@ -1046,6 +905,15 @@ class Tribe__Rewrite {
1046
  return $query_vars;
1047
  }
1048
 
 
 
 
 
 
 
 
 
 
1049
  /**
1050
  * Returns the "clean" version of a URL.
1051
  *
@@ -1078,7 +946,7 @@ class Tribe__Rewrite {
1078
  return home_url();
1079
  }
1080
 
1081
- $clean = $this->get_canonical_url( add_query_arg( $parsed_vars, home_url( '/' ) ), $force );
1082
 
1083
  $this->clean_url_cache[ $url ] = $clean;
1084
 
26
  * @var static
27
  */
28
  public static $instance;
 
 
 
 
 
 
 
 
29
 
30
  /**
31
  * WP_Rewrite Instance
178
 
179
  // Remove percent Placeholders on all items
180
  add_filter( 'rewrite_rules_array', array( $this, 'remove_percent_placeholders' ), 25 );
 
 
181
  }
182
 
183
  protected function remove_hooks() {
184
  remove_filter( 'generate_rewrite_rules', array( $this, 'filter_generate' ) );
185
  remove_filter( 'rewrite_rules_array', array( $this, 'remove_percent_placeholders' ), 25 );
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  }
187
 
188
  /**
403
  $query = (string) parse_url( $url, PHP_URL_QUERY );
404
  wp_parse_str( $query, $query_vars );
405
 
406
+ // Remove the `paged` query var if it's 1.
 
 
407
  if ( isset( $query_vars['paged'] ) && 1 === (int) $query_vars['paged'] ) {
 
408
  unset( $query_vars['paged'] );
409
  }
410
 
412
 
413
  $our_rules = $this->get_handled_rewrite_rules();
414
  $handled_query_vars = $this->get_rules_query_vars( $our_rules );
 
415
 
416
  if (
 
417
  empty( $our_rules )
418
+ || ! in_array( Arr::get( $query_vars, 'post_type', 'post' ), $this->get_post_types(), true )
 
 
 
 
419
  ) {
420
  $wp_canonical = redirect_canonical( $canonical_url, false );
421
  if ( empty( $wp_canonical ) ) {
462
  continue;
463
  }
464
 
465
+ $replace = array_map( static function ( $localized_matcher ) use ( $matched_vars ) {
466
  if ( ! is_array( $localized_matcher ) ) {
467
  // For the dates.
468
  return isset( $matched_vars[ $localized_matcher ] )
470
  : '';
471
  }
472
 
473
+ if ( ! isset( $matched_vars[ $localized_matcher['query_var'] ] ) ) {
 
 
 
 
 
 
 
474
  return '';
475
  }
476
 
 
 
 
 
 
477
  /*
478
  * We use `end` as, by default, the localized version of the slug in the current language will be at the
479
  * end of the array.
480
+ * @todo here we should keep a map, that has to generated at permalink flush time, to map locales/slugs.
481
  */
482
  return end( $localized_matcher['localized_slugs'] );
483
  }, $localized_matchers );
484
 
485
  // Include dynamic matchers now.
486
  $replace = array_merge( $dynamic_matchers, $replace );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
  $replaced = str_replace( array_keys( $replace ), $replace, $link_template );
488
 
489
  // Remove trailing chars.
539
  * @return array An array of rewrite rules handled by the implementation in the shape `[ <regex> => <path> ]`.
540
  */
541
  protected function get_handled_rewrite_rules() {
 
 
 
 
542
  // We need to make sure we are have WP_Rewrite setup
543
  if ( ! $this->rewrite ) {
544
  $this->setup();
545
  }
546
 
547
+ // While this is specific to The Events Calendar we're handling a small enough post type base to keep it here.
548
+ $pattern = '/post_type=tribe_(events|venue|organizer)/';
549
+ // Reverse the rules to try and match the most complex first.
550
+ $rules = isset( $this->rewrite->rules ) ? $this->rewrite->rules : [];
551
+ $handled_rewrite_rules = array_filter( $rules,
552
+ static function ( $rule_query_string ) use ( $pattern ) {
553
+ return preg_match( $pattern, $rule_query_string );
554
+ } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555
 
556
+ return $handled_rewrite_rules;
557
  }
558
 
559
  /**
564
  * @return array A map of localized regex matchers in the shape `[ <localized_regex> => <query_var> ]`.
565
  */
566
  protected function get_localized_matchers() {
 
 
567
  $bases = (array) $this->get_bases();
 
568
  $query_var_map = $this->get_matcher_to_query_var_map();
569
 
570
+ $localized_matchers = [];
 
571
  foreach ( $bases as $base => $localized_matcher ) {
 
 
 
 
 
 
 
572
  if ( isset( $query_var_map[ $base ] ) ) {
573
+ $localized_matchers[ $localized_matcher ] = [
 
574
  'query_var' => $query_var_map[ $base ],
575
  'en_slug' => $base,
576
  'localized_slugs' => [ $base ],
580
  if ( ! empty( $buffer['slugs'] ) ) {
581
  $slugs = explode( '|', $buffer['slugs'] );
582
 
583
+ $localized_matchers[ $localized_matcher ]['localized_slugs'] = array_map(
584
  static function ( $localized_slug ) {
585
  return str_replace( '\-', '-', $localized_slug );
586
  },
588
  );
589
 
590
  // The English version is the first.
591
+ $localized_matchers[ $localized_matcher ]['en_slug'] = reset( $slugs );
592
  }
593
  }
594
  }
595
 
 
 
596
  return $localized_matchers;
597
  }
598
 
599
  /**
600
+ * Returns a map relating localize matcher slugs to the corresponding query var.
601
  *
602
  * @since 4.9.11
603
  *
619
  * @return array A list of all the query vars handled in the rules.
620
  */
621
  protected function get_rules_query_vars( array $rules ) {
622
+ return array_unique( array_filter( array_merge( [], ...
623
+ array_values( array_map( static function ( $rule_string ) {
624
+ wp_parse_str( parse_url( $rule_string, PHP_URL_QUERY ), $vars );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
 
626
+ return array_keys( $vars );
627
+ }, $rules ) ) ) )
628
+ );
629
  }
630
 
631
  /**
640
  protected function get_dynamic_matchers( array $query_vars ) {
641
  $bases = (array) $this->get_bases();
642
  $dynamic_matchers = [];
643
+ if ( isset( $query_vars['paged'] ) ) {
644
+ $page_regex = $bases['page'];
645
+ preg_match( '/^\(\?:(?<slugs>[^\\)]+)\)/', $page_regex, $matches );
646
+ if ( isset( $matches['slugs'] ) ) {
647
+ $slugs = explode( '|', $matches['slugs'] );
648
+ // The localized version is the last.
649
+ $localized_slug = end( $slugs );
650
+ // We use two different regular expressions to read pages, let's add both.
651
+ $dynamic_matchers["{$page_regex}/(\d+)"] = "{$localized_slug}/{$query_vars['paged']}";
652
+ $dynamic_matchers["{$page_regex}/([0-9]{1,})"] = "{$localized_slug}/{$query_vars['paged']}";
 
 
 
 
 
 
 
 
653
  }
654
  }
655
 
682
  * Returns a list of post types supported by the implementation.
683
  *
684
  * @since 4.9.11
 
 
685
  */
686
  protected function get_post_types() {
687
  throw new BadMethodCallException( 'Method get_post_types should be implemented by extending classes.' );
877
  }
878
  }
879
 
 
 
 
 
 
 
 
880
  if ( ! empty( $url_query_vars ) ) {
881
  // If the URL did have query vars keep them if not overridden by our resolution.
882
  $query_vars = array_merge( $url_query_vars, $query_vars );
883
  }
884
 
 
 
 
 
 
885
  /**
886
  * Filters the array of parsed query variables after the class logic has been applied to it.
887
  *
905
  return $query_vars;
906
  }
907
 
908
+ /**
909
+ * Dumps the cache before destruction.
910
+ *
911
+ * @since 4.9.11
912
+ */
913
+ public function __destruct() {
914
+ $this->dump_cache();
915
+ }
916
+
917
  /**
918
  * Returns the "clean" version of a URL.
919
  *
946
  return home_url();
947
  }
948
 
949
+ $clean = $this->get_canonical_url( add_query_arg( $parsed_vars, home_url() ) );
950
 
951
  $this->clean_url_cache[ $url ] = $clean;
952
 
common/src/Tribe/Service_Providers/Debug_Bar.php CHANGED
@@ -1,5 +1,4 @@
1
  <?php
2
-
3
  /**
4
  * Hooks and manages the plugins Debug Bar integrations.
5
  *
@@ -35,7 +34,6 @@ class Tribe__Service_Providers__Debug_Bar extends tad_DI52_ServiceProvider {
35
  */
36
  $tribe_panels = apply_filters( 'tribe_debug_bar_panels', array(
37
  new Tribe__Debug_Bar__Panels__Context(),
38
- new Tribe__Debug_Bar__Panels__Json_Ld(),
39
  ) );
40
 
41
  if ( count( $tribe_panels ) > 0 ) {
1
  <?php
 
2
  /**
3
  * Hooks and manages the plugins Debug Bar integrations.
4
  *
34
  */
35
  $tribe_panels = apply_filters( 'tribe_debug_bar_panels', array(
36
  new Tribe__Debug_Bar__Panels__Context(),
 
37
  ) );
38
 
39
  if ( count( $tribe_panels ) > 0 ) {
common/src/Tribe/Service_Providers/Dialog.php DELETED
@@ -1,110 +0,0 @@
1
- <?php
2
-
3
- namespace Tribe\Service_Providers;
4
-
5
- /**
6
- * Class Dialog
7
- *
8
- * @since 4.10.0
9
- *
10
- * Handles the registration and creation of our async process handlers.
11
- */
12
- class Dialog extends \tad_DI52_ServiceProvider {
13
-
14
- /**
15
- * Binds and sets up implementations.
16
- *
17
- * @since 4.10.0
18
- */
19
- public function register() {
20
- tribe_singleton( 'dialog.view', '\Tribe\Dialog\View' );
21
-
22
- /**
23
- * Allows plugins to hook into the register action to register views, etc
24
- *
25
- * @since 4.10.0
26
- *
27
- * @param Tribe\Service_Providers\Dialog $dialog
28
- */
29
- do_action( 'tribe_dialog_register', $this );
30
-
31
- $this->hooks();
32
- }
33
-
34
- /**
35
- * Set up hooks for classes.
36
- *
37
- * @since 4.10.0
38
- */
39
- private function hooks() {
40
- add_action( 'tribe_common_loaded', [ $this, 'register_dialog_assets' ] );
41
- add_filter( 'tribe_template_public_namespace', [ $this, 'template_public_namespace' ], 10, 2 );
42
-
43
- /**
44
- * Allows plugins to hook into the hooks action to register their own hooks
45
- *
46
- * @since 4.10.0
47
- *
48
- * @param Tribe\Service_Providers\Dialog $dialog
49
- */
50
- do_action( 'tribe_dialog_hooks', $this );
51
- }
52
-
53
- /**
54
- * {@inheritdoc}
55
- *
56
- * @since 4.10.0
57
- */
58
- public function template_public_namespace( $namespace, $obj ) {
59
- if ( ! empty( $obj->template_namespace ) && 'dialog' === $obj->template_namespace ) {
60
- array_push( $namespace, 'dialog' );
61
- }
62
-
63
- return $namespace;
64
- }
65
-
66
- /**
67
- * Register assets associated with dialog
68
- *
69
- * @since 4.10.0
70
- */
71
- public function register_dialog_assets() {
72
- $main = \Tribe__Main::instance();
73
-
74
- tribe_asset(
75
- $main,
76
- 'tribe-dialog',
77
- 'dialog.css',
78
- [],
79
- [],
80
- [ 'groups' => 'tribe-dialog' ]
81
- );
82
-
83
- tribe_asset(
84
- $main,
85
- 'mt-a11y-dialog',
86
- 'vendor/faction23/a11y-dialog/a11y-dialog.js',
87
- [ 'underscore', 'tribe-common' ],
88
- [],
89
- [ 'groups' => 'tribe-dialog' ]
90
- );
91
-
92
- tribe_asset(
93
- $main,
94
- 'tribe-dialog-js',
95
- 'dialog.js',
96
- [ 'mt-a11y-dialog' ],
97
- [],
98
- [ 'groups' => 'tribe-dialog' ]
99
- );
100
-
101
- /**
102
- * Allows plugins to hook into the assets action to register their own assets
103
- *
104
- * @since 4.10.0
105
- *
106
- * @param Tribe\Service_Providers\Dialog $dialog
107
- */
108
- do_action( 'tribe_dialog_assets_registered', $this );
109
- }
110
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Service_Providers/PUE.php DELETED
@@ -1,50 +0,0 @@
1
- <?php
2
- namespace Tribe\Service_Providers;
3
-
4
- use Tribe\PUE\Update_Prevention;
5
-
6
- /**
7
- * Hooks and manages the implementation and loading of PUE.
8
- *
9
- * We are still moving pieces into this Service Provider, so look around
10
- * the `src/Tribe/PUE/` folder for other items that are not managed here
11
- * just yet.
12
- *
13
- * @since 4.9.12
14
- */
15
- class PUE extends \tad_DI52_ServiceProvider {
16
- /**
17
- * Binds and sets up implementations.
18
- *
19
- * @since 4.9.12
20
- */
21
- public function register() {
22
- $this->container->singleton( Update_Prevention::class, Update_Prevention::class );
23
-
24
- // Setup all of WP hooks associated with PUE.
25
- $this->register_hooks();
26
- }
27
-
28
- /**
29
- * Registers the provider handling all the 1st level filters and actions for PUE.
30
- *
31
- * @since 4.9.2
32
- */
33
- protected function register_hooks() {
34
- add_filter( 'upgrader_source_selection', [ $this, 'filter_upgrader_source_selection' ], 15, 4 );
35
- }
36
-
37
- /**
38
- * Filters the source file location for the upgrade package for the PUE Update_Prevention engine.
39
- *
40
- * @since 4.9.12
41
- *
42
- * @param string $source File source location.
43
- * @param string $remote_source Remote file source location.
44
- * @param WP_Upgrader $upgrader WP_Upgrader instance.
45
- * @param array $extra Extra arguments passed to hooked filters.
46
- */
47
- public function filter_upgrader_source_selection( $source, $remote_source, $upgrader, $extras ) {
48
- return $this->container->make( Update_Prevention::class )->filter_upgrader_source_selection( $source, $remote_source, $upgrader, $extras );
49
- }
50
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Service_Providers/Promoter.php DELETED
@@ -1,104 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Class Tribe__Service_Providers__Promoter_Connector
5
- *
6
- * @since 4.9
7
- *
8
- * Handles the registration and creation of our async process handlers.
9
- */
10
- class Tribe__Service_Providers__Promoter extends tad_DI52_ServiceProvider {
11
-
12
- /**
13
- * Binds and sets up implementations.
14
- */
15
- public function register() {
16
- tribe_singleton( 'promoter.auth', 'Tribe__Promoter__Auth' );
17
- tribe_singleton( 'promoter.pue', 'Tribe__Promoter__PUE', array( 'load' ) );
18
- tribe_singleton( 'promoter.view', 'Tribe__Promoter__View' );
19
-
20
- $this->hook();
21
- }
22
-
23
- /**
24
- * Setup hooks for classes.
25
- */
26
- private function hook() {
27
- add_action( 'template_redirect', tribe_callback( 'promoter.view', 'display_auth_check_view' ), 10, 0 );
28
- add_action( 'init', tribe_callback( 'promoter.view', 'add_rewrites' ) );
29
-
30
- tribe( 'promoter.pue' );
31
-
32
- add_filter(
33
- 'tribe_promoter_secret_key',
34
- tribe_callback( 'promoter.auth', 'filter_promoter_secret_key' )
35
- );
36
-
37
- // The usage of a high priority so we can push the icon to the end
38
- add_action( 'admin_bar_menu', array( $this, 'add_promoter_logo_on_admin_bar' ), 1000 );
39
- add_action( 'tribe_common_loaded', array( $this, 'add_promoter_assets' ) );
40
- }
41
-
42
- /**
43
- * Add Admin Bar link to the promoter website
44
- *
45
- * @since 4.9.2
46
- * @param $wp_admin_bar
47
- */
48
- public function add_promoter_logo_on_admin_bar( $wp_admin_bar ) {
49
- /** @var Tribe__Promoter__PUE $pue */
50
- $pue = tribe( 'promoter.pue' );
51
- if ( ! $pue->has_license_key() ) {
52
- return;
53
- }
54
-
55
- /**
56
- * It uses and inline SVG as will provider more flexibility for styling so we can change
57
- * the fill of the path property of the SVG so we can match the WP installations.
58
- */
59
- $args = array(
60
- 'id' => 'promoter-admin-bar',
61
- 'title' => sprintf(
62
- "<span class='promoter-admin-bar__icon'>%s</span><span class='promoter-admin-bar__text'>%s</span>",
63
- '<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21.07 20"><path d="M17.36 9.37l2.39-5.15h-7.44l-.59-2.59A1.72 1.72 0 0 0 10 0H0l.23.88L5.22 20h1.45l-2.5-9.56h3.51L8.24 13A1.73 1.73 0 0 0 10 14.66h11.07zm.19-3.74l-1.8 3.88 2.62 3.74h-7.13l2-3.84a1.58 1.58 0 0 0 .15-.69l-.71-3.09zM1.82 1.41H10a.31.31 0 0 1 .31.31l1.62 7.06a.32.32 0 0 1-.31.25H3.81z" fill="#82878c"/><path d="M4.5 2.74H7a2.24 2.24 0 0 1 2.17 1.65A1.17 1.17 0 0 1 7.92 6H6.73l.48 1.7H6zm2.62 1.08h-1l.32 1.11h1a.4.4 0 0 0 .44-.55.79.79 0 0 0-.76-.56z" fill="#82878c"/></svg>',
64
- 'Promoter'
65
- ),
66
- 'href' => 'https://promoter.theeventscalendar.com/',
67
- 'meta' => array(
68
- 'target' => '_blank',
69
- 'class' => 'promoter-admin-bar-link',
70
- ),
71
- );
72
- $wp_admin_bar->add_node( $args );
73
- }
74
-
75
- /**
76
- * Register assets associated with promoter
77
- *
78
- * @since 4.9.2
79
- */
80
- public function add_promoter_assets() {
81
- tribe_asset(
82
- Tribe__Main::instance(),
83
- 'promoter',
84
- 'promoter.css',
85
- array(),
86
- array( 'wp_enqueue_scripts', 'admin_enqueue_scripts' ),
87
- array(
88
- 'conditionals' => array( $this, 'should_load_promoter_styles' ),
89
- )
90
- );
91
- }
92
-
93
- /**
94
- * Only load the styles related to promoter if user is logged in and there's a valid license
95
- * for promoter
96
- *
97
- * @since 4.9.2
98
- *
99
- * @return bool
100
- */
101
- public function should_load_promoter_styles() {
102
- return is_user_logged_in() && tribe( 'promoter.pue' )->has_license_key();
103
- }
104
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Service_Providers/Promoter_Connector.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Tribe__Service_Providers__Promoter_Connector
5
+ *
6
+ * @since 4.9
7
+ *
8
+ * Handles the registration and creation of our async process handlers.
9
+ */
10
+ class Tribe__Service_Providers__Promoter_Connector extends tad_DI52_ServiceProvider {
11
+
12
+ /**
13
+ * Binds and sets up implementations.
14
+ */
15
+ public function register() {
16
+ tribe_singleton( 'promoter.auth', 'Tribe__Promoter__Auth' );
17
+ tribe_singleton( 'promoter.connector', 'Tribe__Promoter__Connector' );
18
+ tribe_singleton( 'promoter.pue', 'Tribe__Promoter__PUE', array( 'load' ) );
19
+ tribe_singleton( 'promoter.view', 'Tribe__Promoter__View' );
20
+
21
+ $this->hook();
22
+ }
23
+
24
+ /**
25
+ * Setup hooks for classes.
26
+ */
27
+ private function hook() {
28
+ add_action( 'template_redirect', tribe_callback( 'promoter.view', 'display_auth_check_view' ), 10, 0 );
29
+ add_action( 'init', tribe_callback( 'promoter.view', 'add_rewrites' ) );
30
+ // Add early-firing filter for user auth on REST.
31
+ add_filter( 'determine_current_user', tribe_callback( 'promoter.connector', 'authenticate_user_with_connector' ), 10, 1 );
32
+
33
+ tribe( 'promoter.pue' );
34
+
35
+ // The usage of a high priority so we can push the icon to the end
36
+ add_action( 'admin_bar_menu', array( $this, 'add_promoter_logo_on_admin_bar' ), 1000 );
37
+ add_action( 'tribe_common_loaded', array( $this, 'add_promoter_assets' ) );
38
+ }
39
+
40
+ /**
41
+ * Add Admin Bar link to the promoter website
42
+ *
43
+ * @since 4.9.2
44
+ * @param $wp_admin_bar
45
+ */
46
+ public function add_promoter_logo_on_admin_bar( $wp_admin_bar ) {
47
+ /** @var Tribe__Promoter__PUE $pue */
48
+ $pue = tribe( 'promoter.pue' );
49
+ if ( ! $pue->has_license_key() ) {
50
+ return;
51
+ }
52
+
53
+ /**
54
+ * It uses and inline SVG as will provider more flexibility for styling so we can change
55
+ * the fill of the path property of the SVG so we can match the WP installations.
56
+ */
57
+ $args = array(
58
+ 'id' => 'promoter-admin-bar',
59
+ 'title' => sprintf(
60
+ "<span class='promoter-admin-bar__icon'>%s</span><span class='promoter-admin-bar__text'>%s</span>",
61
+ '<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21.07 20"><path d="M17.36 9.37l2.39-5.15h-7.44l-.59-2.59A1.72 1.72 0 0 0 10 0H0l.23.88L5.22 20h1.45l-2.5-9.56h3.51L8.24 13A1.73 1.73 0 0 0 10 14.66h11.07zm.19-3.74l-1.8 3.88 2.62 3.74h-7.13l2-3.84a1.58 1.58 0 0 0 .15-.69l-.71-3.09zM1.82 1.41H10a.31.31 0 0 1 .31.31l1.62 7.06a.32.32 0 0 1-.31.25H3.81z" fill="#82878c"/><path d="M4.5 2.74H7a2.24 2.24 0 0 1 2.17 1.65A1.17 1.17 0 0 1 7.92 6H6.73l.48 1.7H6zm2.62 1.08h-1l.32 1.11h1a.4.4 0 0 0 .44-.55.79.79 0 0 0-.76-.56z" fill="#82878c"/></svg>',
62
+ 'Promoter'
63
+ ),
64
+ 'href' => 'https://promoter.theeventscalendar.com/',
65
+ 'meta' => array(
66
+ 'target' => '_blank',
67
+ 'class' => 'promoter-admin-bar-link',
68
+ ),
69
+ );
70
+ $wp_admin_bar->add_node( $args );
71
+ }
72
+
73
+ /**
74
+ * Register assets associated with promoter
75
+ *
76
+ * @since 4.9.2
77
+ */
78
+ public function add_promoter_assets() {
79
+ tribe_asset(
80
+ Tribe__Main::instance(),
81
+ 'promoter',
82
+ 'promoter.css',
83
+ array(),
84
+ array( 'wp_enqueue_scripts', 'admin_enqueue_scripts' ),
85
+ array(
86
+ 'conditionals' => array( $this, 'should_load_promoter_styles' ),
87
+ )
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Only load the styles related to promoter if user is logged in and there's a valid license
93
+ * for promoter
94
+ *
95
+ * @since 4.9.2
96
+ *
97
+ * @return bool
98
+ */
99
+ public function should_load_promoter_styles() {
100
+ return is_user_logged_in() && tribe( 'promoter.pue' )->has_license_key();
101
+ }
102
+ }
common/src/Tribe/Service_Providers/Shortcodes.php DELETED
@@ -1,80 +0,0 @@
1
- <?php
2
- namespace Tribe\Service_Providers;
3
-
4
- use Tribe\Shortcode\Manager;
5
-
6
- /**
7
- * Class Shortcode
8
- *
9
- * @since 4.12.0
10
- *
11
- * @package Tribe\Service_Providers
12
- */
13
- class Shortcodes extends \tad_DI52_ServiceProvider {
14
-
15
- /**
16
- * Binds and sets up implementations.
17
- *
18
- * @since 4.12.0
19
- */
20
- public function register() {
21
- if ( ! static::is_active() ) {
22
- return;
23
- }
24
-
25
- $this->container->singleton( Manager::class, Manager::class );
26
-
27
- $this->register_hooks();
28
- $this->register_assets();
29
-
30
- $this->container->singleton( static::class, $this );
31
- }
32
-
33
- /**
34
- * Static method wrapper around a filter to allow full deactivation of this provider
35
- *
36
- * @since 4.12.0
37
- *
38
- * @return boolean If this service provider is active.
39
- */
40
- public static function is_active() {
41
- /**
42
- * Allows filtering to deactivate all shortcodes loading.
43
- *
44
- * @since 4.12.0
45
- *
46
- * @param boolean $is_active If shortcodes should be loaded or not.
47
- */
48
- return apply_filters( 'tribe_shortcodes_is_active', true );
49
- }
50
-
51
- /**
52
- * Register all the assets associated with this service provider.
53
- *
54
- * @since 4.12.0
55
- */
56
- protected function register_assets() {
57
-
58
- }
59
-
60
- /**
61
- * Registers the provider handling all the 1st level filters and actions for this service provider.
62
- *
63
- * @since 4.12.0
64
- */
65
- protected function register_hooks() {
66
- add_action( 'init', [ $this, 'action_add_shortcodes' ], 20 );
67
- }
68
-
69
- /**
70
- * Adds the new shortcodes, this normally will trigger on `init@P20` due to how we the
71
- * v1 is added on `init@P10` and we remove them on `init@P15`.
72
- *
73
- * It's important to leave gaps on priority for better injection.
74
- *
75
- * @since 4.12.0
76
- */
77
- public function action_add_shortcodes() {
78
- $this->container->make( Manager::class )->add_shortcodes();
79
- }
80
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Service_Providers/Tooltip.php CHANGED
@@ -1,5 +1,4 @@
1
  <?php
2
- namespace Tribe\Service_Providers;
3
 
4
  /**
5
  * Class Tribe__Service_Providers__Tooltip
@@ -8,7 +7,7 @@ namespace Tribe\Service_Providers;
8
  *
9
  * Handles the registration and creation of our async process handlers.
10
  */
11
- class Tooltip extends \tad_DI52_ServiceProvider {
12
 
13
  /**
14
  * Binds and sets up implementations.
@@ -16,7 +15,7 @@ class Tooltip extends \tad_DI52_ServiceProvider {
16
  * @since 4.9.8
17
  */
18
  public function register() {
19
- tribe_singleton( 'tooltip.view', '\Tribe\Tooltip\View' );
20
 
21
  $this->hook();
22
  }
@@ -36,24 +35,12 @@ class Tooltip extends \tad_DI52_ServiceProvider {
36
  * @since 4.9.8
37
  */
38
  public function add_tooltip_assets() {
39
- $main = \Tribe__Main::instance();
40
-
41
  tribe_asset(
42
- $main,
43
  'tribe-tooltip',
44
  'tooltip.css',
45
- [ 'tribe-common-skeleton-style' ],
46
- [ 'wp_enqueue_scripts', 'admin_enqueue_scripts' ],
47
- [ 'groups' => 'tribe-tooltip' ]
48
- );
49
-
50
- tribe_asset(
51
- $main,
52
- 'tribe-tooltip-js',
53
- 'tooltip.js',
54
- [ 'jquery', 'tribe-common' ],
55
  [],
56
- [ 'groups' => 'tribe-tooltip' ]
57
  );
58
  }
59
  }
1
  <?php
 
2
 
3
  /**
4
  * Class Tribe__Service_Providers__Tooltip
7
  *
8
  * Handles the registration and creation of our async process handlers.
9
  */
10
+ class Tribe__Service_Providers__Tooltip extends tad_DI52_ServiceProvider {
11
 
12
  /**
13
  * Binds and sets up implementations.
15
  * @since 4.9.8
16
  */
17
  public function register() {
18
+ tribe_singleton( 'tooltip.view', 'Tribe__Tooltip__View' );
19
 
20
  $this->hook();
21
  }
35
  * @since 4.9.8
36
  */
37
  public function add_tooltip_assets() {
 
 
38
  tribe_asset(
39
+ Tribe__Main::instance(),
40
  'tribe-tooltip',
41
  'tooltip.css',
 
 
 
 
 
 
 
 
 
 
42
  [],
43
+ [ 'wp_enqueue_scripts', 'admin_enqueue_scripts' ]
44
  );
45
  }
46
  }
common/src/Tribe/Settings.php CHANGED
@@ -295,51 +295,45 @@ if ( ! class_exists( 'Tribe__Settings' ) ) {
295
  * @return void
296
  */
297
  public function initTabs() {
298
- if (
299
- empty( $_GET['page'] )
300
- || $_GET['page'] != $this->adminSlug
301
- ) {
302
- return;
303
- }
304
-
305
- // Load settings tab-specific helpers and enhancements
306
- Tribe__Admin__Live_Date_Preview::instance();
307
-
308
- do_action( 'tribe_settings_do_tabs' ); // this is the hook to use to add new tabs
309
- $this->tabs = (array) apply_filters( 'tribe_settings_tabs', [] );
310
- $this->allTabs = (array) apply_filters( 'tribe_settings_all_tabs', [] );
311
- $this->noSaveTabs = (array) apply_filters( 'tribe_settings_no_save_tabs', [] );
312
-
313
- if ( is_network_admin() ) {
314
- $this->defaultTab = apply_filters( 'tribe_settings_default_tab_network', 'network' );
315
- $this->currentTab = apply_filters( 'tribe_settings_current_tab', ( isset( $_GET['tab'] ) && $_GET['tab'] ) ? esc_attr( $_GET['tab'] ) : $this->defaultTab );
316
- $this->url = apply_filters(
317
- 'tribe_settings_url', add_query_arg(
318
- [
319
- 'page' => $this->adminSlug,
320
- 'tab' => $this->currentTab,
321
- ], network_admin_url( 'settings.php' )
322
- )
323
- );
324
- } else {
325
- $tabs_keys = array_keys( $this->tabs );
326
- $this->defaultTab = in_array( apply_filters( 'tribe_settings_default_tab', 'general' ), $tabs_keys ) ? apply_filters( 'tribe_settings_default_tab', 'general' ) : $tabs_keys[0];
327
- $this->currentTab = apply_filters( 'tribe_settings_current_tab', ( isset( $_GET['tab'] ) && $_GET['tab'] ) ? esc_attr( $_GET['tab'] ) : $this->defaultTab );
328
- $this->url = apply_filters(
329
- 'tribe_settings_url', add_query_arg(
330
- [
331
- 'page' => $this->adminSlug,
332
- 'tab' => $this->currentTab,
333
- ],
334
- admin_url( self::$parent_page )
335
- )
336
- );
337
  }
338
-
339
- $this->fields_for_save = (array) apply_filters( 'tribe_settings_fields', [] );
340
- do_action( 'tribe_settings_after_do_tabs' );
341
- $this->fields = (array) apply_filters( 'tribe_settings_fields', [] );
342
- $this->validate();
343
  }
344
 
345
  /**
295
  * @return void
296
  */
297
  public function initTabs() {
298
+ if ( isset( $_GET['page'] ) && $_GET['page'] == $this->adminSlug ) {
299
+ // Load settings tab-specific helpers and enhancements
300
+ Tribe__Admin__Live_Date_Preview::instance();
301
+
302
+ do_action( 'tribe_settings_do_tabs' ); // this is the hook to use to add new tabs
303
+ $this->tabs = (array) apply_filters( 'tribe_settings_tabs', array() );
304
+ $this->allTabs = (array) apply_filters( 'tribe_settings_all_tabs', array() );
305
+ $this->noSaveTabs = (array) apply_filters( 'tribe_settings_no_save_tabs', array() );
306
+ if ( is_network_admin() ) {
307
+ $this->defaultTab = apply_filters( 'tribe_settings_default_tab_network', 'network' );
308
+ $this->currentTab = apply_filters( 'tribe_settings_current_tab', ( isset( $_GET['tab'] ) && $_GET['tab'] ) ? esc_attr( $_GET['tab'] ) : $this->defaultTab );
309
+ $this->url = apply_filters(
310
+ 'tribe_settings_url', add_query_arg(
311
+ array(
312
+ 'page' => $this->adminSlug,
313
+ 'tab' => $this->currentTab,
314
+ ), network_admin_url( 'settings.php' )
315
+ )
316
+ );
317
+ }
318
+ if ( ! is_network_admin() ) {
319
+ $tabs_keys = array_keys( $this->tabs );
320
+ $this->defaultTab = in_array( apply_filters( 'tribe_settings_default_tab', 'general' ), $tabs_keys ) ? apply_filters( 'tribe_settings_default_tab', 'general' ) : $tabs_keys[0];
321
+ $this->currentTab = apply_filters( 'tribe_settings_current_tab', ( isset( $_GET['tab'] ) && $_GET['tab'] ) ? esc_attr( $_GET['tab'] ) : $this->defaultTab );
322
+ $this->url = apply_filters(
323
+ 'tribe_settings_url', add_query_arg(
324
+ array(
325
+ 'page' => $this->adminSlug,
326
+ 'tab' => $this->currentTab,
327
+ ),
328
+ admin_url( self::$parent_page )
329
+ )
330
+ );
331
+ }
332
+ $this->fields_for_save = (array) apply_filters( 'tribe_settings_fields', array() );
333
+ do_action( 'tribe_settings_after_do_tabs' );
334
+ $this->fields = (array) apply_filters( 'tribe_settings_fields', array() );
335
+ $this->validate();
 
336
  }
 
 
 
 
 
337
  }
338
 
339
  /**
common/src/Tribe/Settings_Manager.php CHANGED
@@ -1,7 +1,6 @@
1
  <?php
2
- class Tribe__Settings_Manager {
3
- const OPTION_CACHE_VAR_NAME = 'Tribe__Settings_Manager:option_cache';
4
 
 
5
  protected static $network_options;
6
  public static $tribe_events_mu_defaults;
7
 
@@ -30,28 +29,6 @@ class Tribe__Settings_Manager {
30
  add_action( 'tribe_settings_do_tabs', array( $this, 'do_setting_tabs' ) );
31
  add_action( 'tribe_settings_do_tabs', array( $this, 'do_network_settings_tab' ), 400 );
32
  add_action( 'tribe_settings_validate_tab_network', array( $this, 'save_all_tabs_hidden' ) );
33
- add_action( 'updated_option', [ $this, 'update_options_cache' ], 10, 3 );
34
- }
35
-
36
- /**
37
- * For performance reasons our options are saved in memory, but we need to make sure we update it when WordPress
38
- * updates the variable directly.
39
- *
40
- * @since 4.11.0
41
- *
42
- * @param string $option Name of the updated option.
43
- * @param mixed $old_value The old option value.
44
- * @param mixed $value The new option value.
45
- *
46
- * @return void
47
- */
48
- public function update_options_cache( $option, $old_value, $value ) {
49
- // Bail when no our option.
50
- if ( Tribe__Main::OPTIONNAME !== $option ) {
51
- return;
52
- }
53
-
54
- tribe_set_var( self::OPTION_CACHE_VAR_NAME, $value );
55
  }
56
 
57
  /**
@@ -69,9 +46,6 @@ class Tribe__Settings_Manager {
69
  * @return void
70
  */
71
  public function do_setting_tabs() {
72
- // Make sure Thickbox is available regardless of which admin page we're on
73
- add_thickbox();
74
-
75
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-general.php';
76
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-display.php';
77
 
@@ -89,14 +63,11 @@ class Tribe__Settings_Manager {
89
  * @return array of options
90
  */
91
  public static function get_options() {
92
- $options = tribe_get_var( self::OPTION_CACHE_VAR_NAME, [] );
93
-
94
- if ( empty( $options ) ) {
95
- $options = (array) get_option( Tribe__Main::OPTIONNAME, [] );
96
-
97
- tribe_set_var( self::OPTION_CACHE_VAR_NAME, $options );
98
- }
99
-
100
  return $options;
101
  }
102
 
@@ -112,7 +83,7 @@ class Tribe__Settings_Manager {
112
  if ( ! $option_name ) {
113
  return null;
114
  }
115
- $options = static::get_options();
116
 
117
  $option = $default;
118
  if ( array_key_exists( $option_name, $options ) ) {
@@ -136,16 +107,10 @@ class Tribe__Settings_Manager {
136
  if ( ! is_array( $options ) ) {
137
  return false;
138
  }
139
- if ( true === $apply_filters ) {
140
  $options = apply_filters( 'tribe-events-save-options', $options );
141
  }
142
- $updated = update_option( Tribe__Main::OPTIONNAME, $options );
143
-
144
- if ( $updated ) {
145
- tribe_set_var( self::OPTION_CACHE_VAR_NAME, $options );
146
- }
147
-
148
- return $updated;
149
  }
150
 
151
  /**
@@ -157,10 +122,10 @@ class Tribe__Settings_Manager {
157
  * @return bool
158
  */
159
  public static function set_option( $name, $value ) {
 
 
160
  $options = self::get_options();
161
- $options[ $name ] = $value;
162
-
163
- return self::set_options( $options );
164
  }
165
 
166
  /**
@@ -171,7 +136,7 @@ class Tribe__Settings_Manager {
171
  */
172
  public static function get_network_options() {
173
  if ( ! isset( self::$network_options ) ) {
174
- $options = get_site_option( Tribe__Main::OPTIONNAMENETWORK, array() );
175
  self::$network_options = apply_filters( 'tribe_get_network_options', $options );
176
  }
177
 
@@ -296,10 +261,6 @@ class Tribe__Settings_Manager {
296
  * Create the help tab
297
  */
298
  public function do_help_tab() {
299
- /**
300
- * Include Help tab Assets here
301
- */
302
-
303
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-help.php';
304
  }
305
 
1
  <?php
 
 
2
 
3
+ class Tribe__Settings_Manager {
4
  protected static $network_options;
5
  public static $tribe_events_mu_defaults;
6
 
29
  add_action( 'tribe_settings_do_tabs', array( $this, 'do_setting_tabs' ) );
30
  add_action( 'tribe_settings_do_tabs', array( $this, 'do_network_settings_tab' ), 400 );
31
  add_action( 'tribe_settings_validate_tab_network', array( $this, 'save_all_tabs_hidden' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  }
33
 
34
  /**
46
  * @return void
47
  */
48
  public function do_setting_tabs() {
 
 
 
49
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-general.php';
50
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-display.php';
51
 
63
  * @return array of options
64
  */
65
  public static function get_options() {
66
+ $options = (array) get_option( Tribe__Main::OPTIONNAME, array() );
67
+ if ( has_filter( 'tribe_get_options' ) ) {
68
+ _deprecated_function( 'tribe_get_options', '3.10', 'option_' . Tribe__Main::OPTIONNAME );
69
+ $options = apply_filters( 'tribe_get_options', $options );
70
+ }
 
 
 
71
  return $options;
72
  }
73
 
83
  if ( ! $option_name ) {
84
  return null;
85
  }
86
+ $options = self::get_options();
87
 
88
  $option = $default;
89
  if ( array_key_exists( $option_name, $options ) ) {
107
  if ( ! is_array( $options ) ) {
108
  return false;
109
  }
110
+ if ( $apply_filters == true ) {
111
  $options = apply_filters( 'tribe-events-save-options', $options );
112
  }
113
+ return update_option( Tribe__Main::OPTIONNAME, $options );
 
 
 
 
 
 
114
  }
115
 
116
  /**
122
  * @return bool
123
  */
124
  public static function set_option( $name, $value ) {
125
+ $newOption = array();
126
+ $newOption[ $name ] = $value;
127
  $options = self::get_options();
128
+ return self::set_options( wp_parse_args( $newOption, $options ) );
 
 
129
  }
130
 
131
  /**
136
  */
137
  public static function get_network_options() {
138
  if ( ! isset( self::$network_options ) ) {
139
+ $options = get_site_option( Tribe__Main::OPTIONNAMENETWORK, array() );
140
  self::$network_options = apply_filters( 'tribe_get_network_options', $options );
141
  }
142
 
261
  * Create the help tab
262
  */
263
  public function do_help_tab() {
 
 
 
 
264
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-help.php';
265
  }
266
 
common/src/Tribe/Shortcode/Manager.php DELETED
@@ -1,107 +0,0 @@
1
- <?php
2
- /**
3
- * Shortcodes manager for Tribe plugins.
4
- *
5
- * @package Tribe\Shortcode
6
- * @since 4.12.0
7
- */
8
- namespace Tribe\Shortcode;
9
-
10
- /**
11
- * Class Shortcode Manager.
12
- *
13
- * @since 4.12.0
14
- *
15
- * @package Tribe\Shortcode
16
- */
17
- class Manager {
18
- /**
19
- * Get the list of shortcodes available for handling.
20
- *
21
- * @since 4.12.0
22
- *
23
- * @return array An associative array of shortcodes in the shape `[ <slug> => <class> ]`
24
- */
25
- public function get_registered_shortcodes() {
26
- $shortcodes = [];
27
-
28
- /**
29
- * Allow the registering of shortcodes into the our Tribe plugins.
30
- *
31
- * @since 4.12.0
32
- *
33
- * @var array An associative array of shortcodes in the shape `[ <slug> => <class> ]`.
34
- */
35
- $shortcodes = apply_filters( 'tribe_shortcodes', $shortcodes );
36
-
37
- return $shortcodes;
38
- }
39
-
40
- /**
41
- * Verifies if a given shortcode slug is registered for handling.
42
- *
43
- * @since 4.12.0
44
- *
45
- * @param string $slug Which slug we are checking if is registered.
46
- *
47
- * @return bool Whether a shortcode is registered or not.
48
- */
49
- public function is_shortcode_registered( $slug ) {
50
- $registered_shortcodes = $this->get_registered_shortcodes();
51
- return isset( $registered_shortcodes[ $slug ] );
52
- }
53
-
54
- /**
55
- * Verifies if a given shortcode class name is registered for handling.
56
- *
57
- * @since 4.12.0
58
- *
59
- * @param string $class_name Which class name we are checking if is registered.
60
- *
61
- * @return bool Whether a shortcode is registered, by class.
62
- */
63
- public function is_shortcode_registered_by_class( $class_name ) {
64
- $registered_shortcodes = $this->get_registered_shortcodes();
65
- return in_array( $class_name, $registered_shortcodes );
66
- }
67
-
68
- /**
69
- * Add new shortcodes handler to catch the correct strings.
70
- *
71
- * @since 4.12.0
72
- */
73
- public function add_shortcodes() {
74
- $registered_shortcodes = $this->get_registered_shortcodes();
75
-
76
- // Add to WordPress all of the registered Shortcodes
77
- foreach ( $registered_shortcodes as $shortcode => $class_name ) {
78
- add_shortcode( $shortcode, [ $this, 'render_shortcode' ] );
79
- }
80
- }
81
-
82
- /**
83
- * Makes sure we are correctly handling the Shortcodes we manage.
84
- *
85
- * @since 4.12.0
86
- *
87
- * @param array $arguments Set of arguments passed to the Shortcode at hand.
88
- * @param string $content Contents passed to the shortcode, inside of the open and close brackets.
89
- * @param string $shortcode Which shortcode tag are we handling here.
90
- *
91
- * @return string The rendered shortcode HTML.
92
- */
93
- public function render_shortcode( $arguments, $content, $shortcode ) {
94
- $registered_shortcodes = $this->get_registered_shortcodes();
95
-
96
- // Bail when we try to handle an unregistered shortcode (shouldn't happen)
97
- if ( ! $this->is_shortcode_registered( $shortcode ) ) {
98
- return false;
99
- }
100
-
101
- /** @var Shortcode_Interface $instance */
102
- $instance = new $registered_shortcodes[ $shortcode ];
103
- $instance->setup( $arguments, $content );
104
-
105
- return $instance->get_html();
106
- }
107
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Shortcode/Shortcode_Abstract.php DELETED
@@ -1,247 +0,0 @@
1
- <?php
2
-
3
- namespace Tribe\Shortcode;
4
-
5
- use Tribe__Utils__Array as Arr;
6
-
7
- /**
8
- * The abstract all shortcodes should implement.
9
- *
10
- * @package Tribe\Shortcode
11
- *
12
- * @since 4.12.0
13
- */
14
- abstract class Shortcode_Abstract implements Shortcode_Interface {
15
- /**
16
- * Slug of the current shortcode.
17
- *
18
- * @since 4.12.0
19
- *
20
- * @var string
21
- */
22
- protected $slug;
23
-
24
- /**
25
- * Default arguments to be merged into final arguments of the shortcode.
26
- *
27
- * @since 4.12.0
28
- *
29
- * @var array
30
- */
31
- protected $default_arguments = [];
32
-
33
- /**
34
- * Array map allowing aliased shortcode arguments.
35
- *
36
- * The array keys are aliases of the array values (i.e. the "real" shortcode attributes to parse).
37
- * Example array: [ 'alias' => 'canonical', 'from' => 'to', 'that' => 'becomes_this' ]
38
- * Example shortcode usage: [some_tag alias=17 to='Fred'] will be parsed as [some_tag canonical=17 to='Fred']
39
- *
40
- * @since 4.12.2
41
- *
42
- * @var array<string,string>
43
- */
44
- protected $aliased_arguments = [];
45
-
46
- /**
47
- * Array of callbacks for arguments validation.
48
- *
49
- * @since 4.12.0
50
- *
51
- * @var array
52
- */
53
- protected $validate_arguments_map = [];
54
-
55
- /**
56
- * Arguments of the current shortcode.
57
- *
58
- * @since 4.12.0
59
- *
60
- * @var array
61
- */
62
- protected $arguments;
63
-
64
- /**
65
- * Content of the current shortcode.
66
- *
67
- * @since 4.12.0
68
- *
69
- * @var string
70
- */
71
- protected $content;
72
-
73
- /**
74
- * {@inheritDoc}
75
- */
76
- public function setup( $arguments, $content ) {
77
- $this->arguments = $this->parse_arguments( (array) $arguments );
78
- $this->content = $content;
79
- }
80
-
81
- /**
82
- * {@inheritDoc}
83
- */
84
- public function set_aliased_arguments( array $alias_map ) {
85
- $this->aliased_arguments = Arr::filter_to_flat_scalar_associative_array( (array) $alias_map );
86
- }
87
-
88
- /**
89
- * {@inheritDoc}
90
- */
91
- public function get_aliased_arguments() {
92
- return $this->aliased_arguments;
93
- }
94
-
95
- /**
96
- * {@inheritDoc}
97
- */
98
- public function parse_arguments( array $arguments ) {
99
- $arguments = Arr::parse_associative_array_alias( (array) $arguments, (array) $this->get_aliased_arguments() );
100
- $arguments = shortcode_atts( $this->get_default_arguments(), $arguments, $this->slug );
101
-
102
- return $this->validate_arguments( $arguments );
103
- }
104
-
105
- /**
106
- * {@inheritDoc}
107
- */
108
- public function validate_arguments( array $arguments ) {
109
- $validate_arguments_map = $this->get_validated_arguments_map();
110
- foreach ( $validate_arguments_map as $key => $callback ) {
111
- $arguments[ $key ] = $callback( isset( $arguments[ $key ] ) ? $arguments[ $key ] : null );
112
- }
113
-
114
- return $arguments;
115
- }
116
-
117
- /**
118
- * {@inheritDoc}
119
- */
120
- public function get_registration_slug() {
121
- return $this->slug;
122
- }
123
-
124
- /**
125
- * {@inheritDoc}
126
- */
127
- public function get_validated_arguments_map() {
128
- /**
129
- * Applies a filter to instance arguments validation callbacks.
130
- *
131
- * @since 4.12.0
132
- *
133
- * @param array $validate_arguments_map Current set of callbacks for arguments.
134
- * @param static $instance Which instance of shortcode we are dealing with.
135
- */
136
- $validate_arguments_map = apply_filters( 'tribe_shortcode_validate_arguments_map', $this->validate_arguments_map, $this );
137
-
138
- $registration_slug = $this->get_registration_slug();
139
-
140
- /**
141
- * Applies a filter to instance arguments validation callbacks based on the registration slug of the shortcode.
142
- *
143
- * @since 4.12.0
144
- *
145
- * @param array $validate_arguments_map Current set of callbacks for arguments.
146
- * @param static $instance Which instance of shortcode we are dealing with.
147
- */
148
- $validate_arguments_map = apply_filters( "tribe__shortcode_{$registration_slug}_validate_arguments_map", $validate_arguments_map, $this );
149
-
150
- return $validate_arguments_map;
151
- }
152
-
153
- /**
154
- * {@inheritDoc}
155
- */
156
- public function get_arguments() {
157
- /**
158
- * Applies a filter to instance arguments.
159
- *
160
- * @since 4.12.0
161
- *
162
- * @param array $arguments Current set of arguments.
163
- * @param static $instance Which instance of shortcode we are dealing with.
164
- */
165
- $arguments = apply_filters( 'tribe_shortcode_arguments', $this->arguments, $this );
166
-
167
- $registration_slug = $this->get_registration_slug();
168
-
169
- /**
170
- * Applies a filter to instance arguments based on the registration slug of the shortcode.
171
- *
172
- * @since 4.12.0
173
- *
174
- * @param array $arguments Current set of arguments.
175
- * @param static $instance Which instance of shortcode we are dealing with.
176
- */
177
- $arguments = apply_filters( "tribe_shortcode_{$registration_slug}_arguments", $arguments, $this );
178
-
179
- return $arguments;
180
- }
181
-
182
- /**
183
- * {@inheritDoc}
184
- */
185
- public function get_argument( $index, $default = null ) {
186
- $arguments = $this->get_arguments();
187
- $argument = Arr::get( $arguments, $index, $default );
188
-
189
- /**
190
- * Applies a filter to a specific shortcode argument, catch all for all shortcodes.
191
- *
192
- * @since 4.12.0
193
- *
194
- * @param mixed $argument The argument.
195
- * @param array $index Which index we indent to fetch from the arguments.
196
- * @param array $default Default value if it doesn't exist.
197
- * @param static $instance Which instance of shortcode we are dealing with.
198
- */
199
- $argument = apply_filters( 'tribe_shortcode_argument', $argument, $index, $default, $this );
200
-
201
- $registration_slug = $this->get_registration_slug();
202
-
203
- /**
204
- * Applies a filter to a specific shortcode argument, to a particular registration slug.
205
- *
206
- * @since 4.12.0
207
- *
208
- * @param mixed $argument The argument value.
209
- * @param array $index Which index we indent to fetch from the arguments.
210
- * @param array $default Default value if it doesn't exist.
211
- * @param static $instance Which instance of shortcode we are dealing with.
212
- */
213
- $argument = apply_filters( "tribe_shortcode_{$registration_slug}_argument", $argument, $index, $default, $this );
214
-
215
- return $argument;
216
- }
217
-
218
- /**
219
- * {@inheritDoc}
220
- */
221
- public function get_default_arguments() {
222
- /**
223
- * Applies a filter to instance default arguments.
224
- *
225
- * @since 4.12.0
226
- *
227
- * @param array $default_arguments Current set of default arguments.
228
- * @param static $instance Which instance of shortcode we are dealing with.
229
- */
230
- $default_arguments = apply_filters( 'tribe_shortcode_default_arguments', $this->default_arguments, $this );
231
-
232
- $registration_slug = $this->get_registration_slug();
233
-
234
- /**
235
- * Applies a filter to instance default arguments based on the registration slug of the shortcode.
236
- *
237
- * @since 4.12.0
238
- *
239
- * @param array $default_arguments Current set of default arguments.
240
- * @param static $instance Which instance of shortcode we are dealing with.
241
- */
242
- $default_arguments = apply_filters( "tribe_shortcode_{$registration_slug}_default_arguments", $default_arguments, $this );
243
-
244
- return $default_arguments;
245
- }
246
-
247
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Shortcode/Shortcode_Interface.php DELETED
@@ -1,125 +0,0 @@
1
- <?php
2
-
3
- namespace Tribe\Shortcode;
4
-
5
- /**
6
- * Interface Shortcode_Interface
7
- *
8
- * @package Tribe\Shortcode
9
- *
10
- * @since 4.12.0
11
- */
12
- interface Shortcode_Interface {
13
-
14
- /**
15
- * Returns the shortcode slug that allows the shortcode to be built via the shortcode class by slug.
16
- *
17
- * @since 4.12.0
18
- *
19
- * @return string The shortcode slug.
20
- */
21
- public function get_registration_slug();
22
-
23
- /**
24
- * Configures the base variables for an instance of shortcode.
25
- *
26
- * @since 4.12.0
27
- *
28
- * @param array|string $arguments Set of arguments passed to the Shortcode at hand. Empty string if no args.
29
- * @param string $content Contents passed to the shortcode, inside of the open and close brackets.
30
- */
31
- public function setup( $arguments, $content );
32
-
33
- /**
34
- * Sets the aliased arguments array.
35
- *
36
- * @see Tribe__Utils__Array::parse_associative_array_alias() The expected format.
37
- *
38
- * @since 4.12.2
39
- *
40
- * @param array $alias_map An associative array of aliases: key as alias, value as mapped canonical.
41
- * Example: [ 'alias' => 'canonical', 'from' => 'to', 'that' => 'becomes_this' ]
42
- */
43
- public function set_aliased_arguments( array $alias_map );
44
-
45
- /**
46
- * Gets the aliased arguments array.
47
- *
48
- * @since 4.12.2
49
- *
50
- * @return array<string,string> The associative array map of aliases and their canonical arguments.
51
- */
52
- public function get_aliased_arguments();
53
-
54
- /**
55
- * Returns the arguments for the shortcode parsed correctly with defaults applied.
56
- *
57
- * @since 4.12.0
58
- *
59
- * @param array $arguments Set of arguments passed to the Shortcode at hand.
60
- *
61
- * @return array<string,mixed> The parsed shortcode arguments map.
62
- */
63
- public function parse_arguments( array $arguments );
64
-
65
- /**
66
- * Returns the array of arguments for this shortcode after applying the validation callbacks.
67
- *
68
- * @since 4.12.0
69
- *
70
- * @param array $arguments Set of arguments passed to the Shortcode at hand.
71
- *
72
- * @return array<string,mixed> The validated shortcode arguments map.
73
- */
74
- public function validate_arguments( array $arguments );
75
-
76
- /**
77
- * Returns the array of callbacks for this shortcode's arguments.
78
- *
79
- * @since 4.12.0
80
- *
81
- * @return array<string,mixed> A map of the shortcode arguments that have survived validation.
82
- */
83
- public function get_validated_arguments_map();
84
-
85
- /**
86
- * Returns a shortcode default arguments.
87
- *
88
- * @since 4.12.0
89
- *
90
- * @return array<string,mixed> The shortcode default arguments map.
91
- */
92
- public function get_default_arguments();
93
-
94
- /**
95
- * Returns a shortcode arguments after been parsed.
96
- *
97
- * @since 4.12.0
98
- *
99
- * @return array<string,mixed> The shortcode arguments, as set by the user in the shortcode string.
100
- */
101
- public function get_arguments();
102
-
103
- /**
104
- * Returns a shortcode argument after it has been parsed.
105
- *
106
- * @since 4.12.0
107
- *
108
- * @param array|string $index Which index we indent to fetch from the arguments.
109
- * @param array $default Default value if it doesn't exist.
110
- *
111
- * @uses Tribe__Utils__Array::get For index fetching and Default.
112
- *
113
- * @return mixed Value for the Index passed as the first argument.
114
- */
115
- public function get_argument( $index, $default = null );
116
-
117
- /**
118
- * Returns a shortcode's HTML.
119
- *
120
- * @since 4.12.0
121
- *
122
- * @return string The shortcode rendered HTML code.
123
- */
124
- public function get_html();
125
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Support.php CHANGED
@@ -87,7 +87,7 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
87
  $plugin .= sprintf( ' by %s', $plugin_details['Author'] );
88
  }
89
  if ( ! empty( $plugin_details['AuthorURI'] ) ) {
90
- $plugin .= sprintf( ' (%s)', $plugin_details['AuthorURI'] );
91
  }
92
  $plugins[] = $plugin;
93
  }
@@ -106,7 +106,7 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
106
  $plugin .= sprintf( ' by %s', $plugin_details['Author'] );
107
  }
108
  if ( ! empty( $plugin_details['AuthorURI'] ) ) {
109
- $plugin .= sprintf( ' (%s)', $plugin_details['AuthorURI'] );
110
  }
111
  $network_plugins[] = $plugin;
112
  }
@@ -124,7 +124,7 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
124
  $plugin .= sprintf( ' by %s', $v['Author'] );
125
  }
126
  if ( ! empty( $v['AuthorURI'] ) ) {
127
- $plugin .= sprintf( ' (%s)', $v['AuthorURI'] );
128
  }
129
  $mu_plugins[] = $plugin;
130
  }
@@ -164,17 +164,6 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
164
  $php_info[ $php_var ] = $val;
165
  }
166
 
167
- $homepage = get_option( 'show_on_front' );
168
- $homepage_page_id = get_option( 'page_on_front' );
169
-
170
- if ( 'page' === $homepage ) {
171
- if ( -10 === (int) $homepage_page_id ) {
172
- $homepage_page_id .= ' (Main Events Page)';
173
- } else {
174
- $homepage_page_id .= ' (' . esc_html( get_the_title( $homepage_page_id ) ) . ')';
175
- }
176
- }
177
-
178
  $site_url = get_site_url();
179
  $systeminfo = array(
180
  'Home URL' => get_home_url(),
@@ -186,8 +175,6 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
186
  'Install keys' => $keys,
187
  'WordPress version' => get_bloginfo( 'version' ),
188
  'Permalink Structure' => $site_url . get_option( 'permalink_structure' ),
189
- 'Your homepage displays' => $homepage,
190
- 'Homepage page ID' => $homepage_page_id,
191
  'PHP version' => phpversion(),
192
  'PHP' => $php_info,
193
  'Server' => $server[0],
@@ -287,7 +274,7 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
287
  }
288
 
289
  /**
290
- * Logs the occurrence of rewrite rule purging
291
  */
292
  public function log_rewrite_rule_purge() {
293
  $this->rewrite_rules_purged = true;
87
  $plugin .= sprintf( ' by %s', $plugin_details['Author'] );
88
  }
89
  if ( ! empty( $plugin_details['AuthorURI'] ) ) {
90
+ $plugin .= sprintf( '(%s)', $plugin_details['AuthorURI'] );
91
  }
92
  $plugins[] = $plugin;
93
  }
106
  $plugin .= sprintf( ' by %s', $plugin_details['Author'] );
107
  }
108
  if ( ! empty( $plugin_details['AuthorURI'] ) ) {
109
+ $plugin .= sprintf( '(%s)', $plugin_details['AuthorURI'] );
110
  }
111
  $network_plugins[] = $plugin;
112
  }
124
  $plugin .= sprintf( ' by %s', $v['Author'] );
125
  }
126
  if ( ! empty( $v['AuthorURI'] ) ) {
127
+ $plugin .= sprintf( '(%s)', $v['AuthorURI'] );
128
  }
129
  $mu_plugins[] = $plugin;
130
  }
164
  $php_info[ $php_var ] = $val;
165
  }
166
 
 
 
 
 
 
 
 
 
 
 
 
167
  $site_url = get_site_url();
168
  $systeminfo = array(
169
  'Home URL' => get_home_url(),
175
  'Install keys' => $keys,
176
  'WordPress version' => get_bloginfo( 'version' ),
177
  'Permalink Structure' => $site_url . get_option( 'permalink_structure' ),
 
 
178
  'PHP version' => phpversion(),
179
  'PHP' => $php_info,
180
  'Server' => $server[0],
274
  }
275
 
276
  /**
277
+ * Logs the occurence of rewrite rule purging
278
  */
279
  public function log_rewrite_rule_purge() {
280
  $this->rewrite_rules_purged = true;
common/src/Tribe/Template.php CHANGED
@@ -1,7 +1,4 @@
1
  <?php
2
-
3
- use Tribe\Utils\Strings;
4
-
5
  class Tribe__Template {
6
  /**
7
  * The folders into which we will look for the template.
@@ -40,16 +37,7 @@ class Tribe__Template {
40
  protected $global = array();
41
 
42
  /**
43
- * Used for finding templates for public templates on themes inside of a folder.
44
- *
45
- * @since 4.10.2
46
- *
47
- * @var string[]
48
- */
49
- protected $template_origin_base_folder = [ 'src', 'views' ];
50
-
51
- /**
52
- * Allow changing if class will extract data from the local context
53
  *
54
  * @since 4.6.2
55
  *
@@ -57,15 +45,6 @@ class Tribe__Template {
57
  */
58
  protected $template_context_extract = false;
59
 
60
- /**
61
- * Current template hook name.
62
- *
63
- * @since 4.12.1
64
- *
65
- * @var string|null
66
- */
67
- protected $template_current_hook_name;
68
-
69
  /**
70
  * Base template for where to look for template
71
  *
@@ -84,15 +63,6 @@ class Tribe__Template {
84
  */
85
  protected $template_folder_lookup = false;
86
 
87
- /**
88
- * Create a class variable for the include path, to avoid conflicting with extract.
89
- *
90
- * @since 4.11.0
91
- *
92
- * @var string
93
- */
94
- protected $template_current_file_path;
95
-
96
  /**
97
  * Configures the class origin plugin path
98
  *
@@ -110,33 +80,19 @@ class Tribe__Template {
110
  if ( is_string( $origin ) ) {
111
  // Origin needs to be a class with a `instance` method
112
  if ( class_exists( $origin ) && method_exists( $origin, 'instance' ) ) {
113
- $origin = call_user_func( [ $origin, 'instance' ] );
114
  }
115
  }
116
 
117
- if (
118
- empty( $origin->plugin_path )
119
- && empty( $origin->pluginPath )
120
- && ! is_dir( $origin )
121
- ) {
122
  throw new InvalidArgumentException( 'Invalid Origin Class for Template Instance' );
123
  }
124
 
125
- if ( is_string( $origin ) ) {
126
- $this->template_base_path = array_filter(
127
- (array) explode(
128
- '/',
129
- untrailingslashit( $origin )
130
- )
131
- );
132
- } else {
133
  $this->origin = $origin;
134
-
135
- $this->template_base_path = untrailingslashit(
136
- ! empty( $this->origin->plugin_path )
137
- ? $this->origin->plugin_path
138
- : $this->origin->pluginPath
139
- );
140
  }
141
 
142
  return $this;
@@ -168,23 +124,12 @@ class Tribe__Template {
168
  return $this;
169
  }
170
 
171
- /**
172
- * Returns the array for which folder this template instance is looking into.
173
- *
174
- * @since 4.11.0
175
- *
176
- * @return array Current folder we are looking for templates.
177
- */
178
- public function get_template_folder() {
179
- return $this->folder;
180
- }
181
-
182
  /**
183
  * Configures the class with the base folder in relation to the Origin
184
  *
185
  * @since 4.7.20
186
  *
187
- * @param mixed $value Should we look for template files in the list of folders.
188
  *
189
  * @return self
190
  */
@@ -194,17 +139,6 @@ class Tribe__Template {
194
  return $this;
195
  }
196
 
197
- /**
198
- * Gets in this instance of the template engine whether we are looking public folders like themes.
199
- *
200
- * @since 4.12.1
201
- *
202
- * @return bool Whether we are looking into theme folders.
203
- */
204
- public function get_template_folder_lookup() {
205
- return $this->template_folder_lookup;
206
- }
207
-
208
  /**
209
  * Configures the class global context
210
  *
@@ -237,32 +171,6 @@ class Tribe__Template {
237
  return $this;
238
  }
239
 
240
- /**
241
- * Set the current hook name for the template include.
242
- *
243
- * @since 4.12.1
244
- *
245
- * @param string $value Which value will be saved as the current hookname.
246
- *
247
- * @return self Allow daisy-chaining.
248
- */
249
- public function set_template_current_hook_name( $value ) {
250
- $this->template_current_hook_name = (string) $value;
251
-
252
- return $this;
253
- }
254
-
255
- /**
256
- * Gets the hook name for the current template setup.
257
- *
258
- * @since 4.12.1
259
- *
260
- * @return string Hook name currently set on the class.
261
- */
262
- public function get_template_current_hook_name() {
263
- return $this->template_current_hook_name;
264
- }
265
-
266
  /**
267
  * Sets a Index inside of the global or local context
268
  * Final to prevent extending the class when the `get` already exists on the child class
@@ -345,14 +253,14 @@ class Tribe__Template {
345
  *
346
  * @return array
347
  */
348
- public function merge_context( $context = [], $file = null, $name = null ) {
349
  // Allow for simple null usage as well as array() for nothing
350
  if ( is_null( $context ) ) {
351
- $context = [];
352
  }
353
 
354
- // Applies new local context on top of Global + Previous local.
355
- $context = wp_parse_args( (array) $context, $this->get_values() );
356
 
357
  /**
358
  * Allows filtering the Local context
@@ -399,20 +307,15 @@ class Tribe__Template {
399
  * in the theme's directory.
400
  *
401
  * @since 4.7.20
402
- * @since 4.11.0 Added param $plugin_namespace.
403
- *
404
- * @param string $plugin_namespace Overwrite the origin namespace with a given one.
405
  *
406
- * @return array Namespace where we to look for templates.
407
  */
408
- protected function get_template_public_namespace( $plugin_namespace ) {
409
- $namespace = [
410
  'tribe',
411
- ];
412
 
413
- if ( ! empty( $plugin_namespace ) ) {
414
- $namespace[] = $plugin_namespace;
415
- } elseif ( ! empty( $this->origin->template_namespace ) ) {
416
  $namespace[] = $this->origin->template_namespace;
417
  }
418
 
@@ -428,46 +331,17 @@ class Tribe__Template {
428
  }
429
 
430
  /**
431
- * Fetches which base folder we look for templates in the origin plugin.
432
- *
433
- * @since 4.10.2
434
- *
435
- * @return array The base folders we look for templates in the origin plugin.
436
- */
437
- public function get_template_origin_base_folder() {
438
- /**
439
- * Allows filtering of the base path for templates.
440
- *
441
- * @since 4.10.2
442
- *
443
- * @param array $namespace Which is the base folder we will look for files in the plugin.
444
- * @param self $template Current instance of the Tribe__Template.
445
- */
446
- return apply_filters( 'tribe_template_origin_base_folder', $this->template_origin_base_folder, $this );
447
- }
448
-
449
- /**
450
- * Fetches the path for locating files given a base folder normally theme related.
451
  *
452
  * @since 4.7.20
453
- * @since 4.11.0 Added the param $namespace.
454
  *
455
- * @param mixed $base Base path to look into.
456
- * @param string $namespace Adds the plugin namespace to the path returned.
457
  *
458
- * @return string The public path for a given base.˙˙
459
  */
460
- protected function get_template_public_path( $base, $namespace ) {
461
-
462
  // Craft the plugin Path
463
- $path = array_merge( (array) $base, (array) $this->get_template_public_namespace( $namespace ) );
464
-
465
- // Pick up if the folder needs to be aded to the public template path.
466
- $folder = array_diff( $this->folder, $this->get_template_origin_base_folder() );
467
-
468
- if ( ! empty( $folder ) ) {
469
- $path = array_merge( $path, $folder );
470
- }
471
 
472
  // Implode to avoid Window Problems
473
  $path = implode( DIRECTORY_SEPARATOR, $path );
@@ -491,13 +365,27 @@ class Tribe__Template {
491
  * @return array
492
  */
493
  protected function get_template_path_list() {
494
- $folders = [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
 
496
- $folders['plugin'] = [
497
- 'id' => 'plugin',
498
- 'priority' => 20,
499
- 'path' => $this->get_template_plugin_path(),
500
- ];
501
 
502
  /**
503
  * Allows filtering of the list of folders in which we will look for the
@@ -508,46 +396,7 @@ class Tribe__Template {
508
  * @param array $folders Complete path to include the base public folder
509
  * @param self $template Current instance of the Tribe__Template
510
  */
511
- $folders = (array) apply_filters( 'tribe_template_path_list', $folders, $this );
512
-
513
- uasort( $folders, 'tribe_sort_by_priority' );
514
-
515
- return $folders;
516
- }
517
-
518
- /**
519
- * Get the list of theme related folders we will look up for the template.
520
- *
521
- * @since 4.11.0
522
- *
523
- * @param string $namespace Which plugin namespace we are looking for.
524
- *
525
- * @return array
526
- */
527
- protected function get_template_theme_path_list( $namespace ) {
528
- $folders = [];
529
-
530
- $folders['child-theme'] = [
531
- 'id' => 'child-theme',
532
- 'priority' => 10,
533
- 'path' => $this->get_template_public_path( STYLESHEETPATH, $namespace ),
534
- ];
535
- $folders['parent-theme'] = [
536
- 'id' => 'parent-theme',
537
- 'priority' => 15,
538
- 'path' => $this->get_template_public_path( TEMPLATEPATH, $namespace ),
539
- ];
540
-
541
- /**
542
- * Allows filtering of the list of theme folders in which we will look for the template.
543
- *
544
- * @since 4.11.0
545
- *
546
- * @param array $folders Complete path to include the base public folder.
547
- * @param string $namespace Loads the files from a specified folder from the themes.
548
- * @param self $template Current instance of the Tribe__Template.
549
- */
550
- $folders = (array) apply_filters( 'tribe_template_theme_path_list', $folders, $namespace, $this );
551
 
552
  uasort( $folders, 'tribe_sort_by_priority' );
553
 
@@ -570,12 +419,11 @@ class Tribe__Template {
570
  $name = (array) explode( '/', $name );
571
  }
572
 
573
- $folders = $this->get_template_path_list();
574
- $found_file = false;
575
- $namespace = false;
576
 
577
  foreach ( $folders as $folder ) {
578
- if ( empty( $folder['path'] ) ) {
 
579
  continue;
580
  }
581
 
@@ -587,272 +435,64 @@ class Tribe__Template {
587
 
588
  // Skip non-existent files
589
  if ( file_exists( $file ) ) {
590
- $found_file = $file;
591
- $namespace = ! empty( $folder['namespace'] ) ? $folder['namespace'] : false;
592
- break;
593
- }
594
- }
595
-
596
- if ( $this->get_template_folder_lookup() ) {
597
- $theme_folders = $this->get_template_theme_path_list( $namespace );
598
-
599
- foreach ( $theme_folders as $folder ) {
600
- if ( empty( $folder['path'] ) ) {
601
- continue;
602
- }
603
-
604
- // Build the File Path
605
- $file = implode( DIRECTORY_SEPARATOR, array_merge( (array) $folder['path'], $name ) );
606
-
607
- // Append the Extension to the file path
608
- $file .= '.php';
609
-
610
- // Skip non-existent files
611
- if ( file_exists( $file ) ) {
612
- $found_file = $file;
613
- break;
614
- }
615
  }
616
  }
617
 
618
- if ( $found_file ) {
619
- /**
620
- * A more Specific Filter that will include the template name
621
- *
622
- * @since 4.6.2
623
- * @since 4.7.20 The $name param no longer contains the extension
624
- *
625
- * @param string $file Complete path to include the PHP File
626
- * @param array $name Template name
627
- * @param self $template Current instance of the Tribe__Template
628
- */
629
- return apply_filters( 'tribe_template_file', $found_file, $name, $this );
630
- }
631
-
632
  // Couldn't find a template on the Stack
633
  return false;
634
  }
635
 
636
- /**
637
- * Runs the entry point hooks and filters.
638
- *
639
- * @param string $entry_point_name The name of the entry point.
640
- * @param boolean $echo If we should also print the entry point content.
641
- *
642
- * @return null|string `null` if an entry point is disabled or the entry point HTML.
643
- */
644
- public function do_entry_point( $entry_point_name, $echo = true ) {
645
- $hook_name = $this->get_template_current_hook_name();
646
-
647
- /**
648
- * Filter if the entry points are enabled.
649
- *
650
- * @since 4.12.1
651
- *
652
- * @param boolean $is_enabled Is entry_point enabled.
653
- * @param string $hook_name For which template include this entry point belongs.
654
- * @param string $entry_point_name Which entry point specifically we are triggering.
655
- * @param self $template Current instance of the template class doing this entry point.
656
- */
657
- $is_entry_point_enabled = apply_filters( 'tribe_template_entry_point_is_enabled', true, $hook_name, $entry_point_name, $this );
658
-
659
- if ( ! $is_entry_point_enabled ) {
660
- return null;
661
- }
662
-
663
- ob_start();
664
-
665
- if ( has_action( "tribe_template_entry_point:$hook_name" ) ) {
666
- /**
667
- * Generic entry point action for the current template.
668
- *
669
- * @since 4.12.1
670
- *
671
- * @param string $hook_name For which template include this entry point belongs.
672
- * @param string $entry_point_name Which entry point specifically we are triggering.
673
- * @param self $template Current instance of the template class doing this entry point.
674
- */
675
- do_action( "tribe_template_entry_point:$hook_name", $hook_name, $entry_point_name, $this );
676
- }
677
-
678
- if ( has_action( "tribe_template_entry_point:$hook_name:$entry_point_name" ) ) {
679
- /**
680
- * Specific named entry point action called.
681
- *
682
- * @since 4.12.1
683
- *
684
- * @param string $hook_name For which template include this entry point belongs.
685
- * @param string $entry_point_name Which entry point specifically we are triggering.
686
- * @param self $template Current instance of the template class doing this entry point.
687
- */
688
- do_action( "tribe_template_entry_point:$hook_name:$entry_point_name", $hook_name, $entry_point_name, $this );
689
- }
690
-
691
- $html = ob_get_clean();
692
-
693
- if ( has_filter( "tribe_template_entry_point_html:$hook_name" ) ) {
694
- /**
695
- * Generic entry point action for the current template.
696
- *
697
- * @since 4.12.1
698
- *
699
- * @param string $html HTML returned and/or echoed for this for this entry point.
700
- * @param string $hook_name For which template include this entry point belongs.
701
- * @param string $entry_point_name Which entry point specifically we are triggering.
702
- * @param self $template Current instance of the template class doing this entry point.
703
- */
704
- $html = apply_filters( "tribe_template_entry_point_html:$hook_name", $html, $hook_name, $entry_point_name, $this );
705
- }
706
-
707
- if ( has_filter( "tribe_template_entry_point_html:$hook_name:$entry_point_name" ) ) {
708
- /**
709
- * Specific named entry point action called.
710
- *
711
- * @since 4.12.1
712
- *
713
- * @param string $html HTML returned and/or echoed for this for this entry point.
714
- * @param string $hook_name For which template include this entry point belongs.
715
- * @param string $entry_point_name Which entry point specifically we are triggering.
716
- * @param self $template Current instance of the template class doing this entry point.
717
- */
718
- $html = apply_filters( "tribe_template_entry_point_html:$hook_name:$entry_point_name", $html, $hook_name, $entry_point_name, $this );
719
- }
720
-
721
- if ( $echo ) {
722
- echo $html;
723
- }
724
-
725
- return $html;
726
- }
727
-
728
  /**
729
  * A very simple method to include a Template, allowing filtering and additions using hooks.
730
  *
731
  * @since 4.6.2
732
  *
733
- * @param string|array $name Which file we are talking about including.
734
- * If an array, each item will add a directory separator to get to the single template.
735
- * @param array $context Any context data you need to expose to this file
736
- * @param boolean $echo If we should also print the Template
737
  *
738
- * @return string|false Either the final content HTML or `false` if no template could be found.
739
  */
740
- public function template( $name, $context = [], $echo = true ) {
741
- static $file_exists = [];
742
- static $files = [];
743
- static $template_names = [];
744
-
745
- /**
746
- * Allow users to disable templates before rendering it by returning empty string.
747
- *
748
- * @since 4.12.0
749
- *
750
- * @param string null Whether to continue displaying the template or not.
751
- * @param array $name Template name.
752
- * @param array $context Any context data you need to expose to this file.
753
- * @param boolean $echo If we should also print the Template.
754
- */
755
- $done = apply_filters( 'tribe_template_done', null, $name, $context, $echo );
756
-
757
- if ( null !== $done ) {
758
- return false;
759
  }
760
 
761
- // Key we'll use for in-memory caching of expensive operations.
762
- $cache_name_key = is_array( $name ) ? implode( '/', $name ) : $name;
763
-
764
- // Cache template name massaging so we don't have to repeat these actions.
765
- if ( ! isset( $template_names[ $cache_name_key ] ) ) {
766
- // If name is String make it an Array
767
- if ( is_string( $name ) ) {
768
- $name = (array) explode( '/', $name );
769
- }
770
 
771
- // Clean this Variable
772
- $name = array_map( 'sanitize_title_with_dashes', $name );
773
-
774
- $template_names[ $cache_name_key ] = $name;
775
  }
776
 
777
- // Cache file location and existence.
778
- if (
779
- ! isset( $file_exists[ $cache_name_key ] )
780
- || ! isset( $files[ $cache_name_key ] )
781
- ) {
782
- // Check if the file exists
783
- $files[ $cache_name_key ] = $file = $this->get_template_file( $name );
784
-
785
- // Check if it's a valid variable
786
- if ( ! $file ) {
787
- return $file_exists[ $cache_name_key ] = false;
788
- }
789
-
790
- // Before we load the file we check if it exists
791
- if ( ! file_exists( $file ) ) {
792
- return $file_exists[ $cache_name_key ] = false;
793
- }
794
 
795
- $file_exists[ $cache_name_key ] = true;
796
- }
797
 
798
- // If the file doesn't exist, bail.
799
- if ( ! $file_exists[ $cache_name_key ] ) {
800
  return false;
801
  }
802
 
803
- // Use filename stored in cache.
804
- $file = $files[ $cache_name_key ];
805
- $name = $template_names[ $cache_name_key ];
806
- $origin_folder_appendix = array_diff( $this->folder, $this->template_origin_base_folder );
807
-
808
- if ( $origin_namespace = $this->template_get_origin_namespace( $file ) ) {
809
- $legacy_namespace = array_merge( (array) $origin_namespace, $name );
810
- $namespace = array_merge( (array) $origin_namespace, $origin_folder_appendix, $name );
811
- } else {
812
- $legacy_namespace = $name;
813
- $namespace = array_merge( $origin_folder_appendix, $legacy_namespace );
814
- }
815
-
816
- // Setup the Hook name.
817
- $legacy_hook_name = implode( '/', $legacy_namespace );
818
- $hook_name = implode( '/', $namespace );
819
-
820
- $prev_hook_name = $this->get_template_current_hook_name();
821
-
822
- // Store the current hook name for the purposes of entry-points.
823
- $this->set_template_current_hook_name( $hook_name );
824
-
825
- /**
826
- * Allow users to filter the HTML before rendering
827
- *
828
- * @since 4.11.0
829
- *
830
- * @param string $html The initial HTML
831
- * @param string $file Complete path to include the PHP File
832
- * @param array $name Template name
833
- * @param self $template Current instance of the Tribe__Template
834
- */
835
- $pre_html = apply_filters( 'tribe_template_pre_html', null, $file, $name, $this );
836
-
837
- /**
838
- * Allow users to filter the HTML by the name before rendering
839
- *
840
- * E.g.:
841
- * `tribe_template_pre_html:events/blocks/parts/details`
842
- * `tribe_template_pre_html:events/embed`
843
- * `tribe_template_pre_html:tickets/login-to-purchase`
844
- *
845
- * @since 4.11.0
846
- *
847
- * @param string $html The initial HTML
848
- * @param string $file Complete path to include the PHP File
849
- * @param array $name Template name
850
- * @param self $template Current instance of the Tribe__Template
851
- */
852
- $pre_html = apply_filters( "tribe_template_pre_html:$hook_name", $pre_html, $file, $name, $this );
853
-
854
- if ( null !== $pre_html ) {
855
- return $pre_html;
856
  }
857
 
858
  ob_start();
@@ -864,7 +504,7 @@ class Tribe__Template {
864
  * Fires an Action before including the template file
865
  *
866
  * @since 4.6.2
867
- * @since 4.7.20 The $name param no longer contains the extension
868
  *
869
  * @param string $file Complete path to include the PHP File
870
  * @param array $name Template name
@@ -872,23 +512,6 @@ class Tribe__Template {
872
  */
873
  do_action( 'tribe_template_before_include', $file, $name, $this );
874
 
875
- /**
876
- * Fires an Action for a given template name before including the template file
877
- *
878
- * E.g.:
879
- * `tribe_template_before_include:events/blocks/parts/details`
880
- * `tribe_template_before_include:events/embed`
881
- * `tribe_template_before_include:tickets/login-to-purchase`
882
- *
883
- * @deprecated 4.11.0
884
- * @since 4.7.20
885
- *
886
- * @param string $file Complete path to include the PHP File
887
- * @param array $name Template name
888
- * @param self $template Current instance of the Tribe__Template
889
- */
890
- do_action( "tribe_template_before_include:$legacy_hook_name", $file, $name, $this );
891
-
892
  /**
893
  * Fires an Action for a given template name before including the template file
894
  *
@@ -905,13 +528,29 @@ class Tribe__Template {
905
  */
906
  do_action( "tribe_template_before_include:$hook_name", $file, $name, $this );
907
 
908
- $this->template_safe_include( $file );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
909
 
910
  /**
911
  * Fires an Action after including the template file
912
  *
913
  * @since 4.6.2
914
- * @since 4.7.20 The $name param no longer contains the extension
915
  *
916
  * @param string $file Complete path to include the PHP File
917
  * @param array $name Template name
@@ -919,23 +558,6 @@ class Tribe__Template {
919
  */
920
  do_action( 'tribe_template_after_include', $file, $name, $this );
921
 
922
- /**
923
- * Fires an Action for a given template name after including the template file
924
- *
925
- * E.g.:
926
- * `tribe_template_after_include:events/blocks/parts/details`
927
- * `tribe_template_after_include:events/embed`
928
- * `tribe_template_after_include:tickets/login-to-purchase`
929
- *
930
- * @deprecated 4.11.0
931
- * @since 4.7.20
932
- *
933
- * @param string $file Complete path to include the PHP File
934
- * @param array $name Template name
935
- * @param self $template Current instance of the Tribe__Template
936
- */
937
- do_action( "tribe_template_after_include:$legacy_hook_name", $file, $name, $this );
938
-
939
  /**
940
  * Fires an Action for a given template name after including the template file
941
  *
@@ -959,7 +581,7 @@ class Tribe__Template {
959
  * Allow users to filter the final HTML
960
  *
961
  * @since 4.6.2
962
- * @since 4.7.20 The $name param no longer contains the extension
963
  *
964
  * @param string $html The final HTML
965
  * @param string $file Complete path to include the PHP File
@@ -968,24 +590,6 @@ class Tribe__Template {
968
  */
969
  $html = apply_filters( 'tribe_template_html', $html, $file, $name, $this );
970
 
971
- /**
972
- * Allow users to filter the final HTML by the name
973
- *
974
- * E.g.:
975
- * `tribe_template_html:events/blocks/parts/details`
976
- * `tribe_template_html:events/embed`
977
- * `tribe_template_html:tickets/login-to-purchase`
978
- *
979
- * @deprecated 4.11.0
980
- * @since 4.7.20
981
- *
982
- * @param string $html The final HTML
983
- * @param string $file Complete path to include the PHP File
984
- * @param array $name Template name
985
- * @param self $template Current instance of the Tribe__Template
986
- */
987
- $html = apply_filters( "tribe_template_html:$legacy_hook_name", $html, $file, $name, $this );
988
-
989
  /**
990
  * Allow users to filter the final HTML by the name
991
  *
@@ -1003,146 +607,13 @@ class Tribe__Template {
1003
  */
1004
  $html = apply_filters( "tribe_template_html:$hook_name", $html, $file, $name, $this );
1005
 
1006
- // Tries to hook container entry points in the HTML.
1007
- $html = $this->template_hook_container_entry_points( $html );
1008
-
1009
  if ( $echo ) {
1010
  echo $html;
1011
  }
1012
 
1013
- // Revert the current hook name.
1014
- $this->set_template_current_hook_name( $prev_hook_name );
1015
-
1016
- return $html;
1017
- }
1018
-
1019
- /**
1020
- * Run the hooks for the container entry points.
1021
- *
1022
- * @since 4.12.1
1023
- *
1024
- * @param string $html The html of the current template.
1025
- *
1026
- * @return string|false Either the final entry point content HTML or `false` if no entry point could be found or set to false.
1027
- */
1028
- private function template_hook_container_entry_points( $html ) {
1029
-
1030
- $matches = $this->get_entry_point_matches( $html );
1031
- $html_matches = $matches[0];
1032
-
1033
- if ( 0 === count( $html_matches ) ) {
1034
- return $html;
1035
- }
1036
-
1037
- $html_tags = $matches['tag'];
1038
- $html_tags_ends = $matches['is_end'];
1039
-
1040
- // Get first and last tags.
1041
- $first_tag = reset( $html_tags );
1042
- $last_tag = end( $html_tags );
1043
-
1044
- // Determine if first last tags are tag ends.
1045
- $first_tag_is_end = '/' === reset( $html_tags_ends );
1046
- $last_tag_is_end = '/' === end( $html_tags_ends );
1047
-
1048
- // When first and last tag are not the same, bail.
1049
- if ( $first_tag !== $last_tag ) {
1050
- return $html;
1051
- }
1052
-
1053
- // If the first tag is a html tag end, bail.
1054
- if ( $first_tag_is_end ) {
1055
- return $html;
1056
- }
1057
-
1058
- // If the last tag is not and html tag end, bail.
1059
- if ( ! $last_tag_is_end ) {
1060
- return $html;
1061
- }
1062
-
1063
- $first_tag_html = reset( $html_matches );
1064
- $last_tag_html = end( $html_matches );
1065
-
1066
- $open_container_entry_point_html = $this->do_entry_point( 'after_container_open', false );
1067
- $close_container_entry_point_html = $this->do_entry_point( 'before_container_close', false );
1068
-
1069
- $html = Strings::replace_first( $first_tag_html, $first_tag_html . $open_container_entry_point_html, $html );
1070
- $html = Strings::replace_last( $last_tag_html, $close_container_entry_point_html . $last_tag_html, $html );
1071
-
1072
  return $html;
1073
  }
1074
 
1075
- /**
1076
- * Based on a path it determines what is the namespace that should be used.
1077
- *
1078
- * @since 4.11.0
1079
- *
1080
- * @param string $path Which file we are going to load.
1081
- *
1082
- * @return string|false The found namespace for that path or false.
1083
- */
1084
- public function template_get_origin_namespace( $path ) {
1085
- $matching_namespace = false;
1086
- /**
1087
- * Allows more namespaces to be added based on the path of the file we are loading.
1088
- *
1089
- * @since 4.11.0
1090
- *
1091
- * @param array $namespace_map Indexed array containing the namespace as the key and path to `strpos`.
1092
- * @param string $path Path we will do the `strpos` to validate a given namespace.
1093
- * @param self $template Current instance of the template class.
1094
- */
1095
- $namespace_map = (array) apply_filters( 'tribe_template_origin_namespace_map', [], $path, $this );
1096
-
1097
- foreach ( $namespace_map as $namespace => $contains_string ) {
1098
- // Skip when we dont have the namespace path.
1099
- if ( false === strpos( $path, $contains_string ) ) {
1100
- continue;
1101
- }
1102
-
1103
- $matching_namespace = $namespace;
1104
-
1105
- // Once the first namespace is found it breaks out.
1106
- break;
1107
- }
1108
-
1109
- if ( empty( $matching_namespace ) && ! empty( $this->origin->template_namespace ) ) {
1110
- $matching_namespace = $this->origin->template_namespace;
1111
- }
1112
-
1113
- return $matching_namespace;
1114
- }
1115
-
1116
- /**
1117
- * Includes a give PHP inside of a safe context.
1118
- *
1119
- * This method is required to prevent template files messing with local variables used inside of the
1120
- * `self::template` method. Also shelters the template loading from any possible variables that could
1121
- * be overwritten by the context.
1122
- *
1123
- * @since 4.11.0
1124
- *
1125
- * @param string $file Which file will be included with safe context.
1126
- *
1127
- * @return void
1128
- */
1129
- public function template_safe_include( $file ) {
1130
- // We use this instance variable to prevent collisions.
1131
- $this->template_current_file_path = $file;
1132
- unset( $file );
1133
-
1134
- // Only do this if really needed (by default it wont).
1135
- if ( true === $this->template_context_extract && ! empty( $this->context ) ) {
1136
- // Make any provided variables available in the template variable scope.
1137
- extract( $this->context ); // @phpcs:ignore
1138
- }
1139
-
1140
- include $this->template_current_file_path;
1141
-
1142
- // After the include we reset the variable.
1143
- unset( $this->template_current_file_path );
1144
- }
1145
-
1146
  /**
1147
  * Sets a number of values at the same time.
1148
  *
@@ -1193,22 +664,4 @@ class Tribe__Template {
1193
  public function get_values() {
1194
  return array_merge( $this->get_global_values(), $this->get_local_values() );
1195
  }
1196
-
1197
- /**
1198
- * Get the Entry Point Matches.
1199
- *
1200
- * @since 4.12.1
1201
- *
1202
- * @param string $html The html of the current template.
1203
- *
1204
- * @return array An array of matches from the regular expression.
1205
- */
1206
- private function get_entry_point_matches( $html ) {
1207
- $regexp = '/<(?<is_end>\/)*(?<tag>[A-Z0-9]*)(?:\b)*[^>]*>/mi';
1208
-
1209
- preg_match_all( $regexp, $html, $matches );
1210
-
1211
- return $matches;
1212
- }
1213
-
1214
  }
1
  <?php
 
 
 
2
  class Tribe__Template {
3
  /**
4
  * The folders into which we will look for the template.
37
  protected $global = array();
38
 
39
  /**
40
+ * Allow chaing if class will extract data from the local context
 
 
 
 
 
 
 
 
 
41
  *
42
  * @since 4.6.2
43
  *
45
  */
46
  protected $template_context_extract = false;
47
 
 
 
 
 
 
 
 
 
 
48
  /**
49
  * Base template for where to look for template
50
  *
63
  */
64
  protected $template_folder_lookup = false;
65
 
 
 
 
 
 
 
 
 
 
66
  /**
67
  * Configures the class origin plugin path
68
  *
80
  if ( is_string( $origin ) ) {
81
  // Origin needs to be a class with a `instance` method
82
  if ( class_exists( $origin ) && method_exists( $origin, 'instance' ) ) {
83
+ $origin = call_user_func( array( $origin, 'instance' ) );
84
  }
85
  }
86
 
87
+ if ( empty( $origin->plugin_path ) && empty( $origin->pluginPath ) && ! is_dir( $origin ) ) {
 
 
 
 
88
  throw new InvalidArgumentException( 'Invalid Origin Class for Template Instance' );
89
  }
90
 
91
+ if ( ! is_string( $origin ) ) {
 
 
 
 
 
 
 
92
  $this->origin = $origin;
93
+ $this->template_base_path = untrailingslashit( ! empty( $this->origin->plugin_path ) ? $this->origin->plugin_path : $this->origin->pluginPath );
94
+ } else {
95
+ $this->template_base_path = untrailingslashit( (array) explode( '/', $origin ) );
 
 
 
96
  }
97
 
98
  return $this;
124
  return $this;
125
  }
126
 
 
 
 
 
 
 
 
 
 
 
 
127
  /**
128
  * Configures the class with the base folder in relation to the Origin
129
  *
130
  * @since 4.7.20
131
  *
132
+ * @param mixed $use Should we look for template files in the list of folders
133
  *
134
  * @return self
135
  */
139
  return $this;
140
  }
141
 
 
 
 
 
 
 
 
 
 
 
 
142
  /**
143
  * Configures the class global context
144
  *
171
  return $this;
172
  }
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  /**
175
  * Sets a Index inside of the global or local context
176
  * Final to prevent extending the class when the `get` already exists on the child class
253
  *
254
  * @return array
255
  */
256
+ public function merge_context( $context = array(), $file = null, $name = null ) {
257
  // Allow for simple null usage as well as array() for nothing
258
  if ( is_null( $context ) ) {
259
+ $context = array();
260
  }
261
 
262
+ // Applies local context on top of Global one
263
+ $context = wp_parse_args( (array) $context, $this->global );
264
 
265
  /**
266
  * Allows filtering the Local context
307
  * in the theme's directory.
308
  *
309
  * @since 4.7.20
 
 
 
310
  *
311
+ * @return array
312
  */
313
+ protected function get_template_public_namespace() {
314
+ $namespace = array(
315
  'tribe',
316
+ );
317
 
318
+ if ( ! empty( $this->origin->template_namespace ) ) {
 
 
319
  $namespace[] = $this->origin->template_namespace;
320
  }
321
 
331
  }
332
 
333
  /**
334
+ * Fetches the path for locating files given a base folder normally theme related
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  *
336
  * @since 4.7.20
 
337
  *
338
+ * @param mixed $base Base path to look into
 
339
  *
340
+ * @return string
341
  */
342
+ protected function get_template_public_path( $base ) {
 
343
  // Craft the plugin Path
344
+ $path = array_merge( (array) $base, (array) $this->get_template_public_namespace() );
 
 
 
 
 
 
 
345
 
346
  // Implode to avoid Window Problems
347
  $path = implode( DIRECTORY_SEPARATOR, $path );
365
  * @return array
366
  */
367
  protected function get_template_path_list() {
368
+ $folders = array();
369
+
370
+ // Only look into public folders if we tell to use folders
371
+ if ( $this->template_folder_lookup ) {
372
+ $folders[] = array(
373
+ 'id' => 'child-theme',
374
+ 'priority' => 10,
375
+ 'path' => $this->get_template_public_path( STYLESHEETPATH ),
376
+ );
377
+ $folders[] = array(
378
+ 'id' => 'parent-theme',
379
+ 'priority' => 15,
380
+ 'path' => $this->get_template_public_path( TEMPLATEPATH ),
381
+ );
382
+ }
383
 
384
+ $folders[] = array(
385
+ 'id' => 'plugin',
386
+ 'priority' => 20,
387
+ 'path' => $this->get_template_plugin_path(),
388
+ );
389
 
390
  /**
391
  * Allows filtering of the list of folders in which we will look for the
396
  * @param array $folders Complete path to include the base public folder
397
  * @param self $template Current instance of the Tribe__Template
398
  */
399
+ $folders = apply_filters( 'tribe_template_path_list', $folders, $this );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
  uasort( $folders, 'tribe_sort_by_priority' );
402
 
419
  $name = (array) explode( '/', $name );
420
  }
421
 
422
+ $folders = $this->get_template_path_list();
 
 
423
 
424
  foreach ( $folders as $folder ) {
425
+ $folder['path'] = trim( $folder['path'] );
426
+ if ( ! $folder['path'] ) {
427
  continue;
428
  }
429
 
435
 
436
  // Skip non-existent files
437
  if ( file_exists( $file ) ) {
438
+ /**
439
+ * A more Specific Filter that will include the template name
440
+ *
441
+ * @since 4.6.2
442
+ * @since 4.7.20 The $name param no longers contains the extension
443
+ *
444
+ * @param string $file Complete path to include the PHP File
445
+ * @param array $name Template name
446
+ * @param self $template Current instance of the Tribe__Template
447
+ */
448
+ return apply_filters( 'tribe_template_file', $file, $name, $this );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
  }
450
  }
451
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
  // Couldn't find a template on the Stack
453
  return false;
454
  }
455
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  /**
457
  * A very simple method to include a Template, allowing filtering and additions using hooks.
458
  *
459
  * @since 4.6.2
460
  *
461
+ * @param string $name Which file we are talking about including
462
+ * @param array $context Any context data you need to expose to this file
463
+ * @param boolean $echo If we should also print the Template
 
464
  *
465
+ * @return string Final Content HTML
466
  */
467
+ public function template( $name, $context = array(), $echo = true ) {
468
+ // If name is String make it an Array
469
+ if ( is_string( $name ) ) {
470
+ $name = (array) explode( '/', $name );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
  }
472
 
473
+ // Clean this Variable
474
+ $name = array_map( 'sanitize_title_with_dashes', $name );
 
 
 
 
 
 
 
475
 
476
+ if ( ! empty( $this->origin->template_namespace ) ) {
477
+ $namespace = array_merge( (array) $this->origin->template_namespace, $name );
478
+ } else {
479
+ $namespace = $name;
480
  }
481
 
482
+ // Setup the Hook name
483
+ $hook_name = implode( '/', $namespace );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
 
485
+ // Check if the file exists
486
+ $file = $this->get_template_file( $name );
487
 
488
+ // Check if it's a valid variable
489
+ if ( ! $file ) {
490
  return false;
491
  }
492
 
493
+ // Before we load the file we check if it exists
494
+ if ( ! file_exists( $file ) ) {
495
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
  }
497
 
498
  ob_start();
504
  * Fires an Action before including the template file
505
  *
506
  * @since 4.6.2
507
+ * @since 4.7.20 The $name param no longers contains the extension
508
  *
509
  * @param string $file Complete path to include the PHP File
510
  * @param array $name Template name
512
  */
513
  do_action( 'tribe_template_before_include', $file, $name, $this );
514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  /**
516
  * Fires an Action for a given template name before including the template file
517
  *
528
  */
529
  do_action( "tribe_template_before_include:$hook_name", $file, $name, $this );
530
 
531
+ // Only do this if really needed (by default it wont)
532
+ if ( true === $this->template_context_extract && ! empty( $this->context ) ) {
533
+ // We don't allow Extrating of a variable called $name
534
+ if ( isset( $this->context['name'] ) ) {
535
+ unset( $this->context['name'] );
536
+ }
537
+
538
+ // We don't allow Extrating of a variable called $file
539
+ if ( isset( $this->context['file'] ) ) {
540
+ unset( $this->context['file'] );
541
+ }
542
+
543
+ // Make any provided variables available in the template variable scope
544
+ extract( $this->context ); // @codingStandardsIgnoreLine
545
+ }
546
+
547
+ include $file;
548
 
549
  /**
550
  * Fires an Action after including the template file
551
  *
552
  * @since 4.6.2
553
+ * @since 4.7.20 The $name param no longers contains the extension
554
  *
555
  * @param string $file Complete path to include the PHP File
556
  * @param array $name Template name
558
  */
559
  do_action( 'tribe_template_after_include', $file, $name, $this );
560
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
561
  /**
562
  * Fires an Action for a given template name after including the template file
563
  *
581
  * Allow users to filter the final HTML
582
  *
583
  * @since 4.6.2
584
+ * @since 4.7.20 The $name param no longers contains the extension
585
  *
586
  * @param string $html The final HTML
587
  * @param string $file Complete path to include the PHP File
590
  */
591
  $html = apply_filters( 'tribe_template_html', $html, $file, $name, $this );
592
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
  /**
594
  * Allow users to filter the final HTML by the name
595
  *
607
  */
608
  $html = apply_filters( "tribe_template_html:$hook_name", $html, $file, $name, $this );
609
 
 
 
 
610
  if ( $echo ) {
611
  echo $html;
612
  }
613
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
  return $html;
615
  }
616
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
617
  /**
618
  * Sets a number of values at the same time.
619
  *
664
  public function get_values() {
665
  return array_merge( $this->get_global_values(), $this->get_local_values() );
666
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
667
  }
common/src/Tribe/Timezones.php CHANGED
@@ -10,6 +10,7 @@ class Tribe__Timezones {
10
  const SITE_TIMEZONE = 'site';
11
  const EVENT_TIMEZONE = 'event';
12
 
 
13
  /**
14
  * Container for reusable DateTimeZone objects.
15
  *
@@ -17,6 +18,7 @@ class Tribe__Timezones {
17
  */
18
  protected static $timezones = array();
19
 
 
20
  public static function init() {
21
  self::invalidate_caches();
22
  }
@@ -579,13 +581,6 @@ class Tribe__Timezones {
579
  return $timezone;
580
  }
581
 
582
- /** @var Tribe__Cache $cache */
583
- $cache = tribe('cache');
584
-
585
- if ( is_string( $timezone ) && $cached = $cache[ __METHOD__ . $timezone ] ) {
586
- return clone $cached;
587
- }
588
-
589
  $timezone = null === $timezone ? self::wp_timezone_string() : $timezone;
590
 
591
  try {
@@ -594,10 +589,6 @@ class Tribe__Timezones {
594
  return new DateTimeZone( 'UTC' );
595
  }
596
 
597
- if ( is_string( $timezone ) ) {
598
- $cache[ __METHOD__ . $timezone ] = $object;
599
- }
600
-
601
  return $object;
602
  }
603
 
@@ -615,7 +606,7 @@ class Tribe__Timezones {
615
  return $timezone_candidate->getName();
616
  }
617
 
618
- $timezone_string = preg_replace( '/(\\+||\\-)0$/', '', $timezone_candidate );
619
  $timezone_string = self::is_utc_offset( $timezone_string )
620
  ? self::generate_timezone_string_from_utc_offset( $timezone_string )
621
  : $timezone_string;
10
  const SITE_TIMEZONE = 'site';
11
  const EVENT_TIMEZONE = 'event';
12
 
13
+
14
  /**
15
  * Container for reusable DateTimeZone objects.
16
  *
18
  */
19
  protected static $timezones = array();
20
 
21
+
22
  public static function init() {
23
  self::invalidate_caches();
24
  }
581
  return $timezone;
582
  }
583
 
 
 
 
 
 
 
 
584
  $timezone = null === $timezone ? self::wp_timezone_string() : $timezone;
585
 
586
  try {
589
  return new DateTimeZone( 'UTC' );
590
  }
591
 
 
 
 
 
592
  return $object;
593
  }
594
 
606
  return $timezone_candidate->getName();
607
  }
608
 
609
+ $timezone_string = preg_replace( '/\\+0$/', '', $timezone_candidate );
610
  $timezone_string = self::is_utc_offset( $timezone_string )
611
  ? self::generate_timezone_string_from_utc_offset( $timezone_string )
612
  : $timezone_string;
common/src/Tribe/Tooltip/View.php CHANGED
@@ -1,30 +1,19 @@
1
  <?php
2
 
3
- namespace Tribe\Tooltip;
4
-
5
  /**
6
- * Class View
7
  *
8
  * @since 4.9.8
9
  */
10
- class View extends \Tribe__Template {
11
 
12
  /**
13
- * Where in the themes we will look for templates
14
- *
15
- * @since 4.10.2
16
- *
17
- * @var string
18
- */
19
- public $template_namespace = 'tooltips';
20
-
21
- /**
22
- * View constructor.
23
  *
24
  * @since 4.9.8
25
  */
26
  public function __construct() {
27
- $this->set_template_origin( \Tribe__Main::instance() );
28
  $this->set_template_folder( 'src/views/tooltip' );
29
 
30
  // Configures this templating class to extract variables
@@ -40,15 +29,7 @@ class View extends \Tribe__Template {
40
  * @since 4.9.8
41
  *
42
  * @param array|string $message Array of messages or single message as string.
43
- * @param array $args {
44
- * List of arguments to override tooltip template.
45
- *
46
- * @var array $context Any additional context data you need to expose to this file (optional).
47
- * @var string $classes Additional classes for the icon span (optional).
48
- * @var string $direction Direction the tooltip should be from the trigger (down).
49
- * @var string $icon dashicon classname to use, without the `dashicon-` (info).
50
- * @var string $wrap_classes Classes for the tooltip wrapper (optional).
51
- * }
52
  * @return string A string of html for the tooltip.
53
  */
54
  public function render_tooltip( $message, $args = [] ) {
@@ -56,10 +37,6 @@ class View extends \Tribe__Template {
56
  return;
57
  }
58
 
59
- /** @var \Tribe__Assets $assets */
60
- $assets = tribe( 'assets' );
61
- $assets->enqueue_group( 'tribe-tooltip' );
62
-
63
  $html = $this->build_tooltip( $message, $args );
64
 
65
  return $html;
@@ -71,23 +48,15 @@ class View extends \Tribe__Template {
71
  * @since 4.9.8
72
  *
73
  * @param array|string $message array of messages or single message as string.
74
- * @param array $args {
75
- * List of arguments to override tooltip template.
76
- *
77
- * @var array $context Any additional context data you need to expose to this file (optional).
78
- * @var string $classes Additional classes for the icon span (optional).
79
- * @var string $direction Direction the tooltip should be from the trigger (down).
80
- * @var string $icon dashicon classname to use, without the `dashicon-` (info).
81
- * @var string $wrap_classes Classes for the tooltip wrapper (optional).
82
- * }
83
  * @return string A string of html for the tooltip.
84
  */
85
  private function build_tooltip( $message, $original_args ) {
86
  $default_args = [
87
  'classes' => '',
88
- 'context' => '',
89
- 'direction' => 'down',
90
  'icon' => 'info',
 
 
91
  'wrap_classes' => '',
92
  ];
93
 
1
  <?php
2
 
 
 
3
  /**
4
+ * Class Tribe__Tooltip__View
5
  *
6
  * @since 4.9.8
7
  */
8
+ class Tribe__Tooltip__View extends Tribe__Template {
9
 
10
  /**
11
+ * Tribe__Tooltip__View constructor.
 
 
 
 
 
 
 
 
 
12
  *
13
  * @since 4.9.8
14
  */
15
  public function __construct() {
16
+ $this->set_template_origin( Tribe__Main::instance() );
17
  $this->set_template_folder( 'src/views/tooltip' );
18
 
19
  // Configures this templating class to extract variables
29
  * @since 4.9.8
30
  *
31
  * @param array|string $message Array of messages or single message as string.
32
+ * @param array $args Extra arguments.
 
 
 
 
 
 
 
 
33
  * @return string A string of html for the tooltip.
34
  */
35
  public function render_tooltip( $message, $args = [] ) {
37
  return;
38
  }
39
 
 
 
 
 
40
  $html = $this->build_tooltip( $message, $args );
41
 
42
  return $html;
48
  * @since 4.9.8
49
  *
50
  * @param array|string $message array of messages or single message as string.
51
+ * @param array $args Extra arguments, defaults include icon, classes, direction, and context (for the filters).
 
 
 
 
 
 
 
 
52
  * @return string A string of html for the tooltip.
53
  */
54
  private function build_tooltip( $message, $original_args ) {
55
  $default_args = [
56
  'classes' => '',
 
 
57
  'icon' => 'info',
58
+ 'direction' => 'down',
59
+ 'context' => '',
60
  'wrap_classes' => '',
61
  ];
62
 
common/src/Tribe/Tracker.php CHANGED
@@ -83,24 +83,7 @@ class Tribe__Tracker {
83
  }
84
 
85
  /**
86
- * Get the date(timestamp) of last modification for a tracked field.
87
- *
88
- * @since 4.12.3
89
- *
90
- * @param string $meta_key The key for the meta field we're interested in.
91
- * @param int $post_id The ID of the post to check.
92
- *
93
- * @return boolean|string The change timestamp or false if the field is not found/empty.
94
- */
95
- public function get_modified_date( $meta_key, $post_id ) {
96
- $modified = get_post_meta( $post_id, self::$field_key, true );
97
-
98
- // If the key is missing or empty/null return false - no recorded change.
99
- return Tribe__Utils__Array::get( $modified, $meta_key, false );
100
- }
101
-
102
- /**
103
- * Easy way to see currently which post types are being tracked by our code.
104
  *
105
  * @return array
106
  */
83
  }
84
 
85
  /**
86
+ * Easy way to see currently which post types are been tracked by our code.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  *
88
  * @return array
89
  */
common/src/Tribe/Traits/Cache_User.php CHANGED
@@ -15,8 +15,8 @@
15
  *
16
  * protected $computation_cache = [];
17
  *
18
- * public function __construct(){
19
- * add_action( 'shutdown', [ $this, 'dump_cache' ] );
20
  * }
21
  *
22
  * public function calculate_something( $key ){
@@ -125,21 +125,4 @@ trait Cache_User {
125
  }
126
  }
127
  }
128
-
129
- /**
130
- * Resets the instance caches for the this instance.
131
- *
132
- * @since 4.11.0
133
- *
134
- * @return string[] A list of the emptied cache properties.
135
- */
136
- public function reset_caches() {
137
- $emptied = [];
138
- foreach ( array_keys( $this->caches ) as $key ) {
139
- $emptied[] = $key;
140
- $this->{"{$key}_cache"} = [];
141
- }
142
-
143
- return $emptied;
144
- }
145
  }
15
  *
16
  * protected $computation_cache = [];
17
  *
18
+ * public function __destruct(){
19
+ * $this->dump_cache();
20
  * }
21
  *
22
  * public function calculate_something( $key ){
125
  }
126
  }
127
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  }
common/src/Tribe/Utils/Array.php CHANGED
@@ -1,525 +1,443 @@
1
  <?php
 
 
 
 
2
 
3
- if ( ! class_exists( 'Tribe__Utils__Array' ) ) {
4
  /**
5
- * Array utilities
 
 
 
 
 
 
 
 
 
 
6
  */
7
- class Tribe__Utils__Array {
8
-
9
- /**
10
- * Set key/value within an array, can set a key nested inside of a multidimensional array.
11
- *
12
- * Example: set( $a, [ 0, 1, 2 ], 'hi' ) sets $a[0][1][2] = 'hi' and returns $a.
13
- *
14
- * @param mixed $array The array containing the key this sets.
15
- * @param string|array $key To set a key nested multiple levels deep pass an array
16
- * specifying each key in order as a value.
17
- * Example: array( 'lvl1', 'lvl2', 'lvl3' );
18
- * @param mixed $value The value.
19
- *
20
- * @return array Full array with the key set to the specified value.
21
- */
22
- public static function set( array $array, $key, $value ) {
23
- // Convert strings and such to array.
24
- $key = (array) $key;
25
-
26
- // Setup a pointer that we can point to the key specified.
27
- $key_pointer = &$array;
28
-
29
- // Iterate through every key, setting the pointer one level deeper each time.
30
- foreach ( $key as $i ) {
31
-
32
- // Ensure current array depth can have children set.
33
- if ( ! is_array( $key_pointer ) ) {
34
- // $key_pointer is set but is not an array. Converting it to an array
35
- // would likely lead to unexpected problems for whatever first set it.
36
- $error = sprintf(
37
- 'Attempted to set $array[%1$s] but %2$s is already set and is not an array.',
38
- implode( $key, '][' ),
39
- $i
40
- );
41
-
42
- _doing_it_wrong( __FUNCTION__, esc_html( $error ), '4.3' );
43
- break;
44
- } elseif ( ! isset( $key_pointer[ $i ] ) ) {
45
- $key_pointer[ $i ] = [];
46
- }
47
-
48
- // Dive one level deeper into the nested array.
49
- $key_pointer = &$key_pointer[ $i ];
50
  }
51
 
52
- // Set the value for the specified key
53
- $key_pointer = $value;
54
-
55
- return $array;
56
  }
57
 
58
- /**
59
- * Find a value inside of an array or object, including one nested a few levels deep.
60
- *
61
- * Example: get( $a, [ 0, 1, 2 ] ) returns the value of $a[0][1][2] or the default.
62
- *
63
- * @param array $variable Array or object to search within.
64
- * @param array|string $indexes Specify each nested index in order.
65
- * Example: array( 'lvl1', 'lvl2' );
66
- * @param mixed $default Default value if the search finds nothing.
67
- *
68
- * @return mixed The value of the specified index or the default if not found.
69
- */
70
- public static function get( $variable, $indexes, $default = null ) {
71
- if ( is_object( $variable ) ) {
72
- $variable = (array) $variable;
73
- }
74
-
75
- if ( ! is_array( $variable ) ) {
76
- return $default;
77
- }
78
-
79
- foreach ( (array) $indexes as $index ) {
80
- if ( ! is_array( $variable ) || ! isset( $variable[ $index ] ) ) {
81
- $variable = $default;
82
- break;
83
- }
84
 
85
- $variable = $variable[ $index ];
86
- }
87
 
88
- return $variable;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
91
- /**
92
- * Find a value inside a list of array or objects, including one nested a few levels deep.
93
- *
94
- * @since 4.7.7
95
- *
96
- * Example: get( [$a, $b, $c], [ 0, 1, 2 ] ) returns the value of $a[0][1][2] found in $a, $b or $c
97
- * or the default.
98
- *
99
- * @param array $variables Array of arrays or objects to search within.
100
- * @param array|string $indexes Specify each nested index in order.
101
- * Example: array( 'lvl1', 'lvl2' );
102
- * @param mixed $default Default value if the search finds nothing.
103
- *
104
- * @return mixed The value of the specified index or the default if not found.
105
- */
106
- public static function get_in_any( array $variables, $indexes, $default = null ) {
107
- foreach ( $variables as $variable ) {
108
- $found = self::get( $variable, $indexes, '__not_found__' );
109
- if ( '__not_found__' !== $found ) {
110
- return $found;
111
- }
112
- }
113
-
114
  return $default;
115
  }
116
 
117
- /**
118
- * Behaves exactly like the native strpos(), but accepts an array of needles.
119
- *
120
- * @param string $haystack String to search in.
121
- * @param array|string $needles Strings to search for.
122
- * @param int $offset Starting position of search.
123
- *
124
- * @return false|int Integer position of first needle occurrence.
125
- * @see strpos()
126
- *
127
- */
128
- public static function strpos( $haystack, $needles, $offset = 0 ) {
129
- $needles = (array) $needles;
130
-
131
- foreach ( $needles as $i ) {
132
- $search = strpos( $haystack, $i, $offset );
133
-
134
- if ( false !== $search ) {
135
- return $search;
136
- }
137
  }
138
 
139
- return false;
140
  }
141
 
142
- /**
143
- * Converts a list to an array filtering out empty string elements.
144
- *
145
- * @param mixed $value A string representing a list of values separated by the specified separator
146
- * or an array. If the list is a string (e.g. a CSV list) then it will urldecoded
147
- * before processing.
148
- * @param string $sep The char(s) separating the list elements; will be ignored if the list is an array.
149
- *
150
- * @return array An array of list elements.
151
- */
152
- public static function list_to_array( $value, $sep = ',' ) {
153
- // since we might receive URL encoded strings for CSV lists let's URL decode them first
154
- $value = is_array( $value ) ? $value : urldecode( $value );
155
-
156
- $sep = is_string( $sep ) ? $sep : ',';
157
-
158
- if ( $value === null || $value === '' ) {
159
- return [];
160
- }
161
 
162
- if ( ! is_array( $value ) ) {
163
- $value = preg_split( '/\\s*' . preg_quote( $sep ) . '\\s*/', $value );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  }
 
165
 
166
- $filtered = [];
167
- foreach ( $value as $v ) {
168
- if ( '' === $v ) {
169
- continue;
170
- }
171
- $filtered[] = is_numeric( $v ) ? $v + 0 : $v;
172
- }
173
 
174
- return $filtered;
175
- }
 
 
 
 
 
 
 
 
 
 
 
176
 
177
- /**
178
- * Returns a list separated by the specified separator.
179
- *
180
- * @since 4.6
181
- *
182
- * @param mixed $list
183
- * @param string $sep
184
- *
185
- * @return string The list separated by the specified separator or the original list if the list is empty.
186
- */
187
- public static function to_list( $list, $sep = ',' ) {
188
- if ( empty( $list ) ) {
189
- return $list;
190
- }
191
 
192
- if ( is_array( $list ) ) {
193
- return implode( $sep, $list );
194
  }
 
195
 
196
- return $list;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  }
198
 
199
- /**
200
- * Sanitize a multidimensional array.
201
- *
202
- * @since 4.7.18
203
- *
204
- * @param array $data The array to sanitize.
205
- *
206
- * @return array The sanitized array
207
- *
208
- * @link https://gist.github.com/esthezia/5804445
209
- */
210
- public static function escape_multidimensional_array( $data = [] ) {
211
-
212
- if ( ! is_array( $data ) || ! count( $data ) ) {
213
- return [];
214
- }
215
 
216
- foreach ( $data as $key => $value ) {
217
- if ( ! is_array( $value ) && ! is_object( $value ) ) {
218
- $data[ $key ] = esc_attr( trim( $value ) );
219
- }
220
- if ( is_array( $value ) ) {
221
- $data[ $key ] = self::escape_multidimensional_array( $value );
222
- }
223
  }
 
 
 
 
 
224
 
225
- return $data;
 
 
 
 
 
 
 
 
 
 
 
 
226
  }
227
 
228
- /**
229
- * Returns an array of values obtained by using the keys on the map; keys
230
- * that do not have a match in map are discarded.
231
- *
232
- * To discriminate from not found results and legitimately `false`
233
- * values from the map the `$found` parameter will be set by reference.
234
- *
235
- * @since 4.7.19
236
- *
237
- * @param string|array $keys One or more keys that should be used to get
238
- * the new values
239
- * @param array $map An associative array relating the keys to the new
240
- * values.
241
- * @param bool $found When using a single key this argument will be
242
- * set to indicate whether the mapping was successful
243
- * or not.
244
- *
245
- * @return array|mixed|false An array of mapped values, a single mapped value when passing
246
- * one key only or `false` if one key was passed but the key could
247
- * not be mapped.
248
- */
249
- public static function map_or_discard( $keys, array $map, &$found = true ) {
250
- $hash = md5( time() );
251
- $mapped = [];
252
-
253
- foreach ( (array) $keys as $key ) {
254
- $meta_key = Tribe__Utils__Array::get( $map, $key, $hash );
255
- if ( $hash === $meta_key ) {
256
- continue;
257
- }
258
- $mapped[] = $meta_key;
259
- }
260
 
261
- $found = (bool) count( $mapped );
 
262
 
263
- if ( is_array( $keys ) ) {
264
- return $mapped;
265
- }
 
 
 
 
 
 
 
 
 
266
 
267
- return $found ? $mapped[0] : false;
 
268
  }
269
 
270
- /**
271
- * Duplicates any key prefixed with '_' creating an un-prefixed duplicate one.
272
- *
273
- * The un-prefixing and duplication is recursive.
274
- *
275
- * @since 4.9.5
276
- *
277
- * @param mixed $array The array whose keys should be duplicated.
278
- * @param bool $recursive Whether the un-prefixing and duplication should be
279
- * recursive or shallow.
280
- *
281
- * @return array The array with the duplicate, unprefixed, keys or the
282
- * original input if not an array.
283
- */
284
- public static function add_unprefixed_keys_to( $array, $recursive = false ) {
285
- if ( ! is_array( $array ) ) {
286
- return $array;
287
  }
288
-
289
- $unprefixed = [];
290
- foreach ( $array as $key => $value ) {
291
- if ( $recursive && is_array( $value ) ) {
292
- $value = self::add_unprefixed_keys_to( $value, true );
293
- // And also add it to the original array.
294
- $array[ $key ] = array_merge( $array[ $key ], $value );
295
- }
296
-
297
- if ( 0 !== strpos( $key, '_' ) ) {
298
- continue;
299
- }
300
- $unprefixed[ substr( $key, 1 ) ] = $value;
301
  }
302
-
303
- return array_merge( $array, $unprefixed );
304
  }
305
 
306
- /**
307
- * Filters an associative array non-recursively, keeping only the values attached
308
- * to keys starting with the specified prefix.
309
- *
310
- * @since 4.9.5
311
- *
312
- * @param array $array The array to filter.
313
- * @param string $prefix The prefix, or prefixes, of the keys to keep.
314
- *
315
- * @return array The filtered array.
316
- */
317
- public static function filter_prefixed( array $array, $prefix ) {
318
- $prefixes = implode( '|', array_map( 'preg_quote', (array) $prefix ) );
319
- $pattern = '/^(' . $prefixes . ')/';
320
- $filtered = [];
321
- foreach ( $array as $key => $value ) {
322
- if ( ! preg_match( $pattern, $key ) ) {
323
- continue;
324
- }
325
- $filtered[ $key ] = $value;
 
 
 
 
 
 
 
 
 
 
 
 
326
  }
 
 
 
 
327
 
328
- return $filtered;
 
329
  }
330
 
331
- /**
332
- * Flattens an array transforming each value that is an array and only contains one
333
- * element into that one element.
334
- *
335
- * Typical use case is to flatten arrays like those returned by `get_post_meta( $id )`.
336
- * Empty arrays are replaced with an empty string.
337
- *
338
- * @since 4.9.5
339
- *
340
- * @param array $array The array to flatten.
341
- *
342
- * @return array The flattened array.
343
- */
344
- public static function flatten( array $array ) {
345
- foreach ( $array as $key => &$value ) {
346
- if ( ! is_array( $value ) ) {
347
- continue;
348
- }
349
-
350
- $count = count( $value );
351
-
352
- switch ( $count ) {
353
- case 0:
354
- $value = '';
355
- break;
356
- case 1:
357
- $value = reset( $value );
358
- break;
359
- default:
360
- break;
361
- }
362
- }
363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  return $array;
365
  }
366
 
367
- /**
368
- * Duplicates any key not prefixed with '_' creating a prefixed duplicate one.
369
- *
370
- * The prefixing and duplication is recursive.
371
- *
372
- * @since 4.9.5
373
- *
374
- * @param mixed $array The array whose keys should be duplicated.
375
- * @param bool $recursive Whether the prefixing and duplication should be
376
- * recursive or shallow.
377
- *
378
- * @return array The array with the duplicate, prefixed, keys or the
379
- * original input if not an array.
380
- */
381
- public static function add_prefixed_keys_to( $array, $recursive = false ) {
382
- if ( ! is_array( $array ) ) {
383
- return $array;
384
  }
385
 
386
- $prefixed = [];
387
- foreach ( $array as $key => $value ) {
388
- if ( $recursive && is_array( $value ) ) {
389
- $value = self::add_prefixed_keys_to( $value, true );
390
- // And also add it to the original array.
391
- $array[ $key ] = array_merge( $array[ $key ], $value );
392
- }
393
 
394
- if ( 0 === strpos( $key, '_' ) ) {
395
- continue;
396
- }
397
 
398
- $prefixed[ '_' . $key ] = $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  }
400
-
401
- return array_merge( $array, $prefixed );
402
  }
403
 
404
- /**
405
- * Recursively key-sort an array.
406
- *
407
- * @since 4.9.5
408
- *
409
- * @param array $array The array to sort, modified by reference.
410
- *
411
- * @return bool The sorting result.
412
- */
413
- public static function recursive_ksort( array &$array ) {
414
- foreach ( $array as &$value ) {
415
- if ( is_array( $value ) ) {
416
- static::recursive_ksort( $value );
417
- }
 
 
 
 
 
 
418
  }
419
 
420
- return ksort( $array );
421
- }
422
 
423
- /**
424
- * Returns the value associated with the first index, among the indexes, that is set in the array..
425
- *
426
- * @since 4.9.11
427
- *
428
- * @param array $array The array to search.
429
- * @param array $indexes The indexes to search; in order the function will look from the first to the last.
430
- * @param null $default The value that will be returned if the array does not have any of the indexes set.
431
- *
432
- * @return mixed|null The set value or the default value.
433
- */
434
- public static function get_first_set( array $array, array $indexes, $default = null ) {
435
- foreach ( $indexes as $index ) {
436
- if ( ! isset( $array[ $index ] ) ) {
437
- continue;
438
- }
439
-
440
- return $array[ $index ];
441
  }
442
-
443
- return $default;
444
  }
445
 
446
- /**
447
- * Discards everything other than array values having string keys and scalar values, ensuring a
448
- * one-dimensional, associative array result.
449
- *
450
- * @link https://www.php.net/manual/language.types.array.php Keys cast to non-strings will be discarded.
451
- *
452
- * @since 4.12.2
453
- *
454
- * @param array $array
455
- *
456
- * @return array Associative or empty array.
457
- */
458
- public static function filter_to_flat_scalar_associative_array( array $array ) {
459
- $result = [];
460
-
461
- if ( ! is_array( $array ) ) {
462
- return $result;
463
- }
464
 
465
- foreach ( $array as $k => $v ) {
466
- if ( ! is_string( $k ) ) {
467
- continue;
468
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
 
470
- if ( ! is_scalar( $v ) ) {
471
- continue;
472
- }
 
 
 
 
473
 
474
- $result[ $k ] = $v;
 
475
  }
476
 
477
- return $result;
478
  }
479
 
480
- /**
481
- * Build an array from migrating aliased key values to their canonical key values, removing all alias keys.
482
- *
483
- * If the original array has values for both the alias and its canonical, keep the canonical's value and
484
- * discard the alias' value.
485
- *
486
- * @since 4.12.2
487
- *
488
- * @param array $original An associative array of values, such as passed shortcode arguments.
489
- * @param array $alias_map An associative array of aliases: key as alias, value as mapped canonical.
490
- * Example: [ 'alias' => 'canonical', 'from' => 'to', 'that' => 'becomes_this' ]
491
- *
492
- * @return array
493
- */
494
- public static function parse_associative_array_alias( array $original, array $alias_map ) {
495
- // Ensure array values.
496
- $original = (array) $original;
497
- $alias_map = static::filter_to_flat_scalar_associative_array( (array) $alias_map );
498
-
499
- // Fail gracefully if alias array wasn't setup as [ 'from' => 'to' ].
500
- if ( empty( $alias_map ) ) {
501
- return $original;
502
- }
503
-
504
- $result = $original;
505
 
506
- // Parse aliases.
507
- foreach ( $alias_map as $from => $to ) {
508
- // If this alias isn't in use, go onto the next.
509
- if ( ! isset( $result[ $from ] ) ) {
510
- continue;
511
- }
 
 
 
 
 
 
 
 
 
512
 
513
- // Only allow setting alias value if canonical value is not already present.
514
- if ( ! isset( $result[ $to ] ) ) {
515
- $result[ $to ] = $result[ $from ];
516
- }
517
 
518
- // Always remove the alias key.
519
- unset( $result[ $from ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  }
521
 
522
- return $result;
523
  }
 
 
524
  }
525
  }
1
  <?php
2
+ /**
3
+ * Array utilities
4
+ */
5
+ class Tribe__Utils__Array {
6
 
 
7
  /**
8
+ * Set key/value within an array, can set a key nested inside of a multidimensional array.
9
+ *
10
+ * Example: set( $a, [ 0, 1, 2 ], 'hi' ) sets $a[0][1][2] = 'hi' and returns $a.
11
+ *
12
+ * @param mixed $array The array containing the key this sets.
13
+ * @param string|array $key To set a key nested multiple levels deep pass an array
14
+ * specifying each key in order as a value.
15
+ * Example: array( 'lvl1', 'lvl2', 'lvl3' );
16
+ * @param mixed $value The value.
17
+ *
18
+ * @return array Full array with the key set to the specified value.
19
  */
20
+ public static function set( array $array, $key, $value ) {
21
+ // Convert strings and such to array.
22
+ $key = (array) $key;
23
+
24
+ // Setup a pointer that we can point to the key specified.
25
+ $key_pointer = &$array;
26
+
27
+ // Iterate through every key, setting the pointer one level deeper each time.
28
+ foreach ( $key as $i ) {
29
+
30
+ // Ensure current array depth can have children set.
31
+ if ( ! is_array( $key_pointer ) ) {
32
+ // $key_pointer is set but is not an array. Converting it to an array
33
+ // would likely lead to unexpected problems for whatever first set it.
34
+ $error = sprintf(
35
+ 'Attempted to set $array[%1$s] but %2$s is already set and is not an array.',
36
+ implode( $key, '][' ),
37
+ $i
38
+ );
39
+
40
+ _doing_it_wrong( __FUNCTION__, esc_html( $error ), '4.3' );
41
+ break;
42
+ } elseif ( ! isset( $key_pointer[ $i ] ) ) {
43
+ $key_pointer[ $i ] = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
 
46
+ // Dive one level deeper into the nested array.
47
+ $key_pointer = &$key_pointer[ $i ];
 
 
48
  }
49
 
50
+ // Set the value for the specified key
51
+ $key_pointer = $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ return $array;
54
+ }
55
 
56
+ /**
57
+ * Find a value inside of an array or object, including one nested a few levels deep.
58
+ *
59
+ * Example: get( $a, [ 0, 1, 2 ] ) returns the value of $a[0][1][2] or the default.
60
+ *
61
+ * @param array $variable Array or object to search within.
62
+ * @param array|string $indexes Specify each nested index in order.
63
+ * Example: array( 'lvl1', 'lvl2' );
64
+ * @param mixed $default Default value if the search finds nothing.
65
+ *
66
+ * @return mixed The value of the specified index or the default if not found.
67
+ */
68
+ public static function get( $variable, $indexes, $default = null ) {
69
+ if ( is_object( $variable ) ) {
70
+ $variable = (array) $variable;
71
  }
72
 
73
+ if ( ! is_array( $variable ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  return $default;
75
  }
76
 
77
+ foreach ( (array) $indexes as $index ) {
78
+ if ( ! is_array( $variable ) || ! isset( $variable[ $index ] ) ) {
79
+ $variable = $default;
80
+ break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
 
83
+ $variable = $variable[ $index ];
84
  }
85
 
86
+ return $variable;
87
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
+ /**
90
+ * Find a value inside a list of array or objects, including one nested a few levels deep.
91
+ *
92
+ * @since 4.7.7
93
+ *
94
+ * Example: get( [$a, $b, $c], [ 0, 1, 2 ] ) returns the value of $a[0][1][2] found in $a, $b or $c
95
+ * or the default.
96
+ *
97
+ * @param array $variables Array of arrays or objects to search within.
98
+ * @param array|string $indexes Specify each nested index in order.
99
+ * Example: array( 'lvl1', 'lvl2' );
100
+ * @param mixed $default Default value if the search finds nothing.
101
+ *
102
+ * @return mixed The value of the specified index or the default if not found.
103
+ */
104
+ public static function get_in_any( array $variables, $indexes, $default = null ) {
105
+ foreach ( $variables as $variable ) {
106
+ $found = self::get( $variable, $indexes, '__not_found__' );
107
+ if ( '__not_found__' !== $found ) {
108
+ return $found;
109
  }
110
+ }
111
 
112
+ return $default;
113
+ }
 
 
 
 
 
114
 
115
+ /**
116
+ * Behaves exactly like the native strpos(), but accepts an array of needles.
117
+ *
118
+ * @see strpos()
119
+ *
120
+ * @param string $haystack String to search in.
121
+ * @param array|string $needles Strings to search for.
122
+ * @param int $offset Starting position of search.
123
+ *
124
+ * @return false|int Integer position of first needle occurrence.
125
+ */
126
+ public static function strpos( $haystack, $needles, $offset = 0 ) {
127
+ $needles = (array) $needles;
128
 
129
+ foreach ( $needles as $i ) {
130
+ $search = strpos( $haystack, $i, $offset );
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
+ if ( false !== $search ) {
133
+ return $search;
134
  }
135
+ }
136
 
137
+ return false;
138
+ }
139
+
140
+ /**
141
+ * Converts a list to an array filtering out empty string elements.
142
+ *
143
+ * @param mixed $value A string representing a list of values separated by the specified separator
144
+ * or an array. If the list is a string (e.g. a CSV list) then it will urldecoded
145
+ * before processing.
146
+ * @param string $sep The char(s) separating the list elements; will be ignored if the list is an array.
147
+ *
148
+ * @return array An array of list elements.
149
+ */
150
+ public static function list_to_array( $value, $sep = ',' ) {
151
+ // since we might receive URL encoded strings for CSV lists let's URL decode them first
152
+ $value = is_array( $value ) ? $value : urldecode( $value );
153
+
154
+ $sep = is_string( $sep ) ? $sep : ',';
155
+
156
+ if ( $value === null || $value === '' ) {
157
+ return array();
158
  }
159
 
160
+ if ( ! is_array( $value ) ) {
161
+ $value = preg_split( '/\\s*' . preg_quote( $sep ) . '\\s*/', $value );
162
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
+ $filtered = array();
165
+ foreach ( $value as $v ) {
166
+ if ( '' === $v ) {
167
+ continue;
 
 
 
168
  }
169
+ $filtered[] = is_numeric( $v ) ? $v + 0 : $v;
170
+ }
171
+
172
+ return $filtered;
173
+ }
174
 
175
+ /**
176
+ * Returns a list separated by the specified separator.
177
+ *
178
+ * @since 4.6
179
+ *
180
+ * @param mixed $list
181
+ * @param string $sep
182
+ *
183
+ * @return string The list separated by the specified separator or the original list if the list is empty.
184
+ */
185
+ public static function to_list( $list, $sep = ',' ) {
186
+ if ( empty( $list ) ) {
187
+ return $list;
188
  }
189
 
190
+ if ( is_array( $list ) ) {
191
+ return implode( $sep, $list );
192
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
+ return $list;
195
+ }
196
 
197
+ /**
198
+ * Sanitize a multidimensional array.
199
+ *
200
+ * @since 4.7.18
201
+ *
202
+ * @param array $data The array to sanitize.
203
+ *
204
+ * @return array The sanitized array
205
+ *
206
+ * @link https://gist.github.com/esthezia/5804445
207
+ */
208
+ public static function escape_multidimensional_array( $data = array() ) {
209
 
210
+ if ( ! is_array( $data ) || ! count( $data ) ) {
211
+ return array();
212
  }
213
 
214
+ foreach ( $data as $key => $value ) {
215
+ if ( ! is_array( $value ) && ! is_object( $value ) ) {
216
+ $data[ $key ] = esc_attr( trim( $value ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
218
+ if ( is_array( $value ) ) {
219
+ $data[ $key ] = self::escape_multidimensional_array( $value );
 
 
 
 
 
 
 
 
 
 
 
220
  }
 
 
221
  }
222
 
223
+ return $data;
224
+ }
225
+
226
+ /**
227
+ * Returns an array of values obtained by using the keys on the map; keys
228
+ * that do not have a match in map are discarded.
229
+ *
230
+ * To discriminate from not found results and legitimately `false`
231
+ * values from the map the `$found` parameter will be set by reference.
232
+ *
233
+ * @since 4.7.19
234
+ *
235
+ * @param string|array $keys One or more keys that should be used to get
236
+ * the new values
237
+ * @param array $map An associative array relating the keys to the new
238
+ * values.
239
+ * @param bool $found When using a single key this argument will be
240
+ * set to indicate whether the mapping was successful
241
+ * or not.
242
+ *
243
+ * @return array|mixed|false An array of mapped values, a single mapped value when passing
244
+ * one key only or `false` if one key was passed but the key could
245
+ * not be mapped.
246
+ */
247
+ public static function map_or_discard( $keys, array $map, &$found = true ) {
248
+ $hash = md5( time() );
249
+ $mapped = array();
250
+
251
+ foreach ( (array) $keys as $key ) {
252
+ $meta_key = Tribe__Utils__Array::get( $map, $key, $hash );
253
+ if ( $hash === $meta_key ) {
254
+ continue;
255
  }
256
+ $mapped[] = $meta_key;
257
+ }
258
+
259
+ $found = (bool) count( $mapped );
260
 
261
+ if ( is_array( $keys ) ) {
262
+ return $mapped;
263
  }
264
 
265
+ return $found ? $mapped[0] : false;
266
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
 
268
+ /**
269
+ * Duplicates any key prefixed with '_' creating an un-prefixed duplicate one.
270
+ *
271
+ * The un-prefixing and duplication is recursive.
272
+ *
273
+ * @since 4.9.5
274
+ *
275
+ * @param mixed $array The array whose keys should be duplicated.
276
+ * @param bool $recursive Whether the un-prefixing and duplication should be
277
+ * recursive or shallow.
278
+ *
279
+ * @return array The array with the duplicate, unprefixed, keys or the
280
+ * original input if not an array.
281
+ */
282
+ public static function add_unprefixed_keys_to( $array, $recursive = false ) {
283
+ if ( ! is_array( $array ) ) {
284
  return $array;
285
  }
286
 
287
+ $unprefixed = array();
288
+ foreach ( $array as $key => $value ) {
289
+ if ( $recursive && is_array( $value ) ) {
290
+ $value = self::add_unprefixed_keys_to( $value, true );
291
+ // And also add it to the original array.
292
+ $array[ $key ] = array_merge( $array[ $key ], $value );
 
 
 
 
 
 
 
 
 
 
 
293
  }
294
 
295
+ if ( 0 !== strpos( $key, '_' ) ) {
296
+ continue;
297
+ }
298
+ $unprefixed[ substr( $key, 1 ) ] = $value;
299
+ }
 
 
300
 
301
+ return array_merge( $array, $unprefixed );
302
+ }
 
303
 
304
+ /**
305
+ * Filters an associative array non-recursively, keeping only the values attached
306
+ * to keys starting with the specified prefix.
307
+ *
308
+ * @since 4.9.5
309
+ *
310
+ * @param array $array The array to filter.
311
+ * @param string $prefix The prefix, or prefixes, of the keys to keep.
312
+ *
313
+ * @return array The filtered array.
314
+ */
315
+ public static function filter_prefixed( array $array, $prefix ) {
316
+ $prefixes = implode( '|', array_map( 'preg_quote', (array) $prefix ) );
317
+ $pattern = '/^(' . $prefixes . ')/';
318
+ $filtered = array();
319
+ foreach ( $array as $key => $value ) {
320
+ if ( ! preg_match( $pattern, $key ) ) {
321
+ continue;
322
  }
323
+ $filtered[ $key ] = $value;
 
324
  }
325
 
326
+ return $filtered;
327
+ }
328
+
329
+ /**
330
+ * Flattens an array transforming each value that is an array and only contains one
331
+ * element into that one element.
332
+ *
333
+ * Typical use case is to flatten arrays like those returned by `get_post_meta( $id )`.
334
+ * Empty arrays are replaced with an empty string.
335
+ *
336
+ * @since 4.9.5
337
+ *
338
+ * @param array $array The array to flatten.
339
+ *
340
+ * @return array The flattened array.
341
+ */
342
+ public static function flatten( array $array ) {
343
+ foreach ( $array as $key => &$value ) {
344
+ if ( ! is_array( $value ) ) {
345
+ continue;
346
  }
347
 
348
+ $count = count( $value );
 
349
 
350
+ switch ( $count ) {
351
+ case 0:
352
+ $value = '';
353
+ break;
354
+ case 1:
355
+ $value = reset( $value );
356
+ break;
357
+ default:
358
+ break;
 
 
 
 
 
 
 
 
 
359
  }
 
 
360
  }
361
 
362
+ return $array;
363
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
 
365
+ /**
366
+ * Duplicates any key not prefixed with '_' creating a prefixed duplicate one.
367
+ *
368
+ * The prefixing and duplication is recursive.
369
+ *
370
+ * @since 4.9.5
371
+ *
372
+ * @param mixed $array The array whose keys should be duplicated.
373
+ * @param bool $recursive Whether the prefixing and duplication should be
374
+ * recursive or shallow.
375
+ *
376
+ * @return array The array with the duplicate, prefixed, keys or the
377
+ * original input if not an array.
378
+ */
379
+ public static function add_prefixed_keys_to( $array, $recursive = false ) {
380
+ if ( ! is_array( $array ) ) {
381
+ return $array;
382
+ }
383
 
384
+ $prefixed = array();
385
+ foreach ( $array as $key => $value ) {
386
+ if ( $recursive && is_array( $value ) ) {
387
+ $value = self::add_prefixed_keys_to( $value, true );
388
+ // And also add it to the original array.
389
+ $array[ $key ] = array_merge( $array[ $key ], $value );
390
+ }
391
 
392
+ if ( 0 === strpos( $key, '_' ) ) {
393
+ continue;
394
  }
395
 
396
+ $prefixed[ '_' . $key ] = $value;
397
  }
398
 
399
+ return array_merge( $array, $prefixed );
400
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
 
402
+ /**
403
+ * Recursively key-sort an array.
404
+ *
405
+ * @since 4.9.5
406
+ *
407
+ * @param array $array The array to sort, modified by reference.
408
+ *
409
+ * @return bool The sorting result.
410
+ */
411
+ public static function recursive_ksort( array &$array ) {
412
+ foreach ( $array as &$value ) {
413
+ if ( is_array( $value ) ) {
414
+ static::recursive_ksort( $value );
415
+ }
416
+ }
417
 
418
+ return ksort( $array );
419
+ }
 
 
420
 
421
+ /**
422
+ * Returns the value associated with the first index, among the indexes, that is set in the array..
423
+ *
424
+ * @since 4.9.11
425
+ *
426
+ * @param array $array The array to search.
427
+ * @param array $indexes The indexes to search; in order the function will look from the first to the last.
428
+ * @param null $default The value that will be returned if the array does not have any of the indexes set.
429
+ *
430
+ * @return mixed|null The set value or the default value.
431
+ */
432
+ public static function get_first_set( array $array, array $indexes, $default = null ) {
433
+ foreach ( $indexes as $index ) {
434
+ if ( ! isset( $array[ $index ] ) ) {
435
+ continue;
436
  }
437
 
438
+ return $array[ $index ];
439
  }
440
+
441
+ return $default;
442
  }
443
  }
common/src/Tribe/Utils/Collection_Interface.php DELETED
@@ -1,57 +0,0 @@
1
- <?php
2
- /**
3
- * A class providing collection methods.
4
- *
5
- * For convenience classes implementing this interface should `use Collection_Trait` and implement just the `all`
6
- * method.
7
- *
8
- * @since 4.9.14
9
- * @package Tribe\Utils
10
- */
11
-
12
- namespace Tribe\Utils;
13
-
14
- /**
15
- * Interface Collection_Interface
16
- * @since 4.9.14
17
- * @package Tribe\Utils
18
- */
19
- interface Collection_Interface extends \ArrayAccess, \SeekableIterator, \Countable, \Serializable, \JsonSerializable {
20
- /**
21
- * Returns all the items in the collection.
22
- *
23
- * @since 4.9.14
24
- *
25
- * @return array All the items in the collection.
26
- */
27
- public function all();
28
-
29
- /**
30
- * Returns the first item in the collection.
31
- *
32
- * @since 4.9.14
33
- *
34
- * @return mixed The first item in the collection.
35
- */
36
- public function first();
37
-
38
- /**
39
- * Returns the last item in the collection.
40
- *
41
- * @since 4.9.14
42
- *
43
- * @return mixed The last item in the collection.
44
- */
45
- public function last();
46
-
47
- /**
48
- * Returns the nth item in the collection.
49
- *
50
- * @since 4.9.14
51
- *
52
- * @param int $n The 1-based index of the item to return. It's not 0-based, `1` will return the first item.
53
- *
54
- * @return mixed|null The nth item in the collection or `null` if not set.
55
- */
56
- public function nth( $n );
57
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Collection_Trait.php DELETED
@@ -1,207 +0,0 @@
1
- <?php
2
- /**
3
- * Implements all the methods required by the `Tribe\Utils\Collection` interface minus the `all` one.
4
- *
5
- * The trait will also implement the `ArrayAccess`, `Iterator` and `Countable` interface methods.
6
- *
7
- * @since 4.9.14
8
- * @package Tribe\Utils
9
- */
10
-
11
- namespace Tribe\Utils;
12
-
13
- /**
14
- * Trait Collection_Trait
15
- * @since 4.9.14
16
- * @package Tribe\Utils
17
- */
18
- trait Collection_Trait {
19
- /**
20
- * The current items index.
21
- *
22
- * @var int
23
- */
24
- protected $items_index = 0;
25
-
26
- /**
27
- * Returns the first item in the collection.
28
- *
29
- * @since 4.9.14
30
- *
31
- * @return mixed The first item in the collection.
32
- */
33
- public function first() {
34
- $items = $this->all();
35
-
36
- return reset( $items );
37
- }
38
-
39
- /**
40
- * Returns the last item in the collection.
41
- *
42
- * @since 4.9.14
43
- *
44
- * @return mixed The last item in the collection.
45
- */
46
- public function last() {
47
- $items = $this->all();
48
-
49
- return end( $items );
50
- }
51
-
52
- /**
53
- * Returns the nth item in the collection.
54
- *
55
- * @since 4.9.14
56
- *
57
- * @param int $n The 1-based index of the item to return. It's not 0-based, `1` will return the first item.
58
- *
59
- * @return mixed|null The nth item in the collection or `null` if not set.
60
- */
61
- public function nth( $n ) {
62
- $items = array_values( $this->all() );
63
-
64
- return isset( $items[ $n - 1 ] ) ? $items[ $n - 1 ] : null;
65
- }
66
-
67
- /**
68
- * {@inheritDoc}
69
- */
70
- public function offsetExists( $offset ) {
71
- $items = $this->all();
72
-
73
- return isset( $items[ $offset ] );
74
- }
75
-
76
- /**
77
- * {@inheritDoc}
78
- */
79
- public function offsetGet( $offset ) {
80
- $items = $this->all();
81
-
82
- return isset( $items[ $offset ] )
83
- ? $items[ $offset ]
84
- : null;
85
- }
86
-
87
- /**
88
- * {@inheritDoc}
89
- */
90
- public function offsetSet( $offset, $value ) {
91
- $this->items = $this->all();
92
-
93
- $this->items[ $offset ] = $value;
94
- }
95
-
96
- /**
97
- * {@inheritDoc}
98
- */
99
- public function offsetUnset( $offset ) {
100
- $this->items = $this->all();
101
-
102
- unset( $this->items[ $offset ] );
103
- }
104
-
105
- /**
106
- * {@inheritDoc}
107
- */
108
- public function next() {
109
- $this->items_index ++;
110
- }
111
-
112
- /**
113
- * {@inheritDoc}
114
- */
115
- public function valid() {
116
- $items = $this->all();
117
-
118
- return ( isset( $items[ $this->items_index ] ) );
119
- }
120
-
121
- /**
122
- * {@inheritDoc}
123
- */
124
- public function key() {
125
- return $this->items_index;
126
- }
127
-
128
- /**
129
- * {@inheritDoc}
130
- */
131
- public function current() {
132
- $items = array_values( $this->all() );
133
-
134
- return isset( $items[ $this->items_index ] ) ? $items[ $this->items_index ] : null;
135
- }
136
-
137
- /**
138
- * {@inheritDoc}
139
- */
140
- public function rewind() {
141
- $this->items_index = 0;
142
- }
143
-
144
- /**
145
- * {@inheritDoc}
146
- */
147
- public function count() {
148
- return count( $this->all() );
149
- }
150
-
151
- /**
152
- * {@inheritDoc}
153
- */
154
- public function serialize() {
155
- $to_serialize = $this->all();
156
-
157
- if ( method_exists( $this, 'before_serialize' ) ) {
158
- $to_serialize = $this->before_serialize( $this->all() );
159
- }
160
-
161
- return serialize( $to_serialize );
162
- }
163
-
164
- /**
165
- * {@inheritDoc}
166
- */
167
- public function unserialize( $serialized ) {
168
- $to_unserialize = $serialized;
169
-
170
- if ( method_exists( $this, 'custom_unserialize' ) ) {
171
- $this->items = $this->custom_unserialize( $to_unserialize );
172
- return;
173
- }
174
-
175
- $this->items = unserialize( $to_unserialize );
176
- }
177
-
178
- /**
179
- * {@inheritDoc}
180
- */
181
- public function seek( $position ) {
182
- $this->items_index = $position;
183
- }
184
-
185
- /**
186
- * Applies a filter callback to each element of this collection changing the collection elements to only those
187
- * passing the filter.
188
- *
189
- * @since 4.10.2
190
- *
191
- * @param callable $filter_callback The filter callback that will be applied to each element of the collection; the
192
- * callback will receive the element as parameter.
193
- *
194
- * @return Collection_Trait A new collection instance, that contains only the elements that passed the filter.
195
- */
196
- public function filter( $filter_callback ) {
197
- if ( $this->count() === 0 ) {
198
- // If there is nothing to filter to begin with, just return this.
199
- return $this;
200
- }
201
-
202
- $filtered = new static();
203
- $filtered->items = array_filter( $this->all(), $filter_callback );
204
-
205
- return $filtered;
206
- }
207
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Date_I18n.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
- /**
3
- * Extends DateTime and includes translation capabilities.
4
- *
5
- * @package Tribe\Utils
6
- * @since 4.11.0
7
- */
8
- namespace Tribe\Utils;
9
-
10
- use Tribe__Date_Utils as Dates;
11
- use DateTime;
12
- use DateTimeImmutable;
13
-
14
- /**
15
- * Class Date i18n
16
- *
17
- * @package Tribe\Utils
18
- * @since 4.11.0
19
- */
20
- class Date_I18n extends DateTime {
21
- /**
22
- * {@inheritDoc}
23
- *
24
- * @return Date_I18n Localizable variation of DateTime.
25
- */
26
- public static function createFromImmutable( $datetime ) {
27
- $date_object = new self;
28
- $date_object->setTimestamp( $datetime->getTimestamp() );
29
- $date_object->setTimezone( $datetime->getTimezone() );
30
- return $date_object;
31
- }
32
-
33
- /**
34
- * Returns a translated string using the params from this DateTime instance.
35
- *
36
- * @since 4.11.0
37
- *
38
- * @param string $date_format Format to be used in the translation.
39
- *
40
- * @return string Translated date.
41
- */
42
- public function format_i18n( $date_format ) {
43
- $unix_with_tz = $this->getTimestamp() + $this->getOffset();
44
- $translated = date_i18n( $date_format, $unix_with_tz );
45
-
46
- return $translated;
47
- }
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Date_I18n_Immutable.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
- /**
3
- * Extends DateTimeImmutable and includes translation capabilities.
4
- *
5
- * @since 4.11.0
6
- * @package Tribe\Utils
7
- */
8
-
9
- namespace Tribe\Utils;
10
-
11
- use DateTimeImmutable;
12
-
13
- /**
14
- * Class Date i18n Immutable
15
- *
16
- * @since 4.11.0
17
- * @package Tribe\Utils
18
- */
19
- class Date_I18n_Immutable extends DateTimeImmutable {
20
- /**
21
- * {@inheritDoc}
22
- *
23
- * @return Date_I18n_Immutable Localizable variation of DateTimeImmutable.
24
- */
25
- public static function createFromMutable( $datetime ) {
26
- $date_object = new self;
27
- $date_object = $date_object->setTimestamp( $datetime->getTimestamp() );
28
- $date_object = $date_object->setTimezone( $datetime->getTimezone() );
29
-
30
- return $date_object;
31
- }
32
-
33
- /**
34
- * Returns a translated string using the params from this Immutable DateTime instance.
35
- *
36
- * @since 4.11.0
37
- *
38
- * @param string $date_format Format to be used in the translation.
39
- *
40
- * @return string Translated date.
41
- */
42
- public function format_i18n( $date_format ) {
43
- $unix_with_tz = $this->getTimestamp() + $this->getOffset();
44
- $translated = date_i18n( $date_format, $unix_with_tz );
45
-
46
- return $translated;
47
- }
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Element_Attributes.php DELETED
@@ -1,241 +0,0 @@
1
- <?php
2
- namespace Tribe\Utils;
3
-
4
- /**
5
- * Class Element_Attributes to handle HTML attributes for elements.
6
- *
7
- * @since 4.12.3
8
- *
9
- * @package Tribe\Utils
10
- */
11
- class Element_Attributes {
12
- /**
13
- * Store the results of parsing the attributes.
14
- *
15
- * @since 4.12.3
16
- *
17
- * @var array<string,string>
18
- */
19
- protected $results = [];
20
-
21
- /**
22
- * Stores the arguments passed.
23
- *
24
- * @since 4.12.3
25
- *
26
- * @var array
27
- */
28
- protected $arguments = [];
29
-
30
- /**
31
- * Setups an instance of Element Attributes.
32
- *
33
- * @since 4.12.3
34
- *
35
- * @return void
36
- */
37
- public function __construct() {
38
- $this->arguments = func_get_args();
39
- }
40
-
41
- /**
42
- * When invoked this class will return the full HTML attributes.
43
- *
44
- * @since 4.12.3
45
- *
46
- * @return string In the format ` attribute1="value1" attribute2="value2" `
47
- */
48
- public function __invoke() {
49
- $this->arguments = func_get_args();
50
- return $this->get_attributes();
51
- }
52
-
53
-
54
- /**
55
- * When cast to string an instance will return the full HTML attributes.
56
- *
57
- * @since 4.12.3
58
- *
59
- * @return string In the format ` attribute1="value1" attribute2="value2" `
60
- */
61
- public function __toString() {
62
- return $this->get_attributes();
63
- }
64
-
65
- /**
66
- * Gets the full HTML attributes for this instance of Element Attributes.
67
- * It will contain a space on each end of the attribute.
68
- *
69
- * @since 4.12.3
70
- *
71
- * @return string In the format ` attribute1="value1" attribute2="value2" `
72
- */
73
- public function get_attributes() {
74
- $attributes = $this->get_attributes_as_string();
75
-
76
- // Bail with empty string when no attributes are present
77
- if ( ! $attributes ) {
78
- return '';
79
- }
80
-
81
- return " {$attributes} ";
82
- }
83
-
84
- /**
85
- * Gets a space separated string of all attributes to be printed.
86
- *
87
- * @since 4.12.3
88
- *
89
- * @return string
90
- */
91
- public function get_attributes_as_string() {
92
- return implode( ' ', $this->get_attributes_array() );
93
- }
94
-
95
- /**
96
- * Get the array of attributes to be printed.
97
- *
98
- * @since 4.12.3
99
- *
100
- * @return array
101
- */
102
- public function get_attributes_array() {
103
- $this->results = [];
104
- $attributes = [];
105
-
106
- $this->parse_array( $this->arguments );
107
-
108
- foreach ( $this->results as $key => $val ) {
109
- if ( ! $val && '0' !== $val ) {
110
- continue;
111
- }
112
-
113
- if ( is_bool( $val ) ) {
114
- $attributes[] = esc_attr( $key );
115
- } else {
116
- // Remove double quotes that might be surrounding the value.
117
- trim( $val, '"' );
118
- $attributes[] = esc_attr( $key ) . '="' . esc_attr( $val ) . '"';
119
- }
120
- }
121
-
122
- return $attributes;
123
- }
124
-
125
- /**
126
- * Parse arguments or argument for this instance, and store values on results.
127
- *
128
- * @since 4.12.3
129
- *
130
- * @param mixed $arguments Any possible set of arguments that this class supports.
131
- *
132
- * @return void
133
- */
134
- protected function parse( $arguments ) {
135
- if ( ! $arguments ) {
136
- return;
137
- }
138
-
139
- if ( is_numeric( $arguments ) ) { // phpcs:ignore
140
- // Bail on any numeric values.
141
- } elseif ( is_array( $arguments ) ) {
142
- // ['foo', 'bar', ...] || ['foo' => TRUE, 'bar' => FALSE, 'baz' => 'foo', ...]
143
- $this->parse_array( $arguments );
144
- } elseif ( is_string( $arguments ) ) {
145
- // 'foo bar'
146
- $this->parse_string( $arguments );
147
- } elseif ( $arguments instanceof \Closure || is_callable( $arguments ) ) {
148
- // function() {}
149
- $this->parse_callable( $arguments );
150
- } elseif ( is_object( $arguments ) ) {
151
- // stdClass
152
- $this->parse_object( $arguments );
153
- }
154
- }
155
-
156
- /**
157
- * Parse an array into an array of acceptable values for the instance.
158
- *
159
- * @since 4.12.3
160
- *
161
- * @param array $values Array of values to be parsed.
162
- *
163
- * @return void
164
- */
165
- protected function parse_array( array $values ) {
166
- foreach ( $values as $key => $value ) {
167
- if ( is_int( $key ) ) {
168
- $this->parse( $value );
169
- } elseif ( is_string( $key ) ) {
170
- if ( ! is_bool( $value ) && ! is_string( $value ) ) {
171
- throw new \UnexpectedValueException( 'Value for key ' . $key . ' must be of type boolean or string' );
172
- }
173
-
174
- $this->results[ $key ] = $value;
175
- }
176
- }
177
- }
178
-
179
- /**
180
- * Parse a string into an array of acceptable values for the instance.
181
- *
182
- * @since 4.12.3
183
- *
184
- * @param string $arguments Space separated string of attributes to be parsed.
185
- *
186
- * @return void
187
- */
188
- protected function parse_string( $arguments ) {
189
- $values = preg_split( '/\s+/', $arguments, -1, PREG_SPLIT_NO_EMPTY );
190
-
191
- // When it doesnt match, bail early.
192
- if ( ! $values ) {
193
- return;
194
- }
195
-
196
- $attrs = [];
197
-
198
- foreach ( $values as $key => $value ) {
199
- if ( preg_match( '/^(?<key>[^=]+)="*(?<value>.*?)"*$/', $value, $m ) ) {
200
- // Something like `f="boo"` or `foo=bar`.
201
- $attrs[ $m['key'] ] = $m['value'];
202
-
203
- continue;
204
- }
205
-
206
- $attrs[ $value ] = true;
207
- }
208
-
209
- $this->parse_array( $attrs );
210
- }
211
-
212
- /**
213
- * Parses an object into the array of considered attributes.
214
- *
215
- * @since 4.12.3
216
- *
217
- * @param mixed $object Object to be converted into array and parsed.
218
- *
219
- * @return void
220
- */
221
- protected function parse_object( $object ) {
222
- $this->parse_array( (array) $object );
223
- }
224
-
225
- /**
226
- * Parses a callable method or function into the array of considered attributes.
227
- *
228
- * The result of the callable will REPLACE the current attributes, callables will work like filters.
229
- *
230
- * @since 4.12.3
231
- *
232
- * @param callable $method_or_function Method or Function to be called.
233
- *
234
- * @return void
235
- */
236
- protected function parse_callable( callable $method_or_function ) {
237
- $filtered = $method_or_function( $this->results );
238
- $this->results = [];
239
- $this->parse( $filtered );
240
- }
241
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Element_Classes.php DELETED
@@ -1,246 +0,0 @@
1
- <?php
2
- namespace Tribe\Utils;
3
-
4
- /**
5
- * Class Element_Classes to handle HTML class attribute for elements.
6
- *
7
- * @since 4.9.13
8
- *
9
- * @package Tribe\Utils
10
- */
11
- class Element_Classes {
12
- /**
13
- * Store the results of parsing the classes.
14
- *
15
- * @since 4.9.13
16
- *
17
- * @var array
18
- */
19
- protected $results = [];
20
-
21
- /**
22
- * Stores the arguments passed.
23
- *
24
- * @since 4.9.13
25
- *
26
- * @var array
27
- */
28
- protected $arguments = [];
29
-
30
- /**
31
- * Setups an instance of Element Classes.
32
- *
33
- * @since 4.9.13
34
- *
35
- * @return void
36
- */
37
- public function __construct() {
38
- $this->arguments = func_get_args();
39
- }
40
-
41
- /**
42
- * When invoked this class will return the full HTML class attribute.
43
- *
44
- * @since 4.9.13
45
- *
46
- * @return string In the format ` class="class1 class2" `
47
- */
48
- public function __invoke() {
49
- $this->arguments = func_get_args();
50
- return $this->get_attribute();
51
- }
52
-
53
-
54
- /**
55
- * When cast to string an instance will return the full HTML class attribute.
56
- *
57
- * @since 4.9.13
58
- *
59
- * @return string In the format ` class="class1 class2" `
60
- */
61
- public function __toString() {
62
- return $this->get_attribute();
63
- }
64
-
65
- /**
66
- * Gets the full HTML class attribute for this instance of Element Classes.
67
- * It will contain a space on each end of the attribute.
68
- *
69
- * @since 4.9.13
70
- *
71
- * @return string In the format ` class="class1 class2" `
72
- */
73
- public function get_attribute() {
74
- $classes = $this->get_classes_as_string();
75
-
76
- // Bail with empty string when no classes are present
77
- if ( ! $classes ) {
78
- return '';
79
- }
80
-
81
- return " class=\"{$classes}\" ";
82
- }
83
-
84
- /**
85
- * Gets a space separated string of all classes to be printed.
86
- *
87
- * @since 4.9.13
88
- *
89
- * @return string
90
- */
91
- public function get_classes_as_string() {
92
- return implode( ' ', $this->get_classes() );
93
- }
94
-
95
- /**
96
- * Get the array of classes to be printed.
97
- *
98
- * @since 4.9.13
99
- *
100
- * @return array
101
- */
102
- public function get_classes() {
103
- $this->results = [];
104
- $classes = [];
105
-
106
- $this->parse_array( $this->arguments );
107
-
108
- foreach ( $this->results as $key => $val ) {
109
- if ( ! $val ) {
110
- continue;
111
- }
112
-
113
- $classes[] = $key;
114
- }
115
-
116
- $classes = array_map( 'sanitize_html_class', $classes );
117
- $classes = array_filter( array_unique( $classes ) );
118
-
119
- return $classes;
120
- }
121
-
122
-
123
- /**
124
- * Get the array of the classes, using [ class_name => bool ] as the format.
125
- *
126
- * @since 4.9.13
127
- *
128
- * @return array [ class_name => bool ]
129
- */
130
- public function get_conditions() {
131
- $this->results = [];
132
- $this->parse_array( $this->arguments );
133
-
134
- return $this->results;
135
- }
136
-
137
- /**
138
- * Parse arguments or argument for this instance, and store values on results.
139
- *
140
- * @since 4.9.13
141
- *
142
- * @param mixed $arguments Any possible set of arguments that this class supports.
143
- * @param boolean $default_value What is the default value for a given class.
144
- *
145
- * @return void
146
- */
147
- protected function parse( $arguments, $default_value = true ) {
148
- if ( ! $arguments ) {
149
- return;
150
- }
151
-
152
- if ( is_numeric( $arguments ) ) { // phpcs:ignore
153
- // Bail on any numeric values
154
- } elseif ( is_string( $arguments ) ) {
155
- // 'foo bar'
156
- $this->parse_string( $arguments );
157
- } elseif ( $arguments instanceof \Closure || is_callable( $arguments ) ) {
158
- // function() {}
159
- $this->parse_callable( $arguments );
160
- } elseif ( is_array( $arguments ) ) {
161
- // ['foo', 'bar', ...] || ['foo' => TRUE, 'bar' => FALSE, ...]
162
- $this->parse_array( $arguments );
163
- } elseif ( is_object( $arguments ) ) {
164
- // stdClass
165
- $this->parse_object( $arguments );
166
- }
167
- }
168
-
169
- /**
170
- * Parse a string into an array of acceptable values for the instance.
171
- *
172
- * @since 4.9.13
173
- *
174
- * @param string $arguments Space separated string of classes to be parsed.
175
- * @param boolean $default_value What is the default value for a given class.
176
- *
177
- * @return void
178
- */
179
- protected function parse_string( $arguments, $default_value = true ) {
180
- $values = preg_split( '/\s+/', $arguments, -1, PREG_SPLIT_NO_EMPTY );
181
-
182
- // When it doesnt match, bail early.
183
- if ( ! $values ) {
184
- return;
185
- }
186
-
187
- foreach ( $values as $class_name ) {
188
- $this->results[ $class_name ] = $default_value;
189
- }
190
- }
191
-
192
- /**
193
- * Parse an array into an array of acceptable values for the instance.
194
- *
195
- * @since 4.9.13
196
- *
197
- * @param array $values Array of values to be parsed.
198
- *
199
- * @return void
200
- */
201
- protected function parse_array( array $values ) {
202
- foreach ( $values as $key => $value ) {
203
- if ( is_int( $key ) ) {
204
- if ( is_bool( $value ) ) {
205
- $this->parse( $key, $value );
206
- } else {
207
- $this->parse( $value );
208
- }
209
- } elseif ( is_string( $key ) ) {
210
- if ( ! is_bool( $value ) ) {
211
- throw new \UnexpectedValueException( 'Value for key ' . $key . ' must be of type boolean' );
212
- }
213
-
214
- $this->parse_string( $key, $value );
215
- }
216
- }
217
- }
218
-
219
- /**
220
- * Parses an object, only if it contains __toString it will be considered.
221
- *
222
- * @since 4.9.13
223
- *
224
- * @param mixed $object Object to be checked for the __toString method
225
- *
226
- * @return void
227
- */
228
- protected function parse_object( $object ) {
229
- if ( method_exists( $object, '__toString' ) ) {
230
- $this->parse( (string) $object );
231
- }
232
- }
233
-
234
- /**
235
- * Parses a callable method or function into the array of considered classes.s
236
- *
237
- * @since 4.9.13
238
- *
239
- * @param callable $method_or_function Method or Function to be called.
240
- *
241
- * @return void
242
- */
243
- protected function parse_callable( callable $method_or_function ) {
244
- $this->parse( $method_or_function( $this->results ) );
245
- }
246
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Global_ID.php CHANGED
@@ -141,7 +141,7 @@ class Tribe__Utils__Global_ID {
141
  *
142
  * @since 4.7.15
143
  */
144
- public function parse( $global_id ) {
145
  $parsed_global_id = null;
146
 
147
  if ( $global_id ) {
141
  *
142
  * @since 4.7.15
143
  */
144
+ public function parse( string $global_id ) {
145
  $parsed_global_id = null;
146
 
147
  if ( $global_id ) {
common/src/Tribe/Utils/Lazy_Collection.php DELETED
@@ -1,117 +0,0 @@
1
- <?php
2
- /**
3
- * An array whose elements will be lazily fetched.
4
- *
5
- * Example usage:
6
- * ```php
7
- * $callback = static function(){
8
- * $posts = costly_get_posts_call();
9
- *
10
- * return $posts;
11
- * };
12
- *
13
- * // The costly query is not ran now!
14
- * $collection = new Lazy_Collection( $callback );
15
- *
16
- * // If we need to get the elements, then the costly query is made.
17
- * if( $really_needs_the_posts ){
18
- * $posts = $collection->all;
19
- * }
20
- * ````
21
- *
22
- * @since 4.9.14
23
- * @package Tribe\Utils
24
- */
25
-
26
- namespace Tribe\Utils;
27
-
28
- /**
29
- * Class Lazy_Collection
30
- *
31
- * @since 4.9.14
32
- * @package Tribe\Utils
33
- */
34
- class Lazy_Collection implements Collection_Interface {
35
- use Collection_Trait;
36
- use Lazy_Events;
37
-
38
- /**
39
- * The callback in charge of providing the elements.
40
- *
41
- * @var callable
42
- */
43
- protected $callback;
44
-
45
- /**
46
- * The elements of the array.
47
- *
48
- * @var array
49
- */
50
- protected $items;
51
-
52
- /**
53
- * Array_Promise constructor.
54
- *
55
- * @since 4.9.14
56
- *
57
- * @param callable $callback The callback that will be used to populate the elements.
58
- */
59
- public function __construct( callable $callback ) {
60
- $this->callback = $callback;
61
- }
62
-
63
- /**
64
- * Fetches the array items and returns them.
65
- *
66
- * @since 4.9.14
67
- *
68
- * @return array The array items.
69
- */
70
- public function all() {
71
- $this->resolve();
72
-
73
- return $this->items;
74
- }
75
-
76
- /**
77
- * Fills the array elements from the callback if required.
78
- *
79
- * @since 4.9.14
80
- */
81
- protected function resolve() {
82
- if ( null !== $this->items ) {
83
- return;
84
- }
85
-
86
- $items = call_user_func( $this->callback );
87
- $this->items = (array) $items;
88
- $this->resolved();
89
- }
90
-
91
- /**
92
- * Allows accessing the collection methods using properties.
93
- *
94
- * E.g. `$collection->first` is equivalent to `$collection->first()`.
95
- *
96
- * @since 4.9.14
97
- *
98
- * @param string $property The name of the property to access.
99
- *
100
- * @return mixed|null The return value of the collection corresponding method or `null` if the collection does not
101
- * have that method.
102
- */
103
- public function __get( $property ) {
104
- if ( method_exists( $this, $property ) ) {
105
- return call_user_func( [ $this, $property ] );
106
- }
107
-
108
- return null;
109
- }
110
-
111
- /**
112
- * {@inheritDoc}
113
- */
114
- public function jsonSerialize() {
115
- return $this->all();
116
- }
117
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Lazy_Events.php DELETED
@@ -1,172 +0,0 @@
1
- <?php
2
- /**
3
- * Provides methods for "lazy" objects to act upon life cycle events.
4
- *
5
- * @since 4.9.16
6
- *
7
- * @example
8
- * ```php
9
- * class Lazy_List_Of_Stuff {
10
- * use Tribe\Utils\Lazy_Events;
11
- *
12
- * protected $list;
13
- *
14
- * public function fetch_list(){
15
- * $cached = wp_cache_get( 'list_of_stuff_one' );
16
- *
17
- * if( false !== $cached ){
18
- * return $cached;
19
- *
20
- * if( null === $this->list ){
21
- * $this->list = really_expensive_calculation();
22
- * }
23
- *
24
- * $this->resolved();
25
- * }
26
- *
27
- * return $this->list;
28
- * }
29
- * }
30
- *
31
- * class Lazy_Value {
32
- * use Tribe\Utils\Lazy_Events;
33
- *
34
- * protected $value;
35
- *
36
- * public function calculate_value(){
37
- * $cached = wp_cache_get( 'expensive_value' );
38
- *
39
- * if( false !== $cached ){
40
- * return $cached;
41
- *
42
- * if( null === $this->value ){
43
- * $this->value = really_expensive_calculation();
44
- * }
45
- *
46
- * $this->resolved();
47
- * }
48
- *
49
- * return $this->value;
50
- * }
51
- * }
52
- *
53
- * class List_And_Value {
54
- * protected $list;
55
- * protected $value;
56
- *
57
- * public function __construct( Lazy_List_Of_Stuff $list, Lazy_Value $value ){
58
- * $this->list = $list;
59
- * $this->value = $value;
60
- * $this->list->on_resolve( [ $this, 'cache' ] );
61
- * $this->value->on_resolve( [ $this, 'cache' ] );
62
- * }
63
- *
64
- * public function cache(){
65
- * wp_cache_set( 'list_and_value', [
66
- * 'list' => $this->list->fetch_list(),
67
- * 'value' => $this->value->calculate_value(),
68
- * ]);
69
- * }
70
- *
71
- * public function get_list(){
72
- * $cached = wp_cache_get( 'list_and_value' );
73
- *
74
- * return $cached ? $cached['list'] : $this->list->fetch_list();
75
- * }
76
- *
77
- * public function get_value(){
78
- * $cached = wp_cache_get( 'list_and_value' );
79
- *
80
- * return $cached ? $cached['value'] : $this->value->fetch_value();
81
- * }
82
- * }
83
- *
84
- *
85
- * $list = new Lazy_List_Of_Stuff();
86
- * $value = new Lazy_Value();
87
- * $list_and_value = new List_And_Value( $list, $value );
88
- *
89
- * // Accessing `value` will make it so that `list` too will be cached.
90
- * $list_and_value->get_value();
91
- * ````
92
- *
93
- * @package Tribe\Utils
94
- */
95
-
96
- namespace Tribe\Utils;
97
-
98
- /**
99
- * Trait Lazy_Events
100
- *
101
- * @since 4.9.16
102
- *
103
- * @package Tribe\Utils
104
- *
105
- * @property string $lazy_resolve_action The action to which the trait will hook to run the callback if the object
106
- * resolved. Using classes should define the property if the default `shutdown`
107
- * one is not correct.
108
- * @property int $lazy_resolve_priority The priority at which the resolution callback will be hooked on the
109
- * `$lazy_resolve_action`; defaults to `10`.
110
- */
111
- trait Lazy_Events {
112
-
113
- /**
114
- * The callback that will be called when, and if, the lazy object resolved at least once.
115
- *
116
- * @since 4.9.16
117
- *
118
- * @var
119
- */
120
- protected $lazy_resolve_callback;
121
-
122
- /**
123
- * Sets the callback that will be hooked to the resolve action when, and if, the `resolved` method is called.
124
- *
125
- * @since 4.9.16
126
- *
127
- * @param callable $callback The callback that will be hooked on the `$lazy_resolve_action` (defaults to `shutdown`)
128
- * if the `resolved` method is called.
129
- *
130
- * @return static The object instance.
131
- *
132
- * @see Lazy_Events::resolved()
133
- */
134
- public function on_resolve( callable $callback = null ) {
135
- if ( null === $callback ) {
136
- return $this;
137
- }
138
-
139
- $this->lazy_resolve_callback = $callback;
140
-
141
- return $this;
142
- }
143
-
144
- /**
145
- * Hooks the `$lazy_resolve_callback` to the `$lazy_resolve_action` with the `$lazy_resolve_priority` if set.
146
- *
147
- * @since 4.9.16
148
- */
149
- protected function resolved() {
150
- if ( empty( $this->lazy_resolve_callback ) ) {
151
- return;
152
- }
153
-
154
- $action = property_exists( $this, 'lazy_resolve_action' ) ?
155
- $this->lazy_resolve_action
156
- : 'shutdown';
157
- $priority = property_exists( $this, 'lazy_resolve_priority' ) ?
158
- $this->lazy_resolve_priority
159
- : 10;
160
-
161
- $hooked = has_action( $action, $this->lazy_resolve_callback );
162
-
163
- // Let's play it safe and move the resoloution as late as possible.
164
- $new_priority = false !== $hooked ? max( $hooked, $priority ) : $priority;
165
-
166
- if ( is_numeric( $hooked ) && $hooked !== $new_priority ) {
167
- remove_action( $action, $this->lazy_resolve_callback, $hooked );
168
- }
169
-
170
- add_action( $action, $this->lazy_resolve_callback, $new_priority );
171
- }
172
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Lazy_String.php DELETED
@@ -1,141 +0,0 @@
1
- <?php
2
- /**
3
- * A string lazily built, suited to any string value that might be costly to be built.
4
- *
5
- * @since 4.9.16
6
- *
7
- * @package Tribe\Utils
8
- */
9
-
10
-
11
- namespace Tribe\Utils;
12
-
13
-
14
- class Lazy_String implements \Serializable, \JsonSerializable {
15
- use Lazy_Events;
16
-
17
- /**
18
- * The string value produced by the callback, cached.
19
- *
20
- * @since 4.9.16
21
- *
22
- * @var string
23
- */
24
- protected $string;
25
-
26
- /**
27
- * The callback that will be used to set the string value when called the first time.
28
- *
29
- * @since 4.9.16
30
- *
31
- * @var callable
32
- */
33
- protected $value_callback;
34
-
35
- /**
36
- * The callback that will be used to escape the string in the `escaped()` method..
37
- *
38
- * @since 4.9.16
39
- *
40
- * @var callable
41
- */
42
- protected $escape_callback;
43
-
44
- /**
45
- * The escaped string value.
46
- *
47
- * @since 4.9.16
48
- *
49
- * @var string
50
- */
51
- protected $escaped;
52
-
53
- /**
54
- * Lazy_String constructor.
55
- *
56
- * @param callable $callback The callback that will be used to populate the string on the first fetch.
57
- * @param string|false $escape_callback The callback that will be used to escape the string in the `escaped`
58
- * method.
59
- */
60
- public function __construct( callable $callback, $escape_callback = 'esc_html' ) {
61
- $this->value_callback = $callback;
62
- $this->escape_callback = $escape_callback;
63
- }
64
-
65
- /**
66
- * Inits, and returns, the string value of the string.
67
- *
68
- * @since 4.9.16
69
- *
70
- * @return string The unescaped string value.
71
- */
72
- public function __toString() {
73
- if ( null === $this->string ) {
74
- $this->string = call_user_func( $this->value_callback );
75
- $this->resolved();
76
- }
77
-
78
- return $this->string;
79
- }
80
-
81
- /**
82
- * Returns the HTML ready, escaped version of the string.
83
- *
84
- * @since 4.9.16
85
- *
86
- * @return string The escaped version of the string.
87
- */
88
- public function escaped() {
89
- if ( null !== $this->escaped ) {
90
- return $this->escaped;
91
- }
92
-
93
- $this->escaped = empty( $this->escape_callback )
94
- ? $this->__toString()
95
- : call_user_func( $this->escape_callback, $this->__toString() );
96
-
97
- return $this->escaped;
98
- }
99
-
100
- /**
101
- * Returns the string value, just a proxy of the `__toString` method.
102
- *
103
- * @since 4.9.16
104
- *
105
- * @return string The string value.
106
- */
107
- public function value() {
108
- return $this->__toString();
109
- }
110
-
111
- /**
112
- * {@inheritDoc}
113
- *
114
- * @since 4.9.16
115
- */
116
- public function serialize() {
117
- $serialized = serialize( [ $this->__toString(), $this->escaped() ] );
118
-
119
- unset( $this->value_callback, $this->escape_callback );
120
-
121
- return $serialized;
122
- }
123
-
124
- /**
125
- * {@inheritDoc}
126
- *
127
- * @since 4.9.16
128
- */
129
- public function unserialize( $serialized ) {
130
- list( $string, $escaped ) = unserialize( $serialized );
131
- $this->string = $string;
132
- $this->escaped = $escaped;
133
- }
134
-
135
- /**
136
- * {@inheritDoc}
137
- */
138
- public function jsonSerialize() {
139
- return $this->value();
140
- }
141
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Post_Thumbnail.php DELETED
@@ -1,330 +0,0 @@
1
- <?php
2
- /**
3
- * Wraps the logic to lazy load a post thumbnail information.
4
- *
5
- * Example usage:
6
- * ```php
7
- * $post_thumbnail = new Tribe\Utils\Post_Thumbnail( $post_id );
8
- *
9
- * // ...some code later...
10
- *
11
- * // The post thumbnail data is fetched only now.
12
- * $large_url = $post_thumbnail->large->url;
13
- * ```
14
- *
15
- * @since 4.9.14
16
- * @package Tribe\Utils
17
- */
18
-
19
-
20
- namespace Tribe\Utils;
21
-
22
- use Tribe__Utils__Array as Arr;
23
-
24
- /**
25
- * Class Post_Thumbnail
26
- *
27
- * @since 4.9.14
28
- * @package Tribe\Utils
29
- */
30
- class Post_Thumbnail implements \ArrayAccess, \Serializable {
31
- use Lazy_Events;
32
-
33
- /**
34
- * An array of the site image sizes, including the `full` one.
35
- *
36
- * @since 4.9.14
37
- *
38
- * @var array
39
- */
40
- protected $image_sizes;
41
-
42
- /**
43
- * The post ID this images collection is for.
44
- *
45
- * @since 4.9.14
46
- *
47
- * @var int
48
- */
49
- protected $post_id;
50
-
51
- /**
52
- * The post thumbnail data.
53
- *
54
- * @since 4.9.14
55
- *
56
- * @var array
57
- */
58
- protected $data;
59
-
60
- /**
61
- * A flag property indicating whether the post thumbnail for the post exists or not.
62
- *
63
- * @since 4.9.16
64
- *
65
- * @var bool
66
- */
67
- protected $exists;
68
-
69
- /**
70
- * The post ID, if any, of the post thumbnail.
71
- *
72
- * @since 4.9.16
73
- *
74
- * @var int
75
- */
76
- protected $thumbnail_id;
77
-
78
- /**
79
- * Post_Images constructor.
80
- *
81
- * @param int $post_id The post ID.
82
- */
83
- public function __construct( $post_id ) {
84
- $this->post_id = $post_id;
85
- }
86
-
87
- /**
88
- * {@inheritDoc}
89
- */
90
- public function __get( $property ) {
91
- if ( 'exists' === $property ) {
92
- return $this->exists();
93
- }
94
-
95
- return $this->offsetGet( $property );
96
- }
97
-
98
- /**
99
- * {@inheritDoc}
100
- */
101
- public function __set( $property, $value ) {
102
- if ( 'exists' === $property ) {
103
- throw new \InvalidArgumentException( 'The `Post_Thumbnail::exists` property cannot be set.' );
104
- }
105
-
106
- $this->offsetSet( $property, $value );
107
- }
108
-
109
- /**
110
- * {@inheritDoc}
111
- */
112
- public function __isset( $property ) {
113
- return $this->offsetExists( $property );
114
- }
115
-
116
- /**
117
- * Fetches and returns the image sizes registered on the site, if any.
118
- *
119
- * @since 4.9.14
120
- *
121
- * @return array An array of the registered image sizes.
122
- */
123
- public function get_image_sizes() {
124
- if ( null !== $this->image_sizes ) {
125
- return $this->image_sizes;
126
- }
127
-
128
- $image_sizes = array_merge( [ 'full' ], get_intermediate_image_sizes() );
129
-
130
- /**
131
- * Filters the image sizes the `Tribe\Utils\Post_Thumbnail` class will manage and fetch data for.
132
- *
133
- * @since 4.9.14
134
- *
135
- * @param array $image_sizes All the available image sizes; this includes the default and the intermediate ones.
136
- */
137
- $this->image_sizes = apply_filters( 'tribe_post_thumbnail_image_sizes', $image_sizes );
138
-
139
- return $this->image_sizes;
140
- }
141
-
142
- /**
143
- * Returns the data about the post thumbnail, if any.
144
- *
145
- * @since 4.9.14
146
- *
147
- * @return array An array of objects containing the post thumbnail data.
148
- */
149
- public function fetch_data() {
150
- static $cache_thumbnail = [];
151
-
152
- if ( ! $this->exists() ) {
153
- return [];
154
- }
155
-
156
- if ( null !== $this->data ) {
157
- return $this->data;
158
- }
159
-
160
- $image_sizes = $this->get_image_sizes();
161
- $thumbnail_id = $this->thumbnail_id;
162
-
163
- $cache_key = empty( $thumbnail_id ) ? -1 : $thumbnail_id;
164
-
165
- if ( empty( $cache_thumbnail[ $cache_key ] ) ) {
166
- $thumbnail_data = array_combine(
167
- $image_sizes,
168
- array_map(
169
- static function( $size ) use ( $thumbnail_id ) {
170
- static $cache_size_data = [];
171
-
172
- $size_data_cache_key = empty( $thumbnail_id ) ? -1 : $thumbnail_id;
173
- $size_data_cache_key = "{$size_data_cache_key}:{$size}";
174
-
175
- if ( ! isset( $cache_size_data[ $size_data_cache_key ] ) ) {
176
- $cache_size_data[ $size_data_cache_key ] = wp_get_attachment_image_src( $thumbnail_id, $size );
177
- }
178
-
179
- $size_data = $cache_size_data[ $size_data_cache_key ];
180
-
181
- if ( false === $size_data ) {
182
- return (object) [
183
- 'url' => '',
184
- 'width' => '',
185
- 'height' => '',
186
- 'is_intermediate' => false,
187
- ];
188
- }
189
-
190
- return (object) [
191
- 'url' => Arr::get( $size_data, 0, '' ),
192
- 'width' => Arr::get( $size_data, 1, '' ),
193
- 'height' => Arr::get( $size_data, 2, '' ),
194
- 'is_intermediate' => (bool) Arr::get( $size_data, 3, false ),
195
- ];
196
- },
197
- $image_sizes
198
- )
199
- );
200
-
201
- $srcset = wp_get_attachment_image_srcset( $thumbnail_id );
202
- $thumbnail_data['srcset'] = ! empty( $srcset ) ? $srcset : false;
203
-
204
- $title = get_the_title( $thumbnail_id );
205
- $thumbnail_data['title'] = ! empty( $title ) ? $title : false;
206
-
207
- $alt = trim( strip_tags( get_post_meta( $thumbnail_id, '_wp_attachment_image_alt', true ) ) );
208
- $thumbnail_data['alt'] = ! empty( $alt ) ? $alt : false;
209
-
210
- $cache_thumbnail[ $cache_key ] = $thumbnail_data;
211
- }
212
-
213
- /**
214
- * Filters the post thumbnail data and information that will be returned for a specific post.
215
- *
216
- * Note that the thumbnail data will be cast to an object after this filtering.
217
- *
218
- * @since 4.9.14
219
- *
220
- * @param array $thumbnail_data The thumbnail data for the post.
221
- * @param int $post_id The ID of the post the data is for.
222
- */
223
- $thumbnail_data = apply_filters( 'tribe_post_thumbnail_data', $cache_thumbnail[ $cache_key ], $this->post_id );
224
-
225
- $this->resolved();
226
-
227
- return $thumbnail_data;
228
- }
229
-
230
- /**
231
- * {@inheritDoc}
232
- */
233
- public function offsetExists( $offset ) {
234
- $this->data = $this->fetch_data();
235
-
236
- return isset( $this->data[ $offset ] );
237
- }
238
-
239
- /**
240
- * {@inheritDoc}
241
- */
242
- public function offsetGet( $offset ) {
243
- $this->data = $this->fetch_data();
244
-
245
- return isset( $this->data[ $offset ] )
246
- ? $this->data[ $offset ]
247
- : null;
248
- }
249
-
250
- /**
251
- * {@inheritDoc}
252
- */
253
- public function offsetSet( $offset, $value ) {
254
- $this->data = $this->fetch_data();
255
-
256
- $this->data[ $offset ] = $value;
257
- }
258
-
259
- /**
260
- * {@inheritDoc}
261
- */
262
- public function offsetUnset( $offset ) {
263
- $this->data = $this->fetch_data();
264
-
265
- unset( $this->data[ $offset ] );
266
- }
267
-
268
- /**
269
- * Returns an array representation of the post thumbnail data.
270
- *
271
- * @since 4.9.14
272
- *
273
- *
274
- * @return array An array representation of the post thumbnail data.
275
- */
276
- public function to_array() {
277
- $this->data = $this->fetch_data();
278
-
279
- return json_decode( json_encode( $this->data ), true );
280
- }
281
-
282
- /**
283
- * {@inheritDoc}
284
- */
285
- public function serialize() {
286
- $data = $this->fetch_data();
287
- $data['post_id'] = $this->post_id;
288
-
289
- return wp_json_encode( $data );
290
- }
291
-
292
- /**
293
- * {@inheritDoc}
294
- */
295
- public function unserialize( $serialized ) {
296
- $data = json_decode( $serialized, true );
297
- array_walk( $data, static function ( &$data_entry ) {
298
- if ( is_array( $data_entry ) ) {
299
- $data_entry = (object) $data_entry;
300
- }
301
- } );
302
- $this->post_id = $data['post_id'];
303
- unset( $data['post_id'] );
304
- $this->data = ! empty( $data ) ? $data : null;
305
- }
306
-
307
- /**
308
- * Returns whether a post thumbnail is set for the post or not.
309
- *
310
- * @since 4.9.16
311
- *
312
- * @return bool Whether a post thumbnail is set for the post or not.
313
- */
314
- public function exists() {
315
- if ( null !== $this->exists ) {
316
- return $this->exists;
317
- }
318
-
319
- $thumbnail_id = get_post_thumbnail_id( $this->post_id );
320
-
321
- if ( empty( $thumbnail_id ) ) {
322
- $this->exists = false;
323
- } else {
324
- $this->thumbnail_id = $thumbnail_id;
325
- $this->exists = true;
326
- }
327
-
328
- return $this->exists;
329
- }
330
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Query.php DELETED
@@ -1,72 +0,0 @@
1
- <?php
2
- /**
3
- * Provides utility method related to the creation and manipulation of queries and query objects.
4
- *
5
- * @since 4.9.21
6
- *
7
- * @package Tribe\Utils
8
- */
9
-
10
- namespace Tribe\Utils;
11
-
12
- /**
13
- * Class Query
14
- *
15
- * @since 4.9.21
16
- *
17
- * @package Tribe\Utils
18
- */
19
- class Query {
20
-
21
- /**
22
- * Builds a new `WP_Query` object and sets the post, and accessory flags, on it.
23
- *
24
- * The query is built to yield to run a query that will yield no result and to have a `request` property that
25
- * will never yield results; calls on the `WP_Query::get_posts` method are filtered to always return the post set.
26
- * Queries built by this function can be spotted by looking for the `tribe_mock_query` property.
27
- *
28
- * @since 4.9.21
29
- *
30
- * @param array $posts The array of posts that should be used to build the query.
31
- *
32
- * @return \WP_Query The new WP_Query object, built to reflect the posts passed to it.
33
- */
34
- public static function for_posts( array $posts = [] ) {
35
- if ( empty( $posts ) ) {
36
- $posts = [];
37
- }
38
-
39
- $query = new \WP_Query();
40
- $query->posts = $posts;
41
- $query->found_posts = count( $posts );
42
- $query->post = reset( $posts );
43
- $query->query = [ 'p' => 0 ];
44
- $query->tribe_mock_query = true;
45
- global $wpdb;
46
- // Use a query that will never yield results.
47
- $query->request = "SELECT ID FROM {$wpdb->posts} WHERE 1=0";
48
-
49
- // Return the same set of posts on each method requiring posts.
50
- $filter_posts_pre_query = static function ( $the_posts, $the_query ) use ( $posts, $query ) {
51
- if ( $the_query !== $query ) {
52
- return $the_posts;
53
- }
54
-
55
- $fields = $query->get( 'fields', false );
56
- // We assume some uniformity here.
57
- $posts_are_objects = ! is_numeric( reset( $posts ) );
58
-
59
- switch ( $fields ) {
60
- case 'ids':
61
- return $posts_are_objects ? wp_list_pluck( $posts, 'ID' ) : $posts;
62
- case 'id=>parent':
63
- default:
64
- return $posts_are_objects ? $posts : array_map( 'get_post', $posts );
65
- }
66
- };
67
-
68
- add_filter( 'posts_pre_query', $filter_posts_pre_query, 10, 2 );
69
-
70
- return $query;
71
- }
72
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Utils/Strings.php DELETED
@@ -1,65 +0,0 @@
1
- <?php
2
- /**
3
- * String Utilities
4
- *
5
- * @since 4.12.1
6
- * @package Tribe\Utils
7
- */
8
-
9
- namespace Tribe\Utils;
10
-
11
- /**
12
- * Class Strings
13
- *
14
- * @since 4.12.1
15
- *
16
- * @package Tribe\Utils
17
- */
18
- class Strings {
19
-
20
- /**
21
- * Replace the first occurrence of a given value in the string.
22
- *
23
- * @since 4.12.1
24
- *
25
- * @param string $search The string to search for and replace.
26
- * @param string $replace The replacement string.
27
- * @param string $subject The string to do the search and replace from.
28
- *
29
- * @return string The string with the first occurrence of a given value replaced.
30
- */
31
- public static function replace_first( $search, $replace, $subject ) {
32
- if ( '' === $search ) {
33
- return $subject;
34
- }
35
-
36
- $position = strpos( $subject, $search );
37
-
38
- if ( $position !== false ) {
39
- return substr_replace( $subject, $replace, $position, strlen( $search ) );
40
- }
41
-
42
- return $subject;
43
- }
44
-
45
- /**
46
- * Replace the last occurrence of a given value in the string.
47
- *
48
- * @since 4.12.1
49
- *
50
- * @param string $search The string to search for and replace.
51
- * @param string $replace The replacement string.
52
- * @param string $subject The string to do the search and replace from.
53
- *
54
- * @return string The string with the last occurrence of a given value replaced.
55
- */
56
- public static function replace_last( $search, $replace, $subject ) {
57
- $position = strrpos( $subject, $search );
58
-
59
- if ( $position !== false ) {
60
- return substr_replace( $subject, $replace, $position, strlen( $search ) );
61
- }
62
-
63
- return $subject;
64
- }
65
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Validate.php CHANGED
@@ -109,6 +109,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
109
 
110
  /**
111
  * validates a field as a string containing only letters and numbers
 
 
112
  */
113
  public function alpha_numeric() {
114
  if ( preg_match( '/^[a-zA-Z0-9]+$/', $this->value ) ) {
@@ -122,6 +124,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
122
  /**
123
  * validates a field as a string containing only letters,
124
  * numbers and carriage returns
 
 
125
  */
126
  public function alpha_numeric_multi_line() {
127
  if ( preg_match( '/^[a-zA-Z0-9\s]+$/', $this->value ) ) {
@@ -136,6 +140,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
136
  /**
137
  * Validates a field as a string containing only letters,
138
  * numbers, dots and carriage returns
 
 
139
  */
140
  public function alpha_numeric_multi_line_with_dots_and_dashes() {
141
  if ( preg_match( '/^[a-zA-Z0-9\s.-]+$/', $this->value ) ) {
@@ -150,6 +156,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
150
  /**
151
  * Validates a field as a string containing only letters,
152
  * numbers, dashes and underscores
 
 
153
  */
154
  public function alpha_numeric_with_dashes_and_underscores() {
155
  $this->value = trim( $this->value );
@@ -165,6 +173,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
165
  * Validates a field as just "not empty".
166
  *
167
  * @since 4.7.6
 
 
168
  */
169
  public function not_empty() {
170
  $this->value = trim( $this->value );
@@ -179,6 +189,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
179
 
180
  /**
181
  * validates a field as being positive decimal
 
 
182
  */
183
  public function positive_decimal() {
184
  if ( preg_match( '/^[0-9]+(\.[0-9]+)?$/', $this->value ) && $this->value > 0 ) {
@@ -191,6 +203,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
191
 
192
  /**
193
  * validates a field as being positive decimal or percent
 
 
194
  */
195
  public function positive_decimal_or_percent() {
196
  if ( preg_match( '/^[0-9]+(\.[0-9]+)?%?$/', $this->value ) && $this->value > 0 ) {
@@ -203,6 +217,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
203
 
204
  /**
205
  * validates a field as being positive integers
 
 
206
  */
207
  public function positive_int() {
208
  if ( preg_match( '/^[0-9]+$/', $this->value ) && $this->value > 0 ) {
@@ -221,6 +237,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
221
  * with a similar name: positive_int(). This method WILL validate whole numbers that go beyond
222
  * values that PHP's int type supports, however, if someone enters something like that, that's
223
  * on them. Smart people do smart things.
 
 
224
  */
225
  public function int() {
226
  if ( preg_match( '/^-?[0-9]+$/', $this->value ) ) {
@@ -233,16 +251,15 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
233
 
234
  /**
235
  * validates & sanitizes fields as URL slugs
 
 
236
  */
237
  public function slug() {
238
  $maybe_valid_value = esc_url_raw( $this->value );
239
 
240
  // esc_url_raw does the work of validating chars, but returns the checked string with a
241
  // prepended URL protocol; so let's use strpos to match the values.
242
- if (
243
- ! empty( $maybe_valid_value )
244
- && false !== strpos( $maybe_valid_value, $this->value )
245
- ) {
246
  $this->result->valid = true;
247
  $this->value = sanitize_title( $this->value );
248
  } else {
@@ -253,6 +270,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
253
 
254
  /**
255
  * validates & sanitizes fields as URLs
 
 
256
  */
257
  public function url() {
258
 
@@ -260,13 +279,15 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
260
  $this->result->valid = true;
261
  } else {
262
  $this->result->valid = false;
263
- $this->result->error = sprintf( esc_html__( '%s must be a valid URL.', 'tribe-common' ), $this->label );
264
  }
265
  }
266
 
267
  /**
268
  * validates fields that have options (radios, dropdowns, etc.)
269
  * by making sure the value is part of the options array
 
 
270
  */
271
  public function options() {
272
  if ( array_key_exists( $this->value, $this->field['options'] ) ) {
@@ -307,8 +328,10 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
307
  /**
308
  * validates fields that have options (radios, dropdowns, etc.)
309
  * by making sure the value is part of the options array
310
- * then combines the value into an array containing the value
311
  * and name from the option
 
 
312
  */
313
  public function options_with_label() {
314
  if ( array_key_exists( $this->value, $this->field['options'] ) ) {
@@ -327,6 +350,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
327
  * validates a field as not being able to be the same
328
  * as the specified value as specified in
329
  * $this->additional_args['compare_name']
 
 
330
  */
331
  public function cannot_be_the_same_as() {
332
  if ( ! isset( $this->additional_args['compare'] ) ) {
@@ -348,6 +373,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
348
 
349
  /**
350
  * validates a field as being a number or a percentage
 
 
351
  */
352
  public function number_or_percent() {
353
  if ( preg_match( '/^[0-9]+%{0,1}$/', $this->value ) ) {
@@ -360,6 +387,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
360
 
361
  /**
362
  * sanitizes an html field
 
 
363
  */
364
  public function html() {
365
  $this->value = balanceTags( $this->value );
@@ -368,6 +397,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
368
 
369
  /**
370
  * sanitizes a license key
 
 
371
  */
372
  public function license_key() {
373
  $this->value = trim( $this->value );
@@ -376,14 +407,18 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
376
 
377
  /**
378
  * sanitizes a textarea field
 
 
379
  */
380
  public function textarea() {
381
- $this->value = wp_kses( $this->value, [] );
382
  $this->result->valid = true;
383
  }
384
 
385
  /**
386
- * sanitizes a field as being a boolean
 
 
387
  */
388
  public function boolean() {
389
  $this->value = (bool) $this->value;
@@ -392,6 +427,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
392
 
393
  /**
394
  * validates a Google Maps Zoom field
 
 
395
  */
396
  public function google_maps_zoom() {
397
  if ( preg_match( '/^([0-9]|[0-1][0-9]|2[0-1])$/', $this->value ) ) {
@@ -404,7 +441,9 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
404
 
405
  /**
406
  * validates a field as being part of an address
407
- * allows for letters, numbers, dashes and spaces only
 
 
408
  */
409
  public function address() {
410
  $this->value = stripslashes( $this->value );
@@ -418,7 +457,9 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
418
 
419
  /**
420
  * validates a field as being a city or province
421
- * allows for letters, dashes and spaces only
 
 
422
  */
423
  public function city_or_province() {
424
  $this->value = stripslashes( $this->value );
@@ -432,6 +473,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
432
 
433
  /**
434
  * validates a field as being a zip code
 
 
435
  */
436
  public function zip() {
437
  if ( preg_match( '/^[0-9]{5}$/', $this->value ) ) {
@@ -444,6 +487,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
444
 
445
  /**
446
  * validates a field as being a phone number
 
 
447
  */
448
  public function phone() {
449
  if ( preg_match( '/^[0-9\(\)\+ -]+$/', $this->value ) ) {
@@ -456,6 +501,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
456
 
457
  /**
458
  * validates & sanitizes a field as being a country list
 
 
459
  */
460
  public function country_list() {
461
  $country_rows = explode( "\n", $this->value );
@@ -477,6 +524,8 @@ if ( ! class_exists( 'Tribe__Validate' ) ) {
477
  /**
478
  * automatically validate a field regardless of the value
479
  * Don't use this unless you know what you are doing
 
 
480
  */
481
  public function none() {
482
  $this->result->valid = true;
109
 
110
  /**
111
  * validates a field as a string containing only letters and numbers
112
+ *
113
+ * @return stdClass validation result object
114
  */
115
  public function alpha_numeric() {
116
  if ( preg_match( '/^[a-zA-Z0-9]+$/', $this->value ) ) {
124
  /**
125
  * validates a field as a string containing only letters,
126
  * numbers and carriage returns
127
+ *
128
+ * @return stdClass validation result object
129
  */
130
  public function alpha_numeric_multi_line() {
131
  if ( preg_match( '/^[a-zA-Z0-9\s]+$/', $this->value ) ) {
140
  /**
141
  * Validates a field as a string containing only letters,
142
  * numbers, dots and carriage returns
143
+ *
144
+ * @return stdClass validation result object
145
  */
146
  public function alpha_numeric_multi_line_with_dots_and_dashes() {
147
  if ( preg_match( '/^[a-zA-Z0-9\s.-]+$/', $this->value ) ) {
156
  /**
157
  * Validates a field as a string containing only letters,
158
  * numbers, dashes and underscores
159
+ *
160
+ * @return stdClass validation result object
161
  */
162
  public function alpha_numeric_with_dashes_and_underscores() {
163
  $this->value = trim( $this->value );
173
  * Validates a field as just "not empty".
174
  *
175
  * @since 4.7.6
176
+ *
177
+ * @return stdClass validation result object
178
  */
179
  public function not_empty() {
180
  $this->value = trim( $this->value );
189
 
190
  /**
191
  * validates a field as being positive decimal
192
+ *
193
+ * @return stdClass validation result object
194
  */
195
  public function positive_decimal() {
196
  if ( preg_match( '/^[0-9]+(\.[0-9]+)?$/', $this->value ) && $this->value > 0 ) {
203
 
204
  /**
205
  * validates a field as being positive decimal or percent
206
+ *
207
+ * @return stdClass validation result object
208
  */
209
  public function positive_decimal_or_percent() {
210
  if ( preg_match( '/^[0-9]+(\.[0-9]+)?%?$/', $this->value ) && $this->value > 0 ) {
217
 
218
  /**
219
  * validates a field as being positive integers
220
+ *
221
+ * @return stdClass validation result object
222
  */
223
  public function positive_int() {
224
  if ( preg_match( '/^[0-9]+$/', $this->value ) && $this->value > 0 ) {
237
  * with a similar name: positive_int(). This method WILL validate whole numbers that go beyond
238
  * values that PHP's int type supports, however, if someone enters something like that, that's
239
  * on them. Smart people do smart things.
240
+ *
241
+ * @return stdClass validation result object
242
  */
243
  public function int() {
244
  if ( preg_match( '/^-?[0-9]+$/', $this->value ) ) {
251
 
252
  /**
253
  * validates & sanitizes fields as URL slugs
254
+ *
255
+ * @return stdClass validation result object
256
  */
257
  public function slug() {
258
  $maybe_valid_value = esc_url_raw( $this->value );
259
 
260
  // esc_url_raw does the work of validating chars, but returns the checked string with a
261
  // prepended URL protocol; so let's use strpos to match the values.
262
+ if ( false !== strpos( $maybe_valid_value, $this->value ) ) {
 
 
 
263
  $this->result->valid = true;
264
  $this->value = sanitize_title( $this->value );
265
  } else {
270
 
271
  /**
272
  * validates & sanitizes fields as URLs
273
+ *
274
+ * @return stdClass validation result object
275
  */
276
  public function url() {
277
 
279
  $this->result->valid = true;
280
  } else {
281
  $this->result->valid = false;
282
+ $this->result->error = sprintf( esc_html__( '%s must be a valid absolute URL.', 'tribe-common' ), $this->label );
283
  }
284
  }
285
 
286
  /**
287
  * validates fields that have options (radios, dropdowns, etc.)
288
  * by making sure the value is part of the options array
289
+ *
290
+ * @return stdClass validation result object
291
  */
292
  public function options() {
293
  if ( array_key_exists( $this->value, $this->field['options'] ) ) {
328
  /**
329
  * validates fields that have options (radios, dropdowns, etc.)
330
  * by making sure the value is part of the options array
331
+ * then combines the value into an array containg the value
332
  * and name from the option
333
+ *
334
+ * @return stdClass validation result object
335
  */
336
  public function options_with_label() {
337
  if ( array_key_exists( $this->value, $this->field['options'] ) ) {
350
  * validates a field as not being able to be the same
351
  * as the specified value as specified in
352
  * $this->additional_args['compare_name']
353
+ *
354
+ * @return stdClass validation result object
355
  */
356
  public function cannot_be_the_same_as() {
357
  if ( ! isset( $this->additional_args['compare'] ) ) {
373
 
374
  /**
375
  * validates a field as being a number or a percentage
376
+ *
377
+ * @return stdClass validation result object
378
  */
379
  public function number_or_percent() {
380
  if ( preg_match( '/^[0-9]+%{0,1}$/', $this->value ) ) {
387
 
388
  /**
389
  * sanitizes an html field
390
+ *
391
+ * @return stdClass validation result object
392
  */
393
  public function html() {
394
  $this->value = balanceTags( $this->value );
397
 
398
  /**
399
  * sanitizes a license key
400
+ *
401
+ * @return stdClass validation result object
402
  */
403
  public function license_key() {
404
  $this->value = trim( $this->value );
407
 
408
  /**
409
  * sanitizes a textarea field
410
+ *
411
+ * @return stdClass validation result object
412
  */
413
  public function textarea() {
414
+ $this->value = wp_kses( $this->value, array() );
415
  $this->result->valid = true;
416
  }
417
 
418
  /**
419
+ * sanitizes a field as beeing a boolean
420
+ *
421
+ * @return stdClass validation result object
422
  */
423
  public function boolean() {
424
  $this->value = (bool) $this->value;
427
 
428
  /**
429
  * validates a Google Maps Zoom field
430
+ *
431
+ * @return stdClass validation result object
432
  */
433
  public function google_maps_zoom() {
434
  if ( preg_match( '/^([0-9]|[0-1][0-9]|2[0-1])$/', $this->value ) ) {
441
 
442
  /**
443
  * validates a field as being part of an address
444
+ * allows for letters, numbers, dashses and spaces only
445
+ *
446
+ * @return stdClass validation result object
447
  */
448
  public function address() {
449
  $this->value = stripslashes( $this->value );
457
 
458
  /**
459
  * validates a field as being a city or province
460
+ * allows for letters, dashses and spaces only
461
+ *
462
+ * @return stdClass validation result object
463
  */
464
  public function city_or_province() {
465
  $this->value = stripslashes( $this->value );
473
 
474
  /**
475
  * validates a field as being a zip code
476
+ *
477
+ * @return stdClass validation result object
478
  */
479
  public function zip() {
480
  if ( preg_match( '/^[0-9]{5}$/', $this->value ) ) {
487
 
488
  /**
489
  * validates a field as being a phone number
490
+ *
491
+ * @return stdClass validation result object
492
  */
493
  public function phone() {
494
  if ( preg_match( '/^[0-9\(\)\+ -]+$/', $this->value ) ) {
501
 
502
  /**
503
  * validates & sanitizes a field as being a country list
504
+ *
505
+ * @return stdClass validation result object
506
  */
507
  public function country_list() {
508
  $country_rows = explode( "\n", $this->value );
524
  /**
525
  * automatically validate a field regardless of the value
526
  * Don't use this unless you know what you are doing
527
+ *
528
+ * @return stdClass validation result object
529
  */
530
  public function none() {
531
  $this->result->valid = true;
common/src/Tribe/Validator/Base.php CHANGED
@@ -158,9 +158,7 @@ class Tribe__Validator__Base implements Tribe__Validator__Interface {
158
  public function is_image( $image ) {
159
  if ( $this->is_numeric( $image ) ) {
160
  return wp_attachment_is_image( $image );
161
- }
162
-
163
- if ( is_string( $image ) ) {
164
  $response = wp_remote_head( $image );
165
 
166
  if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
158
  public function is_image( $image ) {
159
  if ( $this->is_numeric( $image ) ) {
160
  return wp_attachment_is_image( $image );
161
+ } elseif ( is_string( $image ) ) {
 
 
162
  $response = wp_remote_head( $image );
163
 
164
  if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
common/src/Tribe/View_Helpers.php CHANGED
@@ -20,18 +20,10 @@ if ( ! class_exists( 'Tribe__View_Helpers' ) ) {
20
  * @return array The countries array.
21
  */
22
  public static function constructCountries( $postId = '', $useDefault = true ) {
23
- static $cache_var_name = __METHOD__;
24
-
25
- $countries = tribe_get_var( $cache_var_name, null );
26
-
27
- if ( $countries ) {
28
- return $countries;
29
- }
30
-
31
  $eventCountries = tribe_get_option( 'tribeEventsCountries' );
32
 
33
  if ( $eventCountries != '' ) {
34
- $countries = [];
35
 
36
  $country_rows = explode( "\n", $eventCountries );
37
  foreach ( $country_rows as $crow ) {
@@ -73,11 +65,11 @@ if ( ! class_exists( 'Tribe__View_Helpers' ) ) {
73
  $countries = array( '' => $selectCountry ) + $countries;
74
  array_unique( $countries );
75
  }
76
- }
77
 
78
- tribe_set_var( $cache_var_name, $countries );
79
-
80
- return $countries;
 
81
  }
82
 
83
  /**
20
  * @return array The countries array.
21
  */
22
  public static function constructCountries( $postId = '', $useDefault = true ) {
 
 
 
 
 
 
 
 
23
  $eventCountries = tribe_get_option( 'tribeEventsCountries' );
24
 
25
  if ( $eventCountries != '' ) {
26
+ $countries = array();
27
 
28
  $country_rows = explode( "\n", $eventCountries );
29
  foreach ( $country_rows as $crow ) {
65
  $countries = array( '' => $selectCountry ) + $countries;
66
  array_unique( $countries );
67
  }
 
68
 
69
+ return $countries;
70
+ } else {
71
+ return $countries;
72
+ }
73
  }
74
 
75
  /**
common/src/admin-views/app-shop.php CHANGED
@@ -1,189 +1,95 @@
1
- <?php
2
- // $main, $products, $bundles, $extensions must be defined before loading this file
3
 
4
- $all_products = [
5
- 'for-sale' => [],
6
- 'installed' => [],
7
- ];
8
- foreach ( $products as $product ) {
9
- if ( $product->is_installed ) {
10
- $all_products['installed'][] = $product;
11
- } else {
12
- $all_products['for-sale'][] = $product;
13
- }
14
- }
15
- ?>
16
-
17
- <div id="tribe-app-shop">
18
-
19
- <div class="tribe-header">
20
- <div class="content-wrapper">
21
- <div class="logo-word-mark">
22
- <img src="<?php echo esc_url( tribe_resource_url( 'images/icons/horns.svg', false, null, $main ) ); ?>" alt="<?php esc_attr_e( 'TEC Logo', 'tribe-common' ); ?>" />
23
- <h1><span>TEC</span>&nbsp;<?php esc_html_e( 'Add-Ons', 'tribe-common' ); ?></h1>
24
- </div>
25
-
26
- <ul>
27
- <li class="selected" data-tab="tribe-all-solutions"><?php esc_html_e( 'All Solutions', 'tribe-common' ); ?></li>
28
- <li data-tab="tribe-bundles"><?php esc_html_e( 'Save with Bundles', 'tribe-common' ); ?></li>
29
- <li data-tab="tribe-extensions"><?php esc_html_e( 'Extensions', 'tribe-common' ); ?></li>
30
- </ul>
31
- </div>
32
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- <div id="tribe-all-solutions" class="tribe-content">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  <div class="content-wrapper">
36
  <div class="addon-grid">
37
- <?php foreach ( $all_products as $status => $some_products ) : ?>
38
- <?php if ( 'for-sale' == $status ) :?>
39
- <h2><?php esc_html_e( 'One calendar. Countless ways to make it your own.', 'tribe-common' ); ?></h2>
40
- <p><?php esc_html_e( 'Calendars, ticketing, and powerful WordPress tools to manage your events from start to finish.', 'tribe-common' ); ?></p>
41
- <?php else: ?>
42
- <h2 class="already-installed"><?php esc_html_e( 'Already Installed', 'tribe-common' ); ?></h2>
43
- <?php endif; ?>
44
-
45
- <?php foreach ( $some_products as $product ) : ?>
46
- <div class="tribe-addon">
47
- <div class="headline">
48
- <img src="<?php echo esc_url( tribe_resource_url( $product->logo, false, null, $main ) ); ?>" alt="<?php esc_attr_e( 'TEC Logo', 'tribe-common' ); ?>" />
49
- <h3 <?php echo ( 'installed' == $status || $product->free ) ? 'class="has-pill"' : ''; ?>><a href="<?php echo esc_url( $product->link ); ?>" target="_blank"><?php echo esc_html( $product->title ); ?></a></h3>
50
-
51
- <?php if ( 'installed' == $status ) : ?>
52
- <span class="pill active"><?php esc_html_e( 'Active', 'tribe-common' ); ?></span>
53
- <?php elseif ( $product->free ) : ?>
54
- <span class="pill free"><?php esc_html_e( 'FREE', 'tribe-common' ); ?></span>
55
- <?php endif; ?>
56
-
57
- </div>
58
- <div class="promo-image">
59
- <a href="<?php echo esc_url( $product->link ); ?>" target="_blank"><img src="<?php echo esc_url( tribe_resource_url( $product->image, false, null, $main ) ); ?>" /></a>
60
- </div>
61
-
62
- <div class="description">
63
- <p><?php echo esc_html( $product->description ); ?></p>
64
- </div>
65
-
66
- <ul class="features">
67
- <?php foreach( $product->features as $feature ) : ?>
68
- <li>
69
- <span class="check">
70
- <svg fill="none" height="12" viewBox="0 0 16 12" width="16" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m13.7357.374803-8.40784 8.402337-3.06361-3.06158c-.52424-.506-1.357557-.49877-1.872924.01626s-.522608 1.34779-.016275 1.87169l4.008209 4.00559c.52173.5212 1.36747.5212 1.8892 0l9.35244-9.34634c.5064-.5239.4991-1.356665-.0162-1.871692-.5154-.515027-1.3487-.522264-1.873-.016265z" fill="#3d54ff" fill-rule="evenodd"/></svg>
71
- </span><span class="feature-text">
72
- <?php echo esc_html( $feature ); ?>
73
- </span>
74
- </li>
75
- <?php endforeach; ?>
76
- </ul>
77
-
78
- <?php if ( 'installed' == $status ) : ?>
79
- <a class="button" href="https://m.tri.be/1aiz"><?php esc_html_e( 'Manage', 'tribe-common' ); ?></a>
80
- <?php else : ?>
81
- <a class="button" href="<?php echo esc_url( $product->link ); ?>"><?php esc_html_e( 'Learn More', 'tribe-common' ); ?></a>
82
- <?php endif; ?>
83
-
84
  </div>
85
- <?php endforeach; ?>
86
- <?php endforeach; ?>
87
- </div>
88
- </div>
89
- </div>
90
 
91
- <div id="tribe-bundles" class="tribe-content">
92
- <div class="content-wrapper">
93
- <div class="addon-grid">
94
- <h2><?php esc_html_e( 'The plugins you need at one discounted price', 'tribe-common' ); ?></h2>
95
- <p><?php esc_html_e( 'We\'ve packaged our most popular plugins into bundles jam-packed with value.', 'tribe-common' ); ?></p>
96
- <?php foreach ( $bundles as $bundle ) : ?>
97
- <div class="tribe-bundle">
98
- <div class="details">
99
- <div class="headline">
100
- <img src="<?php echo esc_url( tribe_resource_url( $bundle->logo, false, null, $main ) ); ?>" alt="<?php esc_attr_e( 'TEC Logo', 'tribe-common' ); ?>" />
101
- <h3><a href="<?php echo esc_url( $bundle->link ); ?>" target="_blank"><?php echo esc_html( $bundle->title ); ?></a></h3>
102
- </div>
103
-
104
- <p><?php echo esc_html( $bundle->description ); ?></p>
105
-
106
- <div class="cta wide">
107
- <a class="button" href="<?php echo esc_url( $bundle->link ); ?>"><?php esc_html_e( 'Save With A Bundle', 'tribe-common' ); ?></a>
108
- <span class="discount"><?php echo esc_html( $bundle->discount ); ?></span>
109
- </div>
110
- </div>
111
-
112
- <div class="includes">
113
- <h4><?php esc_html_e( 'Includes', 'tribe-common' ); ?></h4>
114
- <ul>
115
- <?php foreach ( $bundle->includes as $i => $product_key ) : ?>
116
- <?php
117
- // get $product object
118
- $product = $products[ $product_key ];
119
  ?>
 
 
 
 
 
120
 
121
- <li>
122
- <img src="<?php echo esc_url( tribe_resource_url( $product->logo, false, null, $main ) ); ?>" alt="<?php esc_attr_e( 'TEC Logo', 'tribe-common' ); ?>" />
123
- <span><?php echo esc_html( $product->title ); ?></span>
124
- </li>
125
-
126
- <?php if ( $i == 4 ) : // if there are 5 products included, then we need 2 lists ?>
127
- </ul><ul class="second">
128
- <?php endif; ?>
129
- <?php endforeach; ?>
130
- </ul>
131
- </div>
132
-
133
- <div class="cta narrow">
134
- <a class="button" href="<?php echo esc_url( $bundle->link ); ?>"><?php esc_html_e( 'Save With A Bundle', 'tribe-common' ); ?></a>
135
- <span class="discount"><?php echo esc_html( $bundle->discount ); ?></span>
136
- </div>
137
-
138
- </div>
139
- <?php endforeach; ?>
140
- </div>
141
- </div>
142
- </div>
143
-
144
- <div id="tribe-extensions" class="tribe-content">
145
- <div class="content-wrapper">
146
- <div class="addon-grid">
147
- <h2><?php esc_html_e( 'Free extensions to power up your plugins', 'tribe-common' ); ?></h2>
148
- <p><?php esc_html_e( 'Extensions are quick solutions our team came up with to solve specific issues you may need. (Just a note - extensions are not covered by our support team.)', 'tribe-common' ); ?></p>
149
- <?php foreach ( $extensions as $extension ) : ?>
150
- <div class="tribe-addon">
151
- <div class="headline">
152
- <h3 class="has-pill"><a href="<?php echo esc_url( $extension->link ); ?>" target="_blank"><?php echo esc_html( $extension->title ); ?></a></h3>
153
- <span class="pill free"><?php esc_html_e( 'FREE', 'tribe-common' ); ?></span>
154
- </div>
155
-
156
- <div class="promo-image">
157
- <a href="<?php echo esc_url( $extension->link ); ?>" target="_blank"><img src="<?php echo esc_url( tribe_resource_url( $extension->image, false, null, $main ) ); ?>" /></a>
158
  </div>
159
-
160
- <p><?php echo esc_html( $extension->description ); ?></p>
161
-
162
- <a class="button" href="<?php echo esc_url( $extension->link ); ?>"><?php esc_html_e( 'Download', 'tribe-common' ); ?></a>
163
  </div>
164
- <?php endforeach; ?>
165
-
166
- <a class="button secondary" href="https://m.tri.be/1ajd"><?php esc_html_e( 'Browse Extensions', 'tribe-common' ); ?></a>
167
 
 
 
 
168
  </div>
169
-
170
  </div>
171
- </div>
172
-
 
173
  </div>
174
-
175
- <?php // this is inline jQuery / javascript for extra simiplicity */ ?>
176
- <script type="text/javascript">
177
- jQuery( document ).ready( function($) {
178
- var current_tab = "#tribe-all-solutions";
179
- $( 'body' ).on( "click", ".tribe-header li", function() {
180
- var tab = "#" + $( this ).data( "tab" );
181
- $( current_tab ).hide();
182
- $( '.tribe-header li' ).removeClass( "selected" );
183
- $( this ).addClass( "selected" );
184
-
185
- $( tab ).show();
186
- current_tab = tab;
187
- } );
188
- } );
189
- </script>
1
+ <div id="tribe-app-shop" class="wrap">
 
2
 
3
+ <div class="header">
4
+ <h1><?php esc_html_e( 'Events Add-Ons', 'tribe-common' ); ?></h1>
5
+ <a class="button" href="https://theeventscalendar.com/?utm_campaign=in-app&utm_source=addonspage&utm_medium=top-banner" target="_blank"><?php esc_html_e( 'Browse All Add-Ons', 'tribe-common' ); ?></a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  </div>
7
+ <?php
8
+ $all_products = array(
9
+ 'for-sale' => array(),
10
+ 'installed' => array(),
11
+ );
12
+ foreach ( $products as $product ) {
13
+ if ( $product->is_installed ) {
14
+ $all_products['installed'][] = $product;
15
+ } else {
16
+ $all_products['for-sale'][] = $product;
17
+ }
18
+ }
19
 
20
+ $products = array();
21
+ foreach ( $all_products as $type => $products ) {
22
+ if ( empty( $products ) ) {
23
+ continue;
24
+ }
25
+
26
+ $button_label = esc_html__( 'Buy This Add-On', 'tribe-common' );
27
+ $button_class = 'button-primary';
28
+ if ( 'installed' === $type ) {
29
+ ?><h1 class="tribe-installed-headline"><?php esc_html_e( 'Installed Add-Ons', 'tribe-common' ); ?></h1><?php
30
+ $button_class = 'button-disabled';
31
+ $button_label = '<span class="dashicons dashicons-yes"></span>' . esc_html__( 'Installed', 'tribe-common' );
32
+ }
33
+
34
+ ?>
35
  <div class="content-wrapper">
36
  <div class="addon-grid">
37
+ <?php
38
+
39
+ $count = count( $products );
40
+
41
+ switch ( $count ) {
42
+ case 0:
43
+ case 3:
44
+ case 6:
45
+ $wide_indexes = array();
46
+ break;
47
+
48
+ case 2:
49
+ $wide_indexes = array( 0, 1 );
50
+ break;
51
+
52
+ case 5:
53
+ $wide_indexes = array( 0, 4 );
54
+ break;
55
+
56
+ case 1:
57
+ case 4:
58
+ case 7:
59
+ default:
60
+ $wide_indexes = array( 0 );
61
+ }
62
+
63
+ foreach ( $products as $i => $product ) {
64
+ ?>
65
+ <div class="tribe-addon<?php echo in_array( $i, $wide_indexes ) ? ' first' : ''; ?>">
66
+ <div class="thumb">
67
+ <a href="<?php echo esc_url( $product->link ); ?>" target="_blank"><img src="<?php echo esc_url( tribe_resource_url( $product->image, false, null, $main ) ); ?>" /></a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  </div>
69
+ <div class="caption">
70
+ <h4><a href="<?php echo esc_url( $product->link ); ?>" target="_blank"><?php echo esc_html( $product->title ); ?></a></h4>
 
 
 
71
 
72
+ <div class="description">
73
+ <p><?php echo $product->description; ?></p>
74
+ <?php
75
+ if ( isset( $product->requires ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  ?>
77
+ <p><strong><?php esc_html_e( 'Requires:', 'tribe-common' );?></strong> <?php echo esc_html( $product->requires ); ?></p>
78
+ <?php
79
+ }
80
+ ?>
81
+ </div>
82
 
83
+ <a class="button <?php esc_attr_e( $button_class ); ?>" href="<?php echo esc_url( $product->link ); ?>"><?php echo $button_label; // escaped above ?></a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  </div>
 
 
 
 
85
  </div>
 
 
 
86
 
87
+ <?php
88
+ }
89
+ ?>
90
  </div>
 
91
  </div>
92
+ <?php
93
+ }
94
+ ?>
95
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/admin-views/tribe-options-display.php CHANGED
@@ -31,12 +31,12 @@ $displayTab = array(
31
  )
32
  . '</p>',
33
  ),
34
- 'datepickerFormat' => [
35
  'type' => 'dropdown',
36
- 'label' => esc_html__( 'Compact Date Format', 'tribe-common' ),
37
- 'tooltip' => esc_html__( 'Select the date format used for elements with minimal space, such as in datepickers.', 'tribe-common' ),
38
- 'default' => 1,
39
- 'options' => [
40
  '0' => date( 'Y-m-d', $sample_date ),
41
  '1' => date( 'n/j/Y', $sample_date ),
42
  '2' => date( 'm/d/Y', $sample_date ),
@@ -49,13 +49,13 @@ $displayTab = array(
49
  '9' => date( 'Y.m.d', $sample_date ),
50
  '10' => date( 'm.d.Y', $sample_date ),
51
  '11' => date( 'd.m.Y', $sample_date ),
52
- ],
53
  'validation_type' => 'options',
54
- ],
55
- 'tribe-form-content-end' => [
56
  'type' => 'html',
57
  'html' => '</div>',
58
- ],
59
  )
60
  ),
61
  );
31
  )
32
  . '</p>',
33
  ),
34
+ 'datepickerFormat' => array(
35
  'type' => 'dropdown',
36
+ 'label' => esc_html__( 'Datepicker Date Format', 'tribe-common' ),
37
+ 'tooltip' => esc_html__( 'Select the date format to use in datepickers', 'tribe-common' ),
38
+ 'default' => 'Y-m-d',
39
+ 'options' => array(
40
  '0' => date( 'Y-m-d', $sample_date ),
41
  '1' => date( 'n/j/Y', $sample_date ),
42
  '2' => date( 'm/d/Y', $sample_date ),
49
  '9' => date( 'Y.m.d', $sample_date ),
50
  '10' => date( 'm.d.Y', $sample_date ),
51
  '11' => date( 'd.m.Y', $sample_date ),
52
+ ),
53
  'validation_type' => 'options',
54
+ ),
55
+ 'tribe-form-content-end' => array(
56
  'type' => 'html',
57
  'html' => '</div>',
58
+ ),
59
  )
60
  ),
61
  );
common/src/admin-views/tribe-options-general.php CHANGED
@@ -1,77 +1,74 @@
1
  <?php
2
 
3
- $generalTabFields = [
4
- 'info-start' => [
5
  'type' => 'html',
6
  'html' => '<div id="modern-tribe-info"><img src="' . plugins_url( 'resources/images/modern-tribe@2x.png', dirname( __FILE__ ) ) . '" alt="Modern Tribe Inc." title="Modern Tribe Inc.">',
7
- ],
8
- 'event-tickets-info' => [
9
  'type' => 'html',
10
  'html' => '<p>' . sprintf( esc_html__( 'Thank you for using Event Tickets! All of us at Modern Tribe sincerely appreciate your support and we\'re excited to see you using our plugins. Check out our handy %1$sNew User Primer%2$s to get started.', 'tribe-common' ), '<a href="http://m.tri.be/18nd">', '</a>' ) . '</p>',
11
  'conditional' => ! class_exists( 'Tribe__Events__Main' ),
12
- ],
13
- 'event-tickets-upsell-info' => [
14
  'type' => 'html',
15
  'html' => '<p>' . sprintf( esc_html__( 'Optimize your site\'s event listings with %1$sThe Events Calendar%2$s, our free calendar plugin. Looking for additional functionality including recurring events, user-submission, advanced ticket sales and more? Check out our %3$spremium add-ons%4$s.', 'tribe-common' ), '<a href="http://m.tri.be/18x6">', '</a>', '<a href="http://m.tri.be/18x5">', '</a>' ) . '</p>',
16
  'conditional' => ! class_exists( 'Tribe__Events__Main' ),
17
- ],
18
- 'upsell-info' => [
19
  'type' => 'html',
20
  'html' => '<p>' . esc_html__( 'Looking for additional functionality including recurring events, custom meta, community events, ticket sales and more?', 'tribe-common' ) . ' <a href="' . Tribe__Main::$tec_url . 'products/?utm_source=generaltab&utm_medium=plugin-tec&utm_campaign=in-app">' . esc_html__( 'Check out the available add-ons', 'tribe-common' ) . '</a>.</p>',
21
  'conditional' => ( ! defined( 'TRIBE_HIDE_UPSELL' ) || ! TRIBE_HIDE_UPSELL ) && class_exists( 'Tribe__Events__Main' ),
22
- ],
23
- 'donate-link-heading' => [
24
  'type' => 'heading',
25
  'label' => esc_html__( 'We hope our plugin is helping you out.', 'tribe-common' ),
26
  'conditional' => class_exists( 'Tribe__Events__Main' ),
27
- ],
28
- 'donate-link-info' => [
29
  'type' => 'html',
30
  'html' => '<p>' . esc_html__( 'Are you thinking "Wow, this plugin is amazing! I should say thanks to Modern Tribe for all their hard work." The greatest thanks we could ask for is recognition. Add a small text-only link at the bottom of your calendar pointing to The Events Calendar project.', 'tribe-common' ) . '<br><a href="' . esc_url( plugins_url( 'resources/images/donate-link-screenshot.png', dirname( __FILE__ ) ) ) . '" class="thickbox">' . esc_html__( 'See an example of the link', 'tribe-common' ) . '</a>.</p>',
31
  'conditional' => class_exists( 'Tribe__Events__Main' ),
32
- ],
33
- 'donate-link' => [
34
  'type' => 'checkbox_bool',
35
  'label' => esc_html__( 'Show The Events Calendar link', 'tribe-common' ),
36
  'default' => false,
37
  'validation_type' => 'boolean',
38
  'conditional' => class_exists( 'Tribe__Events__Main' ),
39
- ],
40
- 'info-end' => [
41
  'type' => 'html',
42
  'html' => '</div>',
43
- ],
44
- 'tribe-form-content-start' => [
45
  'type' => 'html',
46
  'html' => '<div class="tribe-settings-form-wrap">',
47
- ],
48
- ];
49
 
50
  if ( is_super_admin() ) {
51
- $generalTabFields['debugEvents'] = [
52
  'type' => 'checkbox_bool',
53
  'label' => esc_html__( 'Debug mode', 'tribe-common' ),
54
- 'tooltip' => sprintf(
55
- esc_html__(
56
- 'Enable this option to log debug information. By default this will log to your server PHP error log. If you\'d like to see the log messages in your browser, then we recommend that you install the %s and look for the "Tribe" tab in the debug output.',
57
- 'tribe-common'
58
- ),
59
- '<a href="https://wordpress.org/extend/plugins/debug-bar/" target="_blank">' . esc_html__( 'Debug Bar Plugin', 'tribe-common' ) . '</a>'
60
- ),
61
  'default' => false,
62
  'validation_type' => 'boolean',
63
- ];
 
 
 
 
64
  }
65
 
66
  // Closes form
67
- $generalTabFields['tribe-form-content-end'] = [
68
  'type' => 'html',
69
  'html' => '</div>',
70
- ];
71
 
72
 
73
- $generalTab = [
74
  'priority' => 10,
75
  'fields' => apply_filters( 'tribe_general_settings_tab_fields', $generalTabFields ),
76
- ];
77
 
1
  <?php
2
 
3
+ $generalTabFields = array(
4
+ 'info-start' => array(
5
  'type' => 'html',
6
  'html' => '<div id="modern-tribe-info"><img src="' . plugins_url( 'resources/images/modern-tribe@2x.png', dirname( __FILE__ ) ) . '" alt="Modern Tribe Inc." title="Modern Tribe Inc.">',
7
+ ),
8
+ 'event-tickets-info' => array(
9
  'type' => 'html',
10
  'html' => '<p>' . sprintf( esc_html__( 'Thank you for using Event Tickets! All of us at Modern Tribe sincerely appreciate your support and we\'re excited to see you using our plugins. Check out our handy %1$sNew User Primer%2$s to get started.', 'tribe-common' ), '<a href="http://m.tri.be/18nd">', '</a>' ) . '</p>',
11
  'conditional' => ! class_exists( 'Tribe__Events__Main' ),
12
+ ),
13
+ 'event-tickets-upsell-info' => array(
14
  'type' => 'html',
15
  'html' => '<p>' . sprintf( esc_html__( 'Optimize your site\'s event listings with %1$sThe Events Calendar%2$s, our free calendar plugin. Looking for additional functionality including recurring events, user-submission, advanced ticket sales and more? Check out our %3$spremium add-ons%4$s.', 'tribe-common' ), '<a href="http://m.tri.be/18x6">', '</a>', '<a href="http://m.tri.be/18x5">', '</a>' ) . '</p>',
16
  'conditional' => ! class_exists( 'Tribe__Events__Main' ),
17
+ ),
18
+ 'upsell-info' => array(
19
  'type' => 'html',
20
  'html' => '<p>' . esc_html__( 'Looking for additional functionality including recurring events, custom meta, community events, ticket sales and more?', 'tribe-common' ) . ' <a href="' . Tribe__Main::$tec_url . 'products/?utm_source=generaltab&utm_medium=plugin-tec&utm_campaign=in-app">' . esc_html__( 'Check out the available add-ons', 'tribe-common' ) . '</a>.</p>',
21
  'conditional' => ( ! defined( 'TRIBE_HIDE_UPSELL' ) || ! TRIBE_HIDE_UPSELL ) && class_exists( 'Tribe__Events__Main' ),
22
+ ),
23
+ 'donate-link-heading' => array(
24
  'type' => 'heading',
25
  'label' => esc_html__( 'We hope our plugin is helping you out.', 'tribe-common' ),
26
  'conditional' => class_exists( 'Tribe__Events__Main' ),
27
+ ),
28
+ 'donate-link-info' => array(
29
  'type' => 'html',
30
  'html' => '<p>' . esc_html__( 'Are you thinking "Wow, this plugin is amazing! I should say thanks to Modern Tribe for all their hard work." The greatest thanks we could ask for is recognition. Add a small text-only link at the bottom of your calendar pointing to The Events Calendar project.', 'tribe-common' ) . '<br><a href="' . esc_url( plugins_url( 'resources/images/donate-link-screenshot.png', dirname( __FILE__ ) ) ) . '" class="thickbox">' . esc_html__( 'See an example of the link', 'tribe-common' ) . '</a>.</p>',
31
  'conditional' => class_exists( 'Tribe__Events__Main' ),
32
+ ),
33
+ 'donate-link' => array(
34
  'type' => 'checkbox_bool',
35
  'label' => esc_html__( 'Show The Events Calendar link', 'tribe-common' ),
36
  'default' => false,
37
  'validation_type' => 'boolean',
38
  'conditional' => class_exists( 'Tribe__Events__Main' ),
39
+ ),
40
+ 'info-end' => array(
41
  'type' => 'html',
42
  'html' => '</div>',
43
+ ),
44
+ 'tribe-form-content-start' => array(
45
  'type' => 'html',
46
  'html' => '<div class="tribe-settings-form-wrap">',
47
+ ),
48
+ );
49
 
50
  if ( is_super_admin() ) {
51
+ $generalTabFields['debugEvents'] = array(
52
  'type' => 'checkbox_bool',
53
  'label' => esc_html__( 'Debug mode', 'tribe-common' ),
 
 
 
 
 
 
 
54
  'default' => false,
55
  'validation_type' => 'boolean',
56
+ );
57
+ $generalTabFields['debugEventsHelper'] = array(
58
+ 'type' => 'html',
59
+ 'html' => '<p class="tribe-field-indent tribe-field-description description" style="max-width:400px;">' . sprintf( esc_html__( 'Enable this option to log debug information. By default this will log to your server PHP error log. If you\'d like to see the log messages in your browser, then we recommend that you install the %s and look for the "Tribe" tab in the debug output.', 'tribe-common' ), '<a href="https://wordpress.org/extend/plugins/debug-bar/" target="_blank">' . esc_html__( 'Debug Bar Plugin', 'tribe-common' ) . '</a>' ) . '</p>',
60
+ );
61
  }
62
 
63
  // Closes form
64
+ $generalTabFields['tribe-form-content-end'] = array(
65
  'type' => 'html',
66
  'html' => '</div>',
67
+ );
68
 
69
 
70
+ $generalTab = array(
71
  'priority' => 10,
72
  'fields' => apply_filters( 'tribe_general_settings_tab_fields', $generalTabFields ),
73
+ );
74
 
common/src/admin-views/tribe-options-help.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  // Fetch the Help page Instance
3
- $help = tribe( Tribe__Admin__Help_Page::class );
4
 
5
  // Fetch plugins
6
  $plugins = $help->get_plugins( null, false );
1
  <?php
2
  // Fetch the Help page Instance
3
+ $help = Tribe__Admin__Help_Page::instance();
4
 
5
  // Fetch plugins
6
  $plugins = $help->get_plugins( null, false );
common/src/functions/multibyte.php CHANGED
@@ -160,34 +160,3 @@ if ( ! function_exists( 'tribe_uc_first_letter' ) ) {
160
  return $letter;
161
  }
162
  }
163
-
164
- if ( ! function_exists( 'tribe_strpos' ) ) {
165
- /**
166
- * Find the numeric position of the first occurrence of needle in the haystack string using multibyte function if available.
167
- *
168
- * @since 4.9.19
169
- *
170
- * @param string $haystack The string to search in.
171
- * @param string $needle The string to find in haystack.
172
- * @param int $offset The search offset. If it is not specified, 0 is used. A negative offset counts from the end of the string.
173
- *
174
- * @return int|false The numeric position of the first occurrence of needle in the haystack string. If needle is not found, it returns false.
175
- *
176
- * @see strpos The fallback function used if mb_strpos does not exist.
177
- * @see mb_strpos The multibyte compatible version of strpos.
178
- */
179
- function tribe_strpos( $haystack, $needle, $offset = 0 ) {
180
- if ( function_exists( 'mb_strpos' ) ) {
181
- $encoding = tribe_detect_encoding( $haystack );
182
-
183
- // Use encoding if it was detected.
184
- if ( $encoding ) {
185
- return mb_strpos( $haystack, $needle, $offset, $encoding );
186
- }
187
-
188
- return mb_strpos( $haystack, $needle, $offset );
189
- }
190
-
191
- return strpos( $haystack, $needle, $offset );
192
- }
193
- }
160
  return $letter;
161
  }
162
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/functions/template-tags/date.php CHANGED
@@ -277,8 +277,6 @@ if ( ! function_exists( 'tribe_get_start_date' ) ) {
277
  * @return string|null Date
278
  */
279
  function tribe_get_start_date( $event = null, $display_time = true, $date_format = '', $timezone = null ) {
280
- static $cache_var_name = __FUNCTION__;
281
-
282
  if ( is_null( $event ) ) {
283
  global $post;
284
  $event = $post;
@@ -292,23 +290,15 @@ if ( ! function_exists( 'tribe_get_start_date' ) ) {
292
  return '';
293
  }
294
 
295
- $start_dates = tribe_get_var( $cache_var_name, [] );
296
- $cache_key = "{$event->ID}:{$display_time}:{$date_format}:{$timezone}";
297
-
298
- if ( ! isset( $start_dates[ $cache_key ] ) ) {
299
- if ( Tribe__Date_Utils::is_all_day( get_post_meta( $event->ID, '_EventAllDay', true ) ) ) {
300
- $display_time = false;
301
- }
302
-
303
- // @todo move timezones to Common
304
- if ( class_exists( 'Tribe__Events__Timezones' ) ) {
305
- $start_date = Tribe__Events__Timezones::event_start_timestamp( $event->ID, $timezone );
306
- } else {
307
- return null;
308
- }
309
 
310
- $start_dates[ $cache_key ] = tribe_format_date( $start_date, $display_time, $date_format );
311
- tribe_set_var( $cache_var_name, $start_dates );
 
 
 
312
  }
313
 
314
  /**
@@ -317,7 +307,7 @@ if ( ! function_exists( 'tribe_get_start_date' ) ) {
317
  * @param string $start_date
318
  * @param WP_Post $event
319
  */
320
- return apply_filters( 'tribe_get_start_date', $start_dates[ $cache_key ], $event );
321
  }
322
  }
323
 
@@ -339,8 +329,6 @@ if ( ! function_exists( 'tribe_get_end_date' ) ) {
339
  * @return string|null Date
340
  */
341
  function tribe_get_end_date( $event = null, $display_time = true, $date_format = '', $timezone = null ) {
342
- static $cache_var_name = __FUNCTION__;
343
-
344
  if ( is_null( $event ) ) {
345
  global $post;
346
  $event = $post;
@@ -354,23 +342,15 @@ if ( ! function_exists( 'tribe_get_end_date' ) ) {
354
  return '';
355
  }
356
 
357
- $end_dates = tribe_get_var( $cache_var_name, [] );
358
- $cache_key = "{$event->ID}:{$display_time}:{$date_format}:{$timezone}";
359
-
360
- if ( ! isset( $end_dates[ $cache_key ] ) ) {
361
- if ( Tribe__Date_Utils::is_all_day( get_post_meta( $event->ID, '_EventAllDay', true ) ) ) {
362
- $display_time = false;
363
- }
364
-
365
- // @todo move timezones to Common
366
- if ( class_exists( 'Tribe__Events__Timezones' ) ) {
367
- $end_date = Tribe__Events__Timezones::event_end_timestamp( $event->ID );
368
- } else {
369
- return null;
370
- }
371
 
372
- $end_dates[ $cache_key ] = tribe_format_date( $end_date, $display_time, $date_format );
373
- tribe_set_var( $cache_var_name, $end_dates );
 
 
 
374
  }
375
 
376
  /**
@@ -379,7 +359,7 @@ if ( ! function_exists( 'tribe_get_end_date' ) ) {
379
  * @param string $end_date
380
  * @param WP_Post $event
381
  */
382
- return apply_filters( 'tribe_get_end_date', $end_dates[ $cache_key ], $event );
383
  }
384
  }
385
 
277
  * @return string|null Date
278
  */
279
  function tribe_get_start_date( $event = null, $display_time = true, $date_format = '', $timezone = null ) {
 
 
280
  if ( is_null( $event ) ) {
281
  global $post;
282
  $event = $post;
290
  return '';
291
  }
292
 
293
+ if ( Tribe__Date_Utils::is_all_day( get_post_meta( $event->ID, '_EventAllDay', true ) ) ) {
294
+ $display_time = false;
295
+ }
 
 
 
 
 
 
 
 
 
 
 
296
 
297
+ // @todo move timezones to Common
298
+ if ( class_exists( 'Tribe__Events__Timezones' ) ) {
299
+ $start_date = Tribe__Events__Timezones::event_start_timestamp( $event->ID, $timezone );
300
+ } else {
301
+ return null;
302
  }
303
 
304
  /**
307
  * @param string $start_date
308
  * @param WP_Post $event
309
  */
310
+ return apply_filters( 'tribe_get_start_date', tribe_format_date( $start_date, $display_time, $date_format ), $event );
311
  }
312
  }
313
 
329
  * @return string|null Date
330
  */
331
  function tribe_get_end_date( $event = null, $display_time = true, $date_format = '', $timezone = null ) {
 
 
332
  if ( is_null( $event ) ) {
333
  global $post;
334
  $event = $post;
342
  return '';
343
  }
344
 
345
+ if ( Tribe__Date_Utils::is_all_day( get_post_meta( $event->ID, '_EventAllDay', true ) ) ) {
346
+ $display_time = false;
347
+ }
 
 
 
 
 
 
 
 
 
 
 
348
 
349
+ // @todo move timezones to Common
350
+ if ( class_exists( 'Tribe__Events__Timezones' ) ) {
351
+ $end_date = Tribe__Events__Timezones::event_end_timestamp( $event->ID );
352
+ } else {
353
+ return null;
354
  }
355
 
356
  /**
359
  * @param string $end_date
360
  * @param WP_Post $event
361
  */
362
+ return apply_filters( 'tribe_get_end_date', tribe_format_date( $end_date, $display_time, $date_format ), $event );
363
  }
364
  }
365
 
common/src/functions/template-tags/general.php CHANGED
@@ -215,15 +215,7 @@ if ( ! function_exists( 'tribe_get_time_format' ) ) {
215
  * @return mixed|void
216
  */
217
  function tribe_get_time_format( ) {
218
- static $cache_var_name = __FUNCTION__;
219
-
220
- $format = tribe_get_var( $cache_var_name, null );
221
-
222
- if ( ! $format ) {
223
- $format = get_option( 'time_format' );
224
- tribe_set_var( $cache_var_name, $format );
225
- }
226
-
227
  return apply_filters( 'tribe_time_format', $format );
228
  }
229
  }//end if
@@ -596,49 +588,43 @@ function tribe_register_error( $indexes, $message ) {
596
  *
597
  * @since 4.3
598
  *
599
- * @param object $origin The main object for the plugin you are enqueueing the asset for.
600
- * @param string $slug Slug to save the asset - passes through `sanitize_title_with_dashes()`.
601
- * @param string $file The asset file to load (CSS or JS), including non-minified file extension.
602
- * @param array $deps The list of dependencies.
603
- * @param string|array|null $action The WordPress action(s) to enqueue on, such as `wp_enqueue_scripts`,
604
- * `admin_enqueue_scripts`, or `login_enqueue_scripts`.
605
- * @param array $arguments See `Tribe__Assets::register()` for more info.
606
  *
607
- * @return object|false The asset that got registered or false on error.
608
  */
609
- function tribe_asset( $origin, $slug, $file, $deps = [], $action = null, $arguments = [] ) {
610
- /** @var Tribe__Assets $assets */
611
- $assets = tribe( 'assets' );
612
-
613
- return $assets->register( $origin, $slug, $file, $deps, $action, $arguments );
614
  }
615
 
616
  /**
617
- * Shortcut for Tribe__Assets::enqueue() to include assets.
618
  *
619
- * @since 4.7
620
  *
621
- * @param string|array $slug Slug to enqueue
 
 
622
  */
623
  function tribe_asset_enqueue( $slug ) {
624
- /** @var Tribe__Assets $assets */
625
- $assets = tribe( 'assets' );
626
-
627
- $assets->enqueue( $slug );
628
  }
629
 
630
  /**
631
- * Shortcut for Tribe__Assets::enqueue_group() include assets by groups.
632
  *
633
- * @since 4.7
634
  *
635
- * @param string|array $group Which group(s) should be enqueued.
 
 
636
  */
637
  function tribe_asset_enqueue_group( $group ) {
638
- /** @var Tribe__Assets $assets */
639
- $assets = tribe( 'assets' );
640
-
641
- $assets->enqueue_group( $group );
642
  }
643
 
644
  /**
@@ -766,16 +752,3 @@ if ( ! function_exists( 'tribe_context' ) ) {
766
  return $context;
767
  }
768
  }
769
-
770
- if ( ! function_exists( 'tribe_cache' ) ) {
771
- /**
772
- * Returns the current Tribe Cache instance.
773
- *
774
- * @since 4.11.2
775
- *
776
- * @return Tribe__Cache The current cache instance.
777
- */
778
- function tribe_cache() {
779
- return tribe( 'cache' );
780
- }
781
- }
215
  * @return mixed|void
216
  */
217
  function tribe_get_time_format( ) {
218
+ $format = get_option( 'time_format' );
 
 
 
 
 
 
 
 
219
  return apply_filters( 'tribe_time_format', $format );
220
  }
221
  }//end if
588
  *
589
  * @since 4.3
590
  *
591
+ * @param object $origin The main Object for the plugin you are enqueueing the script/style for
592
+ * @param string $slug Slug to save the asset
593
+ * @param string $file Which file will be loaded, either CSS or JS
594
+ * @param array $deps Dependencies
595
+ * @param string $action A WordPress Action, needs to happen after: `wp_enqueue_scripts`, `admin_enqueue_scripts`, or `login_enqueue_scripts`
596
+ * @param array $arguments Look at `Tribe__Assets::register()` for more info
 
597
  *
598
+ * @return array Which Assets was registered
599
  */
600
+ function tribe_asset( $origin, $slug, $file, $deps = array(), $action = null, $arguments = array() ) {
601
+ return tribe( 'assets' )->register( $origin, $slug, $file, $deps, $action, $arguments );
 
 
 
602
  }
603
 
604
  /**
605
+ * Shortcut for Tribe__Assets::enqueue(), include assets
606
  *
607
+ * @since 4.7
608
  *
609
+ * @param string|array $slug Slug to enqueue
610
+ *
611
+ * @return string
612
  */
613
  function tribe_asset_enqueue( $slug ) {
614
+ return tribe( 'assets' )->enqueue( $slug );
 
 
 
615
  }
616
 
617
  /**
618
+ * Shortcut for Tribe__Assets::enqueue_group() include assets by groups
619
  *
620
+ * @since 4.7
621
  *
622
+ * @param string|array $group Which group(s) should be enqueued
623
+ *
624
+ * @return string
625
  */
626
  function tribe_asset_enqueue_group( $group ) {
627
+ return tribe( 'assets' )->enqueue_group( $group );
 
 
 
628
  }
629
 
630
  /**
752
  return $context;
753
  }
754
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/functions/template-tags/html.php DELETED
@@ -1,130 +0,0 @@
1
- <?php
2
- /**
3
- * HTML functions (template-tags) for use in WordPress templates.
4
- */
5
- use Tribe\Utils\Element_Attributes;
6
- use Tribe\Utils\Element_Classes;
7
-
8
- /**
9
- * Parse input values into a valid array of classes to be used in the templates.
10
- *
11
- * @since 4.9.13
12
- *
13
- * @param mixed $classes,... unlimited Any amount of params to be rendered as classes.
14
- *
15
- * @return array
16
- */
17
- function tribe_get_classes() {
18
- $element_classes = new Element_Classes( func_get_args() );
19
- return $element_classes->get_classes();
20
- }
21
-
22
- /**
23
- * Parses input values into a valid class html attribute to be used in the templates.
24
- *
25
- * @since 4.9.13
26
- *
27
- * @param mixed $classes,... unlimited Any amount of params to be rendered as classes.
28
- *
29
- * @return void
30
- */
31
- function tribe_classes() {
32
- $element_classes = new Element_Classes( func_get_args() );
33
- echo $element_classes->get_attribute();
34
- }
35
-
36
- /**
37
- * Parse input values into a valid array of attributes to be used in the templates.
38
- *
39
- * @since 4.12.3
40
- *
41
- * @param mixed $attributes,... unlimited Any amount of params to be rendered as attributes.
42
- *
43
- * @return array<string> An array of the parsed string attributes.
44
- */
45
- function tribe_get_attributes() {
46
- $element_attributes = new Element_Attributes( func_get_args() );
47
- return $element_attributes->get_attributes_array();
48
- }
49
-
50
- /**
51
- * Parse input values into a valid html attributes to be used in the templates.
52
- *
53
- * @since 4.12.3
54
- *
55
- * @param mixed $attributes,... unlimited Any amount of params to be rendered as attributes.
56
- *
57
- * @return void
58
- */
59
- function tribe_attributes() {
60
- $element_attributes = new Element_Attributes( func_get_args() );
61
- echo $element_attributes->get_attributes();
62
- }
63
-
64
- /**
65
- * Get attributes for required fields.
66
- *
67
- * @since 4.10.0
68
- *
69
- * @param boolean $required If the field is required.
70
- * @param boolean $echo Whether to echo the string or return it.
71
- *
72
- * @return string|void If echo is false, returns $required_string.
73
- */
74
- function tribe_required( $required, $echo = true ) {
75
- if ( $required ) {
76
- $required_string = 'required aria-required="true"';
77
-
78
- if ( ! $echo ) {
79
- return $required_string;
80
- } else {
81
- echo $required_string;
82
- }
83
- }
84
- }
85
-
86
- /**
87
- * Get string for required field labels.
88
- *
89
- * @since 4.10.0
90
- *
91
- * @param boolean $required If the field is required.
92
- * @param boolean $echo Whether to echo the string or return it.
93
- *
94
- * @return string|void If echo is false, returns $required_string.
95
- */
96
- function tribe_required_label( $required, $echo = true ) {
97
- if ( $required ) {
98
- $required_string = '<span class="screen-reader-text">'
99
- . esc_html_x( '(required)', 'The associated field is required.', 'tribe-common' )
100
- . '</span><span class="tribe-required" aria-hidden="true" role="presentation">*</span>';
101
-
102
- if ( ! $echo ) {
103
- return $required_string;
104
- } else {
105
- echo $required_string;
106
- }
107
- }
108
- }
109
-
110
- /**
111
- * Get attributes for disabled fields.
112
- *
113
- * @since 4.10.0
114
- *
115
- * @param boolean $disabled If the field is disabled.
116
- * @param boolean $echo Whether to echo the string or return it.
117
- *
118
- * @return string|void If echo is false, returns $disabled_string.
119
- */
120
- function tribe_disabled( $disabled, $echo = true ) {
121
- if ( $disabled ) {
122
- $disabled_string = 'disabled aria-disabled="true"';
123
-
124
- if ( ! $echo ) {
125
- return $disabled_string;
126
- } else {
127
- echo $disabled_string;
128
- }
129
- }
130
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/functions/template-tags/post.php DELETED
@@ -1,100 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Gets the post content. Basically a wrapper around `get_the_content` that will prevent warnings on PHP 7.3
5
- * and be compatible with WP 5.3
6
- *
7
- * @since 4.9.23
8
- *
9
- * @global WP_Post $post Current post on the loop
10
- * @global string $wp_version Which version of WordPress we are currently dealing with
11
- *
12
- * @param string $more_link_text Optional. Content for when there is more text.
13
- * @param bool $strip_teaser Optional. Strip teaser content before the more text. Default is false.
14
- * @param WP_Post|object|int $post_id Optional. WP_Post instance or Post ID/object. Default is null.
15
- *
16
- * @return string
17
- */
18
- function tribe_get_the_content( $more_link_text = null, $strip_teaser = false, $post_id = null ) {
19
- global $post, $wp_version;
20
-
21
- // Save the global post to be able to restore it later.
22
- $previous_post = $post;
23
-
24
- $post = get_post( $post_id );
25
-
26
- // Pass in the third param when dealing with WP version 5.2 or higher.
27
- if ( version_compare( $wp_version, '5.2', '>=' ) ) {
28
- $content = get_the_content( $more_link_text, $strip_teaser, $post );
29
- } else {
30
- $content = get_the_content( $more_link_text, $strip_teaser );
31
- }
32
-
33
- $has_blocks = function_exists( 'has_blocks' ) && has_blocks( $content );
34
- // If blocks are present we need to run the content filter.
35
-
36
- if ( $has_blocks || ! doing_filter( 'the_content' ) ) {
37
- /**
38
- * Filters the post content.
39
- *
40
- * @since 0.71 of WordPress
41
- *
42
- * @param string $content Content of the current post.
43
- */
44
- $content = apply_filters( 'the_content', $content );
45
- }
46
-
47
- $content = str_replace( ']]>', ']]&gt;', $content );
48
-
49
- $post = $previous_post;
50
-
51
- return $content;
52
- }
53
-
54
- /**
55
- * Prints the post content.
56
- *
57
- * @since 4.9.23
58
- *
59
- * @global WP_Post $post Current post on the loop
60
- * @global string $wp_version Which version of WordPress we are currently dealing with
61
- *
62
- * @param string $more_link_text Optional. Content for when there is more text.
63
- * @param bool $strip_teaser Optional. Strip teaser content before the more text. Default is false.
64
- * @param WP_Post|object|int $post_id Optional. WP_Post instance or Post ID/object. Default is null.
65
- *
66
- * @return void
67
- */
68
- function tribe_the_content( $more_link_text = null, $strip_teaser = false, $post_id = null ) {
69
- echo tribe_get_the_content( $more_link_text, $strip_teaser, $post_id );
70
- }
71
-
72
- /**
73
- * Wrapper for post_class function that allows us to in-memory cache
74
- *
75
- * @since 4.11.0
76
- *
77
- * @param string|string[] $class Space-separated string or array of class names to add to the class list.
78
- * @param int|WP_Post $post Optional. Post ID or post object.
79
- *
80
- * @return string[] Array of class names.
81
- */
82
- function tribe_get_post_class( $class, $post ) {
83
- static $post_classes = [];
84
-
85
- if ( is_numeric( $post ) ) {
86
- $post_id = $post;
87
- } else {
88
- $post_id = $post->ID;
89
- }
90
-
91
- if ( ! isset( $post_classes[ $post_id ] ) ) {
92
- $post_classes[ $post_id ] = get_post_class( [], $post );
93
- }
94
-
95
- if ( ! is_array( $class ) ) {
96
- $class = explode( ' ', $class );
97
- }
98
-
99
- return array_merge( $class, $post_classes[ $post_id ] );
100
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/functions/utils.php CHANGED
@@ -36,10 +36,14 @@ if ( ! function_exists( 'tribe_register_plugin' ) ) {
36
  * @param string $version The version
37
  * @param array $classes_req Any Main class files/tribe plugins required for this to run
38
  * @param array $dependencies an array of dependencies to check
 
 
39
  */
40
- function tribe_register_plugin( $file_path, $main_class, $version, $classes_req = [], $dependencies = [] ) {
41
- $tribe_dependency = tribe( Tribe__Dependency::class );
 
42
  $tribe_dependency->register_plugin( $file_path, $main_class, $version, $classes_req, $dependencies );
 
43
  }
44
  }
45
 
@@ -49,14 +53,13 @@ if ( ! function_exists( 'tribe_check_plugin' ) ) {
49
  *
50
  * @since 4.9
51
  *
52
- * @param string $main_class The Main/base class for this plugin
53
  *
54
  * @return bool Indicates if plugin should continue initialization
55
  */
56
  function tribe_check_plugin( $main_class ) {
57
 
58
- $tribe_dependency = Tribe__Dependency::instance();
59
-
60
  return $tribe_dependency->check_plugin( $main_class );
61
 
62
  }
@@ -67,13 +70,12 @@ if ( ! function_exists( 'tribe_append_path' ) ) {
67
  * Append a path fragment to a URL preserving query arguments
68
  * and fragments.
69
  *
70
- * @since 4.3
71
- *
72
  * @param string $path The path to append to the existing, if any, one., e.g. `/some/path`
73
  *
74
- * @param string $url A full URL in the `http://example.com/?query=var#frag` format.
75
- *
76
  * @return mixed|string
 
 
77
  */
78
  function tribe_append_path( $url, $path ) {
79
  $path = trim( $path, '/' );
@@ -134,9 +136,7 @@ if ( ! function_exists( 'tribe_get_request_var' ) ) {
134
  *
135
  * The variable being tested for can be an array if you wish to find a nested value.
136
  *
137
- * @since 4.9.17 Included explicit check against $_REQUEST.
138
- *
139
- * @see Tribe__Utils__Array::get()
140
  *
141
  * @param string|array $var
142
  * @param mixed $default
@@ -144,8 +144,7 @@ if ( ! function_exists( 'tribe_get_request_var' ) ) {
144
  * @return mixed
145
  */
146
  function tribe_get_request_var( $var, $default = null ) {
147
- $unsafe = Tribe__Utils__Array::get_in_any( [ $_GET, $_POST, $_REQUEST ], $var, $default );
148
- return tribe_sanitize_deep( $unsafe );
149
  }
150
  }
151
 
@@ -156,7 +155,7 @@ if ( ! function_exists( 'tribe_get_global_query_object' ) ) {
156
  *
157
  * @since 4.7.8
158
  *
159
- * @return WP_Query The $wp_query, the $wp_the_query if $wp_query empty, null otherwise.
160
  */
161
  function tribe_get_global_query_object() {
162
  global $wp_query;
@@ -193,7 +192,7 @@ if ( ! function_exists( 'tribe_is_truthy' ) ) {
193
  *
194
  * @param array $truthy_strings
195
  */
196
- $truthy_strings = (array) apply_filters( 'tribe_is_truthy_strings', [
197
  '1',
198
  'enable',
199
  'enabled',
@@ -201,7 +200,7 @@ if ( ! function_exists( 'tribe_is_truthy' ) ) {
201
  'y',
202
  'yes',
203
  'true',
204
- ] );
205
  // Makes sure we are dealing with lowercase for testing
206
  if ( is_string( $var ) ) {
207
  $var = strtolower( $var );
@@ -222,53 +221,50 @@ if ( ! function_exists( 'tribe_is_truthy' ) ) {
222
  }
223
  }
224
 
225
- if ( ! function_exists( 'tribe_sort_by_priority' ) ) {
226
- /**
227
- * Sorting function based on Priority
228
- *
229
- * @since 4.7.20
230
- *
231
- * @param object|array $b Second subject to compare
232
- *
233
- * @param object|array $a First Subject to compare
234
- *
235
- * @return int
236
- */
237
- function tribe_sort_by_priority( $a, $b ) {
238
- if ( is_array( $a ) ) {
239
- $a_priority = $a['priority'];
240
- } else {
241
- $a_priority = $a->priority;
242
- }
243
-
244
- if ( is_array( $b ) ) {
245
- $b_priority = $b['priority'];
246
- } else {
247
- $b_priority = $b->priority;
248
- }
249
 
250
- return (int) $a_priority === (int) $b_priority ? 0 : (int) $a_priority > (int) $b_priority;
 
 
 
251
  }
 
 
252
  }
253
 
254
  if ( ! function_exists( 'tribe_normalize_terms_list' ) ) {
255
  /**
256
  * Normalizes a list of terms to a list of fields.
257
  *
258
- * @since 4.5
259
- *
260
  * @param string $taxonomy The terms taxonomy.
261
- * @param string $field The fields the terms should be normalized to.
262
- * @param $terms A term or array of terms to normalize.
 
263
  *
264
  * @return array An array of the valid normalized terms.
265
  */
266
  function tribe_normalize_terms_list( $terms, $taxonomy, $field = 'term_id' ) {
267
  if ( ! is_array( $terms ) ) {
268
- $terms = [ $terms ];
269
  }
270
 
271
- $normalized = [];
272
  foreach ( $terms as $term ) {
273
  if ( is_object( $term ) && ! empty( $term->{$field} ) ) {
274
  $normalized[] = $term->{$field};
@@ -291,13 +287,12 @@ if ( ! function_exists( 'tribe_normalize_terms_list' ) ) {
291
  }
292
 
293
  if ( ! function_exists( 'tribe_upload_image' ) ) {
294
- /**
295
- * @see Tribe__Image__Uploader::upload_and_get_attachment_id()
296
- *
297
- * @param string|int $image The path to an image file, an image URL or an attachment post ID.
298
  *
299
  * @return int|bool The attachment post ID if the uploading and attachment is successful or the ID refers to an attachment;
300
  * `false` otherwise.
 
 
301
  */
302
  function tribe_upload_image( $image ) {
303
  $uploader = new Tribe__Image__Uploader( $image );
@@ -334,9 +329,9 @@ if ( ! function_exists( 'tribe_retrieve_object_by_hook' ) ) {
334
  *
335
  * @since 4.5.8
336
  *
337
- * @param string $class_name
338
- * @param string $hook
339
- * @param int $priority
340
  *
341
  * @return object|false
342
  */
@@ -409,7 +404,7 @@ if ( ! function_exists( 'tribe_post_exists' ) ) {
409
  global $wpdb;
410
 
411
  $query_template = "SELECT ID FROM {$wpdb->posts} WHERE %s";
412
- $query_vars = [];
413
  $where = '';
414
 
415
  if ( is_numeric( $post_id_or_name ) ) {
@@ -498,9 +493,10 @@ if ( ! function_exists( 'tribe_catch_and_throw' ) ) {
498
  *
499
  * @since 4.9.5
500
  *
 
 
501
  * @see set_error_handler()
502
  * @see restore_error_handler()
503
- * @throws RuntimeException The message will be the error message, the code will be the error code.
504
  */
505
  function tribe_catch_and_throw( $errno, $errstr ) {
506
  throw new RuntimeException( $errstr, $errno );
@@ -551,15 +547,15 @@ if ( ! function_exists( 'tribe_unfenced_regex' ) ) {
551
  return $regex;
552
  }
553
 
554
- $str_fence = $regex[0];
555
  // Let's pick a fence char the string itself is not using.
556
  $fence_char = '~' === $str_fence ? '#' : '~';
557
- $pattern = $fence_char
558
- . preg_quote( $str_fence, $fence_char ) // the opening fence
559
- . '(.*)' // keep anything after the opening fence, group 1
560
- . preg_quote( $str_fence, $fence_char ) // the closing fence
561
- . '.*' // any modifier after the closing fence
562
- . $fence_char;
563
 
564
  return preg_replace( $pattern, '$1', $regex );
565
  }
@@ -579,11 +575,9 @@ if ( ! function_exists( 'has_blocks' ) ) {
579
  * you should use the block parser on post content.
580
  *
581
  * @since 4.8
582
- *
583
- * @see https://github.com/WordPress/gutenberg/blob/73d9759116dde896931f4d152f186147a57889fe/lib/register.php#L313-L337s
584
  *
585
  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
586
- *
587
  * @return bool Whether the post has blocks.
588
  */
589
  function has_blocks( $post = null ) {
@@ -593,465 +587,6 @@ if ( ! function_exists( 'has_blocks' ) ) {
593
  $post = $wp_post->post_content;
594
  }
595
  }
596
-
597
  return false !== strpos( (string) $post, '<!-- wp:' );
598
  }
599
  }
600
-
601
- if ( ! function_exists( 'tribe_register_rest_route' ) ) {
602
- /**
603
- * Wrapper function for `register_rest_route` to allow for filtering any Tribe REST API endpoint.
604
- *
605
- * @since 4.9.12
606
- *
607
- * @param string $route The base URL for route you are adding.
608
- * @param array $args Optional. Either an array of options for the endpoint, or an array of arrays for
609
- * multiple methods. Default empty array.
610
- * @param bool $override Optional. If the route already exists, should we override it? True overrides,
611
- * false merges (with newer overriding if duplicate keys exist). Default false.
612
- *
613
- * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
614
- *
615
- * @return bool True on success, false on error.
616
- */
617
- function tribe_register_rest_route( $namespace, $route, $args = [], $override = false ) {
618
- /**
619
- * Allow plugins to customize REST API arguments and callbacks.
620
- *
621
- * @since 4.9.12
622
- *
623
- * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
624
- * @param string $route The base URL for route you are adding.
625
- * @param bool $override Optional. If the route already exists, should we override it? True overrides,
626
- * false merges (with newer overriding if duplicate keys exist). Default false.
627
- *
628
- * @param array $args Either an array of options for the endpoint, or an array of arrays for
629
- * multiple methods. Default empty array.
630
- */
631
- $args = apply_filters( 'tribe_register_rest_route_args_' . $namespace . $route, $args, $namespace, $route, $override );
632
-
633
- /**
634
- * Allow plugins to customize REST API arguments and callbacks.
635
- *
636
- * @since 4.9.12
637
- *
638
- * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
639
- * @param string $route The base URL for route you are adding.
640
- * @param bool $override Optional. If the route already exists, should we override it? True overrides,
641
- * false merges (with newer overriding if duplicate keys exist). Default false.
642
- *
643
- * @param array $args Either an array of options for the endpoint, or an array of arrays for
644
- * multiple methods. Default empty array.
645
- */
646
- $args = apply_filters( 'tribe_register_rest_route_args', $args, $namespace, $route, $override );
647
-
648
- return register_rest_route( $namespace, $route, $args, $override );
649
- }
650
- }
651
-
652
- if ( ! function_exists( 'tribe_get_class_instance' ) ) {
653
- /**
654
- * Gets the class instance / Tribe Container from the passed object or string.
655
- *
656
- * @since 4.10.0
657
- *
658
- * @see \tad_DI52_Container::isBound()
659
- * @see \tribe()
660
- *
661
- * @param string|object $class The plugin class' singleton name, class name, or instance.
662
- *
663
- * @return mixed|object|Tribe__Container|null Null if not found, else the result from tribe().
664
- */
665
- function tribe_get_class_instance( $class ) {
666
- if ( is_object( $class ) ) {
667
- return $class;
668
- }
669
-
670
- if ( ! is_string( $class ) ) {
671
- return null;
672
- }
673
-
674
- // Check if class exists and has instance getter method.
675
- if ( class_exists( $class ) ) {
676
- if ( method_exists( $class, 'instance' ) ) {
677
- return $class::instance();
678
- }
679
-
680
- if ( method_exists( $class, 'get_instance' ) ) {
681
- return $class::get_instance();
682
- }
683
- }
684
-
685
- try {
686
- return tribe( $class );
687
- } catch ( \RuntimeException $exception ) {
688
- return null;
689
- }
690
- }
691
- }
692
-
693
- if ( ! function_exists( 'tribe_get_least_version_ever_installed' ) ) {
694
- /**
695
- * Gets the lowest version number ever installed for the specified class of a plugin having a
696
- * `version_history_slug` property or a `VERSION` constant (i.e. Main classes).
697
- *
698
- * If user initially installed v2, downgraded to v1, then updated to v3, this will return v1.
699
- * If no historical version records, fallback is the class' current version.
700
- * If no version info found, it will return false.
701
- * Zero may have been logged as a past version but gets ignored.
702
- *
703
- * @since 4.10.0
704
- *
705
- * @param string|object $class The plugin class' singleton name, class name, or instance.
706
- *
707
- * @return string|boolean The SemVer version string or false if no info found.
708
- */
709
- function tribe_get_least_version_ever_installed( $class ) {
710
- $instance = tribe_get_class_instance( $class );
711
-
712
- if ( $instance ) {
713
- // Try for the version history first.
714
- if ( ! empty( $instance->version_history_slug ) ) {
715
- $history = (array) Tribe__Settings_Manager::get_option( $instance->version_history_slug );
716
-
717
- // '0' may be logged as a version number, which isn't useful, so we remove it
718
- $history = array_filter( $history );
719
- $history = array_unique( $history );
720
-
721
- if ( ! empty( $history ) ) {
722
- // Sort the array so smallest version number is first (likely how the array is stored anyway)
723
- usort( $history, 'version_compare' );
724
-
725
- return array_shift( $history );
726
- }
727
- }
728
-
729
- // Fall back to the current plugin version.
730
- if ( defined( get_class( $instance ) . '::VERSION' ) ) {
731
- return $instance::VERSION;
732
- }
733
- }
734
-
735
- // No version set.
736
- return false;
737
- }
738
- }
739
-
740
- if ( ! function_exists( 'tribe_get_greatest_version_ever_installed' ) ) {
741
- /**
742
- * Gets the highest version number ever installed for the specified class of a plugin having a
743
- * `version_history_slug` property or a `VERSION` constant (i.e. Main classes).
744
- *
745
- * If user initially installed v2, updated to v3, then downgraded to v2, this will return v3.
746
- * If no historical version records, fallback is the class' current version.
747
- * If no version info found, it will return false.
748
- * Zero may have been logged as a past version but gets ignored.
749
- *
750
- * @since 4.10.0
751
- *
752
- * @see \tribe_get_currently_installed_version() To get the current version, even if it's not the greatest.
753
- *
754
- * @param string|object $class The plugin class' singleton name, class name, or instance.
755
- *
756
- * @return string|boolean The SemVer version string or false if no info found.
757
- */
758
- function tribe_get_greatest_version_ever_installed( $class ) {
759
- $instance = tribe_get_class_instance( $class );
760
-
761
- if ( $instance ) {
762
- // Try for the version history first.
763
- if ( ! empty( $instance->version_history_slug ) ) {
764
- $history = (array) Tribe__Settings_Manager::get_option( $instance->version_history_slug );
765
-
766
- // '0' may be logged as a version number, which isn't useful, so we remove it
767
- $history = array_filter( $history );
768
- $history = array_unique( $history );
769
-
770
- if ( ! empty( $history ) ) {
771
- // Sort the array so smallest version number is first (likely how the array is stored anyway)
772
- usort( $history, 'version_compare' );
773
-
774
- return array_pop( $history );
775
- }
776
- }
777
-
778
- // Fall back to the current plugin version.
779
- if ( defined( get_class( $instance ) . '::VERSION' ) ) {
780
- return $instance::VERSION;
781
- }
782
- }
783
-
784
- // No version set.
785
- return false;
786
- }
787
- }
788
-
789
- if ( ! function_exists( 'tribe_get_first_ever_installed_version' ) ) {
790
- /**
791
- * Gets the initially-recorded version number installed for the specified class of a plugin having a
792
- * `version_history_slug` property or a `VERSION` constant (i.e. Main classes).
793
- *
794
- * If user initially installed v2, downgraded to v1, then updated to v3, this will return v2.
795
- * If no historical version records, fallback is the class' current version.
796
- * If no version info found, it will return false.
797
- * Zero may have been logged as a past version but gets ignored.
798
- *
799
- * @since 4.10.0
800
- *
801
- * @param string|object $class The plugin class' singleton name, class name, or instance.
802
- *
803
- * @return string|boolean The SemVer version string or false if no info found.
804
- */
805
- function tribe_get_first_ever_installed_version( $class ) {
806
- $instance = tribe_get_class_instance( $class );
807
-
808
- if ( $instance ) {
809
- // Try for the version history first.
810
- if ( ! empty( $instance->version_history_slug ) ) {
811
- $history = (array) Tribe__Settings_Manager::get_option( $instance->version_history_slug );
812
-
813
- // '0' may be logged as a version number, which isn't useful, so we remove it
814
- while (
815
- ! empty( $history )
816
- && empty( $history[0] )
817
- ) {
818
- array_shift( $history );
819
- }
820
-
821
- // Found it so return it
822
- if ( ! empty( $history[0] ) ) {
823
- return $history[0];
824
- }
825
- }
826
-
827
- // Fall back to the current plugin version.
828
- if ( defined( get_class( $instance ) . '::VERSION' ) ) {
829
- return $instance::VERSION;
830
- }
831
- }
832
-
833
- // No version set.
834
- return false;
835
- }
836
- }
837
-
838
- if ( ! function_exists( 'tribe_get_currently_installed_version' ) ) {
839
- /**
840
- * Gets the current version number installed for the specified class of a plugin having a
841
- * `VERSION` constant (i.e. Main classes)--different logic than related functions.
842
- *
843
- * If user initially installed v2, downgraded to v1, then updated to v3, this will return v3.
844
- * Only looks at the class' current version, else false.
845
- *
846
- * @since 4.10.0
847
- *
848
- * @see \tribe_get_greatest_version_ever_installed() To get the greatest ever installed, even if not the current.
849
- *
850
- * @param string|object $class The plugin class' singleton name, class name, or instance.
851
- *
852
- * @return string|boolean The SemVer version string or false if no info found.
853
- */
854
- function tribe_get_currently_installed_version( $class ) {
855
- $instance = tribe_get_class_instance( $class );
856
-
857
- if ( $instance ) {
858
- // First try for class constant (different logic from the other similar functions).
859
- if ( defined( get_class( $instance ) . '::VERSION' ) ) {
860
- return $instance::VERSION;
861
- }
862
- }
863
-
864
- // No version set.
865
- return false;
866
- }
867
- }
868
-
869
- if ( ! function_exists( 'tribe_installed_before' ) ) {
870
- /**
871
- * Checks if a plugin's initially-installed version was prior to the passed version.
872
- * If no info found, it will assume the plugin is old and return true.
873
- *
874
- * @since 4.10.0
875
- *
876
- * @param string|object $class The plugin class' singleton name, class name, or instance.
877
- * @param string $version The SemVer version string to compare.
878
- *
879
- * @return boolean Whether the plugin was installed prior to the passed version.
880
- */
881
- function tribe_installed_before( $class, $version ) {
882
- $install_version = tribe_get_first_ever_installed_version( $class );
883
-
884
- // If no install version, let's assume it's been here a while.
885
- if ( empty( $install_version ) ) {
886
- return true;
887
- }
888
-
889
- return 0 > version_compare( $install_version, $version );
890
- }
891
- }
892
-
893
- if ( ! function_exists( 'tribe_installed_after' ) ) {
894
- /**
895
- * Checks if a plugin's initially-installed version was after the passed version.
896
- * If no info found, it will assume the plugin is old and return false.
897
- *
898
- * @since 4.10.0
899
- *
900
- * @param string|object $class The plugin class' singleton name, class name, or instance.
901
- * @param string $version The SemVer version string to compare.
902
- *
903
- * @return boolean Whether the plugin was installed after the passed version.
904
- */
905
- function tribe_installed_after( $class, $version ) {
906
- $install_version = tribe_get_first_ever_installed_version( $class );
907
-
908
- // If no install version, let's assume it's been here a while.
909
- if ( empty( $install_version ) ) {
910
- return false;
911
- }
912
-
913
- return 0 < version_compare( $install_version, $version );
914
- }
915
- }
916
-
917
- if ( ! function_exists( 'tribe_installed_on' ) ) {
918
- /**
919
- * Checks if a plugin was installed at/on the passed version.
920
- * If no info found, it will assume the plugin is old and return false.
921
- *
922
- * @since 4.10.0
923
- *
924
- * @param string|object $class The plugin class' singleton name, class name, or instance.
925
- * @param string $version The SemVer version string to compare.
926
- *
927
- * @return boolean Whether the plugin was installed at/on the passed version.
928
- */
929
- function tribe_installed_on( $class, $version ) {
930
- $install_version = tribe_get_first_ever_installed_version( $class );
931
-
932
- // If no install version, let's assume it's been here a while.
933
- if ( empty( $install_version ) ) {
934
- return false;
935
- }
936
-
937
- return 0 === version_compare( $install_version, $version );
938
- }
939
- }
940
-
941
- if ( ! function_exists( 'tribe_get_request_vars' ) ) {
942
- /**
943
- * Returns the sanitized version of the `$_REQUEST` super-global array.
944
- *
945
- * Note: the return value is cached. It will be resolve the first time the function is called, per HTTP request,
946
- * then the same return value will be returned. After the function has been called the first time, changes to the
947
- * `$_REQUEST` super-global will NOT be reflected in the function return value.
948
- * Call the function with `$refresh` set to `true` to refresh the function value.
949
- *
950
- * @since 4.9.18
951
- *
952
- * @param bool $refresh Whether to parse the `$_REQUEST` cache again and refresh the cache or not; defaults to
953
- * `false`.
954
- *
955
- * @return array The sanitized version of the `$_REQUEST` super-global.
956
- */
957
- function tribe_get_request_vars( $refresh = false ) {
958
- static $cache;
959
-
960
- if ( ! isset( $_REQUEST ) ) {
961
- return [];
962
- }
963
-
964
- if ( null !== $cache && ! $refresh ) {
965
- return $cache;
966
- }
967
-
968
- $cache = array_combine(
969
- array_keys( $_REQUEST ),
970
- array_map( static function ( $v )
971
- {
972
- return filter_var( $v, FILTER_SANITIZE_STRING );
973
- },
974
- $_REQUEST )
975
- );
976
-
977
- return $cache;
978
- }
979
- }
980
-
981
- if ( ! function_exists( 'tribe_sanitize_deep' ) ) {
982
-
983
- /**
984
- * Sanitizes a value according to its type.
985
- *
986
- * The function will recursively sanitize array values.
987
- *
988
- * @since 4.9.20
989
- *
990
- * @param mixed $value The value, or values, to sanitize.
991
- *
992
- * @return mixed|null Either the sanitized version of the value, or `null` if the value is not a string, number or
993
- * array.
994
- */
995
- function tribe_sanitize_deep( &$value ) {
996
- if ( is_bool( $value ) ) {
997
- return $value;
998
- }
999
- if ( is_string( $value ) ) {
1000
- $value = filter_var( $value, FILTER_SANITIZE_STRING );
1001
- return $value;
1002
- }
1003
- if ( is_int( $value ) ) {
1004
- $value = filter_var( $value, FILTER_VALIDATE_INT );
1005
- return $value;
1006
- }
1007
- if ( is_float( $value ) ) {
1008
- $value = filter_var( $value, FILTER_VALIDATE_FLOAT );
1009
- return $value;
1010
- }
1011
- if ( is_array( $value ) ) {
1012
- array_walk( $value, 'tribe_sanitize_deep' );
1013
- return $value;
1014
- }
1015
-
1016
- return null;
1017
- }
1018
- }
1019
-
1020
- if ( ! function_exists( 'tribe_get_query_var' ) ) {
1021
- /**
1022
- * Returns a query var parsing an input URL.
1023
- *
1024
- * @since 4.9.23
1025
- *
1026
- * @param string $url The URL to parse.
1027
- * @param string|array $query_arg The query variable(s) to parse and return.
1028
- * @param mixed|null $default The default value to return if the URL cannot be parsed, or the query variable is
1029
- * not found.
1030
- *
1031
- * @return mixed|null The query variable value, if set, or the default value.
1032
- */
1033
- function tribe_get_query_var( $url, $query_arg, $default = null ) {
1034
- if ( empty( $url ) ) {
1035
- return $default;
1036
- }
1037
-
1038
- $query = wp_parse_url( $url, PHP_URL_QUERY );
1039
-
1040
- if ( empty( $query ) ) {
1041
- return $default;
1042
- }
1043
-
1044
- wp_parse_str( $query, $parsed );
1045
-
1046
- if ( ! is_array( $query_arg ) ) {
1047
- return Tribe__Utils__Array::get( $parsed, $query_arg, $default );
1048
- }
1049
-
1050
- $query_args = (array) ( $query_arg );
1051
-
1052
- return array_intersect_key(
1053
- (array) $parsed,
1054
- array_combine( $query_args, $query_args )
1055
- );
1056
- }
1057
- }
36
  * @param string $version The version
37
  * @param array $classes_req Any Main class files/tribe plugins required for this to run
38
  * @param array $dependencies an array of dependencies to check
39
+ *
40
+ * @return bool Indicates if plugin should continue initialization
41
  */
42
+ function tribe_register_plugin( $file_path, $main_class, $version, $classes_req = array(), $dependencies = array() ) {
43
+
44
+ $tribe_dependency = Tribe__Dependency::instance();
45
  $tribe_dependency->register_plugin( $file_path, $main_class, $version, $classes_req, $dependencies );
46
+
47
  }
48
  }
49
 
53
  *
54
  * @since 4.9
55
  *
56
+ * @param string $main_class The Main/base class for this plugin
57
  *
58
  * @return bool Indicates if plugin should continue initialization
59
  */
60
  function tribe_check_plugin( $main_class ) {
61
 
62
+ $tribe_dependency = Tribe__Dependency::instance();
 
63
  return $tribe_dependency->check_plugin( $main_class );
64
 
65
  }
70
  * Append a path fragment to a URL preserving query arguments
71
  * and fragments.
72
  *
73
+ * @param string $url A full URL in the `http://example.com/?query=var#frag` format.
 
74
  * @param string $path The path to append to the existing, if any, one., e.g. `/some/path`
75
  *
 
 
76
  * @return mixed|string
77
+ *
78
+ * @since 4.3
79
  */
80
  function tribe_append_path( $url, $path ) {
81
  $path = trim( $path, '/' );
136
  *
137
  * The variable being tested for can be an array if you wish to find a nested value.
138
  *
139
+ * @see Tribe__Utils__Array::get()
 
 
140
  *
141
  * @param string|array $var
142
  * @param mixed $default
144
  * @return mixed
145
  */
146
  function tribe_get_request_var( $var, $default = null ) {
147
+ return Tribe__Utils__Array::get_in_any( array( $_GET, $_POST ), $var, $default );
 
148
  }
149
  }
150
 
155
  *
156
  * @since 4.7.8
157
  *
158
+ * @return object The $wp_query, the $wp_the_query if $wp_query empty, null otherwise.
159
  */
160
  function tribe_get_global_query_object() {
161
  global $wp_query;
192
  *
193
  * @param array $truthy_strings
194
  */
195
+ $truthy_strings = (array) apply_filters( 'tribe_is_truthy_strings', array(
196
  '1',
197
  'enable',
198
  'enabled',
200
  'y',
201
  'yes',
202
  'true',
203
+ ) );
204
  // Makes sure we are dealing with lowercase for testing
205
  if ( is_string( $var ) ) {
206
  $var = strtolower( $var );
221
  }
222
  }
223
 
224
+ /**
225
+ * Sorting function based on Priority
226
+ *
227
+ * @since 4.7.20
228
+ *
229
+ * @param object|array $a First Subject to compare
230
+ * @param object|array $b Second subject to compare
231
+ *
232
+ * @return int
233
+ */
234
+ function tribe_sort_by_priority( $a, $b ) {
235
+ if ( is_array( $a ) ) {
236
+ $a_priority = $a['priority'];
237
+ } else {
238
+ $a_priority = $a->priority;
239
+ }
 
 
 
 
 
 
 
 
240
 
241
+ if ( is_array( $b ) ) {
242
+ $b_priority = $b['priority'];
243
+ } else {
244
+ $b_priority = $b->priority;
245
  }
246
+
247
+ return (int) $a_priority === (int) $b_priority ? 0 : (int) $a_priority > (int) $b_priority;
248
  }
249
 
250
  if ( ! function_exists( 'tribe_normalize_terms_list' ) ) {
251
  /**
252
  * Normalizes a list of terms to a list of fields.
253
  *
254
+ * @param $terms A term or array of terms to normalize.
 
255
  * @param string $taxonomy The terms taxonomy.
256
+ * @param string $field Teh fields the terms should be normalized to.
257
+ *
258
+ * @since 4.5
259
  *
260
  * @return array An array of the valid normalized terms.
261
  */
262
  function tribe_normalize_terms_list( $terms, $taxonomy, $field = 'term_id' ) {
263
  if ( ! is_array( $terms ) ) {
264
+ $terms = array( $terms );
265
  }
266
 
267
+ $normalized = array();
268
  foreach ( $terms as $term ) {
269
  if ( is_object( $term ) && ! empty( $term->{$field} ) ) {
270
  $normalized[] = $term->{$field};
287
  }
288
 
289
  if ( ! function_exists( 'tribe_upload_image' ) ) {
290
+ /** * @param string|int $image The path to an image file, an image URL or an attachment post ID.
 
 
 
291
  *
292
  * @return int|bool The attachment post ID if the uploading and attachment is successful or the ID refers to an attachment;
293
  * `false` otherwise.
294
+ *
295
+ * @see Tribe__Image__Uploader::upload_and_get_attachment_id()
296
  */
297
  function tribe_upload_image( $image ) {
298
  $uploader = new Tribe__Image__Uploader( $image );
329
  *
330
  * @since 4.5.8
331
  *
332
+ * @param string $class_name
333
+ * @param string $hook
334
+ * @param int $priority
335
  *
336
  * @return object|false
337
  */
404
  global $wpdb;
405
 
406
  $query_template = "SELECT ID FROM {$wpdb->posts} WHERE %s";
407
+ $query_vars = array();
408
  $where = '';
409
 
410
  if ( is_numeric( $post_id_or_name ) ) {
493
  *
494
  * @since 4.9.5
495
  *
496
+ * @throws RuntimeException The message will be the error message, the code will be the error code.
497
+ *
498
  * @see set_error_handler()
499
  * @see restore_error_handler()
 
500
  */
501
  function tribe_catch_and_throw( $errno, $errstr ) {
502
  throw new RuntimeException( $errstr, $errno );
547
  return $regex;
548
  }
549
 
550
+ $str_fence = $regex[0];
551
  // Let's pick a fence char the string itself is not using.
552
  $fence_char = '~' === $str_fence ? '#' : '~';
553
+ $pattern = $fence_char
554
+ . preg_quote( $str_fence, $fence_char ) // the opening fence
555
+ . '(.*)' // keep anything after the opening fence, group 1
556
+ . preg_quote( $str_fence, $fence_char ) // the closing fence
557
+ . '.*' // any modifier after the closing fence
558
+ . $fence_char;
559
 
560
  return preg_replace( $pattern, '$1', $regex );
561
  }
575
  * you should use the block parser on post content.
576
  *
577
  * @since 4.8
578
+ * @see https://github.com/WordPress/gutenberg/blob/73d9759116dde896931f4d152f186147a57889fe/lib/register.php#L313-L337s
 
579
  *
580
  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
 
581
  * @return bool Whether the post has blocks.
582
  */
583
  function has_blocks( $post = null ) {
587
  $post = $wp_post->post_content;
588
  }
589
  }
 
590
  return false !== strpos( (string) $post, '<!-- wp:' );
591
  }
592
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/modules/components/form/index.js ADDED
@@ -0,0 +1 @@
 
1
+ export { default as Select } from './select';
common/src/modules/components/form/select/component.js ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { PureComponent } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import { noop, partial, find } from 'lodash';
7
+ import classnames from 'classnames';
8
+ import {
9
+ Dropdown,
10
+ Dashicon,
11
+ } from '@wordpress/components';
12
+ import { ScrollTo, ScrollArea } from 'react-scroll-to';
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+ import { PreventBlockClose } from '@moderntribe/common/components';
18
+ import './style.pcss';
19
+
20
+ export default class Select extends PureComponent {
21
+ static propTypes = {
22
+ options: PropTypes.shape( {
23
+ label: PropTypes.string,
24
+ value: PropTypes.any,
25
+ } ),
26
+ onOptionClick: PropTypes.func.isRequired,
27
+ optionClassName: PropTypes.string,
28
+ isOpen: PropTypes.bool.isRequired,
29
+ value: PropTypes.any,
30
+ className: PropTypes.string,
31
+ }
32
+
33
+ static defaultProps = {
34
+ onOptionClick: noop,
35
+ isOpen: true,
36
+ optionClassName: '',
37
+ }
38
+
39
+ _onOptionClick = ( onClose, value, e ) => {
40
+ this.props.onOptionClick( value, e );
41
+ onClose();
42
+ }
43
+
44
+ get selected() {
45
+ return find( this.props.options, option => option.value === this.props.value );
46
+ }
47
+
48
+ get label() {
49
+ const selected = this.selected;
50
+ return selected && selected.label;
51
+ }
52
+
53
+ renderOptions = ( onClose ) => (
54
+ this.props.options.map( ( option ) => (
55
+ <button
56
+ className={ classnames(
57
+ 'tribe-common-form-select__options__option',
58
+ this.props.optionClassName
59
+ ) }
60
+ key={ option.value }
61
+ onClick={ partial( this._onOptionClick, onClose, option.value ) }
62
+ role="menuitem"
63
+ type="button"
64
+ value={ option.value }
65
+ >
66
+ { option.label }
67
+ </button>
68
+ ) )
69
+ )
70
+
71
+ renderToggle = ( { onToggle, isOpen } ) => (
72
+ <div className="tribe-common-form-select__toggle">
73
+ <button
74
+ type="button"
75
+ aria-expanded={ isOpen }
76
+ onClick={ onToggle }
77
+ >
78
+ <span>{ this.label }</span>
79
+ <Dashicon
80
+ className="btn--icon"
81
+ icon={ isOpen ? 'arrow-up' : 'arrow-down' }
82
+ />
83
+ </button>
84
+ </div>
85
+ )
86
+
87
+ renderContent = ( { onClose } ) => (
88
+ <ScrollTo>
89
+ { () => (
90
+ <PreventBlockClose>
91
+ <ScrollArea
92
+ role="menu"
93
+ className={ classnames( 'tribe-common-form-select__options' ) }
94
+ >
95
+ { this.renderOptions( onClose ) }
96
+ </ScrollArea>
97
+ </PreventBlockClose>
98
+ ) }
99
+ </ScrollTo>
100
+
101
+ );
102
+
103
+ render() {
104
+ return (
105
+ <Dropdown
106
+ className={ classnames( 'tribe-common-form-select',
107
+ this.props.className
108
+ ) }
109
+ position="bottom center"
110
+ contentClassName="tribe-common-form-select__content"
111
+ renderToggle={ this.renderToggle }
112
+ renderContent={ this.renderContent }
113
+ />
114
+ );
115
+ }
116
+ }
common/src/modules/components/form/select/index.js ADDED
@@ -0,0 +1 @@
 
1
+ export { default } from './component';
common/src/modules/components/form/select/style.pcss ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-common-form-select {
2
+ display: flex;
3
+
4
+ & > div {
5
+ flex: 0 0 auto;
6
+ min-width: 193px;
7
+ }
8
+
9
+ &__toggle {
10
+ background-color: #FFF;
11
+ border-radius: 2px;
12
+ border: 2px solid #EAEBEC;
13
+
14
+ & button {
15
+ align-items: center;
16
+ background-color: #FEFFFE;
17
+ border: none;
18
+ color: #545D66;
19
+ display: flex;
20
+ font-family: Helvetica, sans-serif;
21
+ font-size: 16px;
22
+ font-weight: 400;
23
+ font-weight: normal;
24
+ justify-content: center;
25
+ line-height: 1.5;
26
+ padding: 6px 10px;
27
+ text-align: left;
28
+ text-decoration: none;
29
+ width: 100%;
30
+
31
+ & > span {
32
+ flex: 1;
33
+ }
34
+ }
35
+ }
36
+
37
+ &__options {
38
+
39
+ &__option {
40
+ background: transparent;
41
+ border: 0;
42
+ color: #555d66;
43
+ cursor: pointer;
44
+ display: block;
45
+ line-height: 20px;
46
+ padding: 5px 12px;
47
+ text-align: left;
48
+ width: 100%;
49
+
50
+ &:hover {
51
+ background-color: #009fd4;
52
+ color: #fff;
53
+ }
54
+ }
55
+ }
56
+ }
common/src/modules/components/index.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /* eslint-disable max-len */
2
+ export { default as PluginBlockHooks } from '@moderntribe/common/components/plugin-block-hooks';
3
+ export { default as PreventBlockClose } from '@moderntribe/common/components/prevent-block-close';
4
+ export * from './form';
5
+
common/src/modules/components/plugin-block-hooks/__tests__/__snapshots__/plugin-block-hooks.spec.js.snap ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`PluginBlockHooks should match snapshot 1`] = `
4
+ <div
5
+ className="tribe-common__plugin-block-hook"
6
+ >
7
+ <InnerBlocks
8
+ template={
9
+ Array [
10
+ Array [
11
+ "tribe/event-datetime",
12
+ Object {},
13
+ ],
14
+ Array [
15
+ "tribe/event-pro-recurring",
16
+ Object {},
17
+ ],
18
+ Array [
19
+ "tribe/event-pro-exclusion",
20
+ Object {},
21
+ ],
22
+ ]
23
+ }
24
+ templateInsertUpdatesSelection={false}
25
+ />
26
+ </div>
27
+ `;
28
+
29
+ exports[`PluginBlockHooks should not hook in unregistered blocks 1`] = `
30
+ <div
31
+ className="tribe-common__plugin-block-hook"
32
+ >
33
+ <InnerBlocks
34
+ template={
35
+ Array [
36
+ Array [
37
+ "tribe/event-datetime",
38
+ Object {},
39
+ ],
40
+ Array [
41
+ "tribe/event-pro-recurring",
42
+ Object {},
43
+ ],
44
+ Array [
45
+ "tribe/event-pro-exclusion",
46
+ Object {},
47
+ ],
48
+ ]
49
+ }
50
+ templateInsertUpdatesSelection={false}
51
+ />
52
+ </div>
53
+ `;
54
+
55
+ exports[`PluginBlockHooks should only render events templates 1`] = `
56
+ <div
57
+ className="tribe-common__plugin-block-hook"
58
+ >
59
+ <InnerBlocks
60
+ template={
61
+ Array [
62
+ Array [
63
+ "tribe/event-datetime",
64
+ Object {},
65
+ ],
66
+ ]
67
+ }
68
+ templateInsertUpdatesSelection={false}
69
+ />
70
+ </div>
71
+ `;
72
+
73
+ exports[`PluginBlockHooks should only render events-pro templates 1`] = `
74
+ <div
75
+ className="tribe-common__plugin-block-hook"
76
+ >
77
+ <InnerBlocks
78
+ template={
79
+ Array [
80
+ Array [
81
+ "tribe/event-pro-recurring",
82
+ Object {},
83
+ ],
84
+ Array [
85
+ "tribe/event-pro-exclusion",
86
+ Object {},
87
+ ],
88
+ ]
89
+ }
90
+ templateInsertUpdatesSelection={false}
91
+ />
92
+ </div>
93
+ `;
94
+
95
+ exports[`PluginBlockHooks should recursively hook blocks 1`] = `
96
+ <div
97
+ className="tribe-common__plugin-block-hook"
98
+ >
99
+ <InnerBlocks
100
+ template={
101
+ Array [
102
+ Array [
103
+ "tribe/event-pro-recurring",
104
+ Object {},
105
+ ],
106
+ Array [
107
+ "tribe/event-cool-container1",
108
+ Object {},
109
+ Array [
110
+ Array [
111
+ "tribe/event-pro-recurring",
112
+ Object {},
113
+ ],
114
+ Array [
115
+ "tribe/event-cool-container2",
116
+ Object {},
117
+ Array [
118
+ Array [
119
+ "tribe/event-pro-recurring",
120
+ Object {},
121
+ ],
122
+ ],
123
+ ],
124
+ ],
125
+ ],
126
+ ]
127
+ }
128
+ templateInsertUpdatesSelection={false}
129
+ />
130
+ </div>
131
+ `;
common/src/modules/components/plugin-block-hooks/__tests__/plugin-block-hooks.spec.js ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External Dependencies
3
+ */
4
+ import React from 'react';
5
+ import renderer from 'react-test-renderer';
6
+
7
+ /**
8
+ * Internal Dependencies
9
+ */
10
+ import PluginBlockHooks from '../component';
11
+
12
+ jest.mock( '@wordpress/editor', () => ( {
13
+ InnerBlocks: 'InnerBlocks',
14
+ } ) );
15
+ jest.mock( '@wordpress/data', () => ( {
16
+ select: jest.fn( () => ( {
17
+ getBlockTypes: jest.fn( () => ( [
18
+ { name: 'tribe/event-datetime' },
19
+ { name: 'tribe/event-pro-recurring' },
20
+ { name: 'tribe/event-pro-exclusion' },
21
+ { name: 'tribe/event-cool-container1' },
22
+ { name: 'tribe/event-cool-container2' },
23
+ ] ) ),
24
+ } ) ),
25
+ } ) );
26
+
27
+ describe( 'PluginBlockHooks', () => {
28
+ let props;
29
+ beforeEach( () => {
30
+ props = {
31
+ plugins: [ 'events', 'events-pro', 'events-cool' ],
32
+ pluginTemplates: {
33
+ 'events': [
34
+ [ 'tribe/event-datetime', {}],
35
+ ],
36
+ 'events-pro': [
37
+ [ 'tribe/event-pro-recurring', {}],
38
+ [ 'tribe/event-pro-exclusion', {}],
39
+ ],
40
+ },
41
+ };
42
+ } );
43
+
44
+ test( 'should match snapshot', () => {
45
+ const component = renderer.create(
46
+ <PluginBlockHooks { ...props } />
47
+ );
48
+ expect( component.toJSON() ).toMatchSnapshot();
49
+ } );
50
+ test( 'should only render events templates', () => {
51
+ props.plugins = props.plugins.filter( plugin => plugin === 'events' );
52
+ const component = renderer.create(
53
+ <PluginBlockHooks { ...props } />
54
+ );
55
+ expect( component.toJSON() ).toMatchSnapshot();
56
+ } );
57
+ test( 'should only render events-pro templates', () => {
58
+ props.plugins = props.plugins.filter( plugin => plugin === 'events-pro' );
59
+ const component = renderer.create(
60
+ <PluginBlockHooks { ...props } />
61
+ );
62
+ expect( component.toJSON() ).toMatchSnapshot();
63
+ } );
64
+ test( 'should not hook in unregistered blocks', () => {
65
+ props.pluginTemplates.events.push( [ 'i-dont-exist', {}] );
66
+ const component = renderer.create(
67
+ <PluginBlockHooks { ...props } />
68
+ );
69
+ expect( component.toJSON() ).toMatchSnapshot();
70
+ } );
71
+ test( 'should recursively hook blocks', () => {
72
+ delete props.pluginTemplates.events;
73
+ delete props.pluginTemplates[ 'events-pro' ];
74
+ props.pluginTemplates[ 'events-cool' ] = [
75
+ [ 'tribe/event-pro-recurring', {}],
76
+ [ 'tribe/event-cool-container1', {}, [
77
+ [ 'tribe/event-pro-recurring', {}],
78
+ [ 'tribe/event-cool-container2', {}, [
79
+ [ 'tribe/event-pro-recurring', {}],
80
+ [ 'dont-register-me', {}],
81
+ ] ],
82
+ [ 'dont-register-2me', {}],
83
+ ],
84
+ [ 'dont-register-me', {}],
85
+ ],
86
+ ];
87
+ const component = renderer.create(
88
+ <PluginBlockHooks { ...props } />
89
+ );
90
+ expect( component.toJSON() ).toMatchSnapshot();
91
+ } );
92
+ } );
common/src/modules/components/plugin-block-hooks/component.js ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { PureComponent } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import { map, reduce, includes, isArray } from 'lodash';
7
+ import { InnerBlocks } from '@wordpress/editor';
8
+ import { select } from '@wordpress/data';
9
+ import './style.pcss';
10
+
11
+ /**
12
+ * Allows for dynamic plugin templates based on current plugins available
13
+ * utilizing InnerBlocks api
14
+ *
15
+ * @export
16
+ * @class PluginBlockHooks
17
+ * @extends {PureComponent}
18
+ */
19
+ export default class PluginBlockHooks extends PureComponent {
20
+ static propTypes = {
21
+ allowedBlocks: PropTypes.arrayOf( PropTypes.string ),
22
+ layouts: PropTypes.oneOfType( [
23
+ PropTypes.object,
24
+ PropTypes.arrayOf( PropTypes.object ),
25
+ ] ),
26
+ /**
27
+ * Plugins to be used
28
+ */
29
+ plugins: PropTypes.arrayOf( PropTypes.string ).isRequired,
30
+ /**
31
+ * Plugin template structure needed to properly
32
+ * register new templates for each plugin
33
+ *
34
+ *
35
+ * ```js
36
+ * {
37
+ * 'events': [
38
+ * [ 'tribe/event-datetime', {}],
39
+ * ],
40
+ * 'events-pro': [
41
+ * [ 'tribe/event-pro-recurring', {}],
42
+ * [ 'tribe/event-pro-exclusion', {}],
43
+ * ],
44
+ * 'events-cool': [
45
+ * [ 'tribe/event-cool-container', {}, [
46
+ * [ 'tribe/event-cool-column', {}],
47
+ * [ 'tribe/event-cool-column', {}],
48
+ * ]]
49
+ * ],
50
+ * }
51
+ * ```
52
+ */
53
+ pluginTemplates: PropTypes.objectOf( PropTypes.arrayOf( PropTypes.array ) ),
54
+ templateInsertUpdatesSelection: PropTypes.bool.isRequired,
55
+ templateLock: PropTypes.oneOf( [
56
+ 'all',
57
+ 'insert',
58
+ false,
59
+ ] ),
60
+ }
61
+
62
+ static defaultProps = {
63
+ templateInsertUpdatesSelection: false,
64
+ }
65
+
66
+ /**
67
+ * Registered block names from core
68
+ *
69
+ * @readonly
70
+ * @memberof PluginBlockHooks
71
+ * @returns {Array} block names
72
+ */
73
+ get registeredBlockNames() {
74
+ const blockTypes = select( 'core/blocks' ).getBlockTypes();
75
+ return map( blockTypes, block => block.name );
76
+ }
77
+
78
+ /**
79
+ * Template for InnerBlocks
80
+ *
81
+ * @readonly
82
+ * @memberof PluginBlockHooks
83
+ * @returns {Array} template
84
+ */
85
+ get template() {
86
+ const blockNames = this.registeredBlockNames;
87
+ return this.props.plugins.reduce( ( acc, plugin ) => {
88
+ const pluginTemplate = this.props.pluginTemplates[ plugin ];
89
+ if ( pluginTemplate ) {
90
+ // Block needs to be registered, otherwise it's dropped
91
+ const blockTemplates = this.filterPluginTemplates( blockNames, pluginTemplate );
92
+ return [
93
+ ...acc,
94
+ ...blockTemplates,
95
+ ];
96
+ }
97
+ return acc;
98
+ }, [] );
99
+ }
100
+
101
+ /**
102
+ * Recursively filters out unregistered blocks
103
+ *
104
+ * @param {Array} blockNames block names currently registered
105
+ * @param {Array} pluginTemplate Template for plugins
106
+ * @returns {Array} Array of plugin template
107
+ */
108
+ filterPluginTemplates( blockNames, pluginTemplate ) {
109
+ return reduce( pluginTemplate, ( acc, [ name, attributes, nestedBlockTemplates ] ) => {
110
+ if ( includes( blockNames, name ) ) {
111
+ const blockTemplate = isArray( nestedBlockTemplates )
112
+ ? [ name, attributes, /* Recursive call */ this.filterPluginTemplates( blockNames, nestedBlockTemplates ) ] // eslint-disable-line max-len
113
+ : [ name, attributes ];
114
+
115
+ return [
116
+ ...acc,
117
+ blockTemplate,
118
+ ];
119
+ }
120
+
121
+ return acc;
122
+ }, [] );
123
+ }
124
+
125
+ render() {
126
+ return (
127
+ <div className="tribe-common__plugin-block-hook">
128
+ <InnerBlocks
129
+ allowedBlocks={ this.props.allowedBlocks }
130
+ layouts={ this.props.layouts }
131
+ template={ this.template }
132
+ templateInsertUpdatesSelection={ this.props.templateInsertUpdatesSelection }
133
+ templateLock={ this.props.templateLock }
134
+ />
135
+ </div>
136
+ );
137
+ }
138
+ }
common/src/modules/components/plugin-block-hooks/container.js ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { connect } from 'react-redux';
5
+ import { compose } from 'redux';
6
+
7
+ /**
8
+ * Internal Dependencies
9
+ */
10
+ import { selectors } from '@moderntribe/common/data/plugins';
11
+ import { withStore } from '@moderntribe/common/hoc';
12
+ import PluginBlockHooks from './component';
13
+
14
+ const mapStateToProps = ( state, ownProps ) => ( {
15
+ plugins: selectors.getPlugins( state ),
16
+ } );
17
+
18
+ export default compose(
19
+ withStore(),
20
+ connect( mapStateToProps ),
21
+ )( PluginBlockHooks );
common/src/modules/components/plugin-block-hooks/index.js ADDED
@@ -0,0 +1 @@
 
1
+ export { default } from './container';
common/src/modules/components/plugin-block-hooks/style.pcss ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-common__plugin-block-hook {
2
+
3
+ & .editor-inner-blocks {
4
+
5
+ & .editor-block-list__layout {
6
+ margin: 0;
7
+
8
+ & .editor-block-list__block {
9
+ margin: 0;
10
+ max-width: 100% !important;
11
+ padding: 0;
12
+
13
+ &.is-selected > .editor-block-list__block-edit:before,
14
+ &.is-selected-parent > .editor-block-list__block-edit:before,
15
+ &.is-hovered > .editor-block-list__block-edit:before {
16
+ outline: none;
17
+ }
18
+
19
+ & .editor-block-list__block-edit {
20
+ margin: 0;
21
+
22
+ &:before {
23
+ outline: none;
24
+ }
25
+
26
+ & .editor-block-contextual-toolbar {
27
+ display: none;
28
+ }
29
+ }
30
+
31
+ {
32
+ outline: none;
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
common/src/modules/components/prevent-block-close/__tests__/component.spec.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import PreventBlockClose from '../component';
3
+
4
+ describe( 'PreventBlockClose', () => {
5
+ test( 'should match snapshot', () => {
6
+ const component = mount(
7
+ <PreventBlockClose>
8
+ <span>Test children</span>
9
+ </PreventBlockClose>
10
+ );
11
+ const child = component.find( 'span' );
12
+ expect( child ).toHaveLength( 1 );
13
+ expect( child.text() ).toEqual( "Test children" );
14
+ } );
15
+ } );
common/src/modules/components/prevent-block-close/component.js ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External Dependencies
3
+ */
4
+ import React, { PureComponent } from 'react';
5
+ import PropTypes from 'prop-types';
6
+
7
+ /**
8
+ * Internal Dependencies
9
+ */
10
+ import { intercept, EVENT_NAMESPACE } from '@moderntribe/common/hoc/with-block-closer';
11
+
12
+ export default class PreventBlockClose extends PureComponent {
13
+ static propTypes = {
14
+ children: PropTypes.node.isRequired,
15
+ }
16
+
17
+ nodeRef = React.createRef();
18
+
19
+ componentDidMount() {
20
+ this.node.addEventListener( EVENT_NAMESPACE, intercept );
21
+ }
22
+
23
+ componentWillUnmount() {
24
+ this.node.removeEventListener( EVENT_NAMESPACE, intercept );
25
+ }
26
+
27
+ get node() {
28
+ return this.nodeRef.current;
29
+ }
30
+
31
+ render() {
32
+ return (
33
+ <div ref={ this.nodeRef }>
34
+ { this.props.children }
35
+ </div>
36
+ );
37
+ }
38
+ }
common/src/modules/components/prevent-block-close/index.js ADDED
@@ -0,0 +1 @@
 
1
+ export { default } from './component';
common/src/modules/data/__tests__/utils.test.js ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { PREFIX_COMMON_STORE } from '@moderntribe/common/data/utils';
5
+
6
+ describe( 'prefix', () => {
7
+ it( 'Should return the prefix', () => {
8
+ expect( PREFIX_COMMON_STORE ).toBe('@@MT/COMMON');
9
+ } );
10
+ } );
common/src/modules/data/editor/post-types.js ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ export const EVENT = 'tribe_events';
2
+ export const VENUE = 'tribe_venue';
3
+ export const ORGANIZER = 'tribe_organizer';
common/src/modules/data/forms/__tests__/__snapshots__/actions.test.js.snap ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`[STORE] - Form actions Add volatile action 1`] = `
4
+ Object {
5
+ "payload": Object {
6
+ "id": 20,
7
+ },
8
+ "type": "@@MT/COMMON/ADD_VOLATILE_ID",
9
+ }
10
+ `;
11
+
12
+ exports[`[STORE] - Form actions Clear form action 1`] = `
13
+ Object {
14
+ "payload": Object {
15
+ "id": 20,
16
+ },
17
+ "type": "@@MT/COMMON/CLEAR_FORM",
18
+ }
19
+ `;
20
+
21
+ exports[`[STORE] - Form actions Create draft entry 1`] = `
22
+ Object {
23
+ "payload": Object {
24
+ "fields": Object {
25
+ "title": "Modern Tribe",
26
+ },
27
+ "id": 20,
28
+ },
29
+ "type": "@@MT/COMMON/CREATE_FORM_DRAFT",
30
+ }
31
+ `;
32
+
33
+ exports[`[STORE] - Form actions Edit the entry action 1`] = `
34
+ Object {
35
+ "payload": Object {
36
+ "fields": Object {
37
+ "title": "Tribe",
38
+ },
39
+ "id": 20,
40
+ },
41
+ "type": "@@MT/COMMON/EDIT_FORM_ENTRY",
42
+ }
43
+ `;
44
+
45
+ exports[`[STORE] - Form actions Register form action 1`] = `
46
+ Object {
47
+ "payload": Object {
48
+ "id": 20,
49
+ "type": "tribe_organizers",
50
+ },
51
+ "type": "@@MT/COMMON/ADD_FORM",
52
+ }
53
+ `;
54
+
55
+ exports[`[STORE] - Form actions Remove volatile action 1`] = `
56
+ Object {
57
+ "payload": Object {
58
+ "id": 20,
59
+ },
60
+ "type": "@@MT/COMMON/REMOVE_VOLATILE_ID",
61
+ }
62
+ `;
63
+
64
+ exports[`[STORE] - Form actions Set saving action 1`] = `
65
+ Object {
66
+ "payload": Object {
67
+ "id": 20,
68
+ "saving": true,
69
+ },
70
+ "type": "@@MT/COMMON/SET_SAVING_FORM",
71
+ }
72
+ `;
73
+
74
+ exports[`[STORE] - Form actions Set saving action 2`] = `
75
+ Object {
76
+ "payload": Object {
77
+ "id": 20,
78
+ "saving": false,
79
+ },
80
+ "type": "@@MT/COMMON/SET_SAVING_FORM",
81
+ }
82
+ `;
83
+
84
+ exports[`[STORE] - Form actions Set submit form 1`] = `
85
+ Object {
86
+ "payload": Object {
87
+ "id": 20,
88
+ },
89
+ "type": "@@MT/COMMON/SUBMIT_FORM",
90
+ }
91
+ `;
92
+
93
+ exports[`[STORE] - form thunk actions Maybe remove entry action with details 1`] = `
94
+ Array [
95
+ Object {
96
+ "meta": Object {
97
+ "actions": Object {
98
+ "success": [Function],
99
+ },
100
+ "path": "tribe_venue/21",
101
+ },
102
+ "type": "@@MT/COMMON/WP_REQUEST",
103
+ },
104
+ ]
105
+ `;
106
+
107
+ exports[`[STORE] - form thunk actions Maybe remove entry action without details 1`] = `Array []`;
108
+
109
+ exports[`[STORE] - form thunk actions Send the form action when creating 1`] = `
110
+ Array [
111
+ Object {
112
+ "meta": Object {
113
+ "actions": Object {
114
+ "error": [Function],
115
+ "start": [Function],
116
+ "success": [Function],
117
+ },
118
+ "params": Object {
119
+ "body": "{\\"title\\":\\"Modern Tribe\\"}",
120
+ "method": "POST",
121
+ },
122
+ "path": "tribe_organizer",
123
+ },
124
+ "type": "@@MT/COMMON/WP_REQUEST",
125
+ },
126
+ ]
127
+ `;
128
+
129
+ exports[`[STORE] - form thunk actions Send the form when editing 1`] = `
130
+ Array [
131
+ Object {
132
+ "meta": Object {
133
+ "actions": Object {
134
+ "error": [Function],
135
+ "start": [Function],
136
+ "success": [Function],
137
+ },
138
+ "params": Object {
139
+ "body": "{\\"title\\":\\"Tribe\\"}",
140
+ "method": "PUT",
141
+ },
142
+ "path": "tribe_venue/21",
143
+ },
144
+ "type": "@@MT/COMMON/WP_REQUEST",
145
+ },
146
+ ]
147
+ `;
common/src/modules/data/forms/__tests__/actions.test.js ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import configureStore from 'redux-mock-store';
5
+ import thunk from 'redux-thunk';
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { actions } from '@moderntribe/common/data/forms';
10
+
11
+ const middlewares = [ thunk ];
12
+ const mockStore = configureStore( middlewares );
13
+
14
+ describe( '[STORE] - Form actions', () => {
15
+ test( 'Register form action', () => {
16
+ expect( actions.registerForm( 20, 'tribe_organizers' ) ).toMatchSnapshot();
17
+ } );
18
+
19
+ test( 'Create draft entry', () => {
20
+ expect( actions.createDraft( 20, { title: 'Modern Tribe' } ) ).toMatchSnapshot();
21
+ } );
22
+
23
+ test( 'Edit the entry action', () => {
24
+ expect( actions.editEntry( 20, { title: 'Tribe' } ) ).toMatchSnapshot();
25
+ } );
26
+
27
+ test( 'Clear form action', () => {
28
+ expect( actions.clearForm( 20 ) ).toMatchSnapshot();
29
+ } );
30
+
31
+ test( 'Set submit form', () => {
32
+ expect( actions.setSubmit( 20 ) ).toMatchSnapshot();
33
+ } );
34
+
35
+ test( 'Set saving action', () => {
36
+ expect( actions.setSaving( 20, true ) ).toMatchSnapshot();
37
+ expect( actions.setSaving( 20, false ) ).toMatchSnapshot();
38
+ } );
39
+
40
+ test( 'Add volatile action', () => {
41
+ expect( actions.addVolatile( 20 ) ).toMatchSnapshot();
42
+ } );
43
+
44
+ test( 'Remove volatile action', () => {
45
+ expect( actions.removeVolatile( 20 ) ).toMatchSnapshot();
46
+ } );
47
+ } );
48
+
49
+ describe( '[STORE] - form thunk actions', () => {
50
+ let store = {};
51
+ beforeAll( () => {
52
+ store = mockStore( {
53
+ events: {
54
+ },
55
+ forms: {
56
+ byId: {
57
+ 20: {
58
+ create: true,
59
+ type: 'tribe_organizer',
60
+ fields: {},
61
+ },
62
+ 21: {
63
+ create: false,
64
+ type: 'tribe_venue',
65
+ fields: {
66
+ id: 21,
67
+ },
68
+ },
69
+ },
70
+ volatile: [],
71
+ },
72
+ } );
73
+ } );
74
+
75
+ afterEach( () => store.clearActions() );
76
+
77
+ test( 'Send the form action when creating', () => {
78
+ store.dispatch( actions.sendForm( 20, { title: 'Modern Tribe' } ) );
79
+ expect( store.getActions() ).toMatchSnapshot();
80
+ } );
81
+
82
+ test( 'Send the form when editing', () => {
83
+ store.dispatch( actions.sendForm( 21, { title: 'Tribe' } ) );
84
+ expect( store.getActions() ).toMatchSnapshot();
85
+ } );
86
+
87
+ test( 'Maybe remove entry action without details', () => {
88
+ store.dispatch( actions.maybeRemoveEntry( 20, {} ) );
89
+ expect( store.getActions() ).toMatchSnapshot();
90
+ } );
91
+
92
+ test( 'Maybe remove entry action with details', () => {
93
+ store.dispatch( actions.maybeRemoveEntry( 21, { id: 21, title: 'Modern Tribe' } ) );
94
+ expect( store.getActions() ).toMatchSnapshot();
95
+ } );
96
+ } );
common/src/modules/data/forms/__tests__/reducer.test.js ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import reducer, { actions } from '@moderntribe/common/data/forms';
5
+ import form, { DEFAULT_STATE } from '@moderntribe/common/data/forms/reducers/form';
6
+
7
+ jest.mock( '@moderntribe/common/data/forms/reducers/form', () => {
8
+ const original = require.requireActual( '@moderntribe/common/data/forms/reducers/form' );
9
+ return {
10
+ __esModule: true,
11
+ ...original,
12
+ default: jest.fn( ( state = original.DEFAULT_STATE ) => state ),
13
+ };
14
+ } );
15
+
16
+ jest.mock( '@moderntribe/common/data/forms/reducers/volatile', () => {
17
+ return {
18
+ __esModule: true,
19
+ default: jest.fn( ( state = [] ) => state ),
20
+ };
21
+ } );
22
+
23
+ describe( '[STORE] - form reducer', () => {
24
+ beforeEach( () => {
25
+ form.mockClear();
26
+ } );
27
+
28
+ it( 'Should return the default state', () => {
29
+ expect( reducer( undefined, {} ) ).toEqual( { byId: {}, volatile: [] } );
30
+ } );
31
+
32
+ it( 'Should add a new form', () => {
33
+ const state = {
34
+ byId: {},
35
+ volatile: [],
36
+ };
37
+ expect( reducer( state, actions.registerForm( 20, 'tribe_organizer' ) ) ).toEqual( {
38
+ byId: {
39
+ 20: DEFAULT_STATE,
40
+ },
41
+ volatile: [],
42
+ } );
43
+ } );
44
+
45
+ it( 'Should pass the actions to the child reducer when block not present', () => {
46
+ const groupAction = [
47
+ actions.registerForm( 10, 'tribe_venue' ),
48
+ actions.editEntry( 20, { title: 'Modern tribe' } ),
49
+ actions.createDraft( 20, { title: 'Tribe' } ),
50
+ actions.setSubmit( 20 ),
51
+ actions.clearForm( 20 ),
52
+ actions.setSaving( 20, true ),
53
+ ];
54
+
55
+ groupAction.forEach( ( action ) => {
56
+ reducer( {}, action );
57
+ expect( form ).toHaveBeenCalledWith( undefined, action );
58
+ expect( form ).toHaveBeenCalledTimes( 1 );
59
+ form.mockClear();
60
+ } );
61
+ } );
62
+
63
+ it( 'It should pass the block to the child reducer', () => {
64
+ const groupAction = [
65
+ actions.registerForm( 10, 'tribe_venue' ),
66
+ actions.editEntry( 20, { title: 'Modern tribe' } ),
67
+ actions.createDraft( 20, { title: 'Tribe' } ),
68
+ actions.setSubmit( 20 ),
69
+ actions.clearForm( 20 ),
70
+ actions.setSaving( 20, true ),
71
+ ];
72
+
73
+ const state = {
74
+ byId: {
75
+ 10: DEFAULT_STATE,
76
+ 20: DEFAULT_STATE,
77
+ },
78
+ };
79
+
80
+ groupAction.forEach( ( action ) => {
81
+ reducer( state, action );
82
+ expect( form ).toHaveBeenCalledWith( DEFAULT_STATE, action );
83
+ expect( form ).toHaveBeenCalledTimes( 1 );
84
+ form.mockClear();
85
+ } );
86
+ } );
87
+ } );
88
+
common/src/modules/data/forms/__tests__/selectors.test.js ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { selectors } from '@moderntribe/common/data/forms';
5
+ import { DEFAULT_STATE } from '@moderntribe/common/data/forms/reducers/form';
6
+
7
+ const state = {
8
+ events: {
9
+ },
10
+ forms: {
11
+ byId: {
12
+ 99: {
13
+ ...DEFAULT_STATE,
14
+ type: 'tribe_organizers',
15
+ create: true,
16
+ fields: {
17
+ name: 'Modern Tribe',
18
+ description: 'The Next Generation of Digital Agency',
19
+ },
20
+ },
21
+ 100: {
22
+ ...DEFAULT_STATE,
23
+ edit: true,
24
+ },
25
+ 101: {
26
+ ...DEFAULT_STATE,
27
+ submit: true,
28
+ },
29
+ 102: {
30
+ ...DEFAULT_STATE,
31
+ saving: true,
32
+ },
33
+ },
34
+ volatile: [ 100, 102 ],
35
+ },
36
+ };
37
+
38
+ describe( '[STORE] - Forms selectors', () => {
39
+ it( 'Should return the forms blocks', () => {
40
+ expect( selectors.formSelector( state, { name: 99 } ) ).toEqual( state.forms.byId[ '99' ] );
41
+ expect( selectors.formSelector( state, { name: 100 } ) ).toEqual( state.forms.byId[ '100' ] );
42
+ expect( selectors.formSelector( state, { name: 200 } ) ).toBe( undefined );
43
+ } );
44
+
45
+ it( 'Should return the form type', () => {
46
+ expect( selectors.getFormType( state, { name: 99 } ) ).toBe( 'tribe_organizers' );
47
+ expect( selectors.getFormType( state, { name: 100 } ) ).toBe( DEFAULT_STATE.type );
48
+ } );
49
+
50
+ it( 'Should return the edit value', () => {
51
+ expect( selectors.getFormEdit( state, { name: 100 } ) ).toBe( true );
52
+ expect( selectors.getFormEdit( state, { name: 99 } ) ).toBe( false );
53
+ expect( selectors.getFormEdit( state, { name: 101 } ) ).toBe( false );
54
+ expect( selectors.getFormEdit( state, { name: 102 } ) ).toBe( false );
55
+ } );
56
+
57
+ it( 'Should return the create value', () => {
58
+ expect( selectors.getFormCreate( state, { name: 99 } ) ).toBe( true );
59
+ expect( selectors.getFormCreate( state, { name: 100 } ) ).toBe( false );
60
+ expect( selectors.getFormCreate( state, { name: 101 } ) ).toBe( false );
61
+ expect( selectors.getFormCreate( state, { name: 102 } ) ).toBe( false );
62
+ } );
63
+
64
+ it( 'Should return the submit value', () => {
65
+ expect( selectors.getFormSubmit( state, { name: 101 } ) ).toBe( true );
66
+ expect( selectors.getFormSubmit( state, { name: 99 } ) ).toBe( false );
67
+ expect( selectors.getFormSubmit( state, { name: 100 } ) ).toBe( false );
68
+ expect( selectors.getFormSubmit( state, { name: 102 } ) ).toBe( false );
69
+ } );
70
+
71
+ it( 'Should return the saving value', () => {
72
+ expect( selectors.getFormSaving( state, { name: 102 } ) ).toBe( true );
73
+ expect( selectors.getFormSaving( state, { name: 99 } ) ).toBe( false );
74
+ expect( selectors.getFormSaving( state, { name: 100 } ) ).toBe( false );
75
+ expect( selectors.getFormSaving( state, { name: 101 } ) ).toBe( false );
76
+ } );
77
+
78
+ it( 'Should return the form fields', () => {
79
+ expect( selectors.getFormFields( state, { name: 99 } ) )
80
+ .toEqual( state.forms.byId[ '99' ].fields );
81
+ expect( selectors.getFormFields( state, { name: 100 } ) ).toEqual( {} );
82
+ expect( selectors.getFormFields( state, { name: 101 } ) ).toEqual( {} );
83
+ expect( selectors.getFormFields( state, { name: 102 } ) ).toEqual( {} );
84
+ } );
85
+
86
+ it( 'Should return the volatile fields', () => {
87
+ expect( selectors.getVolatile( state ) ).toEqual( [ 100, 102 ] );
88
+ } );
89
+ } );
common/src/modules/data/forms/__tests__/types.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { types } from '@moderntribe/common/data/forms';
5
+ import { PREFIX_COMMON_STORE } from '@moderntribe/common/data/utils';
6
+
7
+ describe( '[STORE] - Form types', () => {
8
+ it( 'Should return the types values', () => {
9
+ expect( types.REMOVE_VOLATILE_ID ).toBe( `${ PREFIX_COMMON_STORE }/REMOVE_VOLATILE_ID` );
10
+ expect( types.ADD_VOLATILE_ID ).toBe( `${ PREFIX_COMMON_STORE }/ADD_VOLATILE_ID` );
11
+ expect( types.ADD_FORM ).toBe( `${ PREFIX_COMMON_STORE }/ADD_FORM` );
12
+ expect( types.SET_SAVING_FORM ).toBe( `${ PREFIX_COMMON_STORE }/SET_SAVING_FORM` );
13
+ expect( types.SUBMIT_FORM ).toBe( `${ PREFIX_COMMON_STORE }/SUBMIT_FORM` );
14
+ expect( types.CREATE_FORM_DRAFT ).toBe( `${ PREFIX_COMMON_STORE }/CREATE_FORM_DRAFT` );
15
+ expect( types.EDIT_FORM_ENTRY ).toBe( `${ PREFIX_COMMON_STORE }/EDIT_FORM_ENTRY` );
16
+ expect( types.SET_FORM_FIELDS ).toBe( `${ PREFIX_COMMON_STORE }/SET_FORM_FIELDS` );
17
+ expect( types.CLEAR_FORM ).toBe( `${ PREFIX_COMMON_STORE }/CLEAR_FORM` );
18
+ } );
19
+ } );
common/src/modules/data/forms/actions.js ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { isEmpty, get } from 'lodash';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { actions as requestActions } from '@moderntribe/common/store/middlewares/request';
10
+
11
+ import * as types from './types';
12
+ import * as selectors from './selectors';
13
+
14
+ export const registerForm = ( id, type ) => ( {
15
+ type: types.ADD_FORM,
16
+ payload: {
17
+ id,
18
+ type,
19
+ },
20
+ } );
21
+
22
+ export const clearForm = ( id ) => ( {
23
+ type: types.CLEAR_FORM,
24
+ payload: {
25
+ id,
26
+ },
27
+ } );
28
+
29
+ export const createDraft = ( id, fields ) => ( {
30
+ type: types.CREATE_FORM_DRAFT,
31
+ payload: {
32
+ id,
33
+ fields,
34
+ },
35
+ } );
36
+
37
+ export const editEntry = ( id, fields ) => ( {
38
+ type: types.EDIT_FORM_ENTRY,
39
+ payload: {
40
+ id,
41
+ fields,
42
+ },
43
+ } );
44
+
45
+ export const setSubmit = ( id ) => ( {
46
+ type: types.SUBMIT_FORM,
47
+ payload: {
48
+ id,
49
+ },
50
+ } );
51
+
52
+ export const setSaving = ( id, saving ) => ( {
53
+ type: types.SET_SAVING_FORM,
54
+ payload: {
55
+ id,
56
+ saving,
57
+ },
58
+ } );
59
+
60
+ export const addVolatile = ( id ) => ( {
61
+ type: types.ADD_VOLATILE_ID,
62
+ payload: {
63
+ id,
64
+ },
65
+ } );
66
+
67
+ export const removeVolatile = ( id ) => ( {
68
+ type: types.REMOVE_VOLATILE_ID,
69
+ payload: {
70
+ id,
71
+ },
72
+ } );
73
+
74
+ export const sendForm = ( id, fields = {}, completed ) => ( dispatch, getState ) => {
75
+ const state = getState();
76
+ const props = { name: id };
77
+ const type = selectors.getFormType( state, props );
78
+ const create = selectors.getFormCreate( state, props );
79
+ const details = selectors.getFormFields( state, props );
80
+ const saving = selectors.getFormSaving( state, props );
81
+
82
+ if ( saving ) {
83
+ return;
84
+ }
85
+
86
+ const path = create
87
+ ? `${ type }`
88
+ : `${ type }/${ details.id }`;
89
+
90
+ const options = {
91
+ path,
92
+ params: {
93
+ method: create ? 'POST' : 'PUT',
94
+ body: JSON.stringify( fields ),
95
+ },
96
+ actions: {
97
+ start: () => dispatch( setSaving( id, true ) ),
98
+ success: ( { body } ) => {
99
+ const postID = get( body, 'id', '' );
100
+
101
+ if ( create && postID ) {
102
+ dispatch( addVolatile( postID ) );
103
+ }
104
+ completed( body );
105
+ dispatch( clearForm( id ) );
106
+ dispatch( setSaving( id, false ) );
107
+ },
108
+ error: () => {
109
+ dispatch( clearForm( id ) );
110
+ dispatch( setSaving( id, false ) );
111
+ },
112
+ },
113
+ };
114
+ dispatch( requestActions.wpRequest( options ) );
115
+ };
116
+
117
+ const deleteEntry = ( dispatch ) => ( path ) => ( { body } ) => {
118
+ const { id, status } = body;
119
+
120
+ if ( 'draft' !== status ) {
121
+ dispatch( removeVolatile( id ) );
122
+ return;
123
+ }
124
+
125
+ const options = {
126
+ path,
127
+ params: {
128
+ method: 'DELETE',
129
+ },
130
+ actions: {
131
+ success: () => dispatch( removeVolatile( id ) ),
132
+ },
133
+ };
134
+ dispatch( requestActions.wpRequest( options ) );
135
+ };
136
+
137
+ export const maybeRemoveEntry = ( id, details = {} ) => ( dispatch, getState ) => {
138
+ const state = getState();
139
+ const type = selectors.getFormType( state, { name: id } );
140
+
141
+ if ( isEmpty( details ) ) {
142
+ return;
143
+ }
144
+
145
+ const path = `${ type }/${ details.id }`;
146
+ const options = {
147
+ path,
148
+ actions: {
149
+ success: deleteEntry( dispatch )( path ),
150
+ },
151
+ };
152
+ dispatch( requestActions.wpRequest( options ) );
153
+ };
common/src/modules/data/forms/index.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import reducer from './reducer';
5
+
6
+ import * as types from './types';
7
+ import * as actions from './actions';
8
+ import * as selectors from './selectors';
9
+
10
+ export default reducer;
11
+
12
+ export { types, actions, selectors };
common/src/modules/data/forms/reducer.js ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { combineReducers } from 'redux';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import * as types from './types';
10
+ import { form, volatile } from './reducers';
11
+
12
+ const byId = ( state = {}, action ) => {
13
+ switch ( action.type ) {
14
+ case types.ADD_FORM:
15
+ case types.CLEAR_FORM:
16
+ case types.SET_FORM_FIELDS:
17
+ case types.CREATE_FORM_DRAFT:
18
+ case types.EDIT_FORM_ENTRY:
19
+ case types.SUBMIT_FORM:
20
+ case types.SET_SAVING_FORM:
21
+ return {
22
+ ...state,
23
+ [ action.payload.id ]: form( state[ action.payload.id ], action ),
24
+ };
25
+ default:
26
+ return state;
27
+ }
28
+ };
29
+
30
+ export default combineReducers( {
31
+ byId,
32
+ volatile,
33
+ } );
common/src/modules/data/forms/reducers/__tests__/__snapshots__/form.test.js.snap ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`[STORE] - form reducer Should add a new form 1`] = `
4
+ Object {
5
+ "create": false,
6
+ "edit": false,
7
+ "fields": Object {},
8
+ "saving": false,
9
+ "submit": false,
10
+ "type": "tribe_organizers",
11
+ }
12
+ `;
13
+
14
+ exports[`[STORE] - form reducer Should clear a form 1`] = `
15
+ Object {
16
+ "create": false,
17
+ "edit": false,
18
+ "fields": Object {},
19
+ "saving": false,
20
+ "submit": false,
21
+ "type": "tribe_organizers",
22
+ }
23
+ `;
24
+
25
+ exports[`[STORE] - form reducer Should create a form draft 1`] = `
26
+ Object {
27
+ "create": true,
28
+ "edit": false,
29
+ "fields": Object {
30
+ "id": 20,
31
+ "title": "Modern Tribe",
32
+ },
33
+ "saving": false,
34
+ "submit": false,
35
+ "type": "tribe_events",
36
+ }
37
+ `;
38
+
39
+ exports[`[STORE] - form reducer Should edit the form entry 1`] = `
40
+ Object {
41
+ "create": false,
42
+ "edit": true,
43
+ "fields": Object {
44
+ "description": "",
45
+ "title": "Tribe",
46
+ },
47
+ "saving": false,
48
+ "submit": false,
49
+ "type": "tribe_events",
50
+ }
51
+ `;
52
+
53
+ exports[`[STORE] - form reducer Should submit the form 1`] = `
54
+ Object {
55
+ "create": false,
56
+ "edit": false,
57
+ "fields": Object {},
58
+ "saving": false,
59
+ "submit": true,
60
+ "type": "tribe_events",
61
+ }
62
+ `;
63
+
64
+ exports[`[STORE] - form reducer Should toggle the saving form flag 1`] = `
65
+ Object {
66
+ "create": false,
67
+ "edit": false,
68
+ "fields": Object {},
69
+ "saving": true,
70
+ "submit": false,
71
+ "type": "tribe_events",
72
+ }
73
+ `;
74
+
75
+ exports[`[STORE] - form reducer Should toggle the saving form flag 2`] = `
76
+ Object {
77
+ "create": false,
78
+ "edit": false,
79
+ "fields": Object {},
80
+ "saving": false,
81
+ "submit": false,
82
+ "type": "tribe_events",
83
+ }
84
+ `;
common/src/modules/data/forms/reducers/__tests__/form.test.js ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { form } from '@moderntribe/common/data/forms/reducers';
5
+ import { actions } from '@moderntribe/common/data/forms';
6
+ import { DEFAULT_STATE } from '@moderntribe/common/data/forms/reducers/form';
7
+
8
+ describe( '[STORE] - form reducer', () => {
9
+ it( 'Should return the default state', () => {
10
+ expect( form( undefined, {} ) ).toEqual( DEFAULT_STATE );
11
+ } );
12
+
13
+ it( 'Should add a new form', () => {
14
+ expect( form( DEFAULT_STATE, actions.registerForm( 20, 'tribe_organizers' ) ) ).toMatchSnapshot();
15
+ } );
16
+
17
+ it( 'Should clear a form', () => {
18
+ const state = {
19
+ ...DEFAULT_STATE,
20
+ type: 'tribe_organizers',
21
+ };
22
+ expect( form( state, actions.clearForm( 20 ) ) ).toMatchSnapshot();
23
+ } );
24
+
25
+ it( 'Should create a form draft', () => {
26
+ const fields = {
27
+ id: 20,
28
+ title: 'Modern Tribe',
29
+ };
30
+ expect( form( DEFAULT_STATE, actions.createDraft( 20, fields ) ) ).toMatchSnapshot();
31
+ } );
32
+
33
+ it( 'Should toggle the saving form flag', () => {
34
+ expect( form( DEFAULT_STATE, actions.setSaving( 20, true ) ) ).toMatchSnapshot();
35
+ expect( form( DEFAULT_STATE, actions.setSaving( 20, false ) ) ).toMatchSnapshot();
36
+ } );
37
+
38
+ it( 'Should edit the form entry', () => {
39
+ const fields = {
40
+ title: 'Tribe',
41
+ description: '',
42
+ };
43
+ expect( form( DEFAULT_STATE, actions.editEntry( 20, fields ) ) ).toMatchSnapshot();
44
+ } );
45
+
46
+ it( 'Should submit the form', () => {
47
+ expect( form( DEFAULT_STATE, actions.setSubmit( 20 ) ) ).toMatchSnapshot();
48
+ } );
49
+ } );
common/src/modules/data/forms/reducers/__tests__/volatile.test.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { volatile } from '@moderntribe/common/data/forms/reducers';
5
+ import { actions } from '@moderntribe/common/data/forms';
6
+
7
+ describe( '[STORE] - Volatile reducer', () => {
8
+ it( 'Should return the default state', () => {
9
+ expect( volatile( undefined, {} ) ).toEqual( [] );
10
+ } );
11
+
12
+ it( 'Should add a new volatile value', () => {
13
+ expect( volatile( [], actions.addVolatile( 20 ) ) ).toEqual( [ 20 ] );
14
+ expect( volatile( [ 20 ], actions.addVolatile( 10 ) ) ).toEqual( [ 20, 10 ] );
15
+ } );
16
+
17
+ it( 'Should remove a volatile value', () => {
18
+ expect( volatile( [], actions.removeVolatile( 20 ) ) ).toEqual( [] );
19
+ expect( volatile( [ 20, 10 ], actions.removeVolatile( 20 ) ) ).toEqual( [ 10 ] );
20
+ expect( volatile( [ 20, 10 ], actions.removeVolatile( 10 ) ) ).toEqual( [ 20 ] );
21
+ expect( volatile( [ 10 ], actions.removeVolatile( 10 ) ) ).toEqual( [] );
22
+ } );
23
+ } );
common/src/modules/data/forms/reducers/form.js ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { EVENT } from '@moderntribe/common/data/editor/post-types';
5
+ import * as types from '@moderntribe/common/data/forms/types';
6
+
7
+ export const DEFAULT_STATE = {
8
+ edit: false,
9
+ create: false,
10
+ submit: false,
11
+ saving: false,
12
+ fields: {},
13
+ type: EVENT,
14
+ };
15
+
16
+ export default ( state = DEFAULT_STATE, action ) => {
17
+ switch ( action.type ) {
18
+ case types.ADD_FORM:
19
+ return {
20
+ ...state,
21
+ type: action.payload.type,
22
+ };
23
+ case types.CLEAR_FORM:
24
+ return {
25
+ ...state,
26
+ ...DEFAULT_STATE,
27
+ type: state.type,
28
+ };
29
+ case types.CREATE_FORM_DRAFT:
30
+ return {
31
+ ...state,
32
+ submit: false,
33
+ edit: false,
34
+ create: true,
35
+ fields: action.payload.fields,
36
+ };
37
+ case types.SET_SAVING_FORM:
38
+ return {
39
+ ...state,
40
+ saving: action.payload.saving,
41
+ };
42
+ case types.EDIT_FORM_ENTRY:
43
+ return {
44
+ ...state,
45
+ create: false,
46
+ submit: false,
47
+ edit: true,
48
+ fields: action.payload.fields,
49
+ };
50
+ case types.SUBMIT_FORM:
51
+ return {
52
+ ...state,
53
+ submit: true,
54
+ };
55
+ default:
56
+ return state;
57
+ }
58
+ };
common/src/modules/data/forms/reducers/index.js ADDED
@@ -0,0 +1,2 @@
 
 
1
+ export { default as form } from './form';
2
+ export { default as volatile } from './volatile';
common/src/modules/data/forms/reducers/volatile.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import * as types from '@moderntribe/common/data/forms/types';
5
+
6
+ export default ( state = [], action ) => {
7
+ switch ( action.type ) {
8
+ case types.ADD_VOLATILE_ID:
9
+ return [ ...state, action.payload.id ];
10
+ case types.REMOVE_VOLATILE_ID:
11
+ return state.filter( ( id ) => id !== action.payload.id );
12
+ default:
13
+ return state;
14
+ }
15
+ };
common/src/modules/data/forms/selectors.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { createSelector } from 'reselect';
5
+ import { DEFAULT_STATE } from './reducers/form';
6
+
7
+ export const formSelector = ( state, props ) => state.forms.byId[ props.name ];
8
+
9
+ export const getFormType = createSelector(
10
+ [ formSelector ],
11
+ ( block ) => block ? block.type : DEFAULT_STATE.type
12
+ );
13
+
14
+ export const getFormEdit = createSelector(
15
+ [ formSelector ],
16
+ ( block ) => block ? block.edit : DEFAULT_STATE.edit
17
+ );
18
+
19
+ export const getFormCreate = createSelector(
20
+ [ formSelector ],
21
+ ( block ) => block ? block.create : DEFAULT_STATE.create,
22
+ );
23
+
24
+ export const getFormSubmit = createSelector(
25
+ [ formSelector ],
26
+ ( block ) => block ? block.submit : DEFAULT_STATE.submit
27
+ );
28
+
29
+ export const getFormFields = createSelector(
30
+ [ formSelector ],
31
+ ( block ) => block ? block.fields : DEFAULT_STATE.fields,
32
+ );
33
+
34
+ export const getFormSaving = createSelector(
35
+ [ formSelector ],
36
+ ( block ) => block ? block.saving : DEFAULT_STATE.saving
37
+ );
38
+
39
+ export const getVolatile = ( state ) => state.forms.volatile;
common/src/modules/data/forms/types.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { PREFIX_COMMON_STORE } from '@moderntribe/common/data/utils';
5
+
6
+ export const ADD_FORM = `${ PREFIX_COMMON_STORE }/ADD_FORM`;
7
+ export const SET_FORM_FIELDS = `${ PREFIX_COMMON_STORE }/SET_FORM_FIELDS`;
8
+ export const CREATE_FORM_DRAFT = `${ PREFIX_COMMON_STORE }/CREATE_FORM_DRAFT`;
9
+ export const EDIT_FORM_ENTRY = `${ PREFIX_COMMON_STORE }/EDIT_FORM_ENTRY`;
10
+ export const SUBMIT_FORM = `${ PREFIX_COMMON_STORE }/SUBMIT_FORM`;
11
+ export const CLEAR_FORM = `${ PREFIX_COMMON_STORE }/CLEAR_FORM`;
12
+ export const SET_SAVING_FORM = `${ PREFIX_COMMON_STORE }/SET_SAVING_FORM`;
13
+
14
+ export const ADD_VOLATILE_ID = `${ PREFIX_COMMON_STORE }/ADD_VOLATILE_ID`;
15
+ export const REMOVE_VOLATILE_ID = `${ PREFIX_COMMON_STORE }/REMOVE_VOLATILE_ID`;
common/src/modules/data/index.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal Dependencies
3
+ */
4
+ import * as editor from './editor/post-types';
5
+ import * as forms from './forms';
6
+ import * as plugins from './plugins';
7
+ export { default } from './reducers';
8
+
9
+ export { editor, forms, plugins };
common/src/modules/data/plugins/__tests__/__snapshots__/actions.test.js.snap ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Plugin actions Add Plugin 1`] = `
4
+ Object {
5
+ "payload": Object {
6
+ "name": "events",
7
+ },
8
+ "type": "@@MT/COMMON/ADD_PLUGIN",
9
+ }
10
+ `;
11
+
12
+ exports[`Plugin actions Remove Plugin 1`] = `
13
+ Object {
14
+ "payload": Object {
15
+ "name": "events",
16
+ },
17
+ "type": "@@MT/COMMON/REMOVE_PLUGIN",
18
+ }
19
+ `;
common/src/modules/data/plugins/__tests__/actions.test.js ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { actions } from '@moderntribe/common/data/plugins';
5
+
6
+ describe( 'Plugin actions', () => {
7
+ test( 'Add Plugin', () => {
8
+ expect( actions.addPlugin( 'events' ) ).toMatchSnapshot();
9
+ } );
10
+
11
+ test( 'Remove Plugin', () => {
12
+ expect( actions.removePlugin( 'events' ) ).toMatchSnapshot();
13
+ } );
14
+ } );
common/src/modules/data/plugins/__tests__/reducer.test.js ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import reducer, { actions } from '@moderntribe/common/data/plugins';
5
+
6
+ describe( 'Plugins reducer', () => {
7
+ it( 'Should return the default state', () => {
8
+ expect( reducer( undefined, {} ) ).toEqual( [] );
9
+ } );
10
+
11
+ it( 'Should add a new plugin as active', () => {
12
+ expect( reducer( {}, actions.addPlugin( 'events' ) ) ).toEqual( [ 'events' ] );
13
+ } );
14
+
15
+ it( 'Should avoid adding duplicates entries', () => {
16
+ const state = reducer( {}, actions.addPlugin( 'events' ) );
17
+ expect( reducer( state, actions.addPlugin( 'events' ) ) ).toEqual( [ 'events' ] );
18
+ } );
19
+
20
+ it( 'Should remove the plugin from the reducer', () => {
21
+ const state = reducer( {}, actions.addPlugin( 'events' ) );
22
+ expect( state ).toEqual( [ 'events' ] );
23
+ expect( reducer( state, actions.removePlugin( 'events' ) ) ).toEqual( [] );
24
+ } );
25
+ } );
common/src/modules/data/plugins/__tests__/selectors.test.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { selectors } from '@moderntribe/common/data/plugins';
5
+
6
+ describe( 'Plugin selectors', () => {
7
+ let state;
8
+
9
+ beforeEach( () => {
10
+ state = {
11
+ plugins: [ 'events' ],
12
+ };
13
+ } );
14
+
15
+ it( 'should have plugin', () => {
16
+ expect( selectors.hasPlugin( state, 'events' ) ).toEqual( true );
17
+ expect( selectors.hasPlugin( state )( 'events' ) ).toEqual( true );
18
+ } );
19
+ it( 'should not have plugin', () => {
20
+ expect( selectors.hasPlugin( state, 'events-pro' ) ).toEqual( false );
21
+ expect( selectors.hasPlugin( state )( 'events-pro' ) ).toEqual( false );
22
+ } );
23
+ } );
common/src/modules/data/plugins/__tests__/types.test.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { PREFIX_COMMON_STORE } from '@moderntribe/common/data/utils';
5
+ import { types } from '@moderntribe/common/data/plugins';
6
+
7
+ describe( 'Plugin types', () => {
8
+ it( 'Should return the types with a prefix', () => {
9
+ expect( types.ADD_PLUGIN ).toBe( `${ PREFIX_COMMON_STORE }/ADD_PLUGIN` );
10
+ expect( types.REMOVE_PLUGIN ).toBe( `${ PREFIX_COMMON_STORE }/REMOVE_PLUGIN` );
11
+ } );
12
+ } );
common/src/modules/data/plugins/actions.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import * as types from './types';
5
+
6
+ export const addPlugin = ( name ) => ( {
7
+ type: types.ADD_PLUGIN,
8
+ payload: {
9
+ name,
10
+ },
11
+ } );
12
+
13
+ export const removePlugin = ( name ) => ( {
14
+ type: types.REMOVE_PLUGIN,
15
+ payload: {
16
+ name,
17
+ },
18
+ } );
common/src/modules/data/plugins/constants.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ export const EVENTS_PLUGIN = 'events';
2
+ export const EVENTS_PRO_PLUGIN = 'events-pro';
3
+ export const TICKETS = 'tickets';
4
+ export const TICKETS_PLUS = 'tickets-plus';
common/src/modules/data/plugins/index.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import reducer from './reducer';
5
+ import * as types from './types';
6
+ import * as actions from './actions';
7
+ import * as selectors from './selectors';
8
+ import * as constants from './constants';
9
+ import * as proptypes from './proptypes';
10
+
11
+ export default reducer;
12
+ export { types, actions, selectors, constants, proptypes };
common/src/modules/data/plugins/proptypes.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External Dependencies
3
+ */
4
+ import PropTypes from 'prop-types';
5
+
6
+ //
7
+ // ─── VENDOR ─────────────────────────────────────────────────────────────────────
8
+ //
9
+
10
+ export const ReactSelectOption = PropTypes.shape( {
11
+ label: PropTypes.string.isRequired,
12
+ value: PropTypes.any.isRequired,
13
+ } );
14
+
15
+ export const ReactSelectOptions = PropTypes.arrayOf( ReactSelectOption );
common/src/modules/data/plugins/reducer.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { uniq } from 'lodash';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { types } from '@moderntribe/common/data/plugins';
10
+
11
+ export default ( state = [], action ) => {
12
+ switch ( action.type ) {
13
+ case types.ADD_PLUGIN:
14
+ return uniq( [ ...state, action.payload.name ] );
15
+ case types.REMOVE_PLUGIN:
16
+ return [ ...state ].filter( ( pluginName ) => pluginName !== action.payload.name );
17
+ default:
18
+ return state;
19
+ }
20
+ };
common/src/modules/data/plugins/selectors.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { includes } from 'lodash';
5
+ import { curry } from 'lodash/fp';
6
+
7
+ export const getPlugins = ( state ) => state.plugins;
8
+
9
+ export const hasPlugin = curry( ( state, plugin ) => includes( getPlugins( state ), plugin ) );
common/src/modules/data/plugins/types.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { PREFIX_COMMON_STORE } from '@moderntribe/common/data/utils';
5
+
6
+ export const ADD_PLUGIN = `${ PREFIX_COMMON_STORE }/ADD_PLUGIN`;
7
+ export const REMOVE_PLUGIN = `${ PREFIX_COMMON_STORE }/REMOVE_PLUGIN`;
common/src/modules/data/reducers.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { setupCreateReducer } from '@nfen/redux-reducer-injector';
5
+ import forms from './forms';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import plugins from './plugins';
11
+
12
+ export default setupCreateReducer( {
13
+ plugins,
14
+ forms,
15
+ } );
common/src/modules/data/utils.js ADDED
@@ -0,0 +1 @@
 
1
+ export const PREFIX_COMMON_STORE = '@@MT/COMMON';
common/src/modules/elements/accordion/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Accordion Element renders an accordion 1`] = `
4
+ <div
5
+ aria-multiselectable="true"
6
+ className="tribe-editor__accordion"
7
+ role="tablist"
8
+ >
9
+ <article
10
+ className="tribe-editor__accordion__row"
11
+ >
12
+ <button
13
+ aria-controls="accordion-content-123"
14
+ aria-expanded="false"
15
+ aria-selected="false"
16
+ className="tribe-editor__button tribe-editor__accordion__row-header header-class"
17
+ id="accordion-header-123"
18
+ onClick={[Function]}
19
+ role="tab"
20
+ type="button"
21
+ >
22
+ this is header
23
+ </button>
24
+ <div
25
+ aria-hidden="true"
26
+ aria-labelledby="accordion-header-123"
27
+ className="tribe-editor__accordion__row-content content-class"
28
+ id="accordion-content-123"
29
+ role="tabpanel"
30
+ >
31
+ this is a content
32
+ </div>
33
+ </article>
34
+ </div>
35
+ `;
36
+
37
+ exports[`Accordion Element renders an accordion with wrapper class 1`] = `
38
+ <div
39
+ aria-multiselectable="true"
40
+ className="tribe-editor__accordion test-class"
41
+ role="tablist"
42
+ >
43
+ <article
44
+ className="tribe-editor__accordion__row"
45
+ >
46
+ <button
47
+ aria-controls="accordion-content-123"
48
+ aria-expanded="false"
49
+ aria-selected="false"
50
+ className="tribe-editor__button tribe-editor__accordion__row-header header-class"
51
+ id="accordion-header-123"
52
+ onClick={[Function]}
53
+ role="tab"
54
+ type="button"
55
+ >
56
+ this is header
57
+ </button>
58
+ <div
59
+ aria-hidden="true"
60
+ aria-labelledby="accordion-header-123"
61
+ className="tribe-editor__accordion__row-content content-class"
62
+ id="accordion-content-123"
63
+ role="tabpanel"
64
+ >
65
+ this is a content
66
+ </div>
67
+ </article>
68
+ </div>
69
+ `;
70
+
71
+ exports[`Accordion Element renders null 1`] = `null`;
common/src/modules/elements/accordion/__tests__/element.test.js ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import renderer from 'react-test-renderer';
6
+ import { noop } from 'lodash';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { Accordion } from '@moderntribe/common/elements';
12
+
13
+ let rows;
14
+
15
+ describe( 'Accordion Element', () => {
16
+ beforeEach( () => {
17
+ rows = [
18
+ {
19
+ accordionId: '123',
20
+ content: 'this is a content',
21
+ contentClassName: 'content-class',
22
+ header: 'this is header',
23
+ headerClassName: 'header-class',
24
+ onClick: noop,
25
+ onClose: noop,
26
+ onOpen: noop,
27
+ },
28
+ ];
29
+ } );
30
+
31
+ it( 'renders null', () => {
32
+ const component = renderer.create( <Accordion /> );
33
+ expect( component.toJSON() ).toMatchSnapshot();
34
+ } );
35
+
36
+ it( 'renders an accordion', () => {
37
+ const component = renderer.create( <Accordion rows={ rows } /> );
38
+ expect( component.toJSON() ).toMatchSnapshot();
39
+ } );
40
+
41
+ it( 'renders an accordion with wrapper class', () => {
42
+ const component = renderer.create( <Accordion className="test-class" rows={ rows } /> );
43
+ expect( component.toJSON() ).toMatchSnapshot();
44
+ } );
45
+ } );
common/src/modules/elements/accordion/element.js ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import Row from './row/template';
12
+ import './style.pcss';
13
+
14
+ const Accordion = ( {
15
+ className,
16
+ containerAttrs,
17
+ rows,
18
+ } ) => (
19
+ rows.length
20
+ ? (
21
+ <div
22
+ aria-multiselectable="true"
23
+ className={ classNames(
24
+ 'tribe-editor__accordion',
25
+ className,
26
+ ) }
27
+ role="tablist"
28
+ { ...containerAttrs }
29
+ >
30
+ { rows.map( ( row, index ) => (
31
+ <Row key={ index } { ...row } />
32
+ ) ) }
33
+ </div>
34
+ )
35
+ : null
36
+ );
37
+
38
+ Accordion.defaultProps = {
39
+ containerAttrs: {},
40
+ rows: [],
41
+ };
42
+
43
+ Accordion.propTypes = {
44
+ className: PropTypes.string,
45
+ containerAttrs: PropTypes.object,
46
+ rows: PropTypes.arrayOf( PropTypes.shape( {
47
+ accordionId: PropTypes.string.isRequired,
48
+ content: PropTypes.node,
49
+ contentClassName: PropTypes.string,
50
+ header: PropTypes.node,
51
+ headerClassName: PropTypes.string,
52
+ onClick: PropTypes.func,
53
+ onClose: PropTypes.func,
54
+ onOpen: PropTypes.func,
55
+ } ).isRequired ).isRequired,
56
+ };
57
+
58
+ export default Accordion;
common/src/modules/elements/accordion/row/__tests__/__snapshots__/template.test.js.snap ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Accordion Row Element renders an accordion row 1`] = `
4
+ <article
5
+ className="tribe-editor__accordion__row"
6
+ >
7
+ <button
8
+ aria-controls="accordion-content-123"
9
+ aria-expanded="false"
10
+ aria-selected="false"
11
+ className="tribe-editor__button tribe-editor__accordion__row-header header-class"
12
+ data-attr="header-attr-value"
13
+ id="accordion-header-123"
14
+ onClick={[Function]}
15
+ role="tab"
16
+ type="button"
17
+ >
18
+ this is header
19
+ </button>
20
+ <div
21
+ aria-hidden="true"
22
+ aria-labelledby="accordion-header-123"
23
+ className="tribe-editor__accordion__row-content content-class"
24
+ data-attr="content-attr-value"
25
+ id="accordion-content-123"
26
+ role="tabpanel"
27
+ >
28
+ this is a content
29
+ </div>
30
+ </article>
31
+ `;
common/src/modules/elements/accordion/row/__tests__/template.test.js ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import Row from '@moderntribe/common/elements/accordion/row/template';
10
+
11
+ let row;
12
+
13
+ describe( 'Accordion Row Element', () => {
14
+ beforeEach( () => {
15
+ row = {
16
+ accordionId: '123',
17
+ content: 'this is a content',
18
+ contentAttrs: { 'data-attr': 'content-attr-value' },
19
+ contentClassName: 'content-class',
20
+ header: 'this is header',
21
+ headerAttrs: { 'data-attr': 'header-attr-value' },
22
+ headerClassName: 'header-class',
23
+ onClick: jest.fn(),
24
+ onClose: jest.fn(),
25
+ onOpen: jest.fn(),
26
+ };
27
+ } );
28
+
29
+ it( 'renders an accordion row', () => {
30
+ const component = renderer.create( <Row { ...row } /> );
31
+ expect( component.toJSON() ).toMatchSnapshot();
32
+ } );
33
+
34
+ it( 'executes onClick handler', () => {
35
+ const component = mount( <Row { ...row } /> );
36
+ component.find( 'button' ).simulate( 'click' );
37
+ expect( row.onClick ).toHaveBeenCalled();
38
+ expect( row.onClick ).toHaveBeenCalledTimes( 1 );
39
+ } );
40
+
41
+ it( 'executes onOpen and onClose handlers', async () => {
42
+ const component = mount( <Row { ...row } /> );
43
+ component.find( 'button' ).simulate( 'click' );
44
+ await setTimeout( () => {
45
+ component.find( 'button' ).simulate( 'click' );
46
+ setTimeout( () => {
47
+ expect( row.onOpen ).toHaveBeenCalled();
48
+ expect( row.onOpen ).toHaveBeenCalledTimes( 1 );
49
+ expect( row.onClose ).toHaveBeenCalled();
50
+ expect( row.onClose ).toHaveBeenCalledTimes( 1 );
51
+ expect( row.onClick ).toHaveBeenCalled();
52
+ expect( row.onClick ).toHaveBeenCalledTimes( 2 );
53
+ }, 250 );
54
+ }, 250 );
55
+ } );
56
+ } );
common/src/modules/elements/accordion/row/template.js ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { PureComponent } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import Button from '@moderntribe/common/elements/button/element';
12
+ import { slide } from '@moderntribe/common/utils';
13
+
14
+ class Row extends PureComponent {
15
+ static propTypes = {
16
+ accordionId: PropTypes.string.isRequired,
17
+ content: PropTypes.node,
18
+ contentAttrs: PropTypes.object,
19
+ contentClassName: PropTypes.string,
20
+ header: PropTypes.node,
21
+ headerAttrs: PropTypes.object,
22
+ headerClassName: PropTypes.string,
23
+ onClick: PropTypes.func,
24
+ onClose: PropTypes.func,
25
+ onOpen: PropTypes.func,
26
+ };
27
+
28
+ static defaultProps = {
29
+ contentAttrs: {},
30
+ headerAttrs: {},
31
+ };
32
+
33
+ constructor( props ) {
34
+ super( props );
35
+ this.state = {
36
+ isActive: false,
37
+ };
38
+ this.headerId = `accordion-header-${ this.props.accordionId }`;
39
+ this.contentId = `accordion-content-${ this.props.accordionId }`;
40
+ }
41
+
42
+ getHeaderAttrs = () => {
43
+ const _isActive = this.state.isActive ? 'true' : 'false';
44
+ return {
45
+ 'aria-controls': this.contentId,
46
+ 'aria-expanded': _isActive,
47
+ 'aria-selected': _isActive,
48
+ id: this.headerId,
49
+ role: 'tab',
50
+ ...this.props.headerAttrs,
51
+ };
52
+ };
53
+
54
+ getContentAttrs = () => ( {
55
+ 'aria-hidden': this.state.isActive ? 'false' : 'true',
56
+ 'aria-labelledby': this.headerId,
57
+ id: this.contentId,
58
+ role: 'tabpanel',
59
+ ...this.props.contentAttrs,
60
+ } );
61
+
62
+ onClose = ( parent, e ) => () => {
63
+ parent.classList.remove( 'closing' );
64
+ parent.classList.add( 'closed' );
65
+ this.props.onClose && this.props.onClose( e );
66
+ };
67
+
68
+ onOpen = ( parent, e ) => () => {
69
+ parent.classList.remove( 'opening' );
70
+ parent.classList.add( 'open' );
71
+ this.props.onOpen && this.props.onOpen( e );
72
+ };
73
+
74
+ onClick = ( e ) => {
75
+ const { contentId, onClick } = this.props;
76
+ const parent = e.currentTarget.parentNode;
77
+ const content = e.currentTarget.nextElementSibling;
78
+
79
+ this.state.isActive
80
+ ? parent.classList.add( 'closing' )
81
+ : parent.classList.add( 'opening' );
82
+ this.state.isActive
83
+ ? slide.up( content, contentId, 200, this.onClose( parent, e ) )
84
+ : slide.down( content, contentId, 200, this.onOpen( parent, e ) );
85
+
86
+ onClick && onClick( e );
87
+ this.setState( ( state ) => ( { isActive: ! state.isActive } ) );
88
+ };
89
+
90
+ render() {
91
+ const {
92
+ content,
93
+ contentClassName,
94
+ header,
95
+ headerClassName,
96
+ } = this.props;
97
+
98
+ return (
99
+ <article
100
+ className={ classNames(
101
+ 'tribe-editor__accordion__row',
102
+ { active: this.state.isActive },
103
+ ) }
104
+ >
105
+ <Button
106
+ className={ classNames(
107
+ 'tribe-editor__accordion__row-header',
108
+ headerClassName,
109
+ ) }
110
+ onClick={ ( e ) => this.onClick( e ) }
111
+ { ...this.getHeaderAttrs() }
112
+ >
113
+ { header }
114
+ </Button>
115
+ <div
116
+ className={ classNames(
117
+ 'tribe-editor__accordion__row-content',
118
+ contentClassName,
119
+ ) }
120
+ { ...this.getContentAttrs() }
121
+ >
122
+ { content }
123
+ </div>
124
+ </article>
125
+ );
126
+ }
127
+ }
128
+
129
+ export default Row;
common/src/modules/elements/accordion/style.pcss ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ .tribe-editor__accordion__row-content {
2
+ max-height: 0;
3
+ overflow: hidden;
4
+
5
+ &.active {
6
+ max-height: none;
7
+ }
8
+ }
common/src/modules/elements/block-icon/index.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { TEC } from '@moderntribe/common/icons';
10
+ import './style.pcss';
11
+
12
+ export default () => (
13
+ <div className="tribe-editor__icons__container tribe-editor__icons--tec">
14
+ <TEC />
15
+ </div>
16
+ );
common/src/modules/elements/block-icon/style.pcss ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .editor-block-inspector__card {
2
+ .tribe-editor__icons__container {
3
+ padding: 4px;
4
+ display: flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+
8
+ svg {
9
+ width: 28px;
10
+ height: 28px;
11
+ }
12
+ }
13
+
14
+ .tribe-editor__icons--tec {
15
+ background-color: #199FD1;
16
+ }
17
+ }
18
+
19
+
20
+ button[class^='editor-block-list-item-tribe-'], button[class*=' editor-block-list-item-tribe-'] {
21
+ svg {
22
+ color: #16a0d6;
23
+ }
24
+ }
common/src/modules/elements/button/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Button Element renders button 1`] = `
4
+ <button
5
+ className="tribe-editor__button"
6
+ onClick={[Function]}
7
+ type="button"
8
+ />
9
+ `;
10
+
11
+ exports[`Button Element renders button with class 1`] = `
12
+ <button
13
+ className="tribe-editor__button test-class"
14
+ onClick={[Function]}
15
+ type="button"
16
+ />
17
+ `;
18
+
19
+ exports[`Button Element renders button with extra attributes 1`] = `
20
+ <button
21
+ className="tribe-editor__button"
22
+ hello="world"
23
+ onClick={[Function]}
24
+ test="one-two-three"
25
+ type="button"
26
+ />
27
+ `;
28
+
29
+ exports[`Button Element renders button with label 1`] = `
30
+ <button
31
+ className="tribe-editor__button"
32
+ onClick={[Function]}
33
+ type="button"
34
+ >
35
+ Hello
36
+ </button>
37
+ `;
38
+
39
+ exports[`Button Element renders button with onClick handler 1`] = `
40
+ <button
41
+ className="tribe-editor__button"
42
+ onClick={[MockFunction]}
43
+ type="button"
44
+ />
45
+ `;
46
+
47
+ exports[`Button Element renders button with set type 1`] = `
48
+ <button
49
+ className="tribe-editor__button"
50
+ onClick={[Function]}
51
+ type="submit"
52
+ />
53
+ `;
54
+
55
+ exports[`Button Element renders disabled button 1`] = `
56
+ <button
57
+ className="tribe-editor__button"
58
+ disabled={true}
59
+ onClick={[Function]}
60
+ type="button"
61
+ />
62
+ `;
common/src/modules/elements/button/__tests__/element.test.js ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import renderer from 'react-test-renderer';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { Button } from '@moderntribe/common/elements';
11
+
12
+ describe( 'Button Element', () => {
13
+ it( 'renders button', () => {
14
+ const component = renderer.create( <Button /> );
15
+ expect( component.toJSON() ).toMatchSnapshot();
16
+ } );
17
+
18
+ it( 'renders button with class', () => {
19
+ const component = renderer.create( <Button className="test-class" /> );
20
+ expect( component.toJSON() ).toMatchSnapshot();
21
+ } );
22
+
23
+ it( 'renders disabled button', () => {
24
+ const component = renderer.create( <Button isDisabled={ true } /> );
25
+ expect( component.toJSON() ).toMatchSnapshot();
26
+ } );
27
+
28
+ it( 'renders button with label', () => {
29
+ const component = renderer.create( <Button>Hello</Button> );
30
+ expect( component.toJSON() ).toMatchSnapshot();
31
+ } );
32
+
33
+ it( 'renders button with onClick handler', () => {
34
+ const onClick = jest.fn();
35
+ const component = renderer.create( <Button onClick={ onClick } /> );
36
+ expect( component.toJSON() ).toMatchSnapshot();
37
+ } );
38
+
39
+ it( 'executes the onClick handler', () => {
40
+ const onClick = jest.fn();
41
+ const component = mount( <Button onClick={ onClick } /> );
42
+ component.find( 'button' ).simulate( 'click' );
43
+ expect( onClick ).toHaveBeenCalled();
44
+ expect( onClick ).toHaveBeenCalledTimes( 1 );
45
+ } );
46
+
47
+ it( 'renders button with set type', () => {
48
+ const component = renderer.create( <Button type="submit" /> );
49
+ expect( component.toJSON() ).toMatchSnapshot();
50
+ } );
51
+
52
+ it( 'renders button with extra attributes', () => {
53
+ const attrs = {
54
+ test: 'one-two-three',
55
+ hello: 'world',
56
+ };
57
+ const component = renderer.create( <Button { ...attrs } /> );
58
+ expect( component.toJSON() ).toMatchSnapshot();
59
+ } );
60
+ } );
common/src/modules/elements/button/element.js ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { PureComponent } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+ import { noop } from 'lodash';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import './style.pcss';
13
+
14
+ class Button extends PureComponent {
15
+ static defaultProps = {
16
+ onClick: noop,
17
+ type: 'button',
18
+ };
19
+
20
+ static propTypes = {
21
+ className: PropTypes.oneOfType( [
22
+ PropTypes.string,
23
+ PropTypes.arrayOf( PropTypes.string ),
24
+ PropTypes.object,
25
+ ] ),
26
+ isDisabled: PropTypes.bool,
27
+ children: PropTypes.node,
28
+ onClick: PropTypes.func,
29
+ type: PropTypes.string,
30
+ };
31
+
32
+ render() {
33
+ const {
34
+ children,
35
+ className,
36
+ isDisabled,
37
+ onClick,
38
+ type,
39
+ ...rest
40
+ } = this.props;
41
+
42
+ return (
43
+ <button
44
+ className={ classNames( 'tribe-editor__button', className ) }
45
+ disabled={ isDisabled }
46
+ type={ type }
47
+ onClick={ onClick }
48
+ { ...rest }
49
+ >
50
+ { children }
51
+ </button>
52
+ );
53
+ }
54
+ }
55
+
56
+ export default Button;
common/src/modules/elements/button/style.pcss ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* basic styles are in .tribe-editor__btn, this is kepted for backward compatibility */
2
+ .tribe-editor__btn--label {
3
+ background-color: transparent;
4
+ border: none;
5
+ padding: 0;
6
+ margin: 0;
7
+ text-align: left;
8
+ }
9
+
10
+ .tribe-editor__button {
11
+
12
+ &,
13
+ /* extra editor-styles-wrapper class to override gutenberg styles */
14
+ .editor-styles-wrapper & {
15
+
16
+ background-color: transparent;
17
+ border: none;
18
+ padding: 0;
19
+ margin: 0;
20
+ font-family: 'Helvetica', 'sans-serif';
21
+ cursor: pointer;
22
+
23
+ &[disabled] {
24
+ cursor: default;
25
+ }
26
+ }
27
+ }
28
+
29
+ .tribe-editor__button--sm {
30
+
31
+ &,
32
+ /* extra editor-styles-wrapper class to override gutenberg styles */
33
+ .editor-styles-wrapper & {
34
+
35
+ background-color: #009fd4;
36
+ color: #ffffff;
37
+ padding: 11px 16px 9px;
38
+ font-size: 15px;
39
+ font-weight: bold;
40
+ line-height: 18px;
41
+ transition: background-color 0.2s ease;
42
+
43
+ &:hover,
44
+ &:focus {
45
+ background-color: #007bb4;
46
+ }
47
+
48
+ &[disabled] {
49
+
50
+ &,
51
+ &:hover,
52
+ &:focus {
53
+ background-color: #f3f4f5;
54
+ color: #8d949b;
55
+ }
56
+ }
57
+ }
58
+ }
common/src/modules/elements/checkbox-input/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Input element Should render the component 1`] = `
4
+ <input
5
+ className="tribe-editor__input tribe-editor__input--checkbox"
6
+ type="checkbox"
7
+ />
8
+ `;
9
+
10
+ exports[`Input element Should render the component with checked 1`] = `
11
+ <input
12
+ checked={true}
13
+ className="tribe-editor__input tribe-editor__input--checkbox"
14
+ type="checkbox"
15
+ />
16
+ `;
17
+
18
+ exports[`Input element Should render the component with checked 2`] = `
19
+ <input
20
+ checked={false}
21
+ className="tribe-editor__input tribe-editor__input--checkbox"
22
+ type="checkbox"
23
+ />
24
+ `;
25
+
26
+ exports[`Input element Should render the component with class 1`] = `
27
+ <input
28
+ className="tribe-editor__input tribe-editor__input--checkbox checkbox-class"
29
+ type="checkbox"
30
+ />
31
+ `;
32
+
33
+ exports[`Input element Should render the component with extra props 1`] = `
34
+ <input
35
+ className="tribe-editor__input tribe-editor__input--checkbox"
36
+ id="checkbox-id"
37
+ type="checkbox"
38
+ />
39
+ `;
40
+
41
+ exports[`Input element Should render the component with onChange handler 1`] = `
42
+ <input
43
+ className="tribe-editor__input tribe-editor__input--checkbox"
44
+ onChange={[Function]}
45
+ type="checkbox"
46
+ />
47
+ `;
common/src/modules/elements/checkbox-input/__tests__/element.test.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import { noop } from 'lodash';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import CheckboxInput from '../element.js';
11
+
12
+ describe( 'Input element', () => {
13
+ it( 'Should render the component', () => {
14
+ const component = renderer.create( <CheckboxInput /> );
15
+ expect( component.toJSON() ).toMatchSnapshot();
16
+ } );
17
+
18
+ it( 'Should render the component with checked', () => {
19
+ const component1 = renderer.create( <CheckboxInput checked={ true } /> );
20
+ expect( component1.toJSON() ).toMatchSnapshot();
21
+ const component2 = renderer.create( <CheckboxInput checked={ false } /> );
22
+ expect( component2.toJSON() ).toMatchSnapshot();
23
+ } );
24
+
25
+ it( 'Should render the component with class', () => {
26
+ const component = renderer.create( <CheckboxInput className="checkbox-class" /> );
27
+ expect( component.toJSON() ).toMatchSnapshot();
28
+ } );
29
+
30
+ it( 'Should render the component with onChange handler', () => {
31
+ const component = renderer.create( <CheckboxInput onChange={ noop } /> );
32
+ expect( component.toJSON() ).toMatchSnapshot();
33
+ } );
34
+
35
+ it( 'Should render the component with extra props', () => {
36
+ const component = renderer.create( <CheckboxInput id="checkbox-id" /> );
37
+ expect( component.toJSON() ).toMatchSnapshot();
38
+ } );
39
+ } );
common/src/modules/elements/checkbox-input/element.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External Dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import Input from '@moderntribe/common/elements/input/element';
12
+ import './style.pcss';
13
+
14
+ const CheckboxInput = ( {
15
+ checked,
16
+ className,
17
+ onChange,
18
+ ...rest
19
+ } ) => (
20
+ <Input
21
+ checked={ checked }
22
+ className={ classNames( 'tribe-editor__input--checkbox', className ) }
23
+ onChange={ onChange }
24
+ type="checkbox"
25
+ { ...rest }
26
+ />
27
+ );
28
+
29
+ CheckboxInput.propTypes = {
30
+ checked: PropTypes.bool,
31
+ className: PropTypes.string,
32
+ onChange: PropTypes.func,
33
+ };
34
+
35
+ export default CheckboxInput;
common/src/modules/elements/checkbox-input/style.pcss ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* increased specificity required to override gutenberg editor styles */
2
+ input[type=checkbox].tribe-editor__input--checkbox {
3
+ background-color: #ffffff;
4
+ border: 1px solid #E0E5E9;
5
+ border-radius: 0;
6
+
7
+ &:focus {
8
+ border: 1px solid #E0E5E9;
9
+ box-shadow: 0 0 0 1px #E0E5E9;
10
+ }
11
+
12
+ &:checked {
13
+ background-color: #ffffff;
14
+ border: 1px solid #E0E5E9;
15
+
16
+ &:focus {
17
+ border: 1px solid #E0E5E9;
18
+ box-shadow: 0 0 0 1px #E0E5E9;
19
+ }
20
+
21
+ &:before {
22
+ color: #11A0D2;
23
+ }
24
+ }
25
+ }
common/src/modules/elements/checkbox/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Checkbox Element renders checkbox 1`] = `
4
+ <div
5
+ className="tribe-editor__checkbox"
6
+ >
7
+ <input
8
+ checked={false}
9
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
10
+ onChange={[Function]}
11
+ type="checkbox"
12
+ />
13
+ <label
14
+ className="tribe-editor__checkbox__label"
15
+ />
16
+ </div>
17
+ `;
18
+
19
+ exports[`Checkbox Element renders checkbox with class 1`] = `
20
+ <div
21
+ className="tribe-editor__checkbox test-class"
22
+ >
23
+ <input
24
+ checked={false}
25
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
26
+ onChange={[Function]}
27
+ type="checkbox"
28
+ />
29
+ <label
30
+ className="tribe-editor__checkbox__label"
31
+ />
32
+ </div>
33
+ `;
34
+
35
+ exports[`Checkbox Element renders checkbox with id 1`] = `
36
+ <div
37
+ className="tribe-editor__checkbox"
38
+ >
39
+ <input
40
+ checked={false}
41
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
42
+ id="test-id"
43
+ onChange={[Function]}
44
+ type="checkbox"
45
+ />
46
+ <label
47
+ className="tribe-editor__checkbox__label"
48
+ htmlFor="test-id"
49
+ />
50
+ </div>
51
+ `;
52
+
53
+ exports[`Checkbox Element renders checkbox with label 1`] = `
54
+ <div
55
+ className="tribe-editor__checkbox"
56
+ >
57
+ <input
58
+ checked={false}
59
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
60
+ onChange={[Function]}
61
+ type="checkbox"
62
+ />
63
+ <label
64
+ className="tribe-editor__checkbox__label"
65
+ >
66
+ Test Label
67
+ </label>
68
+ </div>
69
+ `;
70
+
71
+ exports[`Checkbox Element renders checkbox with name 1`] = `
72
+ <div
73
+ className="tribe-editor__checkbox"
74
+ >
75
+ <input
76
+ checked={false}
77
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
78
+ name="test-name"
79
+ onChange={[Function]}
80
+ type="checkbox"
81
+ />
82
+ <label
83
+ className="tribe-editor__checkbox__label"
84
+ />
85
+ </div>
86
+ `;
87
+
88
+ exports[`Checkbox Element renders checkbox with onChange handler 1`] = `
89
+ <div
90
+ className="tribe-editor__checkbox"
91
+ >
92
+ <input
93
+ checked={false}
94
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
95
+ onChange={[MockFunction]}
96
+ type="checkbox"
97
+ />
98
+ <label
99
+ className="tribe-editor__checkbox__label"
100
+ />
101
+ </div>
102
+ `;
103
+
104
+ exports[`Checkbox Element renders checkbox with value 1`] = `
105
+ <div
106
+ className="tribe-editor__checkbox"
107
+ >
108
+ <input
109
+ checked={false}
110
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
111
+ onChange={[Function]}
112
+ type="checkbox"
113
+ value="Test Value"
114
+ />
115
+ <label
116
+ className="tribe-editor__checkbox__label"
117
+ />
118
+ </div>
119
+ `;
120
+
121
+ exports[`Checkbox Element renders checked checkbox 1`] = `
122
+ <div
123
+ className="tribe-editor__checkbox"
124
+ >
125
+ <input
126
+ checked={true}
127
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
128
+ onChange={[Function]}
129
+ type="checkbox"
130
+ />
131
+ <label
132
+ className="tribe-editor__checkbox__label"
133
+ />
134
+ </div>
135
+ `;
136
+
137
+ exports[`Checkbox Element renders disabled checkbox 1`] = `
138
+ <div
139
+ className="tribe-editor__checkbox"
140
+ >
141
+ <input
142
+ checked={false}
143
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
144
+ disabled={true}
145
+ onChange={[Function]}
146
+ type="checkbox"
147
+ />
148
+ <label
149
+ className="tribe-editor__checkbox__label"
150
+ />
151
+ </div>
152
+ `;
153
+
154
+ exports[`Checkbox Element renders unchecked checkbox 1`] = `
155
+ <div
156
+ className="tribe-editor__checkbox"
157
+ >
158
+ <input
159
+ checked={false}
160
+ className="tribe-editor__input tribe-editor__input--checkbox tribe-editor__checkbox__input"
161
+ onChange={[Function]}
162
+ type="checkbox"
163
+ />
164
+ <label
165
+ className="tribe-editor__checkbox__label"
166
+ />
167
+ </div>
168
+ `;
common/src/modules/elements/checkbox/__tests__/element.test.js ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { Checkbox } from '@moderntribe/common/elements';
10
+
11
+ describe( 'Checkbox Element', () => {
12
+ it( 'renders checkbox', () => {
13
+ const component = renderer.create( <Checkbox /> );
14
+ expect( component.toJSON() ).toMatchSnapshot();
15
+ } );
16
+
17
+ it( 'renders checked checkbox', () => {
18
+ const component = renderer.create( <Checkbox checked={ true } /> );
19
+ expect( component.toJSON() ).toMatchSnapshot();
20
+ } );
21
+
22
+ it( 'renders unchecked checkbox', () => {
23
+ const component = renderer.create( <Checkbox checked={ false } /> );
24
+ expect( component.toJSON() ).toMatchSnapshot();
25
+ } );
26
+
27
+ it( 'renders checkbox with class', () => {
28
+ const component = renderer.create( <Checkbox className="test-class" /> );
29
+ expect( component.toJSON() ).toMatchSnapshot();
30
+ } );
31
+
32
+ it( 'renders disabled checkbox', () => {
33
+ const component = renderer.create( <Checkbox disabled={ true } /> );
34
+ expect( component.toJSON() ).toMatchSnapshot();
35
+ } );
36
+
37
+ it( 'renders checkbox with id', () => {
38
+ const component = renderer.create( <Checkbox id="test-id" /> );
39
+ expect( component.toJSON() ).toMatchSnapshot();
40
+ } );
41
+
42
+ it( 'renders checkbox with label', () => {
43
+ const component = renderer.create( <Checkbox label="Test Label" /> );
44
+ expect( component.toJSON() ).toMatchSnapshot();
45
+ } );
46
+
47
+ it( 'renders checkbox with name', () => {
48
+ const component = renderer.create( <Checkbox name="test-name" /> );
49
+ expect( component.toJSON() ).toMatchSnapshot();
50
+ } );
51
+
52
+ it( 'renders checkbox with value', () => {
53
+ const component = renderer.create( <Checkbox value="Test Value" /> );
54
+ expect( component.toJSON() ).toMatchSnapshot();
55
+ } );
56
+
57
+ it( 'renders checkbox with onChange handler', () => {
58
+ const onChange = jest.fn();
59
+ const component = renderer.create( <Checkbox onChange={ onChange } /> );
60
+ expect( component.toJSON() ).toMatchSnapshot();
61
+ } );
62
+
63
+ it( 'executes checkbox with onChange handler', () => {
64
+ const onChange = jest.fn();
65
+ const component = mount( <Checkbox onChange={ onChange } /> );
66
+ component.find( 'input' ).simulate( 'change' );
67
+ expect( onChange ).toHaveBeenCalled();
68
+ expect( onChange ).toHaveBeenCalledTimes( 1 );
69
+ } );
70
+ } );
common/src/modules/elements/checkbox/element.js ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+ import { noop } from 'lodash';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { CheckboxInput } from '@moderntribe/common/elements';
13
+
14
+ const Checkbox = ( {
15
+ checked,
16
+ className,
17
+ disabled,
18
+ id,
19
+ label,
20
+ onChange,
21
+ name,
22
+ value,
23
+ } ) => {
24
+ return (
25
+ <div className={ classNames( 'tribe-editor__checkbox', className ) }>
26
+ <CheckboxInput
27
+ checked={ checked }
28
+ className="tribe-editor__checkbox__input"
29
+ disabled={ disabled }
30
+ id={ id }
31
+ name={ name }
32
+ onChange={ onChange }
33
+ value={ value }
34
+ />
35
+ <label
36
+ className="tribe-editor__checkbox__label"
37
+ htmlFor={ id }
38
+ >
39
+ { label }
40
+ </label>
41
+ </div>
42
+ );
43
+ };
44
+
45
+ Checkbox.defaultProps = {
46
+ checked: false,
47
+ onChange: noop,
48
+ }
49
+
50
+ Checkbox.propTypes = {
51
+ checked: PropTypes.bool.isRequired,
52
+ className: PropTypes.string,
53
+ disabled: PropTypes.bool,
54
+ id: PropTypes.string,
55
+ label: PropTypes.node,
56
+ name: PropTypes.string,
57
+ onChange: PropTypes.func,
58
+ value: PropTypes.string,
59
+ };
60
+
61
+ export default Checkbox;
common/src/modules/elements/counter/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Counter Element renders counter 1`] = `
4
+ <div
5
+ className="tribe-editor__counter"
6
+ >
7
+ <span
8
+ className="tribe-editor__counter__count"
9
+ />
10
+ <span
11
+ className="tribe-editor__counter__label"
12
+ />
13
+ </div>
14
+ `;
15
+
16
+ exports[`Counter Element renders counter with class 1`] = `
17
+ <div
18
+ className="tribe-editor__counter test-class"
19
+ >
20
+ <span
21
+ className="tribe-editor__counter__count"
22
+ />
23
+ <span
24
+ className="tribe-editor__counter__label"
25
+ />
26
+ </div>
27
+ `;
28
+
29
+ exports[`Counter Element renders counter with count 1`] = `
30
+ <div
31
+ className="tribe-editor__counter"
32
+ >
33
+ <span
34
+ className="tribe-editor__counter__count"
35
+ >
36
+ 42
37
+ </span>
38
+ <span
39
+ className="tribe-editor__counter__label"
40
+ />
41
+ </div>
42
+ `;
43
+
44
+ exports[`Counter Element renders counter with label 1`] = `
45
+ <div
46
+ className="tribe-editor__counter"
47
+ >
48
+ <span
49
+ className="tribe-editor__counter__count"
50
+ />
51
+ <span
52
+ className="tribe-editor__counter__label"
53
+ >
54
+ test-label
55
+ </span>
56
+ </div>
57
+ `;
common/src/modules/elements/counter/__tests__/element.test.js ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import renderer from 'react-test-renderer';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { Counter } from '@moderntribe/common/elements';
11
+
12
+ describe( 'Counter Element', () => {
13
+ it( 'renders counter', () => {
14
+ const component = renderer.create( <Counter /> );
15
+ expect( component.toJSON() ).toMatchSnapshot();
16
+ } );
17
+
18
+ it( 'renders counter with class', () => {
19
+ const component = renderer.create( <Counter className="test-class" /> );
20
+ expect( component.toJSON() ).toMatchSnapshot();
21
+ } );
22
+
23
+ it( 'renders counter with count', () => {
24
+ const component = renderer.create( <Counter count={ 42 } /> );
25
+ expect( component.toJSON() ).toMatchSnapshot();
26
+ } );
27
+
28
+ it( 'renders counter with label', () => {
29
+ const component = renderer.create( <Counter label="test-label" /> );
30
+ expect( component.toJSON() ).toMatchSnapshot();
31
+ } );
32
+ } );
common/src/modules/elements/counter/element.js ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import './style.pcss';
12
+
13
+ const Counter = ( {
14
+ className,
15
+ count,
16
+ label,
17
+ } ) => (
18
+ <div className={ classNames(
19
+ 'tribe-editor__counter',
20
+ className,
21
+ ) }>
22
+ <span className="tribe-editor__counter__count">
23
+ { count }
24
+ </span>
25
+ <span className="tribe-editor__counter__label">
26
+ { label }
27
+ </span>
28
+ </div>
29
+ );
30
+
31
+ Counter.propTypes = {
32
+ className: PropTypes.string,
33
+ count: PropTypes.number,
34
+ label: PropTypes.string,
35
+ };
36
+
37
+ export default Counter;
common/src/modules/elements/counter/style.pcss ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__counter {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ }
6
+
7
+ .tribe-editor__counter__count {
8
+ flex: none;
9
+ color: #AEB4BB;
10
+ font-size: 32px;
11
+ font-weight: bold;
12
+ line-height: 40px;
13
+ margin-bottom: 10px;
14
+ }
15
+
16
+ .tribe-editor__counter__label {
17
+ flex: none;
18
+ color: #AEB4BB;
19
+ font-size: 12px;
20
+ line-height: 14px;
21
+ letter-spacing: 0.04px;
22
+ }
common/src/modules/elements/creatable-select/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`CreatableSelect element Should render the component 1`] = `
4
+ <div
5
+ className="css-10nd86i tribe-editor__creatable-select"
6
+ onKeyDown={[Function]}
7
+ >
8
+ <div
9
+ className="css-vj8t7z tribe-editor__creatable-select__control"
10
+ onMouseDown={[Function]}
11
+ onTouchEnd={[Function]}
12
+ >
13
+ <div
14
+ className="css-1hwfws3 tribe-editor__creatable-select__value-container"
15
+ >
16
+ <div
17
+ className="css-1492t68 tribe-editor__creatable-select__placeholder"
18
+ >
19
+ Select...
20
+ </div>
21
+ <div
22
+ className="css-1g6gooi"
23
+ >
24
+ <div
25
+ className="tribe-editor__creatable-select__input"
26
+ style={
27
+ Object {
28
+ "display": "inline-block",
29
+ }
30
+ }
31
+ >
32
+ <input
33
+ aria-autocomplete="list"
34
+ autoCapitalize="none"
35
+ autoComplete="off"
36
+ autoCorrect="off"
37
+ disabled={false}
38
+ id="react-select-2-input"
39
+ onBlur={[Function]}
40
+ onChange={[Function]}
41
+ onFocus={[Function]}
42
+ spellCheck="false"
43
+ style={
44
+ Object {
45
+ "background": 0,
46
+ "border": 0,
47
+ "boxSizing": "content-box",
48
+ "color": "inherit",
49
+ "fontSize": "inherit",
50
+ "opacity": 1,
51
+ "outline": 0,
52
+ "padding": 0,
53
+ "width": "1px",
54
+ }
55
+ }
56
+ tabIndex="0"
57
+ type="text"
58
+ value=""
59
+ />
60
+ <div
61
+ style={
62
+ Object {
63
+ "height": 0,
64
+ "left": 0,
65
+ "overflow": "scroll",
66
+ "position": "absolute",
67
+ "top": 0,
68
+ "visibility": "hidden",
69
+ "whiteSpace": "pre",
70
+ }
71
+ }
72
+ >
73
+
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ <div
79
+ className="css-1wy0on6 tribe-editor__creatable-select__indicators"
80
+ >
81
+ <div
82
+ aria-hidden="true"
83
+ className="css-1ep9fjw tribe-editor__creatable-select__indicator tribe-editor__creatable-select__dropdown-indicator"
84
+ onMouseDown={[Function]}
85
+ onTouchEnd={[Function]}
86
+ >
87
+ <span
88
+ className="tribe-editor__creatable-select__dropdown-indicator"
89
+ >
90
+ arrow-down
91
+ </span>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ `;
97
+
98
+ exports[`CreatableSelect element Should render the component with class 1`] = `
99
+ <div
100
+ className="css-10nd86i tribe-editor__creatable-select test-class"
101
+ onKeyDown={[Function]}
102
+ >
103
+ <div
104
+ className="css-vj8t7z tribe-editor__creatable-select__control"
105
+ onMouseDown={[Function]}
106
+ onTouchEnd={[Function]}
107
+ >
108
+ <div
109
+ className="css-1hwfws3 tribe-editor__creatable-select__value-container"
110
+ >
111
+ <div
112
+ className="css-1492t68 tribe-editor__creatable-select__placeholder"
113
+ >
114
+ Select...
115
+ </div>
116
+ <div
117
+ className="css-1g6gooi"
118
+ >
119
+ <div
120
+ className="tribe-editor__creatable-select__input"
121
+ style={
122
+ Object {
123
+ "display": "inline-block",
124
+ }
125
+ }
126
+ >
127
+ <input
128
+ aria-autocomplete="list"
129
+ autoCapitalize="none"
130
+ autoComplete="off"
131
+ autoCorrect="off"
132
+ disabled={false}
133
+ id="react-select-3-input"
134
+ onBlur={[Function]}
135
+ onChange={[Function]}
136
+ onFocus={[Function]}
137
+ spellCheck="false"
138
+ style={
139
+ Object {
140
+ "background": 0,
141
+ "border": 0,
142
+ "boxSizing": "content-box",
143
+ "color": "inherit",
144
+ "fontSize": "inherit",
145
+ "opacity": 1,
146
+ "outline": 0,
147
+ "padding": 0,
148
+ "width": "1px",
149
+ }
150
+ }
151
+ tabIndex="0"
152
+ type="text"
153
+ value=""
154
+ />
155
+ <div
156
+ style={
157
+ Object {
158
+ "height": 0,
159
+ "left": 0,
160
+ "overflow": "scroll",
161
+ "position": "absolute",
162
+ "top": 0,
163
+ "visibility": "hidden",
164
+ "whiteSpace": "pre",
165
+ }
166
+ }
167
+ >
168
+
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ <div
174
+ className="css-1wy0on6 tribe-editor__creatable-select__indicators"
175
+ >
176
+ <div
177
+ aria-hidden="true"
178
+ className="css-1ep9fjw tribe-editor__creatable-select__indicator tribe-editor__creatable-select__dropdown-indicator"
179
+ onMouseDown={[Function]}
180
+ onTouchEnd={[Function]}
181
+ >
182
+ <span
183
+ className="tribe-editor__creatable-select__dropdown-indicator"
184
+ >
185
+ arrow-down
186
+ </span>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ `;
192
+
193
+ exports[`CreatableSelect element Should render the component with extra props 1`] = `
194
+ <div
195
+ className="css-10nd86i tribe-editor__creatable-select"
196
+ onKeyDown={[Function]}
197
+ >
198
+ <div
199
+ className="css-vj8t7z tribe-editor__creatable-select__control"
200
+ onMouseDown={[Function]}
201
+ onTouchEnd={[Function]}
202
+ >
203
+ <div
204
+ className="css-1hwfws3 tribe-editor__creatable-select__value-container"
205
+ >
206
+ <div
207
+ className="css-1492t68 tribe-editor__creatable-select__placeholder"
208
+ >
209
+ Select...
210
+ </div>
211
+ <input
212
+ className="css-14uuagi"
213
+ disabled={false}
214
+ id="react-select-4-input"
215
+ onBlur={[Function]}
216
+ onChange={[Function]}
217
+ onFocus={[Function]}
218
+ readOnly={true}
219
+ tabIndex="0"
220
+ value=""
221
+ />
222
+ </div>
223
+ <div
224
+ className="css-1wy0on6 tribe-editor__creatable-select__indicators"
225
+ >
226
+ <div
227
+ aria-hidden="true"
228
+ className="css-1ep9fjw tribe-editor__creatable-select__indicator tribe-editor__creatable-select__dropdown-indicator"
229
+ onMouseDown={[Function]}
230
+ onTouchEnd={[Function]}
231
+ >
232
+ <span
233
+ className="tribe-editor__creatable-select__dropdown-indicator"
234
+ >
235
+ arrow-down
236
+ </span>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ `;
common/src/modules/elements/creatable-select/__tests__/element.test.js ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import CreatableSelect from '../element.js';
10
+
11
+ const options = [
12
+ { label: 'Test 1', value: 'test-1' },
13
+ { label: 'Test 2', value: 'test-2' },
14
+ ];
15
+
16
+ describe( 'CreatableSelect element', () => {
17
+ it( 'Should render the component', () => {
18
+ const component = renderer.create( <CreatableSelect options={ options } /> );
19
+ expect( component.toJSON() ).toMatchSnapshot();
20
+ } );
21
+
22
+ it( 'Should render the component with class', () => {
23
+ const component = renderer.create( <CreatableSelect options={ options } className="test-class" /> );
24
+ expect( component.toJSON() ).toMatchSnapshot();
25
+ } );
26
+
27
+ it( 'Should render the component with extra props', () => {
28
+ const component = renderer.create( <CreatableSelect options={ options } isSearchable={ false } /> );
29
+ expect( component.toJSON() ).toMatchSnapshot();
30
+ } );
31
+ } );
common/src/modules/elements/creatable-select/element.js ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+ import { components } from 'react-select';
8
+ import ReactCreatableSelect from 'react-select/lib/Creatable';
9
+ import { Dashicon } from '@wordpress/components';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import './style.pcss';
15
+
16
+ const DropdownIndicator = ( props ) => (
17
+ components.DropdownIndicator && (
18
+ <components.DropdownIndicator { ...props }>
19
+ <Dashicon
20
+ className="tribe-editor__creatable-select__dropdown-indicator"
21
+ icon={ 'arrow-down' }
22
+ />
23
+ </components.DropdownIndicator>
24
+ )
25
+ );
26
+
27
+ const IndicatorSeparator = () => null;
28
+
29
+ /**
30
+ * There seems to be an issue with Creatable and a custom isValidNewOption
31
+ * prop needs to be passed in for this to work.
32
+ *
33
+ * See:
34
+ * - https://github.com/JedWatson/react-select/issues/2630
35
+ * - https://github.com/JedWatson/react-select/issues/2944
36
+ */
37
+ const CreatableSelect = ( { className, ...rest } ) => (
38
+ <ReactCreatableSelect
39
+ className={ classNames( 'tribe-editor__creatable-select', className ) }
40
+ classNamePrefix="tribe-editor__creatable-select"
41
+ components={ { DropdownIndicator, IndicatorSeparator } }
42
+ { ...rest }
43
+ />
44
+ );
45
+
46
+ CreatableSelect.propTypes = {
47
+ className: PropTypes.string,
48
+ };
49
+
50
+ export default CreatableSelect;
common/src/modules/elements/creatable-select/style.pcss ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__creatable-select {
2
+
3
+ .tribe-editor__creatable-select__control {
4
+ height: 40px;
5
+ border: 1px solid #E1E3E6;
6
+ border-radius: 3px;
7
+ background-color: #FFFFFF;
8
+
9
+ &:hover {
10
+ border: 1px solid #E1E3E6;
11
+ }
12
+
13
+ &--is-focused {
14
+ box-shadow: none;
15
+ }
16
+ }
17
+
18
+ .tribe-editor__creatable-select__value-container {
19
+ padding: 2px 5px 2px 15px;
20
+ }
21
+
22
+ .tribe-editor__creatable-select__single-value {
23
+ margin: 0;
24
+ max-width: calc(100% - 15px);
25
+ font-size: 16px;
26
+ line-height: 1.5;
27
+ }
28
+
29
+ .tribe-editor__creatable-select__input {
30
+ font-size: 16px;
31
+
32
+ & > input {
33
+ margin: 0;
34
+ line-height: 1.5;
35
+
36
+ &,
37
+ &:focus {
38
+ box-shadow: none;
39
+ }
40
+ }
41
+ }
42
+
43
+ svg.tribe-editor__creatable-select__dropdown-indicator {
44
+ fill: #555D66;
45
+ }
46
+
47
+ .tribe-editor__creatable-select__menu {
48
+ margin: 0;
49
+ border: 1px solid #E1E3E6;
50
+ border-top: none;
51
+ border-radius: 0;
52
+ border-bottom-left-radius: 3px;
53
+ border-bottom-right-radius: 3px;
54
+ box-shadow: none;
55
+ transform: translateY(-7px);
56
+ }
57
+
58
+ .tribe-editor__creatable-select__menu-list {
59
+ padding: 0;
60
+ }
61
+
62
+ .tribe-editor__creatable-select__option {
63
+ font-size: 16px;
64
+ line-height: 1.5;
65
+ padding: 3px 15px;
66
+
67
+ &--is-focused {
68
+ background-color: #E7F5FA;
69
+ }
70
+
71
+ &--is-selected {
72
+ background-color: #11A0D2;
73
+ }
74
+ }
75
+ }
common/src/modules/elements/day-picker-input/element.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import classNames from 'classnames';
6
+ import 'react-day-picker/lib/style.css';
7
+ import ReactDayPickerInput from 'react-day-picker/DayPickerInput';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import './style.pcss';
13
+
14
+ const DayPickerInput = ( props ) => (
15
+ <ReactDayPickerInput
16
+ classNames={ {
17
+ container: classNames(
18
+ 'tribe-editor__day-picker-input',
19
+ 'DayPickerInput',
20
+ ),
21
+ overlayWrapper: classNames(
22
+ 'tribe-editor__day-picker-input__overlay-wrapper',
23
+ 'DayPickerInput-OverlayWrapper',
24
+ ),
25
+ overlay: classNames(
26
+ 'tribe-editor__day-picker-input__overlay',
27
+ 'DayPickerInput-Overlay',
28
+ ),
29
+ } }
30
+ { ...props }
31
+ />
32
+ );
33
+
34
+ export default DayPickerInput;
common/src/modules/elements/day-picker-input/style.pcss ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__day-picker-input.DayPickerInput {
2
+
3
+ & > input {
4
+ border: 1px solid #e1e3e6;
5
+ color: #545d66;
6
+ font-size: 16px;
7
+ line-height: 24px;
8
+ padding: 7px 15px;
9
+ width: 100%;
10
+ height: 40px;
11
+
12
+ &:disabled {
13
+ color: #AEB4BB;
14
+ }
15
+ }
16
+ }
17
+
18
+ /* -----------------------------------------------------------
19
+ DayPicker Input Calender Styles
20
+ ----------------------------------------------------------- */
21
+
22
+ .tribe-editor__day-picker-input {
23
+
24
+ .DayPickerInput-Overlay {
25
+ padding: 20px;
26
+ z-index: 10;
27
+ }
28
+
29
+ .DayPicker {
30
+ width: 100%;
31
+ }
32
+
33
+ .DayPicker-Month {
34
+ margin: 0;
35
+ }
36
+
37
+ .DayPicker-Caption > div,
38
+ .DayPicker-Weekday,
39
+ .DayPicker-Day {
40
+ color: #545d66;
41
+ font-family: 'Helvetica', 'sans-serif';
42
+ font-weight: normal;
43
+ }
44
+
45
+ .DayPicker-Caption > div {
46
+ font-size: 16px;
47
+ margin-bottom: 12px;
48
+ text-align: center;
49
+ }
50
+
51
+ .DayPicker-Weekday {
52
+ font-size: 12px;
53
+ }
54
+
55
+ .DayPicker-Day {
56
+ font-size: 14px;
57
+
58
+ &:hover {
59
+ color: #007bb4;
60
+ background-color: #ffffff;
61
+ }
62
+ }
63
+
64
+ .DayPicker-Day--today {
65
+ color: #545D66;
66
+ }
67
+
68
+ .DayPicker-Day--disabled {
69
+ pointer-events: none;
70
+ color: #cccccc;
71
+ }
72
+
73
+ .DayPicker-Day--selected:not(.DayPicker-Day--outside) {
74
+ border-radius: 0;
75
+ background-color: #009fd4;
76
+ color: #ffffff;
77
+
78
+ &:hover {
79
+ background-color: #007bb4;
80
+ color: #ffffff;
81
+ }
82
+ }
83
+
84
+ .DayPicker-NavButton--prev {
85
+ left: 0;
86
+ top: 0;
87
+ }
88
+
89
+ .DayPicker-NavButton--next {
90
+ right: 0;
91
+ top: 0;
92
+ }
93
+
94
+ .DayPicker:not(.DayPicker--interactionDisabled) .DayPicker-Day:not(.DayPicker-Day--disabled):not(.DayPicker-Day--selected):not(.DayPicker-Day--outside) {
95
+
96
+ &:hover {
97
+ background-color: #ffffff;
98
+ }
99
+ }
100
+
101
+ .DayPicker-Day--selected:not(.DayPicker-Day--start):not(.DayPicker-Day--end):not(.DayPicker-Day--outside) {
102
+ background-color: #e7f5fa;
103
+ color: #545d66;
104
+
105
+ &:hover {
106
+ color: #007bb4;
107
+ }
108
+ }
109
+ }
common/src/modules/elements/heading/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<Heading> <h1> 1`] = `
4
+ <h1
5
+ className="tribe-editor__heading tribe-editor__heading--h1"
6
+ >
7
+ Modern Tribe
8
+ </h1>
9
+ `;
10
+
11
+ exports[`<Heading> <h2> 1`] = `
12
+ <h2
13
+ className="tribe-editor__heading tribe-editor__heading--h2"
14
+ >
15
+ Modern Tribe
16
+ </h2>
17
+ `;
18
+
19
+ exports[`<Heading> <h3> 1`] = `
20
+ <h3
21
+ className="tribe-editor__heading tribe-editor__heading--h3"
22
+ >
23
+ Modern Tribe
24
+ </h3>
25
+ `;
26
+
27
+ exports[`<Heading> <h4> 1`] = `
28
+ <h4
29
+ className="tribe-editor__heading tribe-editor__heading--h4"
30
+ >
31
+ Modern Tribe
32
+ </h4>
33
+ `;
34
+
35
+ exports[`<Heading> <h5> 1`] = `
36
+ <h5
37
+ className="tribe-editor__heading tribe-editor__heading--h5"
38
+ >
39
+ Modern Tribe
40
+ </h5>
41
+ `;
42
+
43
+ exports[`<Heading> <h6> 1`] = `
44
+ <h6
45
+ className="tribe-editor__heading tribe-editor__heading--h6"
46
+ >
47
+ Modern Tribe
48
+ </h6>
49
+ `;
common/src/modules/elements/heading/__tests__/element.test.js ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+
3
+ import Heading from '../element';
4
+
5
+ describe( '<Heading>', () => {
6
+ test( '<h1>', () => {
7
+ const component = renderer.create(
8
+ <Heading level={ 1 }>Modern Tribe</Heading>,
9
+ );
10
+ expect( component.toJSON() ).toMatchSnapshot();
11
+ } );
12
+
13
+ test( '<h2>', () => {
14
+ const component = renderer.create(
15
+ <Heading level={ 2 }>Modern Tribe</Heading>,
16
+ );
17
+ expect( component.toJSON() ).toMatchSnapshot();
18
+ } );
19
+
20
+ test( '<h3>', () => {
21
+ const component = renderer.create(
22
+ <Heading level={ 3 }>Modern Tribe</Heading>,
23
+ );
24
+ expect( component.toJSON() ).toMatchSnapshot();
25
+ } );
26
+
27
+ test( '<h4>', () => {
28
+ const component = renderer.create(
29
+ <Heading level={ 4 }>Modern Tribe</Heading>,
30
+ );
31
+ expect( component.toJSON() ).toMatchSnapshot();
32
+ } );
33
+
34
+ test( '<h5>', () => {
35
+ const component = renderer.create(
36
+ <Heading level={ 5 }>Modern Tribe</Heading>,
37
+ );
38
+ expect( component.toJSON() ).toMatchSnapshot();
39
+ } );
40
+
41
+ test( '<h6>', () => {
42
+ const component = renderer.create(
43
+ <Heading level={ 6 }>Modern Tribe</Heading>,
44
+ );
45
+ expect( component.toJSON() ).toMatchSnapshot();
46
+ } );
47
+ } );
common/src/modules/elements/heading/element.js ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import './style.pcss';
12
+
13
+ const Heading = ( { level, children, className } ) => {
14
+ const HeadingLevel = `h${ level }`;
15
+ const headingClassName = classNames(
16
+ 'tribe-editor__heading',
17
+ `tribe-editor__heading--h${ level }`,
18
+ className,
19
+ );
20
+ return (
21
+ <HeadingLevel className={ headingClassName }>
22
+ { children }
23
+ </HeadingLevel>
24
+ );
25
+ };
26
+
27
+ Heading.propTypes = {
28
+ children: PropTypes.node.isRequired,
29
+ level: PropTypes.oneOf( [ 1, 2, 3, 4, 5, 6 ] ).isRequired,
30
+ };
31
+
32
+ export default Heading;
common/src/modules/elements/heading/style.pcss ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__heading {
2
+ font-family: 'Helvetica', 'Arial', 'sans-serif';
3
+ color: #000;
4
+ }
5
+
6
+ .tribe-editor__heading--h1 {
7
+ font-size: 2.375rem; /* 38pts */
8
+ }
9
+
10
+ .tribe-editor__heading--h2 {
11
+ font-size: 1.3125rem; /* 21pts */
12
+ }
13
+
14
+ .tribe-editor__heading--h3 {
15
+ font-size: 1rem; /* 16pts */
16
+ }
common/src/modules/elements/image-upload/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ImageUpload renders the component 1`] = `
4
+ <div
5
+ className="tribe-editor__image-upload"
6
+ >
7
+ <div
8
+ className="tribe-editor__image-upload__content"
9
+ >
10
+ <button>
11
+ Media Upload
12
+ </button>
13
+ </div>
14
+ </div>
15
+ `;
16
+
17
+ exports[`ImageUpload renders upload image button 1`] = `
18
+ <div
19
+ className="tribe-editor__image-upload"
20
+ >
21
+ <div
22
+ className="tribe-editor__image-upload__content"
23
+ >
24
+ <button>
25
+ Media Upload
26
+ </button>
27
+ </div>
28
+ </div>
29
+ `;
30
+
31
+ exports[`ImageUpload renders uploaded image 1`] = `
32
+ <div
33
+ className="tribe-editor__image-upload tribe-editor__image-upload--has-image"
34
+ >
35
+ <div
36
+ className="tribe-editor__image-upload__content"
37
+ >
38
+ <div
39
+ className="tribe-editor__image-upload__image-wrapper"
40
+ >
41
+ <img
42
+ alt="test-alt"
43
+ className="tribe-editor__image tribe-editor__image-upload__image"
44
+ src="test-src"
45
+ />
46
+ <button
47
+ className="tribe-editor__button tribe-editor__image-upload__remove-button"
48
+ onClick={[MockFunction]}
49
+ type="button"
50
+ >
51
+ <span>
52
+ icon
53
+ </span>
54
+ <span
55
+ className="tribe-editor__image-upload__remove-button-text"
56
+ >
57
+ remove
58
+ </span>
59
+ </button>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ `;
64
+
65
+ exports[`ImageUpload renders with class 1`] = `
66
+ <div
67
+ className="tribe-editor__image-upload test-class"
68
+ >
69
+ <div
70
+ className="tribe-editor__image-upload__content"
71
+ >
72
+ <button>
73
+ Media Upload
74
+ </button>
75
+ </div>
76
+ </div>
77
+ `;
78
+
79
+ exports[`ImageUpload renders with description 1`] = `
80
+ <div
81
+ className="tribe-editor__image-upload"
82
+ >
83
+ <div
84
+ className="tribe-editor__image-upload__content"
85
+ >
86
+ <p
87
+ className="tribe-editor__image-upload__description"
88
+ >
89
+ The Next Generation of Digital Agency
90
+ </p>
91
+ <button>
92
+ Media Upload
93
+ </button>
94
+ </div>
95
+ </div>
96
+ `;
97
+
98
+ exports[`ImageUpload renders with title 1`] = `
99
+ <div
100
+ className="tribe-editor__image-upload"
101
+ >
102
+ <h3
103
+ className="tribe-editor__image-upload__title"
104
+ >
105
+ Modern Tribe
106
+ </h3>
107
+ <div
108
+ className="tribe-editor__image-upload__content"
109
+ >
110
+ <button>
111
+ Media Upload
112
+ </button>
113
+ </div>
114
+ </div>
115
+ `;
116
+
117
+ exports[`renderImage renders the image and button 1`] = `
118
+ <div
119
+ className="tribe-editor__image-upload__image-wrapper"
120
+ >
121
+ <img
122
+ alt="test-alt"
123
+ className="tribe-editor__image tribe-editor__image-upload__image"
124
+ src="test-src"
125
+ />
126
+ <button
127
+ className="tribe-editor__button tribe-editor__image-upload__remove-button"
128
+ disabled={false}
129
+ onClick={[MockFunction]}
130
+ type="button"
131
+ >
132
+ <span>
133
+ icon
134
+ </span>
135
+ <span
136
+ className="tribe-editor__image-upload__remove-button-text"
137
+ >
138
+ remove
139
+ </span>
140
+ </button>
141
+ </div>
142
+ `;
143
+
144
+ exports[`renderImage renders the image and disabled button 1`] = `
145
+ <div
146
+ className="tribe-editor__image-upload__image-wrapper"
147
+ >
148
+ <img
149
+ alt="test-alt"
150
+ className="tribe-editor__image tribe-editor__image-upload__image"
151
+ src="test-src"
152
+ />
153
+ <button
154
+ className="tribe-editor__button tribe-editor__image-upload__remove-button"
155
+ disabled={true}
156
+ onClick={[MockFunction]}
157
+ type="button"
158
+ >
159
+ <span>
160
+ icon
161
+ </span>
162
+ <span
163
+ className="tribe-editor__image-upload__remove-button-text"
164
+ >
165
+ remove
166
+ </span>
167
+ </button>
168
+ </div>
169
+ `;
170
+
171
+ exports[`renderImageUploadButton renders the button 1`] = `
172
+ <button
173
+ className="tribe-editor__button tribe-editor__button--sm tribe-editor__image-upload__upload-button"
174
+ disabled={false}
175
+ onClick={[MockFunction]}
176
+ type="button"
177
+ >
178
+ label
179
+ </button>
180
+ `;
181
+
182
+ exports[`renderImageUploadButton renders the button disabled 1`] = `
183
+ <button
184
+ className="tribe-editor__button tribe-editor__button--sm tribe-editor__image-upload__upload-button"
185
+ disabled={true}
186
+ onClick={[MockFunction]}
187
+ type="button"
188
+ >
189
+ label
190
+ </button>
191
+ `;
common/src/modules/elements/image-upload/__tests__/element.test.js ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import ImageUpload, {
10
+ renderImageUploadButton,
11
+ renderImage,
12
+ } from '@moderntribe/common/elements/image-upload/element';
13
+
14
+ jest.mock( '@wordpress/editor', () => ( {
15
+ MediaUpload: () => ( <button>Media Upload</button> ),
16
+ } ) );
17
+
18
+ jest.mock( '@moderntribe/common/icons', () => ( {
19
+ Close: () => <span>icon</span>,
20
+ } ) );
21
+
22
+ describe( 'renderImageUploadButton', () => {
23
+ const open = jest.fn();
24
+
25
+ afterEach( () => {
26
+ open.mockClear();
27
+ } );
28
+
29
+ it( 'renders the button', () => {
30
+ const component = renderer.create( renderImageUploadButton( false, 'label' )( { open } ) );
31
+ expect( component.toJSON() ).toMatchSnapshot();
32
+ } );
33
+
34
+ it( 'renders the button disabled', () => {
35
+ const component = renderer.create( renderImageUploadButton( true, 'label' )( { open } ) );
36
+ expect( component.toJSON() ).toMatchSnapshot();
37
+ } );
38
+
39
+ it( 'executes the open action when the mediaUpload is fired', () => {
40
+ const component = mount( renderImageUploadButton( false, 'label' )( { open } ) );
41
+ component.find( 'button' ).simulate( 'click' );
42
+ expect( open ).toHaveBeenCalled();
43
+ expect( open ).toHaveBeenCalledTimes( 1 );
44
+ } );
45
+ } );
46
+
47
+ describe( 'renderImage', () => {
48
+ const onRemove = jest.fn();
49
+ const image = {
50
+ id: 42,
51
+ src: 'test-src',
52
+ alt: 'test-alt',
53
+ };
54
+
55
+ afterEach( () => {
56
+ onRemove.mockClear();
57
+ } );
58
+
59
+ it( 'renders the image and button', () => {
60
+ const component = renderer.create( renderImage( false, image, onRemove ) );
61
+ expect( component.toJSON() ).toMatchSnapshot();
62
+ } );
63
+
64
+ it( 'renders the image and disabled button', () => {
65
+ const component = renderer.create( renderImage( true, image, onRemove ) );
66
+ expect( component.toJSON() ).toMatchSnapshot();
67
+ } );
68
+
69
+ it( 'executes onRemove on click', () => {
70
+ const component = mount( renderImage( false, image, onRemove ) );
71
+ component.find( 'button' ).simulate( 'click' );
72
+ expect( onRemove ).toHaveBeenCalled();
73
+ expect( onRemove ).toHaveBeenCalledTimes( 1 );
74
+ } );
75
+ } );
76
+
77
+ describe( 'ImageUpload', () => {
78
+ const onRemove = jest.fn();
79
+ const onSelect = jest.fn();
80
+ let image;
81
+
82
+ beforeEach( () => {
83
+ image = {
84
+ id: 0,
85
+ src: '',
86
+ alt: '',
87
+ };
88
+ } );
89
+
90
+ afterEach( () => {
91
+ onRemove.mockClear();
92
+ onSelect.mockClear();
93
+ } );
94
+
95
+ it( 'renders the component', () => {
96
+ const component = renderer.create(
97
+ <ImageUpload
98
+ image={ image }
99
+ onSelect={ onSelect }
100
+ onRemove={ onRemove }
101
+ />
102
+ );
103
+ expect( component.toJSON() ).toMatchSnapshot();
104
+ } );
105
+
106
+ it( 'renders with title', () => {
107
+ const component = renderer.create(
108
+ <ImageUpload
109
+ image={ image }
110
+ onSelect={ onSelect }
111
+ onRemove={ onRemove }
112
+ title="Modern Tribe"
113
+ />
114
+ );
115
+ expect( component.toJSON() ).toMatchSnapshot();
116
+ } );
117
+
118
+ it( 'renders with description', () => {
119
+ const component = renderer.create(
120
+ <ImageUpload
121
+ image={ image }
122
+ onSelect={ onSelect}
123
+ onRemove={ onRemove }
124
+ description="The Next Generation of Digital Agency"
125
+ />
126
+ );
127
+ expect( component.toJSON() ).toMatchSnapshot();
128
+ } );
129
+
130
+ it( 'renders with class', () => {
131
+ const component = renderer.create(
132
+ <ImageUpload
133
+ image={ image }
134
+ onSelect={ onSelect }
135
+ onRemove={ onRemove }
136
+ className="test-class"
137
+ />
138
+ );
139
+ expect( component.toJSON() ).toMatchSnapshot();
140
+ } );
141
+
142
+ it( 'renders uploaded image', () => {
143
+ image = {
144
+ id: 42,
145
+ src: 'test-src',
146
+ alt: 'test-alt',
147
+ };
148
+ const component = renderer.create(
149
+ <ImageUpload
150
+ image={ image }
151
+ onSelect={ onSelect }
152
+ onRemove={ onRemove }
153
+ />
154
+ );
155
+ expect( component.toJSON() ).toMatchSnapshot();
156
+ } );
157
+
158
+ it( 'renders upload image button', () => {
159
+ const component = renderer.create(
160
+ <ImageUpload
161
+ image={ image }
162
+ onSelect={ onSelect }
163
+ onRemove={ onRemove }
164
+ />
165
+ );
166
+ expect( component.toJSON() ).toMatchSnapshot();
167
+ } );
168
+ } );
common/src/modules/elements/image-upload/element.js ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+ import { noop } from 'lodash';
8
+
9
+ /**
10
+ * WordPress dependencies
11
+ */
12
+ import { __ } from '@wordpress/i18n';
13
+ import { MediaUpload } from '@wordpress/editor';
14
+
15
+ /**
16
+ * Internal dependencies
17
+ */
18
+ import { Button, Image } from '@moderntribe/common/elements';
19
+ import { Close as CloseIcon } from '@moderntribe/common/icons';
20
+ import './style.pcss';
21
+
22
+ export const renderImageUploadButton = ( disabled, label ) => ( { open } ) => (
23
+ <Button
24
+ onClick={ open }
25
+ className={ [ 'tribe-editor__button--sm', 'tribe-editor__image-upload__upload-button' ] }
26
+ disabled={ disabled }
27
+ >
28
+ { label }
29
+ </Button>
30
+ );
31
+
32
+ export const renderImage = ( disabled, image, onRemove ) => (
33
+ <div className="tribe-editor__image-upload__image-wrapper">
34
+ <Image
35
+ src={ image.src }
36
+ alt={ image.alt }
37
+ className="tribe-editor__image-upload__image"
38
+ />
39
+ <Button
40
+ className="tribe-editor__image-upload__remove-button"
41
+ onClick={ onRemove }
42
+ disabled={ disabled }
43
+ >
44
+ <CloseIcon />
45
+ <span className="tribe-editor__image-upload__remove-button-text">
46
+ { __( 'remove', 'tribe-common' ) }
47
+ </span>
48
+ </Button>
49
+ </div>
50
+ );
51
+
52
+ const ImageUpload = ( {
53
+ buttonDisabled,
54
+ buttonLabel,
55
+ className,
56
+ description,
57
+ image,
58
+ onRemove,
59
+ onSelect,
60
+ removeButtonDisabled,
61
+ title,
62
+ } ) => {
63
+ const hasImageClass = { 'tribe-editor__image-upload--has-image': image.id };
64
+
65
+ return (
66
+ <div className={ classNames(
67
+ 'tribe-editor__image-upload',
68
+ hasImageClass,
69
+ className,
70
+ ) }>
71
+ { title && <h3 className="tribe-editor__image-upload__title">{ title }</h3> }
72
+ <div className="tribe-editor__image-upload__content">
73
+ { description && (
74
+ <p className="tribe-editor__image-upload__description">{ description }</p>
75
+ ) }
76
+ {
77
+ image.id
78
+ ? renderImage( removeButtonDisabled, image, onRemove )
79
+ : (
80
+ <MediaUpload
81
+ onSelect={ onSelect }
82
+ type="image"
83
+ render={ renderImageUploadButton( buttonDisabled, buttonLabel ) }
84
+ value={ image.id }
85
+ />
86
+ )
87
+ }
88
+ </div>
89
+ </div>
90
+ );
91
+ };
92
+
93
+ ImageUpload.propTypes = {
94
+ buttonDisabled: PropTypes.bool,
95
+ buttonLabel: PropTypes.string,
96
+ className: PropTypes.string,
97
+ description: PropTypes.string,
98
+ image: PropTypes.shape( {
99
+ alt: PropTypes.string.isRequired,
100
+ id: PropTypes.number.isRequired,
101
+ src: PropTypes.string.isRequired,
102
+ } ).isRequired,
103
+ onRemove: PropTypes.func.isRequired,
104
+ onSelect: PropTypes.func.isRequired,
105
+ removeButtonDisabled: PropTypes.bool,
106
+ title: PropTypes.string,
107
+ };
108
+
109
+ ImageUpload.defaultProps = {
110
+ onRemove: noop,
111
+ onSelect: noop,
112
+ };
113
+
114
+ export default ImageUpload;
common/src/modules/elements/image-upload/style.pcss ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__image-upload {}
2
+
3
+ .tribe-editor__image-upload__title {
4
+
5
+ /* extra classes to override gutenberg styles for h3 */
6
+ .edit-post-visual-editor .editor-block-list__block & {
7
+ padding: 0;
8
+ margin: 0 0 12px;
9
+ color: #000000;
10
+ font-size: 15px;
11
+ font-weight: bold;
12
+ line-height: 18px;
13
+ }
14
+ }
15
+
16
+ .tribe-editor__image-upload__content {
17
+ display: flex;
18
+ justify-content: space-between;
19
+ align-items: center;
20
+
21
+ .tribe-editor__image-upload--has-image & {
22
+ align-items: flex-start;
23
+ }
24
+ }
25
+
26
+ p.tribe-editor__image-upload__description {
27
+
28
+ .tribe-editor__image-upload__content & {
29
+ flex: none;
30
+ width: 52%;
31
+ font-family: Helvetica, sans-serif, arial;
32
+ font-size: 14px;
33
+ color: #545D66;
34
+ line-height: 18px;
35
+ margin: 0;
36
+ }
37
+ }
38
+
39
+ .tribe-editor__image-upload__upload-button {
40
+ flex: none;
41
+ margin-right: 10px;
42
+ }
43
+
44
+ .tribe-editor__image-upload__image-wrapper {
45
+ flex: none;
46
+ width: 42%;
47
+ max-width: 325px;
48
+ padding-left: 25px;
49
+ position: relative;
50
+ }
51
+
52
+ .tribe-editor__image-upload__remove-button {
53
+ position: absolute;
54
+ top: 10px;
55
+ right: 10px;
56
+ width: 32px;
57
+ height: 32px;
58
+ padding: 8px;
59
+ border-radius: 50%;
60
+ background-color: #F8F9FB;
61
+
62
+ & > svg {
63
+
64
+ &,
65
+ & path {
66
+ fill: #545D66;
67
+ }
68
+ }
69
+
70
+ &:hover,
71
+ &:focus {
72
+
73
+ & > svg {
74
+
75
+ &,
76
+ & path {
77
+ fill: #009FD4;
78
+ }
79
+ }
80
+ }
81
+
82
+ &:disabled {
83
+
84
+ &,
85
+ &:hover,
86
+ &:focus {
87
+
88
+ & > svg {
89
+
90
+ &,
91
+ & path {
92
+ fill: #AEB4BB;
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ /* visually hide remove button text */
100
+ .tribe-editor__image-upload__remove-button-text {
101
+ border: 0;
102
+ clip: rect(0 0 0 0);
103
+ height: 1px;
104
+ margin: -1px;
105
+ overflow: hidden;
106
+ padding: 0;
107
+ position: absolute;
108
+ width: 1px;
109
+ }
common/src/modules/elements/image/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Image Element renders image 1`] = `
4
+ <img
5
+ alt="test-alt"
6
+ className="tribe-editor__image"
7
+ src="test-src"
8
+ />
9
+ `;
10
+
11
+ exports[`Image Element renders image with class 1`] = `
12
+ <img
13
+ alt="test-alt"
14
+ className="tribe-editor__image test-class"
15
+ src="test-src"
16
+ />
17
+ `;
18
+
19
+ exports[`Image Element renders image with extra props 1`] = `
20
+ <img
21
+ alt="test-alt"
22
+ className="tribe-editor__image"
23
+ height="42"
24
+ src="test-src"
25
+ width="42"
26
+ />
27
+ `;
common/src/modules/elements/image/__tests__/element.test.js ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { Image } from '@moderntribe/common/elements';
10
+
11
+ let imageProps;
12
+
13
+ describe( 'Image Element', () => {
14
+ beforeEach( () => {
15
+ imageProps = {
16
+ src: 'test-src',
17
+ alt: 'test-alt',
18
+ };
19
+ } );
20
+
21
+ it( 'renders image', () => {
22
+ const component = renderer.create( <Image { ...imageProps } /> );
23
+ expect( component.toJSON() ).toMatchSnapshot();
24
+ } );
25
+
26
+ it( 'renders image with class', () => {
27
+ imageProps.className = 'test-class';
28
+ const component = renderer.create( <Image { ...imageProps } /> );
29
+ expect( component.toJSON() ).toMatchSnapshot();
30
+ } );
31
+
32
+ it( 'renders image with extra props', () => {
33
+ imageProps.width = "42";
34
+ imageProps.height = "42";
35
+ const component = renderer.create( <Image { ...imageProps } /> );
36
+ expect( component.toJSON() ).toMatchSnapshot();
37
+ } );
38
+ } );
common/src/modules/elements/image/element.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ const Image = ( {
9
+ alt,
10
+ className,
11
+ src,
12
+ ...rest,
13
+ } ) => (
14
+ <img
15
+ src={ src }
16
+ alt={ alt }
17
+ className={ classNames( 'tribe-editor__image', className ) }
18
+ { ...rest }
19
+ />
20
+ );
21
+
22
+ Image.propTypes = {
23
+ alt: PropTypes.string.isRequired,
24
+ className: PropTypes.string,
25
+ src: PropTypes.string.isRequired,
26
+ };
27
+
28
+ export default Image;
common/src/modules/elements/index.js ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export { default as Accordion } from './accordion/element';
2
+ export { default as BlockIcon } from '@moderntribe/common/elements/block-icon';
3
+ export { default as Button } from './button/element';
4
+
5
+ export { default as Counter } from './counter/element';
6
+ export { default as Image } from './image/element';
7
+ export { default as ImageUpload } from './image-upload/element';
8
+ export { default as LabeledItem } from './labeled-item/element';
9
+ export { default as LabelWithLink } from './label-with-link/element';
10
+ export { default as LabelWithModal } from './label-with-modal/element';
11
+ export { default as Link } from './link/element';
12
+ export { default as ModalButton } from './modal-button/element';
13
+ export { default as TimePicker } from './time-picker/element';
14
+ export { default as Tooltip } from './tooltip/element';
15
+ export { default as DayPickerInput } from './day-picker-input/element';
16
+ export { default as CreatableSelect } from './creatable-select/element';
17
+ export { default as Placeholder } from './placeholder/element';
18
+ export { default as Heading } from './heading/element';
19
+ export { default as Paragraph } from './paragraph/element';
20
+
21
+ // Inputs
22
+ export { default as Input } from './input/element';
23
+ export { default as UrlInput } from './url-input/element';
24
+ export { default as NumberInput } from './number-input/element';
25
+ export { default as Radio } from './radio/element';
26
+ export { default as RadioInput } from './radio-input/element';
27
+ export { default as Checkbox } from './checkbox/element';
28
+ export { default as CheckboxInput } from './checkbox-input/element';
29
+ export { default as Select } from './select/element';
30
+ export { default as Textarea } from './textarea/element';
31
+
32
+ /**
33
+ * @todo move this into Editor Module
34
+ */
35
+
36
+ import './style.pcss'
common/src/modules/elements/input/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Input element Should render the component 1`] = `
4
+ <input
5
+ className="tribe-editor__input"
6
+ type="text"
7
+ />
8
+ `;
9
+
10
+ exports[`Input element Should render the component with class 1`] = `
11
+ <input
12
+ className="tribe-editor__input input-class"
13
+ type="text"
14
+ />
15
+ `;
16
+
17
+ exports[`Input element Should render the component with extra props 1`] = `
18
+ <input
19
+ className="tribe-editor__input"
20
+ onChange={[Function]}
21
+ type="text"
22
+ />
23
+ `;
common/src/modules/elements/input/__tests__/element.test.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import renderer from 'react-test-renderer';
6
+ import { noop } from 'lodash';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import Input from '../element.js';
12
+
13
+ describe( 'Input element', () => {
14
+ it( 'Should render the component', () => {
15
+ const component = renderer.create( <Input type="text" /> );
16
+ expect( component.toJSON() ).toMatchSnapshot();
17
+ } );
18
+
19
+ it( 'Should render the component with class', () => {
20
+ const component = renderer.create( <Input type="text" className="input-class" /> );
21
+ expect( component.toJSON() ).toMatchSnapshot();
22
+ } );
23
+
24
+ it( 'Should render the component with extra props', () => {
25
+ const component = renderer.create( <Input type="text" onChange={ noop } /> );
26
+ expect( component.toJSON() ).toMatchSnapshot();
27
+ } );
28
+ } );
common/src/modules/elements/input/element.js ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External Dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import './style.pcss';
12
+
13
+ const Input = ( {
14
+ className,
15
+ type,
16
+ ...rest,
17
+ } ) => (
18
+ <input
19
+ className={ classNames( 'tribe-editor__input', className ) }
20
+ type={ type }
21
+ { ...rest }
22
+ />
23
+ );
24
+
25
+ Input.propTypes = {
26
+ className: PropTypes.string,
27
+ type: PropTypes.string.isRequired,
28
+ };
29
+
30
+ export default Input;
common/src/modules/elements/input/style.pcss ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* increased specificity required to override gutenberg editor styles */
2
+ input.tribe-editor__input {
3
+
4
+ /* this list will grow with more input types used */
5
+ &[type=number],
6
+ &[type=text] {
7
+ color: #000000;
8
+ font-size: 16px;
9
+ line-height: 24px;
10
+ border: 1px solid #E1E3E6;
11
+ padding: 7px 15px;
12
+ margin: 0;
13
+ height: 40px;
14
+
15
+ &:focus {
16
+ color: #000000;
17
+ box-shadow: none;
18
+ outline: none;
19
+ }
20
+
21
+ &:disabled {
22
+ color: #AEB4BB;
23
+ }
24
+ }
25
+ }
common/src/modules/elements/label-with-link/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Label With Link Element renders a label with link 1`] = `
4
+ <div
5
+ className="tribe-editor__labeled-item tribe-editor__label-with-link"
6
+ >
7
+ <span
8
+ className="tribe-editor__labeled-item__label"
9
+ />
10
+ <a
11
+ className="tribe-editor__link tribe-editor__label-with-link__link"
12
+ href="#"
13
+ >
14
+ test-text
15
+ </a>
16
+ </div>
17
+ `;
18
+
19
+ exports[`Label With Link Element renders a label with link with class 1`] = `
20
+ <div
21
+ className="tribe-editor__labeled-item tribe-editor__label-with-link test-class"
22
+ >
23
+ <span
24
+ className="tribe-editor__labeled-item__label"
25
+ />
26
+ <a
27
+ className="tribe-editor__link tribe-editor__label-with-link__link"
28
+ href="#"
29
+ >
30
+ test-text
31
+ </a>
32
+ </div>
33
+ `;
34
+
35
+ exports[`Label With Link Element renders a label with link with label 1`] = `
36
+ <div
37
+ className="tribe-editor__labeled-item tribe-editor__label-with-link"
38
+ >
39
+ <span
40
+ className="tribe-editor__labeled-item__label"
41
+ >
42
+ test label
43
+ </span>
44
+ <a
45
+ className="tribe-editor__link tribe-editor__label-with-link__link"
46
+ href="#"
47
+ >
48
+ test-text
49
+ </a>
50
+ </div>
51
+ `;
common/src/modules/elements/label-with-link/__tests__/element.test.js ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import LabelWithLink from '../element';
10
+
11
+ describe( 'Label With Link Element', () => {
12
+ it( 'renders a label with link', () => {
13
+ const component = renderer.create(
14
+ <LabelWithLink linkHref="#" linkText="test-text" />
15
+ );
16
+ expect( component.toJSON() ).toMatchSnapshot();
17
+ } );
18
+
19
+ it( 'renders a label with link with class', () => {
20
+ const component = renderer.create(
21
+ <LabelWithLink linkHref="#" linkText="test-text" className="test-class" />
22
+ );
23
+ expect( component.toJSON() ).toMatchSnapshot();
24
+ } );
25
+
26
+ it( 'renders a label with link with label', () => {
27
+ const component = renderer.create(
28
+ <LabelWithLink linkHref="#" linkText="test-text" label="test label" />
29
+ );
30
+ expect( component.toJSON() ).toMatchSnapshot();
31
+ } );
32
+ } );
common/src/modules/elements/label-with-link/element.js ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import Button from '@moderntribe/common/elements/button/element';
12
+ import LabeledItem from '@moderntribe/common/elements/labeled-item/element';
13
+ import Link from '@moderntribe/common/elements/link/element';
14
+ import './style.pcss';
15
+
16
+ const LabelWithLink = ( {
17
+ className,
18
+ label,
19
+ linkDisabled,
20
+ linkHref,
21
+ linkTarget,
22
+ linkText,
23
+ } ) => {
24
+ const getLink = () => {
25
+ const linkClass = 'tribe-editor__label-with-link__link';
26
+
27
+ return linkDisabled
28
+ ? (
29
+ <Button
30
+ className={ classNames( linkClass, `${ linkClass }--disabled` ) }
31
+ disabled={ true }
32
+ >
33
+ { linkText }
34
+ </Button>
35
+ )
36
+ : (
37
+ <Link
38
+ className={ linkClass }
39
+ href={ linkHref }
40
+ target={ linkTarget }
41
+ >
42
+ { linkText }
43
+ </Link>
44
+ );
45
+ };
46
+
47
+ return (
48
+ <LabeledItem
49
+ className={ classNames( 'tribe-editor__label-with-link', className ) }
50
+ label={ label }
51
+ >
52
+ { getLink() }
53
+ </LabeledItem>
54
+ );
55
+ };
56
+
57
+ LabelWithLink.propTypes = {
58
+ className: PropTypes.string,
59
+ label: PropTypes.node,
60
+ linkDisabled: PropTypes.bool,
61
+ linkHref: PropTypes.string.isRequired,
62
+ linkTarget: PropTypes.string,
63
+ linkText: PropTypes.string,
64
+ };
65
+
66
+ export default LabelWithLink;
common/src/modules/elements/label-with-link/style.pcss ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__label-with-link {
2
+
3
+ .tribe-editor__rsvp &,
4
+ .tribe-editor__ticket & {
5
+ display: flex;
6
+ align-items: center;
7
+ background-color: #FFFFFF;
8
+ padding: 10px 17px;
9
+ border: 1px solid #E1E3E6;
10
+ }
11
+
12
+ .tribe-editor__labeled-item__label {
13
+
14
+ .tribe-editor__rsvp &,
15
+ .tribe-editor__ticket & {
16
+ flex: auto;
17
+ color: #545D66;
18
+ font-size: 15px;
19
+ font-weight: bold;
20
+ line-height: 18px;
21
+ letter-spacing: 0.38px;
22
+ padding-right: 10px;
23
+ }
24
+ }
25
+
26
+ .tribe-editor__label-with-link__link {
27
+
28
+ .tribe-editor__rsvp &,
29
+ .tribe-editor__ticket & {
30
+ flex: none;
31
+ color: #009FD4;
32
+ font-size: 15px;
33
+ font-weight: bold;
34
+ line-height: 18px;
35
+ letter-spacing: 0.38px;
36
+ text-decoration: none;
37
+ box-shadow: none;
38
+ transition: color 0.2s ease;
39
+
40
+ &:hover,
41
+ &:focus {
42
+ color: #007BB4;
43
+ }
44
+ }
45
+ }
46
+
47
+ .tribe-editor__label-with-link__link.tribe-editor__label-with-link__link--disabled {
48
+
49
+ .tribe-editor__rsvp &,
50
+ .tribe-editor__ticket & {
51
+ color: #AEB4BB;
52
+ }
53
+ }
54
+ }
common/src/modules/elements/label-with-modal/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Label With Modal Element renders a label with modal 1`] = `
4
+ <div
5
+ className="tribe-editor__labeled-item tribe-editor__label-with-modal"
6
+ >
7
+ <span
8
+ className="tribe-editor__labeled-item__label"
9
+ />
10
+ <div
11
+ className="tribe-editor__modal-button tribe-editor__label-with-modal__modal-button"
12
+ >
13
+ <button
14
+ className="tribe-editor__button tribe-editor__modal-button__button"
15
+ onClick={[Function]}
16
+ type="button"
17
+ />
18
+ </div>
19
+ </div>
20
+ `;
21
+
22
+ exports[`Label With Modal Element renders a label with modal with class 1`] = `
23
+ <div
24
+ className="tribe-editor__labeled-item tribe-editor__label-with-modal test-class"
25
+ >
26
+ <span
27
+ className="tribe-editor__labeled-item__label"
28
+ />
29
+ <div
30
+ className="tribe-editor__modal-button tribe-editor__label-with-modal__modal-button"
31
+ >
32
+ <button
33
+ className="tribe-editor__button tribe-editor__modal-button__button"
34
+ onClick={[Function]}
35
+ type="button"
36
+ />
37
+ </div>
38
+ </div>
39
+ `;
40
+
41
+ exports[`Label With Modal Element renders a label with modal with label 1`] = `
42
+ <div
43
+ className="tribe-editor__labeled-item tribe-editor__label-with-modal"
44
+ >
45
+ <span
46
+ className="tribe-editor__labeled-item__label"
47
+ >
48
+ test label
49
+ </span>
50
+ <div
51
+ className="tribe-editor__modal-button tribe-editor__label-with-modal__modal-button"
52
+ >
53
+ <button
54
+ className="tribe-editor__button tribe-editor__modal-button__button"
55
+ onClick={[Function]}
56
+ type="button"
57
+ />
58
+ </div>
59
+ </div>
60
+ `;
common/src/modules/elements/label-with-modal/__tests__/element.test.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import LabelWithModal from '@moderntribe/common/elements/label-with-modal/element';
10
+
11
+ describe( 'Label With Modal Element', () => {
12
+ it( 'renders a label with modal', () => {
13
+ const component = renderer.create( <LabelWithModal /> );
14
+ expect( component.toJSON() ).toMatchSnapshot();
15
+ } );
16
+
17
+ it( 'renders a label with modal with class', () => {
18
+ const component = renderer.create( <LabelWithModal className="test-class" /> );
19
+ expect( component.toJSON() ).toMatchSnapshot();
20
+ } );
21
+
22
+ it( 'renders a label with modal with label', () => {
23
+ const component = renderer.create( <LabelWithModal label="test label" /> );
24
+ expect( component.toJSON() ).toMatchSnapshot();
25
+ } );
26
+ } );
common/src/modules/elements/label-with-modal/element.js ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+ import { noop } from 'lodash';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import LabeledItem from '@moderntribe/common/elements/labeled-item/element';
13
+ import ModalButton from '@moderntribe/common/elements/modal-button/element';
14
+ import './style.pcss';
15
+
16
+ const LabelWithModal = ( {
17
+ className,
18
+ isOpen,
19
+ label,
20
+ modalButtonDisabled,
21
+ modalButtonLabel,
22
+ modalClassName,
23
+ modalContent,
24
+ modalOverlayClassName,
25
+ modalTitle,
26
+ onClick,
27
+ onClose,
28
+ onOpen,
29
+ } ) => (
30
+ <LabeledItem
31
+ className={ classNames( 'tribe-editor__label-with-modal', className ) }
32
+ label={ label }
33
+ >
34
+ <ModalButton
35
+ className="tribe-editor__label-with-modal__modal-button"
36
+ disabled={ modalButtonDisabled }
37
+ isOpen={ isOpen }
38
+ label={ modalButtonLabel }
39
+ modalClassName={ modalClassName }
40
+ modalContent={ modalContent }
41
+ modalOverlayClassName={ modalOverlayClassName }
42
+ modalTitle={ modalTitle }
43
+ onClick={ onClick }
44
+ onClose={ onClose }
45
+ onOpen={ onOpen }
46
+ />
47
+ </LabeledItem>
48
+ );
49
+
50
+ LabelWithModal.defaultProps = {
51
+ onClick: noop,
52
+ onClose: noop,
53
+ onOpen: noop,
54
+ };
55
+
56
+ LabelWithModal.propTypes = {
57
+ className: PropTypes.string,
58
+ isOpen: PropTypes.bool,
59
+ label: PropTypes.node,
60
+ modalButtonDisabled: PropTypes.bool,
61
+ modalButtonLabel: PropTypes.string,
62
+ modalClassName: PropTypes.string,
63
+ modalContent: PropTypes.node,
64
+ modalOverlayClassName: PropTypes.string,
65
+ modalTitle: PropTypes.string,
66
+ onClick: PropTypes.func,
67
+ onClose: PropTypes.func,
68
+ onOpen: PropTypes.func,
69
+ };
70
+
71
+ export default LabelWithModal;
common/src/modules/elements/label-with-modal/style.pcss ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__label-with-modal {
2
+ display: flex;
3
+ align-items: center;
4
+ background-color: #FFFFFF;
5
+ padding: 11px 17px;
6
+ border: 1px solid #E1E3E6;
7
+ height: 40px;
8
+ }
9
+
10
+ .tribe-editor__labeled-item__label {
11
+
12
+ .tribe-editor__label-with-modal & {
13
+ flex: auto;
14
+ color: #545D66;
15
+ font-size: 15px;
16
+ font-weight: bold;
17
+ line-height: 18px;
18
+ letter-spacing: 0.38px;
19
+ }
20
+ }
21
+
22
+ .tribe-editor__label-with-modal__modal-button {
23
+
24
+ .tribe-editor__label-with-modal & {
25
+ flex: none;
26
+ }
27
+ }
28
+
29
+ .tribe-editor__modal-button__button {
30
+
31
+ .tribe-editor__label-with-modal & {
32
+ color: #009FD4;
33
+ font-size: 15px;
34
+ font-weight: bold;
35
+ line-height: 18px;
36
+ letter-spacing: 0.38px;
37
+ transition: color 0.2s ease;
38
+
39
+ &:hover,
40
+ &:focus {
41
+ color: #007BB4;
42
+ }
43
+
44
+ &:disabled {
45
+
46
+ &,
47
+ &:hover,
48
+ &:focus {
49
+ color: #AEB4BB;
50
+ }
51
+ }
52
+ }
53
+ }
common/src/modules/elements/labeled-item/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Labeled Item Element renders labeled item 1`] = `
4
+ <div
5
+ className="tribe-editor__labeled-item"
6
+ >
7
+ <span
8
+ className="tribe-editor__labeled-item__label"
9
+ />
10
+ Test
11
+ </div>
12
+ `;
13
+
14
+ exports[`Labeled Item Element renders labeled item with class 1`] = `
15
+ <div
16
+ className="tribe-editor__labeled-item test-class"
17
+ >
18
+ <span
19
+ className="tribe-editor__labeled-item__label"
20
+ />
21
+ Test
22
+ </div>
23
+ `;
24
+
25
+ exports[`Labeled Item Element renders labeled item with label 1`] = `
26
+ <div
27
+ className="tribe-editor__labeled-item"
28
+ >
29
+ <span
30
+ className="tribe-editor__labeled-item__label"
31
+ >
32
+ test label
33
+ </span>
34
+ Test
35
+ </div>
36
+ `;
37
+
38
+ exports[`Labeled Item Element renders labeled item with label element and for id 1`] = `
39
+ <div
40
+ className="tribe-editor__labeled-item"
41
+ >
42
+ <label
43
+ className="tribe-editor__labeled-item__label"
44
+ htmlFor="test-id"
45
+ >
46
+ test label
47
+ </label>
48
+ Test
49
+ </div>
50
+ `;
common/src/modules/elements/labeled-item/__tests__/element.test.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { LabeledItem } from '@moderntribe/common/elements';
10
+
11
+ describe( 'Labeled Item Element', () => {
12
+ it( 'renders labeled item', () => {
13
+ const component = renderer.create( <LabeledItem>Test</LabeledItem> );
14
+ expect( component.toJSON() ).toMatchSnapshot();
15
+ } );
16
+
17
+ it( 'renders labeled item with class', () => {
18
+ const component = renderer.create( <LabeledItem className="test-class">Test</LabeledItem> );
19
+ expect( component.toJSON() ).toMatchSnapshot();
20
+ } );
21
+
22
+ it( 'renders labeled item with label', () => {
23
+ const component = renderer.create( <LabeledItem label="test label">Test</LabeledItem> );
24
+ expect( component.toJSON() ).toMatchSnapshot();
25
+ } );
26
+
27
+ it( 'renders labeled item with label element and for id', () => {
28
+ const component = renderer.create(
29
+ <LabeledItem
30
+ label="test label"
31
+ isLabel={ true }
32
+ forId="test-id"
33
+ >
34
+ Test
35
+ </LabeledItem>
36
+ );
37
+ expect( component.toJSON() ).toMatchSnapshot();
38
+ } );
39
+ } );
common/src/modules/elements/labeled-item/element.js ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ const LabeledItem = ( {
9
+ className,
10
+ forId,
11
+ isLabel,
12
+ label,
13
+ children,
14
+ } ) => {
15
+ const renderLabel = (
16
+ isLabel
17
+ ? (
18
+ <label className="tribe-editor__labeled-item__label" htmlFor={ forId }>
19
+ { label }
20
+ </label>
21
+ )
22
+ : (
23
+ <span className="tribe-editor__labeled-item__label">
24
+ { label }
25
+ </span>
26
+ )
27
+ );
28
+
29
+ return (
30
+ <div className={ classNames(
31
+ 'tribe-editor__labeled-item',
32
+ className,
33
+ ) }>
34
+ { renderLabel }
35
+ { children }
36
+ </div>
37
+ );
38
+ };
39
+
40
+ LabeledItem.defaultProps = {
41
+ isLabel: false,
42
+ };
43
+
44
+ LabeledItem.propTypes = {
45
+ className: PropTypes.string,
46
+ isLabel: PropTypes.bool.isRequired,
47
+ forId: PropTypes.string,
48
+ label: PropTypes.node,
49
+ children: PropTypes.node.isRequired,
50
+ };
51
+
52
+ export default LabeledItem;
common/src/modules/elements/link/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Link Element renders button with class 1`] = `
4
+ <a
5
+ className="tribe-editor__link test-class"
6
+ href="#"
7
+ >
8
+ label
9
+ </a>
10
+ `;
11
+
12
+ exports[`Link Element renders button with prop 1`] = `
13
+ <a
14
+ className="tribe-editor__link"
15
+ href="#"
16
+ title="title"
17
+ >
18
+ label
19
+ </a>
20
+ `;
21
+
22
+ exports[`Link Element renders button with target 1`] = `
23
+ <a
24
+ className="tribe-editor__link"
25
+ href="#"
26
+ rel="noopener noreferrer"
27
+ target="_blank"
28
+ >
29
+ label
30
+ </a>
31
+ `;
32
+
33
+ exports[`Link Element renders link 1`] = `
34
+ <a
35
+ className="tribe-editor__link"
36
+ href="#"
37
+ >
38
+ label
39
+ </a>
40
+ `;
common/src/modules/elements/link/__tests__/element.test.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import Link from '../element.js';
10
+
11
+ describe( 'Link Element', () => {
12
+ it( 'renders link', () => {
13
+ const component = renderer.create(
14
+ <Link href="#">label</Link>
15
+ );
16
+ expect( component.toJSON() ).toMatchSnapshot();
17
+ } );
18
+
19
+ it( 'renders button with class', () => {
20
+ const component = renderer.create(
21
+ <Link className="test-class" href="#">label</Link>
22
+ );
23
+ expect( component.toJSON() ).toMatchSnapshot();
24
+ } );
25
+
26
+ it( 'renders button with target', () => {
27
+ const component = renderer.create(
28
+ <Link href="#" target="_blank">label</Link>
29
+ );
30
+ expect( component.toJSON() ).toMatchSnapshot();
31
+ } );
32
+
33
+ it( 'renders button with prop', () => {
34
+ const component = renderer.create(
35
+ <Link href="#" title="title">label</Link>
36
+ );
37
+ expect( component.toJSON() ).toMatchSnapshot();
38
+ } );
39
+ } );
common/src/modules/elements/link/element.js ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ const Link = ( {
9
+ children,
10
+ className,
11
+ href,
12
+ target,
13
+ ...props,
14
+ } ) => {
15
+ const getProps = () => {
16
+ const elemProps = { ...props };
17
+
18
+ if ( target === '_blank' ) {
19
+ elemProps.rel = 'noopener noreferrer';
20
+ }
21
+
22
+ return elemProps;
23
+ };
24
+
25
+ return (
26
+ <a
27
+ className={ classNames( 'tribe-editor__link', className ) }
28
+ href={ href }
29
+ target={ target }
30
+ { ...getProps() }
31
+ >
32
+ { children }
33
+ </a>
34
+ );
35
+ };
36
+
37
+ Link.propTypes = {
38
+ children: PropTypes.node,
39
+ className: PropTypes.string,
40
+ href: PropTypes.string.isRequired,
41
+ target: PropTypes.string,
42
+ };
43
+
44
+ export default Link;
common/src/modules/elements/modal-button/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Modal Button Element renders a modal button 1`] = `
4
+ <div
5
+ className="tribe-editor__modal-button"
6
+ >
7
+ <button
8
+ className="tribe-editor__button tribe-editor__modal-button__button"
9
+ onClick={[Function]}
10
+ type="button"
11
+ />
12
+ </div>
13
+ `;
14
+
15
+ exports[`Modal Button Element renders a modal button with class 1`] = `
16
+ <div
17
+ className="tribe-editor__modal-button test-class"
18
+ >
19
+ <button
20
+ className="tribe-editor__button tribe-editor__modal-button__button"
21
+ onClick={[Function]}
22
+ type="button"
23
+ />
24
+ </div>
25
+ `;
26
+
27
+ exports[`Modal Button Element renders a modal button with label 1`] = `
28
+ <div
29
+ className="tribe-editor__modal-button"
30
+ >
31
+ <button
32
+ className="tribe-editor__button tribe-editor__modal-button__button"
33
+ onClick={[Function]}
34
+ type="button"
35
+ >
36
+ Test Label
37
+ </button>
38
+ </div>
39
+ `;
40
+
41
+ exports[`Modal Button Element renders a modal button with onClick handler 1`] = `
42
+ <div
43
+ className="tribe-editor__modal-button"
44
+ >
45
+ <button
46
+ className="tribe-editor__button tribe-editor__modal-button__button"
47
+ onClick={[Function]}
48
+ type="button"
49
+ />
50
+ </div>
51
+ `;
common/src/modules/elements/modal-button/__tests__/element.test.js ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import { noop } from 'lodash';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import ModalButton from '@moderntribe/common/elements/modal-button/element';
11
+
12
+ describe( 'Modal Button Element', () => {
13
+ it( 'renders a modal button', () => {
14
+ const component = renderer.create( <ModalButton /> );
15
+ expect( component.toJSON() ).toMatchSnapshot();
16
+ } );
17
+
18
+ it( 'renders a modal button with class', () => {
19
+ const component = renderer.create( <ModalButton className="test-class" /> );
20
+ expect( component.toJSON() ).toMatchSnapshot();
21
+ } );
22
+
23
+ it( 'renders a modal button with onClick handler', () => {
24
+ const component = renderer.create( <ModalButton onClick={ noop } /> );
25
+ expect( component.toJSON() ).toMatchSnapshot();
26
+ } );
27
+
28
+ it( 'renders a modal button with label', () => {
29
+ const component = renderer.create( <ModalButton label="Test Label" /> );
30
+ expect( component.toJSON() ).toMatchSnapshot();
31
+ } );
32
+
33
+ it( 'executes onClick and onOpen handlers', () => {
34
+ const props = {
35
+ onClick: jest.fn(),
36
+ onOpen: jest.fn(),
37
+ };
38
+
39
+ const component = mount( <ModalButton { ...props } /> );
40
+ component.find( 'button.tribe-editor__modal-button__button' ).simulate( 'click' );
41
+ expect( props.onClick ).toHaveBeenCalled();
42
+ expect( props.onClick ).toHaveBeenCalledTimes( 1 );
43
+ expect( props.onOpen ).toHaveBeenCalled();
44
+ expect( props.onOpen ).toHaveBeenCalledTimes( 1 );
45
+ } );
46
+ } );
common/src/modules/elements/modal-button/element.js ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { PureComponent } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * WordPress dependencies
10
+ */
11
+ import { Modal } from '@wordpress/components';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import Button from '@moderntribe/common/elements/button/element';
17
+
18
+ class ModalButton extends PureComponent {
19
+ static propTypes = {
20
+ className: PropTypes.string,
21
+ disabled: PropTypes.bool,
22
+ isOpen: PropTypes.bool,
23
+ label: PropTypes.string,
24
+ modalClassName: PropTypes.string,
25
+ modalContent: PropTypes.node,
26
+ modalOverlayClassName: PropTypes.string,
27
+ modalTitle: PropTypes.string,
28
+ onClick: PropTypes.func,
29
+ onClose: PropTypes.func,
30
+ onOpen: PropTypes.func,
31
+ };
32
+
33
+ constructor( props ) {
34
+ super( props );
35
+ this.state = {
36
+ isOpen: false,
37
+ };
38
+ }
39
+
40
+ onClick = ( e ) => {
41
+ this.props.onClick && this.props.onClick( e );
42
+ this.onOpen();
43
+ this.props.isOpen === undefined && this.setState( { isOpen: true } );
44
+ };
45
+
46
+ onRequestClose = () => {
47
+ this.onClose();
48
+ this.props.isOpen === undefined && this.setState( { isOpen: false } );
49
+ }
50
+
51
+ onOpen = () => this.props.onOpen && this.props.onOpen();
52
+
53
+ onClose = () => this.props.onClose && this.props.onClose();
54
+
55
+ renderModal = () => {
56
+ const {
57
+ modalClassName,
58
+ modalContent,
59
+ modalOverlayClassName,
60
+ modalTitle,
61
+ } = this.props;
62
+
63
+ const isOpen = this.props.isOpen !== undefined ? this.props.isOpen : this.state.isOpen;
64
+
65
+ return ( isOpen && (
66
+ <Modal
67
+ className={ classNames(
68
+ 'tribe-editor__modal-button__modal-content',
69
+ modalClassName,
70
+ ) }
71
+ onRequestClose={ this.onRequestClose }
72
+ overlayClassName={ classNames(
73
+ 'tribe-editor__modal-button__modal-overlay',
74
+ modalOverlayClassName,
75
+ ) }
76
+ title={ modalTitle }
77
+ >
78
+ { modalContent }
79
+ </Modal>
80
+ ) );
81
+ };
82
+
83
+ render() {
84
+ const { className, disabled, label } = this.props;
85
+ return (
86
+ <div className={ classNames(
87
+ 'tribe-editor__modal-button',
88
+ className,
89
+ ) }>
90
+ <Button
91
+ className="tribe-editor__modal-button__button"
92
+ onClick={ this.onClick }
93
+ disabled={ disabled }
94
+ >
95
+ { label }
96
+ </Button>
97
+ { this.renderModal() }
98
+ </div>
99
+ );
100
+ }
101
+ }
102
+
103
+ export default ModalButton;
common/src/modules/elements/number-input/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Input element Should render the component 1`] = `
4
+ <input
5
+ className="tribe-editor__input tribe-editor__input--number"
6
+ type="number"
7
+ />
8
+ `;
9
+
10
+ exports[`Input element Should render the component with class 1`] = `
11
+ <input
12
+ className="tribe-editor__input tribe-editor__input--number input-class"
13
+ type="number"
14
+ />
15
+ `;
16
+
17
+ exports[`Input element Should render the component with extra props 1`] = `
18
+ <input
19
+ className="tribe-editor__input tribe-editor__input--number"
20
+ id="input-id"
21
+ type="number"
22
+ />
23
+ `;
24
+
25
+ exports[`Input element Should render the component with max 1`] = `
26
+ <input
27
+ className="tribe-editor__input tribe-editor__input--number"
28
+ max={42}
29
+ type="number"
30
+ />
31
+ `;
32
+
33
+ exports[`Input element Should render the component with min 1`] = `
34
+ <input
35
+ className="tribe-editor__input tribe-editor__input--number"
36
+ min={-42}
37
+ type="number"
38
+ />
39
+ `;
40
+
41
+ exports[`Input element Should render the component with onChange handler 1`] = `
42
+ <input
43
+ className="tribe-editor__input tribe-editor__input--number"
44
+ onChange={[Function]}
45
+ type="number"
46
+ />
47
+ `;
48
+
49
+ exports[`Input element Should render the component with step 1`] = `
50
+ <input
51
+ className="tribe-editor__input tribe-editor__input--number"
52
+ step={10}
53
+ type="number"
54
+ />
55
+ `;
common/src/modules/elements/number-input/__tests__/element.test.js ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import { noop } from 'lodash';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import NumberInput from '../element.js';
11
+
12
+ describe( 'Input element', () => {
13
+ it( 'Should render the component', () => {
14
+ const component = renderer.create( <NumberInput /> );
15
+ expect( component.toJSON() ).toMatchSnapshot();
16
+ } );
17
+
18
+ it( 'Should render the component with class', () => {
19
+ const component = renderer.create( <NumberInput className="input-class" /> );
20
+ expect( component.toJSON() ).toMatchSnapshot();
21
+ } );
22
+
23
+ it( 'Should render the component with max', () => {
24
+ const component = renderer.create( <NumberInput max={ 42 } /> );
25
+ expect( component.toJSON() ).toMatchSnapshot();
26
+ } );
27
+
28
+ it( 'Should render the component with min', () => {
29
+ const component = renderer.create( <NumberInput min={ -42 } /> );
30
+ expect( component.toJSON() ).toMatchSnapshot();
31
+ } );
32
+
33
+ it( 'Should render the component with onChange handler', () => {
34
+ const component = renderer.create( <NumberInput onChange={ noop } /> );
35
+ expect( component.toJSON() ).toMatchSnapshot();
36
+ } );
37
+
38
+ it( 'Should render the component with step', () => {
39
+ const component = renderer.create( <NumberInput step={ 10 } /> );
40
+ expect( component.toJSON() ).toMatchSnapshot();
41
+ } );
42
+
43
+ it( 'Should render the component with extra props', () => {
44
+ const component = renderer.create( <NumberInput id="input-id" /> );
45
+ expect( component.toJSON() ).toMatchSnapshot();
46
+ } );
47
+ } );
common/src/modules/elements/number-input/element.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External Dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import Input from '@moderntribe/common/elements/input/element';
12
+
13
+ const NumberInput = ( {
14
+ className,
15
+ max,
16
+ min,
17
+ onChange,
18
+ step,
19
+ ...rest
20
+ } ) => (
21
+ <Input
22
+ className={ classNames( 'tribe-editor__input--number', className ) }
23
+ max={ max }
24
+ min={ min }
25
+ onChange={ onChange }
26
+ step={ step }
27
+ type="number"
28
+ { ...rest }
29
+ />
30
+ );
31
+
32
+ NumberInput.propTypes = {
33
+ className: PropTypes.string,
34
+ max: PropTypes.number,
35
+ min: PropTypes.number,
36
+ onChange: PropTypes.func,
37
+ step: PropTypes.number,
38
+ };
39
+
40
+ export default NumberInput;
common/src/modules/elements/paragraph/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<Paragraph> default paragraph 1`] = `
4
+ <p
5
+ className="tribe-editor__paragraph tribe-editor__paragraph--medium"
6
+ >
7
+ Modern Tribe
8
+ </p>
9
+ `;
10
+
11
+ exports[`<Paragraph> smaller paragraph 1`] = `
12
+ <p
13
+ className="tribe-editor__paragraph tribe-editor__paragraph--small"
14
+ >
15
+ Modern Tribe
16
+ </p>
17
+ `;
common/src/modules/elements/paragraph/__tests__/element.test.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+
3
+ import Paragraph, { SIZES } from '../element';
4
+
5
+ describe( '<Paragraph>', () => {
6
+ test( 'default paragraph', () => {
7
+ const component = renderer.create(
8
+ <Paragraph>Modern Tribe</Paragraph>,
9
+ );
10
+ expect( component.toJSON() ).toMatchSnapshot();
11
+ } );
12
+
13
+ test( 'smaller paragraph', () => {
14
+ const component = renderer.create(
15
+ <Paragraph size={ SIZES.small }>Modern Tribe</Paragraph>,
16
+ );
17
+ expect( component.toJSON() ).toMatchSnapshot();
18
+ } );
19
+ } );
common/src/modules/elements/paragraph/element.js ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import './style.pcss';
12
+
13
+ export const SIZES = {
14
+ medium: 'medium',
15
+ small: 'small',
16
+ };
17
+
18
+ const Paragraph = ( { children, size, className } ) => (
19
+ <p
20
+ className={
21
+ classNames(
22
+ 'tribe-editor__paragraph',
23
+ `tribe-editor__paragraph--${ size }`,
24
+ className,
25
+ )
26
+ }
27
+ >
28
+ { children }
29
+ </p>
30
+ );
31
+
32
+ Paragraph.propTypes = {
33
+ children: PropTypes.node.isRequired,
34
+ size: PropTypes.oneOf( Object.keys( SIZES ) ),
35
+ };
36
+
37
+ Paragraph.defaultProps = {
38
+ size: SIZES.medium,
39
+ };
40
+
41
+ export default Paragraph;
common/src/modules/elements/paragraph/style.pcss ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__paragraph {
2
+ font-family: 'Helvetica', 'Arial', 'sans-serif';
3
+ line-height: 1.5;
4
+ font-weight: normal;
5
+
6
+ &--medium {
7
+ /* 16pt */
8
+ font-size: 1rem;
9
+ }
10
+
11
+ &--small {
12
+ /* 14pt */
13
+ font-size: 0.875rem;
14
+ }
15
+
16
+ a {
17
+ color: #11A0D2;
18
+
19
+ &:hover {
20
+ text-decoration: none;
21
+ color: #007BB4;
22
+ }
23
+ }
24
+ }
common/src/modules/elements/placeholder/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<Placeholder> component Custom Class name attached 1`] = `
4
+ <div
5
+ className="tribe-editor__placeholder custom-class-name"
6
+ >
7
+ Custom Text
8
+ </div>
9
+ `;
10
+
11
+ exports[`<Placeholder> component Default behavior 1`] = `
12
+ <div
13
+ className="tribe-editor__placeholder"
14
+ >
15
+ Custom Text
16
+ </div>
17
+ `;
common/src/modules/elements/placeholder/__tests__/element.test.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import Placeholder from '../element';
3
+
4
+ describe( '<Placeholder> component', () => {
5
+ test( 'Default behavior', () => {
6
+ const component = renderer.create(
7
+ <Placeholder>Custom Text</Placeholder>
8
+ );
9
+ expect( component.toJSON() ).toMatchSnapshot();
10
+ } );
11
+
12
+ test( 'Custom Class name attached', () => {
13
+ const component = renderer.create(
14
+ <Placeholder className='custom-class-name'>Custom Text</Placeholder>
15
+ );
16
+ expect( component.toJSON() ).toMatchSnapshot();
17
+ } );
18
+ } );
common/src/modules/elements/placeholder/element.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import './style.pcss';
12
+
13
+ const Placeholder = ( { children, className } ) => (
14
+ <div className={ classNames( 'tribe-editor__placeholder', className ) }>
15
+ { children }
16
+ </div>
17
+ );
18
+
19
+ Placeholder.propTypes = {
20
+ children: PropTypes.node.isRequired,
21
+ };
22
+
23
+ export default Placeholder;
common/src/modules/elements/placeholder/style.pcss ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__placeholder {
2
+ border: 2px dashed #E1E3E6;
3
+ padding: 12px 15px 14px;
4
+ text-align: center;
5
+ font-family: 'Helvetica', 'Arial', 'sans-serif';
6
+ font-size: 1rem;
7
+ line-height: 1.5;
8
+ font-weight: bold;
9
+ color: #8D949B;
10
+ min-width: 260px;
11
+ display: inline-block;
12
+ }
common/src/modules/elements/radio-input/element.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External Dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import Input from '@moderntribe/common/elements/input/element';
12
+
13
+ const RadioInput = ( { checked, className, onChange, ...rest } ) => (
14
+ <Input
15
+ checked={ checked }
16
+ className={ classNames( 'tribe-editor__input--radio', className ) }
17
+ onChange={ onChange }
18
+ type="radio"
19
+ { ...rest }
20
+ />
21
+ );
22
+
23
+ RadioInput.propTypes = {
24
+ checked: PropTypes.bool,
25
+ className: PropTypes.string,
26
+ onChange: PropTypes.func,
27
+ };
28
+
29
+ export default RadioInput;
common/src/modules/elements/radio/element.js ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+ import { noop } from 'lodash';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { RadioInput } from '@moderntribe/common/elements';
13
+
14
+ const Radio = ( { checked, className, disabled, id, label, onChange, name, value } ) => (
15
+ <div className={ classNames( 'tribe-editor__radio', className ) }>
16
+ <RadioInput
17
+ checked={ checked }
18
+ className="tribe-editor__radio__input"
19
+ disabled={ disabled }
20
+ id={ id }
21
+ name={ name }
22
+ onChange={ onChange }
23
+ value={ value }
24
+ />
25
+ <label
26
+ className="tribe-editor__radio_label"
27
+ htmlFor={ id }
28
+ >
29
+ { label }
30
+ </label>
31
+ </div>
32
+ );
33
+
34
+ Radio.defaultProps = {
35
+ checked: false,
36
+ onChange: noop,
37
+ };
38
+
39
+ Radio.propTypes = {
40
+ checked: PropTypes.bool.isRequired,
41
+ className: PropTypes.string,
42
+ disabled: PropTypes.bool,
43
+ id: PropTypes.string,
44
+ label: PropTypes.string,
45
+ name: PropTypes.string,
46
+ onChange: PropTypes.func,
47
+ value: PropTypes.string,
48
+ };
49
+
50
+ export default Radio;
common/src/modules/elements/select/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Select element Should render the component 1`] = `
4
+ <div
5
+ className="css-10nd86i tribe-editor__select"
6
+ onKeyDown={[Function]}
7
+ >
8
+ <div
9
+ className="css-vj8t7z tribe-editor__select__control"
10
+ onMouseDown={[Function]}
11
+ onTouchEnd={[Function]}
12
+ >
13
+ <div
14
+ className="css-1hwfws3 tribe-editor__select__value-container"
15
+ >
16
+ <div
17
+ className="css-1492t68 tribe-editor__select__placeholder"
18
+ >
19
+ Select...
20
+ </div>
21
+ <div
22
+ className="css-1g6gooi"
23
+ >
24
+ <div
25
+ className="tribe-editor__select__input"
26
+ style={
27
+ Object {
28
+ "display": "inline-block",
29
+ }
30
+ }
31
+ >
32
+ <input
33
+ aria-autocomplete="list"
34
+ autoCapitalize="none"
35
+ autoComplete="off"
36
+ autoCorrect="off"
37
+ disabled={false}
38
+ id="react-select-2-input"
39
+ onBlur={[Function]}
40
+ onChange={[Function]}
41
+ onFocus={[Function]}
42
+ spellCheck="false"
43
+ style={
44
+ Object {
45
+ "background": 0,
46
+ "border": 0,
47
+ "boxSizing": "content-box",
48
+ "color": "inherit",
49
+ "fontSize": "inherit",
50
+ "opacity": 1,
51
+ "outline": 0,
52
+ "padding": 0,
53
+ "width": "1px",
54
+ }
55
+ }
56
+ tabIndex="0"
57
+ type="text"
58
+ value=""
59
+ />
60
+ <div
61
+ style={
62
+ Object {
63
+ "height": 0,
64
+ "left": 0,
65
+ "overflow": "scroll",
66
+ "position": "absolute",
67
+ "top": 0,
68
+ "visibility": "hidden",
69
+ "whiteSpace": "pre",
70
+ }
71
+ }
72
+ >
73
+
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ <div
79
+ className="css-1wy0on6 tribe-editor__select__indicators"
80
+ >
81
+ <div
82
+ aria-hidden="true"
83
+ className="css-1ep9fjw tribe-editor__select__indicator tribe-editor__select__dropdown-indicator"
84
+ onMouseDown={[Function]}
85
+ onTouchEnd={[Function]}
86
+ >
87
+ <span
88
+ className="tribe-editor__select__dropdown-indicator"
89
+ >
90
+ arrow-down
91
+ </span>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ `;
97
+
98
+ exports[`Select element Should render the component with class 1`] = `
99
+ <div
100
+ className="css-10nd86i tribe-editor__select test-class"
101
+ onKeyDown={[Function]}
102
+ >
103
+ <div
104
+ className="css-vj8t7z tribe-editor__select__control"
105
+ onMouseDown={[Function]}
106
+ onTouchEnd={[Function]}
107
+ >
108
+ <div
109
+ className="css-1hwfws3 tribe-editor__select__value-container"
110
+ >
111
+ <div
112
+ className="css-1492t68 tribe-editor__select__placeholder"
113
+ >
114
+ Select...
115
+ </div>
116
+ <div
117
+ className="css-1g6gooi"
118
+ >
119
+ <div
120
+ className="tribe-editor__select__input"
121
+ style={
122
+ Object {
123
+ "display": "inline-block",
124
+ }
125
+ }
126
+ >
127
+ <input
128
+ aria-autocomplete="list"
129
+ autoCapitalize="none"
130
+ autoComplete="off"
131
+ autoCorrect="off"
132
+ disabled={false}
133
+ id="react-select-3-input"
134
+ onBlur={[Function]}
135
+ onChange={[Function]}
136
+ onFocus={[Function]}
137
+ spellCheck="false"
138
+ style={
139
+ Object {
140
+ "background": 0,
141
+ "border": 0,
142
+ "boxSizing": "content-box",
143
+ "color": "inherit",
144
+ "fontSize": "inherit",
145
+ "opacity": 1,
146
+ "outline": 0,
147
+ "padding": 0,
148
+ "width": "1px",
149
+ }
150
+ }
151
+ tabIndex="0"
152
+ type="text"
153
+ value=""
154
+ />
155
+ <div
156
+ style={
157
+ Object {
158
+ "height": 0,
159
+ "left": 0,
160
+ "overflow": "scroll",
161
+ "position": "absolute",
162
+ "top": 0,
163
+ "visibility": "hidden",
164
+ "whiteSpace": "pre",
165
+ }
166
+ }
167
+ >
168
+
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ <div
174
+ className="css-1wy0on6 tribe-editor__select__indicators"
175
+ >
176
+ <div
177
+ aria-hidden="true"
178
+ className="css-1ep9fjw tribe-editor__select__indicator tribe-editor__select__dropdown-indicator"
179
+ onMouseDown={[Function]}
180
+ onTouchEnd={[Function]}
181
+ >
182
+ <span
183
+ className="tribe-editor__select__dropdown-indicator"
184
+ >
185
+ arrow-down
186
+ </span>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ `;
192
+
193
+ exports[`Select element Should render the component with extra props 1`] = `
194
+ <div
195
+ className="css-10nd86i tribe-editor__select"
196
+ onKeyDown={[Function]}
197
+ >
198
+ <div
199
+ className="css-vj8t7z tribe-editor__select__control"
200
+ onMouseDown={[Function]}
201
+ onTouchEnd={[Function]}
202
+ >
203
+ <div
204
+ className="css-1hwfws3 tribe-editor__select__value-container"
205
+ >
206
+ <div
207
+ className="css-1492t68 tribe-editor__select__placeholder"
208
+ >
209
+ Select...
210
+ </div>
211
+ <input
212
+ className="css-14uuagi"
213
+ disabled={false}
214
+ id="react-select-4-input"
215
+ onBlur={[Function]}
216
+ onChange={[Function]}
217
+ onFocus={[Function]}
218
+ readOnly={true}
219
+ tabIndex="0"
220
+ value=""
221
+ />
222
+ </div>
223
+ <div
224
+ className="css-1wy0on6 tribe-editor__select__indicators"
225
+ >
226
+ <div
227
+ aria-hidden="true"
228
+ className="css-1ep9fjw tribe-editor__select__indicator tribe-editor__select__dropdown-indicator"
229
+ onMouseDown={[Function]}
230
+ onTouchEnd={[Function]}
231
+ >
232
+ <span
233
+ className="tribe-editor__select__dropdown-indicator"
234
+ >
235
+ arrow-down
236
+ </span>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ `;
common/src/modules/elements/select/__tests__/element.test.js ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import Select from '../element.js';
10
+
11
+ const options = [
12
+ { label: 'Test 1', value: 'test-1' },
13
+ { label: 'Test 2', value: 'test-2' },
14
+ ];
15
+
16
+ describe( 'Select element', () => {
17
+ it( 'Should render the component', () => {
18
+ const component = renderer.create( <Select options={ options } /> );
19
+ expect( component.toJSON() ).toMatchSnapshot();
20
+ } );
21
+
22
+ it( 'Should render the component with class', () => {
23
+ const component = renderer.create( <Select options={ options } className="test-class" /> );
24
+ expect( component.toJSON() ).toMatchSnapshot();
25
+ } );
26
+
27
+ it( 'Should render the component with extra props', () => {
28
+ const component = renderer.create( <Select options={ options } isSearchable={ false } /> );
29
+ expect( component.toJSON() ).toMatchSnapshot();
30
+ } );
31
+ } );
common/src/modules/elements/select/element.js ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+ import ReactSelect, { components } from 'react-select';
8
+ import { Dashicon } from '@wordpress/components';
9
+
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import './style.pcss';
14
+
15
+ const DropdownIndicator = ( props ) => (
16
+ components.DropdownIndicator && (
17
+ <components.DropdownIndicator { ...props }>
18
+ <Dashicon
19
+ className="tribe-editor__select__dropdown-indicator"
20
+ icon={ 'arrow-down' }
21
+ />
22
+ </components.DropdownIndicator>
23
+ )
24
+ );
25
+
26
+ const IndicatorSeparator = () => null;
27
+
28
+ const Select = ( { className, ...rest } ) => (
29
+ <ReactSelect
30
+ className={ classNames( 'tribe-editor__select', className ) }
31
+ classNamePrefix="tribe-editor__select"
32
+ components={ { DropdownIndicator, IndicatorSeparator } }
33
+ { ...rest }
34
+ />
35
+ );
36
+
37
+ Select.propTypes = {
38
+ className: PropTypes.string,
39
+ };
40
+
41
+ export default Select;
common/src/modules/elements/select/style.pcss ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__select {
2
+
3
+ .tribe-editor__select__control {
4
+ height: 46px;
5
+ border: 1px solid #E1E3E6;
6
+ border-radius: 3px;
7
+ background-color: #FFFFFF;
8
+
9
+ &:hover {
10
+ border: 1px solid #E1E3E6;
11
+ }
12
+
13
+ &--is-focused {
14
+ box-shadow: none;
15
+ }
16
+ }
17
+
18
+ .tribe-editor__select__value-container {
19
+ padding: 2px 10px 2px 15px;
20
+ }
21
+
22
+ .tribe-editor__select__single-value {
23
+ margin: 0;
24
+ max-width: calc(100% - 15px);
25
+ font-size: 16px;
26
+ line-height: 1.5;
27
+ }
28
+
29
+ .tribe-editor__select__input {
30
+ font-size: 16px;
31
+
32
+ & > input {
33
+ margin: 0;
34
+ line-height: 1.5;
35
+ }
36
+ }
37
+
38
+ svg.tribe-editor__select__dropdown-indicator {
39
+ fill: #555D66;
40
+ }
41
+
42
+ .tribe-editor__select__menu {
43
+ margin: 0;
44
+ border: 1px solid #E1E3E6;
45
+ border-top: none;
46
+ border-radius: 0;
47
+ border-bottom-left-radius: 3px;
48
+ border-bottom-right-radius: 3px;
49
+ box-shadow: none;
50
+ transform: translateY(-7px);
51
+ z-index: 10;
52
+ }
53
+
54
+ .tribe-editor__select__menu-list {
55
+ padding: 0;
56
+ }
57
+
58
+ .tribe-editor__select__option {
59
+ font-size: 16px;
60
+ line-height: 1.5;
61
+ padding: 3px 15px;
62
+
63
+ &--is-focused {
64
+ background-color: #E7F5FA;
65
+ }
66
+
67
+ &--is-selected {
68
+ background-color: #11A0D2;
69
+ }
70
+ }
71
+
72
+ &--is-disabled {
73
+
74
+ svg.tribe-editor__select__dropdown-indicator {
75
+ fill: #AEB4BB;
76
+ }
77
+ }
78
+ }
common/src/modules/elements/style.pcss ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ .post-type-tribe_events .editor-styles-wrapper {
2
+ max-width: none!important;
3
+ }
common/src/modules/elements/textarea/element.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External Dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+
12
+ const Textarea = ( { className, ...rest } ) => (
13
+ <textarea className={ classNames( 'tribe-editor__textarea', className ) } { ...rest } />
14
+ );
15
+
16
+ Textarea.propTypes = {
17
+ className: PropTypes.string,
18
+ };
19
+
20
+ export default Textarea;
common/src/modules/elements/time-picker/element.js ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { Fragment } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import moment from 'moment';
7
+ import { noop } from 'lodash';
8
+ import classNames from 'classnames';
9
+ import { ScrollTo, ScrollArea } from 'react-scroll-to';
10
+
11
+ /**
12
+ * WordPress dependencies
13
+ */
14
+ import {
15
+ Dropdown,
16
+ Dashicon,
17
+ } from '@wordpress/components';
18
+ import { __ } from '@wordpress/i18n';
19
+
20
+ /**
21
+ * Internal dependencies
22
+ */
23
+ import { PreventBlockClose } from '@moderntribe/common/components';
24
+ import Button from '@moderntribe/common/elements/button/element';
25
+ import Input from '@moderntribe/common/elements/input/element';
26
+ import {
27
+ date as dateUtil,
28
+ moment as momentUtil,
29
+ time as timeUtil,
30
+ TribePropTypes,
31
+ } from '@moderntribe/common/utils';
32
+ import './style.pcss';
33
+
34
+ const TimePicker = ( {
35
+ allDay,
36
+ current,
37
+ disabled,
38
+ end,
39
+ onBlur,
40
+ onChange,
41
+ onClick,
42
+ onFocus,
43
+ showAllDay,
44
+ start,
45
+ step,
46
+ timeFormat,
47
+ } ) => {
48
+
49
+ const renderLabel = ( onAllDayClick ) => {
50
+ if ( allDay ) {
51
+ return (
52
+ <Button
53
+ className="tribe-editor__timepicker__all-day-btn"
54
+ disabled={ disabled }
55
+ onClick={ onAllDayClick }
56
+ >
57
+ { __( 'All Day', 'tribe-common' ) }
58
+ </Button>
59
+ );
60
+ }
61
+
62
+ return (
63
+ <Input
64
+ className="tribe-editor__timepicker__input"
65
+ disabled={ disabled }
66
+ onBlur={ onBlur }
67
+ onChange={ onChange }
68
+ onFocus={ onFocus }
69
+ type="text"
70
+ value={ current }
71
+ />
72
+ );
73
+ };
74
+
75
+ const renderToggle = ( { onToggle, isOpen } ) => (
76
+ <Fragment>
77
+ { renderLabel( onToggle ) }
78
+ <Button
79
+ aria-expanded={ isOpen }
80
+ className="tribe-editor__timepicker__toggle-btn"
81
+ disabled={ disabled }
82
+ onClick={ onToggle }
83
+ >
84
+ <Dashicon
85
+ className="tribe-editor__timepicker__toggle-btn-icon"
86
+ icon={ isOpen ? 'arrow-up' : 'arrow-down' }
87
+ />
88
+ </Button>
89
+ </Fragment>
90
+ );
91
+
92
+ const getItems = () => {
93
+ const items = [];
94
+
95
+ const startSeconds = timeUtil.toSeconds( start, timeUtil.TIME_FORMAT_HH_MM );
96
+ const endSeconds = timeUtil.toSeconds( end, timeUtil.TIME_FORMAT_HH_MM );
97
+
98
+ const currentMoment = moment( current, momentUtil.TIME_FORMAT );
99
+
100
+ for ( let time = startSeconds; time <= endSeconds; time += step ) {
101
+ let isCurrent = false;
102
+ if ( currentMoment.isValid() ) {
103
+ const currentTime = momentUtil.toTime24Hr( currentMoment );
104
+ isCurrent = time === timeUtil.toSeconds( currentTime, timeUtil.TIME_FORMAT_HH_MM );
105
+ }
106
+
107
+ items.push( {
108
+ value: time,
109
+ text: formatLabel( time ),
110
+ isCurrent,
111
+ } );
112
+ }
113
+
114
+ return items;
115
+ };
116
+
117
+ const formatLabel = ( seconds ) => {
118
+ return momentUtil.setTimeInSeconds( moment(), seconds ).format( momentUtil.toFormat( timeFormat ) );
119
+ };
120
+
121
+ const renderItem = ( item, onClose ) => {
122
+ const itemClasses = {
123
+ 'tribe-editor__timepicker__item': true,
124
+ 'tribe-editor__timepicker__item--current': item.isCurrent && ! allDay,
125
+ };
126
+
127
+ return (
128
+ <Button
129
+ key={ `time-${ item.value }` }
130
+ className={ classNames( itemClasses ) }
131
+ value={ item.value }
132
+ onClick={ () => onClick( item.value, onClose ) }
133
+ >
134
+ { item.text }
135
+ </Button>
136
+ );
137
+ };
138
+
139
+ const renderContent = ( { onClose } ) => (
140
+ <ScrollTo>
141
+ { () => (
142
+ <PreventBlockClose>
143
+ <ScrollArea
144
+ key="tribe-element-timepicker-items"
145
+ className="tribe-editor__timepicker__items"
146
+ >
147
+ { showAllDay && renderItem(
148
+ { text: __( 'All Day', 'tribe-common' ), value: 'all-day' },
149
+ onClose,
150
+ ) }
151
+ { getItems().map( ( item ) => renderItem( item, onClose ) ) }
152
+ </ScrollArea>
153
+ </PreventBlockClose>
154
+ ) }
155
+ </ScrollTo>
156
+ );
157
+
158
+ return (
159
+ <div
160
+ key="tribe-element-timepicker"
161
+ className="tribe-editor__timepicker"
162
+ >
163
+ <Dropdown
164
+ className="tribe-editor__timepicker__toggle"
165
+ contentClassName="tribe-editor__timepicker__content"
166
+ position="bottom center"
167
+ renderToggle={ renderToggle }
168
+ renderContent={ renderContent }
169
+ />
170
+ </div>
171
+ );
172
+ };
173
+
174
+ TimePicker.defaultProps = {
175
+ allDay: false,
176
+ onBlur: noop,
177
+ onChange: noop,
178
+ onClick: noop,
179
+ onFocus: noop,
180
+ step: timeUtil.HALF_HOUR_IN_SECONDS,
181
+ timeFormat: dateUtil.FORMATS.WP.time,
182
+ };
183
+
184
+ TimePicker.propTypes = {
185
+ /**
186
+ * TribePropTypes.timeFormat check for string formatted as a time
187
+ * using 24h clock in hh:mm format
188
+ * e.g. 00:24, 03:57, 21:12
189
+ */
190
+ allDay: PropTypes.bool,
191
+ current: PropTypes.string,
192
+ disabled: PropTypes.bool,
193
+ end: TribePropTypes.timeFormat.isRequired,
194
+ onBlur: PropTypes.func,
195
+ onChange: PropTypes.func,
196
+ onClick: PropTypes.func,
197
+ onFocus: PropTypes.func,
198
+ showAllDay: PropTypes.bool,
199
+ start: TribePropTypes.timeFormat.isRequired,
200
+ step: PropTypes.number,
201
+ timeFormat: PropTypes.string,
202
+ };
203
+
204
+ export default TimePicker;
common/src/modules/elements/time-picker/style.pcss ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .tribe-editor__timepicker {
2
+ display: inline-block;
3
+ }
4
+
5
+ .tribe-editor__timepicker__toggle {
6
+ background-color: #FFF;
7
+ border: 1px solid #e1e3e6;
8
+ border-radius: 2px;
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+
13
+ input.tribe-editor__timepicker__input[type="text"] {
14
+ font-family: Helvetica, sans-serif;
15
+ font-size: 14px;
16
+ line-height: 1.5;
17
+ padding: 6px 0 6px 10px;
18
+ border: none;
19
+ width: 80px;
20
+ }
21
+
22
+ button.tribe-editor__timepicker__toggle-btn {
23
+ padding: 10px;
24
+
25
+ &:disabled > svg.dashicon {
26
+ fill: #AEB4BB;
27
+ }
28
+ }
29
+
30
+ button.tribe-editor__timepicker__all-day-btn {
31
+ color: #545D66;
32
+ line-height: 20px;
33
+ padding: 6px 10px 6px;
34
+ text-decoration: none;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ }
39
+ }
40
+
41
+ .tribe-editor__timepicker__content {
42
+
43
+ &.components-popover {
44
+
45
+ .components-popover__content {
46
+ min-width: 110px;
47
+ }
48
+ }
49
+
50
+ .tribe-editor__timepicker__items {
51
+ height: 250px;
52
+ overflow: auto;
53
+
54
+ .components-placeholder {
55
+ height: inherit;
56
+ }
57
+ }
58
+
59
+ .tribe-editor__timepicker__item {
60
+ display: block;
61
+ width: 100%;
62
+ color: #555d66;
63
+ padding: 5px 12px;
64
+ cursor: pointer;
65
+ border-bottom: 1px solid #e2e4e7;
66
+ line-height: 20px;
67
+ background: transparent;
68
+ text-align: left;
69
+
70
+ &:hover,
71
+ &:focus {
72
+ background-color: #e7f5fa;
73
+ }
74
+
75
+ &--current {
76
+ color: #ffffff;
77
+ background-color: #009fd4;
78
+
79
+ &:hover,
80
+ &:focus {
81
+ color: #ffffff;
82
+ background-color: #009fd4;
83
+ }
84
+ }
85
+ }
86
+ }
common/src/modules/elements/tooltip/__tests__/__snapshots__/element.test.js.snap ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Tooltip Element renders a tooltip 1`] = `
4
+ <div>
5
+ <span>
6
+ here is the tooltip text
7
+ </span>
8
+ <span>
9
+ bottom left
10
+ </span>
11
+ <span>
12
+ <button
13
+ aria-label="here is the tooltip text"
14
+ className="tribe-editor__button tribe-editor__tooltip-label label-class-name"
15
+ onClick={[Function]}
16
+ type="button"
17
+ >
18
+ some label
19
+ </button>
20
+ </span>
21
+ </div>
22
+ `;
common/src/modules/elements/tooltip/__tests__/element.test.js ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+ import renderer from 'react-test-renderer';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import Tooltip from '@moderntribe/common/elements/tooltip/element';
11
+
12
+ jest.mock( '@wordpress/components', () => ( {
13
+ Tooltip: ({ text, position, children }) => (
14
+ <div>
15
+ <span>{ text }</span>
16
+ <span>{ position }</span>
17
+ <span>{ children }</span>
18
+ </div>
19
+ ),
20
+ } ) );
21
+
22
+ describe( 'Tooltip Element', () => {
23
+ it( 'renders a tooltip', () => {
24
+ const props = {
25
+ label: 'some label',
26
+ labelClassName: 'label-class-name',
27
+ position: 'bottom left',
28
+ text: 'here is the tooltip text',
29
+ };
30
+ const component = renderer.create( <Tooltip { ...props } />)
31
+ expect( component.toJSON() ).toMatchSnapshot()
32
+ } );
33
+ } );
common/src/modules/elements/tooltip/element.js ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { PureComponent } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * WordPress dependencies
10
+ */
11
+ import { Tooltip as WpTooltip } from '@wordpress/components';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import { Button } from '@moderntribe/common/elements';
17
+
18
+ class Tooltip extends PureComponent {
19
+ static defaultProps = {
20
+ position: 'top right',
21
+ text: '',
22
+ };
23
+
24
+ static propTypes = {
25
+ disabled: PropTypes.bool,
26
+ label: PropTypes.node,
27
+ labelClassName: PropTypes.string,
28
+ position: PropTypes.oneOf( [
29
+ 'top left',
30
+ 'top center',
31
+ 'top right',
32
+ 'bottom left',
33
+ 'bottom center',
34
+ 'bottom right',
35
+ ] ),
36
+ text: PropTypes.string,
37
+ };
38
+
39
+ render() {
40
+ const { disabled, label, labelClassName, position, text } = this.props;
41
+
42
+ return (
43
+ <WpTooltip text={ text } position={ position }>
44
+ <Button
45
+ aria-label={ text }
46
+ className={ classNames( 'tribe-editor__tooltip-label', labelClassName ) }
47
+ disabled={ disabled }
48
+ >
49
+ { label }
50
+ </Button>
51
+ </WpTooltip>
52
+ );
53
+ }
54
+ }
55
+
56
+ export default Tooltip;
common/src/modules/elements/tooltip/style.pcss ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .components-tooltip {
2
+
3
+ .components-popover__content {
4
+ white-space: normal;
5
+ min-width: 350px;
6
+ max-width: 600px;
7
+ }
8
+
9
+ &:not(.is-mobile) {
10
+ .components-popover__content {
11
+ min-width: 350px;
12
+ }
13
+ }
14
+ }
common/src/modules/elements/url-input/element.js ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External Dependencies
3
+ */
4
+ import React from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import Input from '@moderntribe/common/elements/input/element';
12
+
13
+ const UrlInput = ( { checked, className, onChange, ...rest } ) => (
14
+ <Input
15
+ type="url"
16
+ className={ classNames( 'tribe-editor__input--url', className ) }
17
+ onChange={ onChange }
18
+ { ...rest }
19
+ />
20
+ );
21
+
22
+ UrlInput.propTypes = {
23
+ className: PropTypes.string,
24
+ onChange: PropTypes.func,
25
+ };
26
+
27
+ export default UrlInput;
common/src/modules/hoc/__tests__/__snapshots__/with-form.test.js.snap ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`HOC - With Form Should register the postType by dispatching the actions 1`] = `
4
+ Array [
5
+ Object {
6
+ "payload": Object {
7
+ "id": "posts",
8
+ "type": "post",
9
+ },
10
+ "type": "@@MT/COMMON/ADD_FORM",
11
+ },
12
+ ]
13
+ `;
14
+
15
+ exports[`HOC - With Form Should render a component 1`] = `
16
+ <div>
17
+ With Form!
18
+ </div>
19
+ `;
common/src/modules/hoc/__tests__/__snapshots__/with-save-data.test.js.snap ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`HOC - With Details Should render a component 1`] = `
4
+ <div
5
+ attributes={
6
+ Object {
7
+ "description": "",
8
+ "organizers": Array [],
9
+ "title": "Modern Tribe",
10
+ }
11
+ }
12
+ description="The Next Generation of Digital Agency"
13
+ isolated={false}
14
+ name="tribe/event"
15
+ onBlockCreated={[Function]}
16
+ onBlockRemoved={[Function]}
17
+ organizers={Array []}
18
+ setAttributes={[MockFunction]}
19
+ setInitialState={
20
+ [MockFunction] {
21
+ "calls": Array [
22
+ Array [
23
+ Object {
24
+ "attributes": Object {
25
+ "description": "",
26
+ "organizers": Array [],
27
+ "title": "Modern Tribe",
28
+ },
29
+ "description": "The Next Generation of Digital Agency",
30
+ "get": [Function],
31
+ "isolated": false,
32
+ "name": "tribe/event",
33
+ "onBlockCreated": [Function],
34
+ "onBlockRemoved": [Function],
35
+ "organizers": Array [],
36
+ "setAttributes": [MockFunction],
37
+ "setInitialState": [MockFunction] {
38
+ "calls": [Circular],
39
+ "results": Array [
40
+ Object {
41
+ "isThrow": false,
42
+ "value": undefined,
43
+ },
44
+ ],
45
+ },
46
+ "title": "Modern Tribe!",
47
+ },
48
+ ],
49
+ ],
50
+ "results": Array [
51
+ Object {
52
+ "isThrow": false,
53
+ "value": undefined,
54
+ },
55
+ ],
56
+ }
57
+ }
58
+ title="Modern Tribe!"
59
+ >
60
+ With Save Data!
61
+ </div>
62
+ `;
common/src/modules/hoc/__tests__/__snapshots__/with-store.test.js.snap ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`HOC - With Store Should add the store property 1`] = `
4
+ <div
5
+ store={
6
+ Object {
7
+ "dispatch": [Function],
8
+ "getState": [Function],
9
+ "injectReducers": [Function],
10
+ "injectedReducers": Object {},
11
+ "replaceReducer": [Function],
12
+ "run": [Function],
13
+ "subscribe": [Function],
14
+ Symbol(observable): [Function],
15
+ }
16
+ }
17
+ />
18
+ `;
common/src/modules/hoc/__tests__/with-form.test.js ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import renderer from 'react-test-renderer';
5
+ import React from 'react';
6
+ import configureStore from 'redux-mock-store';
7
+ import thunk from 'redux-thunk';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { withForm } from '@moderntribe/common/hoc';
13
+
14
+ const initialState = {
15
+ events: {
16
+ },
17
+ forms: {
18
+ byId: {},
19
+ },
20
+ };
21
+ // here it is possible to pass in any middleware if needed into //configureStore
22
+ const mockStore = configureStore( [ thunk ] );
23
+ const store = mockStore( initialState );
24
+
25
+ const Block = () => <div>With Form!</div>;
26
+ let setFormID;
27
+ let Wrapper;
28
+ let component;
29
+ let instance;
30
+
31
+ describe( 'HOC - With Form', () => {
32
+ beforeEach( () => {
33
+ setFormID = jest.fn( () => 'posts' );
34
+ Wrapper = withForm( setFormID )( Block );
35
+ component = renderer.create( <Wrapper store={ store } postType="post"/> );
36
+ instance = component.root;
37
+ } );
38
+
39
+ afterEach( () => {
40
+ mockStore( initialState );
41
+ store.clearActions();
42
+ setFormID.mockClear();
43
+ } );
44
+
45
+ it( 'Should render a component', () => {
46
+ expect( component.toJSON() ).toMatchSnapshot();
47
+ } );
48
+
49
+ it( 'Should render the inner component', () => {
50
+ expect( instance ).not.toBe( null );
51
+ expect( () => instance.findByType( Block ) ).not.toThrowError();
52
+ } );
53
+
54
+ it( 'Should attach the form properties', () => {
55
+ const expected = {
56
+ edit: false,
57
+ create: false,
58
+ fields: {},
59
+ submit: false,
60
+ };
61
+ expect( instance.findByType( Block ).props ).toMatchObject( expected );
62
+ } );
63
+
64
+ it( 'Should have properties as functions', () => {
65
+ const props = instance.findByType( Block ).props;
66
+ const expectedProps = [
67
+ 'maybeRemoveEntry',
68
+ 'setSubmit',
69
+ 'sendForm',
70
+ 'editEntry',
71
+ 'createDraft',
72
+ ];
73
+
74
+ expectedProps.forEach( ( property ) => {
75
+ expect( props[ property ] ).not.toBeUndefined();
76
+ expect( typeof props[ property ] ).toBe( 'function' );
77
+ } );
78
+ } );
79
+
80
+ it( 'Should register the postType by dispatching the actions', () => {
81
+ expect( store.getActions() ).toMatchSnapshot();
82
+ } );
83
+
84
+ it( 'Should register the ID of the form', () => {
85
+ expect( setFormID ).toHaveBeenCalled();
86
+ expect( setFormID ).toHaveBeenCalledTimes( 3 );
87
+ } );
88
+ } );
89
+
common/src/modules/hoc/__tests__/with-save-data.test.js ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import renderer from 'react-test-renderer';
5
+ import React from 'react';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { withSaveData } from '@moderntribe/common/hoc';
11
+
12
+ const Block = ( props ) => <div { ...props }>With Save Data!</div>;
13
+ const props = {
14
+ name: 'tribe/event',
15
+ setInitialState: jest.fn(),
16
+ setAttributes: jest.fn(),
17
+ title: 'Modern Tribe!',
18
+ description: 'The Next Generation of Digital Agency',
19
+ organizers: [],
20
+ attributes: {
21
+ title: 'Modern Tribe',
22
+ description: '',
23
+ organizers: [],
24
+ },
25
+ };
26
+
27
+ describe( 'HOC - With Details', () => {
28
+ let Wrapper;
29
+ let component;
30
+ let instance;
31
+
32
+ beforeEach( () => {
33
+ Wrapper = withSaveData()( Block );
34
+ component = renderer.create( <Wrapper { ...props } /> );
35
+ instance = component.root;
36
+ } );
37
+
38
+ afterEach( () => {
39
+ props.setInitialState.mockClear();
40
+ props.setAttributes.mockClear();
41
+ component.getInstance().unregisterBlock();
42
+ } );
43
+
44
+ it( 'Should render a component', () => {
45
+ expect( component.toJSON() ).toMatchSnapshot();
46
+ } );
47
+
48
+ it( 'Should render the inner component', () => {
49
+ expect( instance ).not.toBe( null );
50
+ expect( () => instance.findByType( Block ) ).not.toThrowError();
51
+ } );
52
+
53
+ it( 'Should set the initial state', () => {
54
+ expect( props.setInitialState ).toHaveBeenCalled();
55
+ expect( props.setInitialState ).toHaveBeenCalledTimes( 1 );
56
+ } );
57
+
58
+ it( 'Should generate the attributes', () => {
59
+ const HOC = component.getInstance();
60
+ expect( HOC.attrs ).toEqual( props.attributes );
61
+ } );
62
+
63
+ it( 'Should generate the keys', () => {
64
+ const HOC = component.getInstance();
65
+ expect( HOC.keys ).toEqual( Object.keys( props.attributes ) );
66
+ } );
67
+
68
+ it( 'Simulate componentDidUpdate call', () => {
69
+ const wrapper = shallow( <Wrapper { ...props } /> );
70
+ const wrapperInstance = wrapper.instance();
71
+ expect( wrapperInstance.calculateDiff() ).toEqual( {
72
+ description: 'The Next Generation of Digital Agency',
73
+ title: 'Modern Tribe!',
74
+ } );
75
+ wrapper.setProps( {
76
+ attributes: {
77
+ title: 'Modern Tribe!',
78
+ description: 'The Next Generation of Digital Agency',
79
+ organizers: [],
80
+ }
81
+ } );
82
+ expect( wrapperInstance.calculateDiff() ).toEqual( {} );
83
+ wrapper.setProps( {
84
+ organizers: [ 3 ],
85
+ attributes: {
86
+ title: 'Modern Tribe!',
87
+ description: 'The Next Generation of Digital Agency',
88
+ organizers: [ 3 ],
89
+ },
90
+ } );
91
+ expect( wrapperInstance.calculateDiff() ).toEqual( {} );
92
+ wrapper.setProps( {
93
+ organizers: [ 2, 3 ],
94
+ attributes: {
95
+ title: 'Modern Tribe!',
96
+ description: 'The Next Generation of Digital Agency',
97
+ organizers: [ 3, 2 ],
98
+ },
99
+ } );
100
+ expect( wrapperInstance.calculateDiff() ).toEqual( { organizers: [ 2, 3 ] } );
101
+ wrapperInstance.unregisterBlock();
102
+ } );
103
+
104
+ it( 'Should calculate the diff', () => {
105
+ const HOC = component.getInstance();
106
+ const expected = {
107
+ title: props.title,
108
+ description: props.description,
109
+ };
110
+ expect( HOC.calculateDiff() ).toEqual( expected );
111
+ } );
112
+
113
+ it( 'Should count a single block', () => {
114
+ const HOC = component.getInstance();
115
+ expect( HOC.blockCount() ).toBe( 1 );
116
+ } );
117
+ } );
118
+
119
+ describe( 'HOC - With Details on multiple instances', () => {
120
+ afterEach( () => {
121
+ props.setInitialState.mockClear();
122
+ props.setAttributes.mockClear();
123
+ } );
124
+
125
+ it( 'Should register the initial state just once', () => {
126
+ const WrapperComponent = withSaveData()( Block );
127
+
128
+ renderer.create( <WrapperComponent { ...props } /> );
129
+ renderer.create( <WrapperComponent { ...props } /> );
130
+ renderer.create( <WrapperComponent { ...props } /> );
131
+
132
+ expect( props.setInitialState ).toHaveBeenCalled();
133
+ expect( props.setInitialState ).toHaveBeenCalledTimes( 1 );
134
+ } );
135
+
136
+ it( 'Should register the state multiple times on non isolated instances', () => {
137
+ const WrapperComponent = withSaveData()( Block );
138
+ props.isolated = true;
139
+ renderer.create( <WrapperComponent { ...props } /> );
140
+ renderer.create( <WrapperComponent { ...props } /> );
141
+ renderer.create( <WrapperComponent { ...props } /> );
142
+
143
+ expect( props.setInitialState ).toHaveBeenCalled();
144
+ expect( props.setInitialState ).toHaveBeenCalledTimes( 3 );
145
+ } );
146
+ } );
147
+
148
+ describe( 'HOC - test life cycle callbacks', () => {
149
+ let Wrapper;
150
+ let component;
151
+ let instance;
152
+ let properties = {};
153
+
154
+ beforeAll( () => {
155
+ props.onBlockCreated = jest.fn();
156
+ props.onBlockRemoved = jest.fn();
157
+ });
158
+
159
+ beforeEach( () => {
160
+ Wrapper = withSaveData()( Block );
161
+ component = renderer.create( <Wrapper { ...props } /> );
162
+ instance = component.root;
163
+ properties = instance.props;
164
+ } );
165
+
166
+ afterEach( () => {
167
+ props.setInitialState.mockClear();
168
+ props.setAttributes.mockClear();
169
+ props.onBlockCreated.mockClear();
170
+ props.onBlockRemoved.mockClear();
171
+ } );
172
+
173
+ afterAll( () => {
174
+ delete props.onBlockCreated;
175
+ delete props.onBlockRemoved;
176
+ } );
177
+
178
+ it( 'Should call the onBlockCreated callback on mount', () => {
179
+ expect( props.onBlockCreated ).toHaveBeenCalled();
180
+ expect( props.onBlockCreated ).toHaveBeenCalledWith( properties );
181
+ expect( props.onBlockRemoved ).not.toHaveBeenCalled();
182
+ } );
183
+
184
+ it( 'Should call the onBlockRemoved callback on unmount of the block', () => {
185
+ component.unmount();
186
+ expect( props.onBlockCreated ).toHaveBeenCalled();
187
+ expect( props.onBlockCreated ).toHaveBeenCalledWith( properties );
188
+ expect( props.onBlockRemoved ).toHaveBeenCalled();
189
+ expect( props.onBlockRemoved ).toHaveBeenCalledWith( properties );
190
+ } );
191
+ } );
common/src/modules/hoc/__tests__/with-selected.test.js ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { withSelected } from '@moderntribe/common/hoc';
10
+
11
+ const Block = () => ( <div>With Selected!</div> );
12
+
13
+ describe( 'withSelected', () => {
14
+ let HOC;
15
+ const onFocus = jest.fn();
16
+ const onBlur = jest.fn();
17
+ const props = {
18
+ onBlockFocus: onFocus,
19
+ onBlockBlur: onBlur,
20
+ };
21
+
22
+ beforeEach( () => {
23
+ HOC = withSelected()( Block );
24
+ } );
25
+
26
+ afterEach( () => {
27
+ props.onBlockFocus.mockClear();
28
+ props.onBlockBlur.mockClear();
29
+ } );
30
+
31
+ test( 'onBlur called when is not selected on mount', () => {
32
+ props.isSelected = false;
33
+ const component = mount( <HOC { ...props } /> );
34
+ expect( props.onBlockBlur ).toHaveBeenCalled();
35
+ expect( props.onBlockFocus ).not.toHaveBeenCalled();
36
+ } );
37
+
38
+ test( 'onFocus called when is selected on mount', () => {
39
+ props.isSelected = true;
40
+ const component = mount( <HOC { ...props } /> );
41
+ expect( props.onBlockFocus ).toHaveBeenCalled();
42
+ expect( props.onBlockBlur ).not.toHaveBeenCalled();
43
+ } );
44
+
45
+ test( 'trigger focus when isSelected changes after mounted', () => {
46
+ props.isSelected = false;
47
+ const component = mount( <HOC { ...props } /> );
48
+ expect( props.onBlockBlur ).toHaveBeenCalled();
49
+ expect( props.onBlockFocus ).not.toHaveBeenCalled();
50
+
51
+ props.onBlockBlur.mockClear();
52
+ props.onBlockFocus.mockClear();
53
+
54
+ component.setProps( { isSelected: true } );
55
+
56
+ expect( props.onBlockFocus ).toHaveBeenCalled();
57
+ expect( props.onBlockBlur ).not.toHaveBeenCalled();
58
+ } );
59
+
60
+ test( 'trigger onBlur when isSelected changes after mounted', () => {
61
+ props.isSelected = true;
62
+ const component = mount( <HOC { ...props } /> );
63
+ expect( props.onBlockFocus ).toHaveBeenCalled();
64
+ expect( props.onBlockBlur ).not.toHaveBeenCalled();
65
+
66
+ props.onBlockBlur.mockClear();
67
+ props.onBlockFocus.mockClear();
68
+
69
+ component.setProps( { isSelected: false } );
70
+
71
+ expect( props.onBlockBlur ).toHaveBeenCalled();
72
+ expect( props.onBlockFocus ).not.toHaveBeenCalled();
73
+ } );
74
+
75
+ test( 'blue and focus on the different props changes', () => {
76
+ props.isSelected = false;
77
+ const component = mount( <HOC { ...props } /> );
78
+ expect( props.onBlockBlur ).toHaveBeenCalled();
79
+ expect( props.onBlockFocus ).not.toHaveBeenCalled();
80
+
81
+ props.onBlockBlur.mockClear();
82
+ props.onBlockFocus.mockClear();
83
+
84
+ component.setProps( { isSelected: true } );
85
+
86
+ expect( props.onBlockFocus ).toHaveBeenCalled();
87
+ expect( props.onBlockBlur ).not.toHaveBeenCalled();
88
+
89
+ props.onBlockBlur.mockClear();
90
+ props.onBlockFocus.mockClear();
91
+
92
+ component.setProps( { isSelected: false } );
93
+
94
+ expect( props.onBlockBlur ).toHaveBeenCalled();
95
+ expect( props.onBlockFocus ).not.toHaveBeenCalled();
96
+ } );
97
+ } );
common/src/modules/hoc/__tests__/with-store.test.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import renderer from 'react-test-renderer';
5
+ import React from 'react';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { withStore } from '@moderntribe/common/hoc';
11
+
12
+ describe( 'HOC - With Store', () => {
13
+ it( 'Should add the store property', () => {
14
+ const Block = ( props ) => <div { ...props } />;
15
+ const Wrapper = withStore()( Block );
16
+ const component = renderer.create( <Wrapper /> );
17
+ expect( component.toJSON() ).toMatchSnapshot();
18
+
19
+ const instance = component.root;
20
+ expect( instance ).not.toBe( null );
21
+ const props = instance.findByType( Block ).props;
22
+ expect( props ).toHaveProperty( 'store' );
23
+ const { store } = props;
24
+ expect( store ).toHaveProperty( 'dispatch' );
25
+ expect( store ).toHaveProperty( 'getState' );
26
+ } );
27
+ } );
28
+
common/src/modules/hoc/index.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ export { default as withStore } from './with-store';
2
+ export { default as withSaveData } from './with-save-data';
3
+ export { default as withForm } from './with-form';
4
+ export { default as withBlockCloser } from './with-block-closer';
5
+ export { default as withSelected } from './with-selected';
common/src/modules/hoc/with-block-closer.js ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint-disable max-len */
2
+ /**
3
+ * External dependencies
4
+ */
5
+ import React, { PureComponent } from 'react';
6
+ import PropTypes from 'prop-types';
7
+ import { noop } from 'lodash';
8
+
9
+ const ESCAPE_KEY = 27;
10
+ export const EVENT_NAMESPACE = 'tribe:click:proxy';
11
+ export const dispatch = ( e ) => {
12
+ e.target.dispatchEvent( new CustomEvent( EVENT_NAMESPACE, { bubbles: true } ) );
13
+ };
14
+ export const intercept = e => e.stopPropagation();
15
+
16
+ export default ( WrappedComponent ) => {
17
+ /**
18
+ * Prevents clicks on block or blacklisted DOM elements
19
+ * from closing the block
20
+ *
21
+ * @class WithBlockCloser
22
+ * @extends {PureComponent}
23
+ */
24
+ class WithBlockCloser extends PureComponent {
25
+ static displayName = `WithBlockCloser( ${ WrappedComponent.displayName || WrappedComponent.name || 'Component ' }`
26
+
27
+ static propTypes = {
28
+ onClose: PropTypes.func.isRequired,
29
+ classNameClickBlacklist: PropTypes.arrayOf( PropTypes.string ).isRequired,
30
+ isOpen: PropTypes.bool.isRequired,
31
+ };
32
+
33
+ static defaultProps = {
34
+ classNameClickBlacklist: [ '.edit-post-sidebar' ],
35
+ onClose: noop,
36
+ isOpen: false,
37
+ }
38
+
39
+ nodeRef = React.createRef();
40
+ _eventNamespace = EVENT_NAMESPACE;
41
+
42
+ /**
43
+ * dispatches custom events
44
+ *
45
+ * @memberof WithBlockCloser
46
+ * @param {Event} e event
47
+ */
48
+ _dispatchClickProxyEvent = dispatch;
49
+
50
+ // Prevent CustomEvents from propagating to document proxy listeners
51
+ _interceptClickProxyEvent = intercept;
52
+
53
+ /**
54
+ * keydown handler
55
+ *
56
+ * @memberof WithBlockCloser
57
+ * @param {Event} e event
58
+ */
59
+ handleKeyDown = ( e ) => {
60
+ if ( e.keyCode === ESCAPE_KEY ) {
61
+ this.props.onClose();
62
+ }
63
+ }
64
+
65
+ handleClick = () => this.props.onClose()
66
+
67
+ componentDidMount() {
68
+ this.props.isOpen && this._addEventListeners();
69
+ }
70
+
71
+ componentDidUpdate( prevProps ) {
72
+ if ( prevProps.isOpen !== this.props.isOpen ) {
73
+ this.props.isOpen
74
+ ? this._addEventListeners()
75
+ : this._removeEventListeners();
76
+ }
77
+ }
78
+
79
+ componentWillUnmount() {
80
+ this._removeEventListeners();
81
+ }
82
+
83
+ get blacklistedNodes() {
84
+ const classNames = this.props.classNameClickBlacklist.join( ', ' );
85
+ return Array.from( document.querySelectorAll( classNames ) );
86
+ }
87
+
88
+ get node() {
89
+ return this.nodeRef.current;
90
+ }
91
+
92
+ _addEventListeners() {
93
+ // Intercept custom events bubbled in block or blacklisted nodes
94
+ this.node.addEventListener( this._eventNamespace, this._interceptClickProxyEvent );
95
+ this.blacklistedNodes.forEach(
96
+ node => node.addEventListener( this._eventNamespace, this._interceptClickProxyEvent )
97
+ );
98
+
99
+ // Wait to receive custom events, if not intercepted, then go to click handler
100
+ document.addEventListener( this._eventNamespace, this.handleClick );
101
+ // Dispatch custom event on regular clicks
102
+ document.addEventListener( 'click', this._dispatchClickProxyEvent );
103
+
104
+ // Close on certain keypresses
105
+ document.addEventListener( 'keydown', this.handleKeyDown );
106
+ }
107
+
108
+ _removeEventListeners() {
109
+ this.node.removeEventListener( this._eventNamespace, this._interceptClickProxyEvent );
110
+ this.blacklistedNodes.forEach(
111
+ node => node.removeEventListener( this._eventNamespace, this._interceptClickProxyEvent )
112
+ );
113
+
114
+ document.removeEventListener( 'keydown', this.handleKeyDown );
115
+ document.removeEventListener( this._eventNamespace, this.handleClick );
116
+ document.removeEventListener( 'click', this._dispatchClickProxyEvent );
117
+ }
118
+
119
+ render() {
120
+ return (
121
+ <div ref={ this.nodeRef }>
122
+ <WrappedComponent { ...this.props } />
123
+ </div>
124
+ );
125
+ }
126
+ }
127
+
128
+ return WithBlockCloser;
129
+ };
common/src/modules/hoc/with-form.js ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { Component } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import { bindActionCreators } from 'redux';
7
+ import { connect } from 'react-redux';
8
+ import { noop } from 'lodash';
9
+
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import { actions, selectors } from '@moderntribe/common/data/forms';
14
+
15
+ /**
16
+ * HOC that register a new object associated with set of fields for a form
17
+ *
18
+ * @param {function} getName Function used to set the name of the form, has a props param to generate the name
19
+ * @returns {function(*): *} Returns a function that takes a Component as argument and returns a component.
20
+ */
21
+ export default ( getName = noop ) => ( WrappedComponent ) => {
22
+ class WithForm extends Component {
23
+ static propTypes = {
24
+ registerForm: PropTypes.func,
25
+ postType: PropTypes.string,
26
+ };
27
+
28
+ componentDidMount() {
29
+ const name = getName( this.props );
30
+ const { registerForm, postType } = this.props;
31
+ registerForm( name, postType );
32
+ }
33
+
34
+ render() {
35
+ return <WrappedComponent { ...this.props } { ...this.additionalProps() } />;
36
+ }
37
+
38
+ additionalProps() {
39
+ const {
40
+ createDraft,
41
+ sendForm,
42
+ setSubmit,
43
+ editEntry,
44
+ maybeRemoveEntry,
45
+ } = this.props;
46
+ const name = getName( this.props );
47
+ return {
48
+ createDraft: ( fieldsObject ) => createDraft( name, fieldsObject ),
49
+ editEntry: ( fieldsObject ) => editEntry( name, fieldsObject ),
50
+ sendForm: ( fieldsObject, callback ) => sendForm( name, fieldsObject, callback ),
51
+ setSubmit: () => setSubmit( name ),
52
+ maybeRemoveEntry: ( details ) => maybeRemoveEntry( name, details ),
53
+ };
54
+ }
55
+ }
56
+
57
+ const mapStateToProps = ( state, props ) => {
58
+ const name = getName( props );
59
+ const modifiedProps = { name };
60
+ return {
61
+ edit: selectors.getFormEdit( state, modifiedProps ),
62
+ create: selectors.getFormCreate( state, modifiedProps ),
63
+ fields: selectors.getFormFields( state, modifiedProps ),
64
+ submit: selectors.getFormSubmit( state, modifiedProps ),
65
+ };
66
+ };
67
+
68
+ const mapDispatchToProps = ( dispatch ) => bindActionCreators( actions, dispatch );
69
+
70
+ return connect( mapStateToProps, mapDispatchToProps )( WithForm );
71
+ };
common/src/modules/hoc/with-save-data.js ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { Component } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import {
7
+ noop,
8
+ isEmpty,
9
+ isArray,
10
+ isObject,
11
+ keys,
12
+ } from 'lodash';
13
+ import isShallowEqual from '@wordpress/is-shallow-equal';
14
+
15
+ const blockRegister = {};
16
+
17
+ /**
18
+ * Higher order component that updates the attributes of a component if any of the properties of the
19
+ * attributes changes.
20
+ *
21
+ * Only updates the attributes that has changed with the new updates into the properties and only
22
+ * the ones specified as attributes params otherwise will fallback to the property attributes of the
23
+ * component to extract the keys of those to do the comparision.
24
+ *
25
+ * @param {object} selectedAttributes Set of attributes to only update fallback to this.props.attributes
26
+ * @returns {function} Return a new HOC
27
+ */
28
+ export default ( selectedAttributes = null ) => ( WrappedComponent ) => {
29
+ class WithSaveData extends Component {
30
+ static defaultProps = {
31
+ attributes: {},
32
+ setInitialState: noop,
33
+ setAttributes: noop,
34
+ name: '',
35
+ isolated: false,
36
+ onBlockCreated: noop,
37
+ onBlockRemoved: noop,
38
+ };
39
+
40
+ static propTypes = {
41
+ setAttributes: PropTypes.func,
42
+ setInitialState: PropTypes.func,
43
+ attributes: PropTypes.object,
44
+ name: PropTypes.string,
45
+ isolated: PropTypes.bool,
46
+ increaseRegister: PropTypes.func,
47
+ decreaseRegister: PropTypes.func,
48
+ onBlockCreated: PropTypes.func,
49
+ onBlockRemoved: PropTypes.func,
50
+ };
51
+
52
+ keys = [];
53
+ saving = null;
54
+
55
+ constructor( props ) {
56
+ super( props );
57
+ this.keys = this.generateKeys();
58
+ }
59
+
60
+ generateKeys() {
61
+ if ( isArray( this.attrs ) ) {
62
+ return this.attrs;
63
+ }
64
+
65
+ if ( isObject( this.attrs ) ) {
66
+ return keys( this.attrs );
67
+ }
68
+
69
+ console.warn( 'Make sure attributes is from a valid type: Array or Object' );
70
+
71
+ return [];
72
+ }
73
+
74
+ // At this point attributes has been set so no need to be set the initial state into the store here.
75
+ componentDidMount() {
76
+ const { setInitialState, attributes = {}, isolated, onBlockCreated } = this.props;
77
+
78
+ onBlockCreated( this.props );
79
+ this.registerBlock();
80
+
81
+ // Prevent to set the initial state for blocks that are copies from others
82
+ // overwrite this with the isolated property of the block to `true`
83
+ if ( this.blockCount() > 1 && ! isolated ) {
84
+ return;
85
+ }
86
+
87
+ setInitialState( {
88
+ ...this.props,
89
+ get( key, defaultValue ) {
90
+ return key in attributes ? attributes[ key ] : defaultValue;
91
+ },
92
+ } );
93
+ }
94
+
95
+ componentWillUnmount() {
96
+ const { onBlockRemoved } = this.props;
97
+ this.unregisterBlock();
98
+ onBlockRemoved( this.props );
99
+ }
100
+
101
+ registerBlock() {
102
+ const { name } = this.props;
103
+ blockRegister[ name ] = name in blockRegister ? blockRegister[ name ] + 1 : 1;
104
+ }
105
+
106
+ unregisterBlock() {
107
+ const { name } = this.props;
108
+ blockRegister[ name ] -= 1;
109
+ }
110
+
111
+ blockCount() {
112
+ const { name } = this.props;
113
+ return blockRegister[ name ];
114
+ }
115
+
116
+ componentDidUpdate() {
117
+ const diff = this.calculateDiff();
118
+
119
+ if ( isShallowEqual( this.saving, diff ) ) {
120
+ return;
121
+ }
122
+
123
+ this.saving = diff;
124
+
125
+ if ( isEmpty( diff ) ) {
126
+ return;
127
+ }
128
+
129
+ this.props.setAttributes( diff );
130
+ }
131
+
132
+ calculateDiff() {
133
+ const attributes = this.attrs;
134
+ return this.keys.reduce( ( diff, key ) => {
135
+ if ( key in this.props && ! isShallowEqual( attributes[ key ], this.props[ key ] ) ) {
136
+ diff[ key ] = this.props[ key ];
137
+ }
138
+ return diff;
139
+ }, {} );
140
+ }
141
+
142
+ get attrs() {
143
+ return selectedAttributes || this.props.attributes || {};
144
+ }
145
+
146
+ render() {
147
+ return <WrappedComponent { ...this.props } />;
148
+ }
149
+ }
150
+
151
+ WithSaveData.displayName = `WithSaveData( ${ WrappedComponent.displayName || WrappedComponent.name || 'Component ' }`;
152
+
153
+ return WithSaveData;
154
+ };
155
+
common/src/modules/hoc/with-selected.js ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React, { Component } from 'react';
5
+ import PropTypes from 'prop-types';
6
+ import {
7
+ noop,
8
+ } from 'lodash';
9
+
10
+ /**
11
+ * Higher order component that executes two functions:
12
+ *
13
+ * - `onBlockFocus` when the block is selected
14
+ * - `onBlockBlur` when the block losses focus after being selected
15
+ *
16
+ * @returns {function} Return a new HOC
17
+ */
18
+ export default () => ( WrappedComponent ) => {
19
+ class WithSelected extends Component {
20
+ static defaultProps = {
21
+ isSelected: false,
22
+ onBlockFocus: noop,
23
+ onBlockBlur: noop,
24
+ };
25
+
26
+ static propTypes = {
27
+ onBlockFocus: PropTypes.func,
28
+ onBlockBlur: PropTypes.func,
29
+ isSelected: PropTypes.bool,
30
+ };
31
+
32
+ componentDidMount() {
33
+ const { isSelected, onBlockFocus, onBlockBlur } = this.props;
34
+ if ( isSelected ) {
35
+ onBlockFocus();
36
+ } else {
37
+ onBlockBlur();
38
+ }
39
+ }
40
+
41
+ componentDidUpdate( prevProps ) {
42
+ const { isSelected, onBlockFocus, onBlockBlur } = this.props;
43
+
44
+ if ( prevProps.isSelected === isSelected ) {
45
+ return;
46
+ }
47
+
48
+ if ( isSelected ) {
49
+ onBlockFocus();
50
+ } else {
51
+ onBlockBlur();
52
+ }
53
+ }
54
+
55
+ render() {
56
+ return <WrappedComponent { ...this.props } />;
57
+ }
58
+ }
59
+
60
+ WithSelected.displayName = `WithIsSelected( ${ WrappedComponent.displayName || WrappedComponent.name || 'Component ' }`;
61
+
62
+ return WithSelected;
63
+ };
64
+
common/src/modules/hoc/with-store.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { store } from '@moderntribe/common/store';
10
+
11
+ const getStore = () => store;
12
+
13
+ export default ( additionalProps = {} ) => ( WrappedComponent ) => {
14
+
15
+ const WithStore = ( props ) => {
16
+ const extraProps = {
17
+ ...additionalProps,
18
+ store: getStore(),
19
+ };
20
+
21
+ return <WrappedComponent { ...props } { ...extraProps } />;
22
+ };
23
+
24
+ return WithStore;
25
+
26
+ };
common/src/modules/package.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "@moderntribe/common",
3
+ "version": "0.3.2-alpha",
4
+ "description": "Common Blocks Editor modules",
5
+ "main": "src/resources/main.js",
6
+ "scripts": {},
7
+ "keywords": [
8
+ "gutenberg",
9
+ "common"
10
+ ],
11
+ "private": true,
12
+ "repository": "git+https://github.com/moderntribe/tribe-common.git",
13
+ "author": "Modern Tribe",
14
+ "license": "GPL-2.0-or-later",
15
+ "dependencies": {
16
+ }
17
+ }
common/src/modules/store/configure-store.js ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { createStore, applyMiddleware } from 'redux';
5
+ import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
6
+ import { augmentStore } from '@nfen/redux-reducer-injector';
7
+ import thunk from 'redux-thunk';
8
+ import createSagaMiddleware from 'redux-saga';
9
+
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import reducer from '@moderntribe/common/data';
14
+ import { wpRequest } from './middlewares';
15
+
16
+ const sagaMiddleware = createSagaMiddleware();
17
+
18
+ export default () => {
19
+ if ( window.__tribe_common_store__ ) {
20
+ return window.__tribe_common_store__;
21
+ }
22
+
23
+ const middlewares = [
24
+ thunk,
25
+ sagaMiddleware,
26
+ wpRequest,
27
+ ];
28
+
29
+ const composeEnhancers = composeWithDevTools( { name: 'tribe/common' } );
30
+
31
+ const store = createStore( reducer( {} ), composeEnhancers( applyMiddleware( ...middlewares ) ) );
32
+ augmentStore( reducer, store );
33
+ store.run = sagaMiddleware.run;
34
+ window.__tribe_common_store__ = store;
35
+
36
+ return store;
37
+ };
common/src/modules/store/index.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import 'regenerator-runtime/runtime';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import configureStore from './configure-store';
10
+ import * as middlewares from './middlewares';
11
+
12
+ export const store = configureStore();
13
+ export { middlewares };
common/src/modules/store/middlewares/index.js ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ export { default as wpRequest } from './request';
2
+ import * as request from './request';
3
+ export { request };
common/src/modules/store/middlewares/request/__tests__/__snapshots__/actions.test.js.snap ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`[STORE] - Request actions WP Request action 1`] = `
4
+ Object {
5
+ "meta": Object {
6
+ "actions": Object {},
7
+ "path": "tribe_organizer/1225",
8
+ },
9
+ "type": "@@MT/COMMON/WP_REQUEST",
10
+ }
11
+ `;
common/src/modules/store/middlewares/request/__tests__/actions.test.js ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { actions } from '@moderntribe/common/store/middlewares/request';
5
+
6
+ describe( '[STORE] - Request actions', () => {
7
+ test( 'WP Request action', () => {
8
+ const meta = {
9
+ path: 'tribe_organizer/1225',
10
+ actions: {},
11
+ };
12
+ expect( actions.wpRequest( meta ) ).toMatchSnapshot();
13
+ } );
14
+ } );
common/src/modules/store/middlewares/request/__tests__/types.test.js ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { PREFIX_COMMON_STORE } from '@moderntribe/common/data/utils';
5
+ import { types } from '@moderntribe/common/store/middlewares/request';
6
+
7
+ describe( '[STORE] - Request types', () => {
8
+ it( 'Should return the types values', () => {
9
+ expect( types.WP_REQUEST ).toBe( `${ PREFIX_COMMON_STORE }/WP_REQUEST` );
10
+ } );
11
+ } );
common/src/modules/store/middlewares/request/__tests__/utils.test.js ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { utils } from '@moderntribe/common/store/middlewares/request';
5
+
6
+ const wpParamsExpected = {
7
+ orderby: 'title',
8
+ status: [ 'draft', 'publish' ],
9
+ order: 'asc',
10
+ page: 1,
11
+ };
12
+
13
+ describe( 'Request utils', () => {
14
+ it( 'Should generate the WP params', () => {
15
+ expect( utils.toWpParams( {} ) ).toEqual( wpParamsExpected );
16
+ } );
17
+
18
+ it( 'Should order by relevance if has search', () => {
19
+ expect( utils.toWpParams( { search: 'tribe' } ) )
20
+ .toEqual( {
21
+ ...wpParamsExpected,
22
+ search: 'tribe',
23
+ orderby: 'relevance',
24
+ } );
25
+ } );
26
+
27
+ it( 'Should update the exclude parameter', () => {
28
+ expect( utils.toWpParams( { exclude: [] } ) ).toEqual( wpParamsExpected );
29
+ expect( utils.toWpParams( { exclude: [ 1, 2 ] } ) )
30
+ .toEqual( {
31
+ ...wpParamsExpected,
32
+ exclude: [ 1, 2 ],
33
+ } );
34
+ } );
35
+
36
+ it( 'Should generate a WP Query', () => {
37
+ expect( utils.toWPQuery() ).toBe( 'orderby=title&status=draft%2Cpublish&order=asc&page=1' );
38
+ expect( utils.toWPQuery( { search: 'Modern Tribe' } ) )
39
+ .toBe( 'orderby=relevance&status=draft%2Cpublish&order=asc&page=1&search=Modern%20Tribe' );
40
+ } );
41
+
42
+ it( 'Should return the total of pages', () => {
43
+ const headers = new Headers();
44
+ headers.append( 'x-wp-totalpages', 5 );
45
+ expect( headers.get( 'x-wp-totalpages' ) ).toBe( '5' );
46
+ expect( utils.getTotalPages( headers ) ).toBe( 5 );
47
+
48
+ headers.set( 'x-wp-totalpages', '5' );
49
+ expect( headers.get( 'x-wp-totalpages' ) ).toBe( '5' );
50
+ expect( utils.getTotalPages( headers ) ).toBe( 5 );
51
+
52
+ headers.set( 'x-wp-totalpages', '5.3' );
53
+ expect( headers.get( 'x-wp-totalpages' ) ).toBe( '5.3' );
54
+ expect( utils.getTotalPages( headers ) ).toBe( 5 );
55
+
56
+ headers.delete( 'x-wp-totalpages' );
57
+ headers.set( 'x-wp', 5 );
58
+ expect( utils.getTotalPages( headers ) ).toBe( 0 );
59
+ expect( utils.getTotalPages( new Headers() ) ).toBe( 0 );
60
+ } );
61
+ } );
common/src/modules/store/middlewares/request/__tests__/wp-request.test.js ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import wpRequest, { actions } from '@moderntribe/common/store/middlewares/request';
5
+
6
+ let create;
7
+ const nextMock = jest.fn();
8
+ const meta = {
9
+ path: '',
10
+ params: {},
11
+ actions: {
12
+ none: jest.fn(),
13
+ start: jest.fn(),
14
+ success: jest.fn(),
15
+ error: jest.fn(),
16
+ },
17
+ };
18
+
19
+ describe( '[STORE] - wp-request middleware', () => {
20
+ let _fetch;
21
+ beforeAll( () => {
22
+ create = () => {
23
+ const invoke = ( action ) => wpRequest()( nextMock )( action );
24
+ return { next: nextMock, invoke };
25
+ };
26
+ _fetch = global.fetch;
27
+ } );
28
+
29
+ afterEach( () => {
30
+ global.fetch = _fetch;
31
+ } );
32
+
33
+ afterEach( () => {
34
+ nextMock.mockClear();
35
+ meta.actions.start.mockClear();
36
+ meta.actions.error.mockClear();
37
+ meta.actions.none.mockClear();
38
+ meta.actions.success.mockClear();
39
+ window.wp.apiRequest = undefined;
40
+ } );
41
+
42
+ it( 'Should move through a unknown action', () => {
43
+ const { next, invoke } = create();
44
+ const action = { type: 'UNKNOWN' };
45
+ invoke( action );
46
+
47
+ expect( next ).toHaveBeenCalled();
48
+ expect( next ).toHaveBeenCalledTimes( 1 );
49
+ expect( next ).toHaveBeenCalledWith( action );
50
+ } );
51
+
52
+ it( 'Should execute the none action if the path is empty', () => {
53
+ const { next, invoke } = create();
54
+ const action = actions.wpRequest( meta );
55
+ invoke( action );
56
+
57
+ expect( next ).toHaveBeenCalled();
58
+ expect( next ).toHaveBeenCalledTimes( 1 );
59
+ expect( next ).toHaveBeenCalledWith( action );
60
+ expect( meta.actions.none ).toHaveBeenCalled();
61
+ expect( meta.actions.none ).toHaveBeenCalledTimes( 1 );
62
+ expect( meta.actions.none ).toHaveBeenLastCalledWith( meta.path );
63
+ expect( meta.actions.start ).not.toHaveBeenCalled();
64
+ expect( meta.actions.success ).not.toHaveBeenCalled();
65
+ expect( meta.actions.error ).not.toHaveBeenCalled();
66
+ } );
67
+
68
+ it( 'Should execute the correct actions on success', async () => {
69
+ const { invoke } = create();
70
+
71
+ const body = {
72
+ id: 1217,
73
+ date: '2018-05-26T23:07:05',
74
+ meta: {},
75
+ };
76
+
77
+ const headers = new Headers();
78
+
79
+ global.fetch = jest.fn().mockImplementation( () =>
80
+ Promise.resolve( {
81
+ ok: true,
82
+ status: 200,
83
+ json: () => body,
84
+ headers,
85
+ } ),
86
+ );
87
+
88
+ await invoke( actions.wpRequest( { ...meta, path: 'tribe_organizer/1217' } ) );
89
+
90
+ expect.assertions( 8 );
91
+ expect( meta.actions.none ).not.toHaveBeenCalled();
92
+ expect( meta.actions.error ).not.toHaveBeenCalled();
93
+ expect( meta.actions.start ).toHaveBeenCalledWith( 'wp/v2/tribe_organizer/1217', {} );
94
+ expect( meta.actions.start ).toHaveBeenCalled();
95
+ expect( meta.actions.start ).toHaveBeenCalledTimes( 1 );
96
+ expect( meta.actions.success ).toHaveBeenCalled();
97
+ expect( meta.actions.success )
98
+ .toHaveBeenCalledWith( { body, headers } );
99
+ expect( meta.actions.success ).toHaveBeenCalledTimes( 1 );
100
+ } );
101
+
102
+ it( 'execute success actions on 201 response code - creation code', async () => {
103
+ const { invoke } = create();
104
+
105
+ const body = {
106
+ id: 201,
107
+ date: '2018-05-26T23:07:05',
108
+ meta: {
109
+ title: 'Creating a post....'
110
+ },
111
+ };
112
+
113
+ const headers = new Headers();
114
+
115
+ global.fetch = jest.fn().mockImplementation( () =>
116
+ Promise.resolve( {
117
+ ok: true,
118
+ status: 201,
119
+ json: () => body,
120
+ headers,
121
+ } ),
122
+ );
123
+
124
+ await invoke( actions.wpRequest( { ...meta, path: 'tribe_organizer/1217' } ) );
125
+
126
+ expect.assertions( 8 );
127
+ expect( meta.actions.none ).not.toHaveBeenCalled();
128
+ expect( meta.actions.error ).not.toHaveBeenCalled();
129
+ expect( meta.actions.start ).toHaveBeenCalledWith( 'wp/v2/tribe_organizer/1217', {} );
130
+ expect( meta.actions.start ).toHaveBeenCalled();
131
+ expect( meta.actions.start ).toHaveBeenCalledTimes( 1 );
132
+ expect( meta.actions.success ).toHaveBeenCalled();
133
+ expect( meta.actions.success ).toHaveBeenCalledWith( { body, headers } );
134
+ expect( meta.actions.success ).toHaveBeenCalledTimes( 1 );
135
+ } );
136
+
137
+ it( 'Should reject on 404 status code', async () => {
138
+ const { invoke } = create();
139
+
140
+ global.fetch = jest.fn().mockImplementation( () => Promise.resolve( { status: 404 } ) );
141
+
142
+ const error = await invoke( actions.wpRequest( { ...meta, path: 'tribe_organizer/1217' } ) );
143
+ expect.assertions( 6 );
144
+ expect( meta.actions.none ).not.toHaveBeenCalled();
145
+ expect( meta.actions.success ).not.toHaveBeenCalled();
146
+ expect( meta.actions.start ).toHaveBeenCalled();
147
+ expect( meta.actions.start ).toHaveBeenCalledWith( 'wp/v2/tribe_organizer/1217', {} );
148
+ expect( meta.actions.error ).toHaveBeenCalled();
149
+ expect( meta.actions.error ).toHaveBeenCalledWith( error );
150
+ } );
151
+
152
+ it( 'Should execute the correct actions on failure', async () => {
153
+ const { invoke } = create();
154
+
155
+ global.fetch = jest.fn().mockImplementation( () => Promise.reject( 'Wrong path' ) );
156
+
157
+ const error = await invoke( actions.wpRequest( {
158
+ ...meta,
159
+ path: 'tribe_organizer/1217//////',
160
+ } ) );
161
+ expect.assertions( 6 );
162
+ expect( meta.actions.none ).not.toHaveBeenCalled();
163
+ expect( meta.actions.success ).not.toHaveBeenCalled();
164
+ expect( meta.actions.start ).toHaveBeenCalled();
165
+ expect( meta.actions.start ).toHaveBeenCalledWith( 'wp/v2/tribe_organizer/1217//////', {} );
166
+ expect( meta.actions.error ).toHaveBeenCalled();
167
+ expect( meta.actions.error ).toHaveBeenCalledWith( error );
168
+ } );
169
+ } );
common/src/modules/store/middlewares/request/actions.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import * as types from './types';
5
+
6
+ export const wpRequest = ( meta ) => ( {
7
+ type: types.WP_REQUEST,
8
+ meta,
9
+ } );
common/src/modules/store/middlewares/request/index.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import * as types from './types';
5
+ import * as actions from './actions';
6
+ import * as utils from './utils';
7
+
8
+ export { default } from './wp-request';
9
+ export { types, actions, utils };
common/src/modules/store/middlewares/request/types.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { PREFIX_COMMON_STORE } from '@moderntribe/common/data/utils';
5
+
6
+ export const WP_REQUEST = `${ PREFIX_COMMON_STORE }/WP_REQUEST`;
common/src/modules/store/middlewares/request/utils.js ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { isEmpty, isUndefined } from 'lodash';
5
+ import { stringify } from 'querystringify';
6
+
7
+ export const toWpParams = ( args = {} ) => {
8
+ const params = {
9
+ orderby: 'title',
10
+ status: [ 'draft', 'publish' ],
11
+ order: 'asc',
12
+ page: 1,
13
+ ...args,
14
+ };
15
+
16
+ if ( ! isUndefined( params.search ) && ! isEmpty( params.search ) ) {
17
+ params.orderby = 'relevance';
18
+ }
19
+
20
+ if ( isEmpty( params.exclude ) ) {
21
+ delete params.exclude;
22
+ }
23
+
24
+ return params;
25
+ };
26
+
27
+ export const toWPQuery = ( args = {} ) => stringify( toWpParams( args ) );
28
+
29
+ export const getTotalPages = ( headers ) => {
30
+ const totalPages = parseInt( headers.get( 'x-wp-totalpages' ), 10 );
31
+ return isNaN( totalPages ) ? 0 : totalPages;
32
+ };
common/src/modules/store/middlewares/request/wp-request.js ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { noop, get, inRange } from 'lodash';
5
+ import 'whatwg-fetch';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { rest } from '@moderntribe/common/utils/globals';
11
+ import { types } from '@moderntribe/common/store/middlewares/request';
12
+
13
+ export default () => ( next ) => async ( action ) => {
14
+ if ( action.type !== types.WP_REQUEST ) {
15
+ return next( action );
16
+ }
17
+
18
+ const { meta = {} } = action;
19
+
20
+ const {
21
+ path = '',
22
+ params = {},
23
+ } = meta;
24
+
25
+ next( action );
26
+
27
+ const { url = '', nonce = {} } = rest();
28
+ const wpRESTNonce = nonce.wp_rest || '';
29
+ const namespaces = rest.namespaces || {};
30
+ const core = namespaces.core || 'wp/v2';
31
+ const BASE = `${ url }${ core }`;
32
+
33
+ const actions = {
34
+ start: noop,
35
+ success: noop,
36
+ error: noop,
37
+ none: noop,
38
+ ...get( meta, 'actions', {} ),
39
+ };
40
+
41
+ if ( path === '' ) {
42
+ actions.none( path );
43
+ return;
44
+ }
45
+
46
+ const endpoint = `${ BASE }/${ path }`;
47
+
48
+ actions.start( endpoint, params );
49
+
50
+ const headers = {
51
+ 'Accept': 'application/json',
52
+ 'Content-Type': 'application/json',
53
+ ...get( params, 'headers', {} ),
54
+ 'X-WP-Nonce': wpRESTNonce,
55
+ };
56
+
57
+ try {
58
+ const response = await fetch( endpoint, {
59
+ ...params,
60
+ credentials: 'include',
61
+ headers,
62
+ } );
63
+
64
+ const { status } = response;
65
+ // inRange includes 200 but excludes 300 from the range so it's from 200 up to 299
66
+ if ( ! inRange( status, 200, 300 ) ) {
67
+ throw response;
68
+ }
69
+ const body = await response.json();
70
+ actions.success( { body, headers: response.headers } );
71
+ return [ response, body ];
72
+ } catch ( error ) {
73
+ actions.error( error );
74
+ return error;
75
+ }
76
+ };
common/src/modules/utils/__tests__/__snapshots__/globals.test.js.snap ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Tests for globals.js Should match the default value for the globals values 1`] = `
4
+ Object {
5
+ "common": Object {
6
+ "countries": Object {},
7
+ "rest": Object {
8
+ "namespaces": Object {
9
+ "core": "wp/v2",
10
+ },
11
+ "nonce": Object {
12
+ "add_ticket_nonce": "0878f40fb2",
13
+ "wp_rest": "cedcd6967b",
14
+ },
15
+ "url": "http://gutenberg.local/wp-json/",
16
+ },
17
+ "settings": Object {},
18
+ "usStates": Object {},
19
+ },
20
+ "tec": Object {
21
+ "googleMap": Object {},
22
+ },
23
+ "tickets": Object {
24
+ "default_currency": "$",
25
+ "default_provider": "Tribe__Tickets_Plus__Commerce__WooCommerce__Main",
26
+ "providers": Array [
27
+ Object {
28
+ "class": "Tribe__Tickets_Plus__Commerce__WooCommerce__Main",
29
+ "currency": "$",
30
+ "currency_position": "prefix",
31
+ "name": "WooCommerce",
32
+ },
33
+ ],
34
+ },
35
+ }
36
+ `;
37
+
38
+ exports[`Tests for globals.js Should match the default value for the globals values 2`] = `
39
+ Object {
40
+ "common": Object {
41
+ "countries": Object {},
42
+ "rest": Object {
43
+ "namespaces": Object {
44
+ "core": "wp/v2",
45
+ },
46
+ "nonce": Object {
47
+ "add_ticket_nonce": "0878f40fb2",
48
+ "wp_rest": "cedcd6967b",
49
+ },
50
+ "url": "http://gutenberg.local/wp-json/",
51
+ },
52
+ "settings": Object {},
53
+ "usStates": Object {},
54
+ },
55
+ "tec": Object {
56
+ "googleMap": Object {},
57
+ },
58
+ "tickets": Object {
59
+ "default_currency": "$",
60
+ "default_provider": "Tribe__Tickets_Plus__Commerce__WooCommerce__Main",
61
+ "providers": Array [
62
+ Object {
63
+ "class": "Tribe__Tickets_Plus__Commerce__WooCommerce__Main",
64
+ "currency": "$",
65
+ "currency_position": "prefix",
66
+ "name": "WooCommerce",
67
+ },
68
+ ],
69
+ },
70
+ }
71
+ `;
72
+
73
+ exports[`Tests for globals.js get default value 1`] = `Array []`;
74
+
75
+ exports[`Tests for globals.js rest value 1`] = `
76
+ Object {
77
+ "namespaces": Object {
78
+ "core": "wp/v2",
79
+ },
80
+ "nonce": Object {
81
+ "add_ticket_nonce": "0878f40fb2",
82
+ "wp_rest": "cedcd6967b",
83
+ },
84
+ "url": "http://gutenberg.local/wp-json/",
85
+ }
86
+ `;
87
+
88
+ exports[`Tests for globals.js rest value 2`] = `
89
+ Object {
90
+ "add_ticket_nonce": "0878f40fb2",
91
+ "wp_rest": "cedcd6967b",
92
+ }
93
+ `;
common/src/modules/utils/__tests__/__snapshots__/time.test.js.snap ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Tests for time.js fromMilliseconds() test 1`] = `"Argument \`ms\` provided to \`fromMilliseconds\` is not a number or is NaN."`;
4
+
5
+ exports[`Tests for time.js fromMilliseconds() test 2`] = `"Argument \`ms\` provided to \`fromMilliseconds\` is not a number or is NaN."`;
6
+
7
+ exports[`Tests for time.js fromMilliseconds() test 3`] = `"Argument \`format\` provided to \`formatTime\` is not a recognized format."`;
8
+
9
+ exports[`Tests for time.js fromSeconds() test 1`] = `"Argument \`s\` provided to \`fromSeconds\` is not a number or is NaN."`;
10
+
11
+ exports[`Tests for time.js fromSeconds() test 2`] = `"Argument \`s\` provided to \`fromSeconds\` is not a number or is NaN."`;
12
+
13
+ exports[`Tests for time.js fromSeconds() test 3`] = `"Argument \`format\` provided to \`formatTime\` is not a recognized format."`;
14
+
15
+ exports[`Tests for time.js toMilliseconds() test 1`] = `"Argument \`time\` provided to \`toMilliseconds\` is not a recognized format."`;
16
+
17
+ exports[`Tests for time.js toMilliseconds() test 2`] = `"Argument \`time\` provided to \`toMilliseconds\` is not a recognized format."`;
18
+
19
+ exports[`Tests for time.js toMilliseconds() test 3`] = `"Argument \`time\` provided to \`toMilliseconds\` is not a recognized format."`;
20
+
21
+ exports[`Tests for time.js toMilliseconds() test 4`] = `"Argument \`time\` provided to \`toMilliseconds\` contains minutes or seconds greater than 59."`;
22
+
23
+ exports[`Tests for time.js toMilliseconds() test 5`] = `"Argument \`format\` provided to \`toMilliseconds\` is not a recognized format."`;
24
+
25
+ exports[`Tests for time.js toSeconds() test 1`] = `"Argument \`time\` provided to \`toMilliseconds\` is not a recognized format."`;
26
+
27
+ exports[`Tests for time.js toSeconds() test 2`] = `"Argument \`time\` provided to \`toMilliseconds\` is not a recognized format."`;
28
+
29
+ exports[`Tests for time.js toSeconds() test 3`] = `"Argument \`time\` provided to \`toMilliseconds\` is not a recognized format."`;
30
+
31
+ exports[`Tests for time.js toSeconds() test 4`] = `"Argument \`time\` provided to \`toMilliseconds\` contains minutes or seconds greater than 59."`;
32
+
33
+ exports[`Tests for time.js toSeconds() test 5`] = `"Argument \`format\` provided to \`toMilliseconds\` is not a recognized format."`;
common/src/modules/utils/__tests__/date.test.js ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { date, moment as momentUtil } from '@moderntribe/common/utils';
5
+ import moment from 'moment';
6
+
7
+ const {
8
+ FORMATS,
9
+ TODAY,
10
+ timezones,
11
+ timezonesAsSelectData,
12
+ toNaturalLanguage,
13
+ rangeToNaturalLanguage,
14
+ labelToDate,
15
+ } = date;
16
+
17
+ jest.mock( '@moderntribe/common/utils/timezone', () => ( {
18
+ getItems: () => [
19
+ {
20
+ options: [
21
+ {
22
+ key: 'America/Argentina/Buenos_Aires',
23
+ text: 'Argentina - Buenos Aires',
24
+ },
25
+ ],
26
+ },
27
+ {
28
+ options: [
29
+ {
30
+ key: 'America/Argentina/Catamarca',
31
+ text: 'Argentina - Catamarca',
32
+ },
33
+ ],
34
+ },
35
+ ],
36
+ } ) );
37
+
38
+ afterAll( () => {
39
+ jest.unmock( '@moderntribe/common/utils/timezone' );
40
+ } );
41
+
42
+ describe( 'Tests for date.js', () => {
43
+ test( 'formats', () => {
44
+ const draft = {
45
+ TIME: 'HH:mm:ss',
46
+ DATE_TIME: 'YYYY-MM-DD HH:mm:ss',
47
+ WP: {
48
+ time: 'g:i a',
49
+ time24Hr: 'H:i',
50
+ date: 'F j, Y',
51
+ datetime: 'F j, Y g:i a',
52
+ dateNoYear: 'F j',
53
+ },
54
+ DATABASE: {
55
+ date: 'Y-m-d',
56
+ datetime: 'Y-m-d H:i:s',
57
+ time: 'H:i:s',
58
+ },
59
+ TIMEZONE: {
60
+ string: 'UTC',
61
+ },
62
+ };
63
+ expect( FORMATS ).toEqual( draft );
64
+ } );
65
+
66
+ test( 'today', () => {
67
+ const now = new Date();
68
+ expect( TODAY ).toBeInstanceOf( Date );
69
+ expect( TODAY ).hasOwnProperty( 'getDay' );
70
+ expect( TODAY.getDay() ).toEqual( now.getDay() );
71
+ } );
72
+
73
+ test( 'timezones', () => {
74
+ const expected = [
75
+ {
76
+ key: 'America/Argentina/Buenos_Aires',
77
+ text: 'Argentina - Buenos Aires',
78
+ },
79
+ {
80
+ key: 'America/Argentina/Catamarca',
81
+ text: 'Argentina - Catamarca',
82
+ },
83
+ ];
84
+ expect( timezones() ).toEqual( expected );
85
+ } );
86
+
87
+ test( 'timezonesAsSelectData', () => {
88
+ const expected = [
89
+ {
90
+ value: 'America/Argentina/Buenos_Aires',
91
+ label: 'Argentina - Buenos Aires',
92
+ },
93
+ {
94
+ value: 'America/Argentina/Catamarca',
95
+ label: 'Argentina - Catamarca',
96
+ },
97
+ ];
98
+ expect( timezonesAsSelectData() ).toEqual( expected );
99
+ } );
100
+
101
+ describe( 'toNaturalLanguage', () => {
102
+ it( 'Should return empty string when non parsed', () => {
103
+ const defaultDetail = { month: '', day: '', year: '', time: '' };
104
+ expect( toNaturalLanguage( {} ) ).toEqual( { moment: null, text: '', detail: defaultDetail, isValid: false } );
105
+ expect( toNaturalLanguage( { date: undefined } ) ).toEqual( {
106
+ moment: undefined,
107
+ text: '',
108
+ detail: defaultDetail,
109
+ isValid: false
110
+ } );
111
+ expect( toNaturalLanguage( { date: '' } ) ).toEqual( {
112
+ moment: '',
113
+ text: '',
114
+ detail: defaultDetail,
115
+ isValid: false
116
+ } );
117
+ } );
118
+
119
+ it( 'Should return the parsed date', () => {
120
+ expect( toNaturalLanguage( { date: '2018-05-04 17:00:00' } ) )
121
+ .toEqual( {
122
+ moment: momentUtil.toMoment( '2018-05-04 17:00:00' ),
123
+ text: 'May 4 2018 5:00 pm',
124
+ detail: {
125
+ month: 'May',
126
+ day: '4',
127
+ year: '2018',
128
+ time: '5:00 pm',
129
+ },
130
+ isValid: true,
131
+ } );
132
+ expect( toNaturalLanguage( { date: '2019-12-24 12:00:00' } ) )
133
+ .toEqual( {
134
+ moment: momentUtil.toMoment( '2019-12-24 12:00:00' ),
135
+ text: 'December 24 2019 12:00 pm',
136
+ detail: {
137
+ month: 'December',
138
+ day: '24',
139
+ year: '2019',
140
+ time: '12:00 pm',
141
+ },
142
+ isValid: true,
143
+ } );
144
+ } );
145
+ } );
146
+
147
+ describe( 'rangeToNaturalLanguage', () => {
148
+ it( 'Should return empty string when range is invalid', () => {
149
+ expect( rangeToNaturalLanguage( null, null ) ).toBe( '' );
150
+ expect( rangeToNaturalLanguage( undefined, undefined ) ).toBe( '' );
151
+ expect( rangeToNaturalLanguage( '', '' ) ).toBe( '' );
152
+ } );
153
+
154
+ it( 'Should return only the start date', () => {
155
+ expect( rangeToNaturalLanguage( '2019-12-24 12:00:00' ) )
156
+ .toBe( 'December 24 2019 at 12:00 pm' );
157
+ expect( rangeToNaturalLanguage( '2019-12-24 12:00:00', '' ) )
158
+ .toBe( 'December 24 2019 at 12:00 pm' );
159
+ } );
160
+
161
+ it( 'Should return the range with time on same day', () => {
162
+ expect( rangeToNaturalLanguage( '2019-12-24 12:00:00', '2019-12-24 17:00:00' ) )
163
+ .toBe( 'December 24 2019 at 12:00 pm - 5:00 pm' );
164
+ } );
165
+
166
+ it( 'Should return the range without year on same year', () => {
167
+ expect( rangeToNaturalLanguage( '2019-12-24 12:00:00', '2019-12-29 17:00:00' ) )
168
+ .toBe( 'December 24 2019 at 12:00 pm - December 29 at 5:00 pm' );
169
+ } );
170
+
171
+ it( 'Should return the range on different years', () => {
172
+ expect( rangeToNaturalLanguage( '2019-12-24 12:00:00', '2020-12-24 17:00:00' ) )
173
+ .toBe( 'December 24 2019 at 12:00 pm - December 24 2020 at 5:00 pm' );
174
+ } );
175
+ } );
176
+
177
+ describe( 'labelToDate - be aware chrono module is being mocked to avoid parsing', () => {
178
+ test( 'Default value when date is invalid', () => {
179
+ expect( labelToDate() ).toEqual( { start: null, end: null } );
180
+ } );
181
+
182
+ test( 'Valid dates', () => {
183
+ const momentDate = moment( '12-25-1995', 'MM-DD-YYYY' );
184
+ expect( labelToDate( momentDate ) )
185
+ .toEqual( { start: momentUtil.toDateTime( momentDate ), end: momentUtil.toDateTime( momentDate ) } );
186
+ } );
187
+ } );
188
+
189
+ } );
common/src/modules/utils/__tests__/dom.test.js ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import {
5
+ hasClass,
6
+ isRootNode,
7
+ searchParent,
8
+ } from '@moderntribe/common/utils/dom';
9
+
10
+ describe( 'Tests for dom.js', () => {
11
+ beforeAll( () => {
12
+ window.document.body.classList.add( 'one', 'two' );
13
+ } );
14
+
15
+ afterAll( () => {
16
+ window.document.body.classList.remove( 'one', 'two' );
17
+ } );
18
+
19
+ describe( 'hasClass dom utility', () => {
20
+ it( 'Should return false when the dom element does not have any class', () => {
21
+ expect( hasClass( window.document.body, [] ) ).toBe( false );
22
+ expect( hasClass( window.document.body, [ 'five', 'seven' ] ) ).toBe( false );
23
+ expect( hasClass( window.document.body, [ 'eight' ] ) ).toBe( false );
24
+ } );
25
+
26
+ it( 'Should return true whe the dom element has any of the classes', () => {
27
+ expect( hasClass( window.document.body, [ 'one', 'two' ] ) ).toBe( true );
28
+ expect( hasClass( window.document.body, [ 'two', 'one' ] ) ).toBe( true );
29
+ expect( hasClass( window.document.body, [ 'four', 'two' ] ) ).toBe( true );
30
+ expect( hasClass( window.document.body, [ 'one', 'five' ] ) ).toBe( true );
31
+ expect( hasClass( window.document.body, [ 'nostyle', 'six', 'seven', 'one' ] ) ).toBe( true );
32
+ } );
33
+ } );
34
+
35
+ test( 'Test for searchParent', () => {
36
+ expect( searchParent( null ) ).toBeFalsy();
37
+ const treeWithNode = {
38
+ parentNode: {
39
+ value: 10,
40
+ parentNode: {
41
+ value: 20,
42
+ },
43
+ },
44
+ };
45
+
46
+ const callback = jest.fn( ( node ) => node.value === 20 );
47
+ const result = searchParent( treeWithNode, callback );
48
+ expect( callback ).toBeCalled();
49
+ expect( callback ).toBeCalledWith( { value: 20 } );
50
+ expect( result ).toBeTruthy();
51
+
52
+ const treeWithoutNode = {
53
+ parentNode: {
54
+ parentNode: {
55
+ parentNode: {
56
+ top: {
57
+ document: 'global',
58
+ },
59
+ },
60
+ },
61
+ },
62
+ };
63
+
64
+ expect( searchParent( treeWithoutNode ) ).toBeFalsy();
65
+ } );
66
+
67
+ test( 'Test for isRootNode', () => {
68
+ expect( isRootNode( null ) ).toBeFalsy();
69
+ expect( isRootNode( 'text' ) ).toBeFalsy();
70
+ expect( isRootNode( window.document.body ) ).toBeFalsy();
71
+ expect( isRootNode( window.document ) ).toBeTruthy();
72
+ } );
73
+ } );
common/src/modules/utils/__tests__/globals.test.js ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import {
5
+ google,
6
+ mapsAPI,
7
+ settings,
8
+ list,
9
+ get,
10
+ config,
11
+ rest,
12
+ restNonce,
13
+ } from '@moderntribe/common/utils/globals';
14
+
15
+ describe( 'Tests for globals.js', () => {
16
+
17
+ beforeAll( () => {
18
+ window.tribe_editor_config = {
19
+ common: {
20
+ countries: {},
21
+ usStates: {},
22
+ settings: {},
23
+ rest: {
24
+ namespaces: {
25
+ core: 'wp/v2',
26
+ },
27
+ nonce: {
28
+ wp_rest: 'cedcd6967b',
29
+ add_ticket_nonce: '0878f40fb2',
30
+ },
31
+ url: 'http://gutenberg.local/wp-json/',
32
+ },
33
+ },
34
+ tec: {
35
+ googleMap: {},
36
+ },
37
+ tickets: {
38
+ providers: [ {
39
+ class: 'Tribe__Tickets_Plus__Commerce__WooCommerce__Main',
40
+ currency: '$',
41
+ currency_position: 'prefix',
42
+ name: 'WooCommerce',
43
+ } ],
44
+ default_provider: 'Tribe__Tickets_Plus__Commerce__WooCommerce__Main',
45
+ default_currency: '$',
46
+ },
47
+ };
48
+ } );
49
+
50
+ test( 'Should match the default value for the globals values', () => {
51
+ expect( get( 'random' ) ).toBe( undefined );
52
+ expect( get( 'google' ) ).toBe( undefined );
53
+ expect( google() ).toBe( undefined );
54
+ expect( get( 'tribe_editor_config' ) ).toMatchSnapshot();
55
+ expect( settings() ).toEqual( {} );
56
+ expect( mapsAPI() ).toEqual( {} );
57
+ expect( list() ).toEqual( {
58
+ countries: {},
59
+ us_states: {},
60
+ } );
61
+ expect( config() ).toMatchSnapshot();
62
+ } );
63
+
64
+ test( 'get default value', () => {
65
+ expect( get( 'UNKNOWN', 10 ) ).toBe( 10 );
66
+ expect( get( 'tribe_js_config', [] ) ).toMatchSnapshot();
67
+ } );
68
+
69
+ test( 'rest value', () => {
70
+ expect( rest() ).toMatchSnapshot();
71
+ expect( restNonce() ).toMatchSnapshot();
72
+ } );
73
+
74
+ afterAll( () => {
75
+ delete window.tribe_editor_config;
76
+ } );
77
+ } );
78
+
79
+ describe( 'Test default values on globals', () => {
80
+ test( 'rest default values', () => {
81
+ expect( rest() ).toEqual( {} );
82
+ expect( restNonce() ).toEqual( {} );
83
+ } );
84
+ } );
common/src/modules/utils/__tests__/input.test.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { sendValue } from '@moderntribe/common/utils/input';
5
+
6
+ describe( 'Tests for input.js', () => {
7
+ const event = {
8
+ target: {
9
+ value: 'Sample',
10
+ },
11
+ };
12
+
13
+ test( 'Callback being executed', () => {
14
+ const mockCallback = jest.fn();
15
+ sendValue( mockCallback )( event );
16
+ expect( mockCallback ).toHaveBeenCalled();
17
+ expect( mockCallback ).toHaveBeenCalledTimes( 1 );
18
+ expect( mockCallback ).toHaveBeenCalledWith( 'Sample' );
19
+ } );
20
+ } );
common/src/modules/utils/__tests__/moment.test.js ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import moment from 'moment/moment';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import {
10
+ date,
11
+ moment as momentUtil,
12
+ time,
13
+ } from '@moderntribe/common/utils';
14
+
15
+ const FORMAT = 'MM-DD-YYYY HH:mm:ss';
16
+
17
+ describe( 'Tests for moment.js', () => {
18
+ let console;
19
+ beforeAll( () => {
20
+ console = window.console;
21
+ window.console = {
22
+ ...console,
23
+ warn: jest.fn(),
24
+ };
25
+ } );
26
+
27
+ afterAll( () => {
28
+ window.console = console;
29
+ } );
30
+
31
+ test( 'TIME_FORMAT', () => {
32
+ expect( momentUtil.TIME_FORMAT ).toEqual( 'h:mm a' );
33
+ } );
34
+
35
+ test( 'roundTime', () => {
36
+ const test1 = momentUtil.roundTime(
37
+ moment( '05-09-2018 12:26:02', FORMAT ),
38
+ );
39
+ expect( test1 ).toBeInstanceOf( moment );
40
+ expect( test1.hour() ).toEqual( 12 );
41
+ expect( test1.minutes() ).toEqual( 0 );
42
+ expect( test1.seconds() ).toEqual( 0 );
43
+
44
+ const test2 = momentUtil.roundTime(
45
+ moment( '05-09-2018 15:30:02', FORMAT ),
46
+ );
47
+ expect( test2 ).toBeInstanceOf( moment );
48
+ expect( test2.hour() ).toEqual( 15 );
49
+ expect( test2.minutes() ).toEqual( 30 );
50
+ expect( test2.seconds() ).toEqual( 0 );
51
+
52
+ const test3 = momentUtil.roundTime(
53
+ moment( '05-09-2018 23:59:59', FORMAT ),
54
+ );
55
+ expect( test3 ).toBeInstanceOf( moment );
56
+ expect( test3.hour() ).toEqual( 23 );
57
+ expect( test3.minutes() ).toEqual( 30 );
58
+ expect( test3.seconds() ).toEqual( 0 );
59
+
60
+ const test4 = momentUtil.roundTime(
61
+ moment( '05-09-2018 08:01:59', FORMAT ),
62
+ );
63
+ expect( test4 ).toBeInstanceOf( moment );
64
+ expect( test4.hour() ).toEqual( 8 );
65
+ expect( test4.minutes() ).toEqual( 0 );
66
+ expect( test4.seconds() ).toEqual( 0 );
67
+ } );
68
+
69
+ test( 'toMoment', () => {
70
+ const input = momentUtil.toMoment( new Date( 'January 2, 2015 08:01:59 UTC' ).toISOString() );
71
+
72
+ expect( input ).toBeInstanceOf( moment );
73
+ expect( input.date() ).toEqual( 2 );
74
+ expect( input.month() ).toEqual( 0 );
75
+ expect( input.year() ).toEqual( 2015 );
76
+ expect( input.hour() ).toEqual( 8 );
77
+ expect( input.minutes() ).toEqual( 1 );
78
+ expect( input.seconds() ).toEqual( 59 );
79
+ expect( input.milliseconds() ).toEqual( 0 );
80
+ expect( input.format( FORMAT ) ).toEqual( '01-02-2015 08:01:59' );
81
+ } );
82
+
83
+ test( 'replaceDate', () => {
84
+ expect( () => momentUtil.replaceDate( 'Sample string', 123123 ) ).toThrowError();
85
+
86
+ const a = moment( '02-28-2010 14:24:40', FORMAT );
87
+ const b = moment( '05-10-2012 20:14:20', FORMAT );
88
+
89
+ const replaced = momentUtil.replaceDate( a, b );
90
+ expect( replaced ).toBeInstanceOf( moment );
91
+ expect( replaced.date() ).toEqual( 10 );
92
+ expect( replaced.month() ).toEqual( 4 );
93
+ expect( replaced.year() ).toEqual( 2012 );
94
+ expect( replaced.hour() ).toEqual( 14 );
95
+ expect( replaced.minute() ).toEqual( 24 );
96
+ expect( replaced.second() ).toEqual( 40 );
97
+ expect( replaced.format( FORMAT ) ).toEqual( '05-10-2012 14:24:40' );
98
+ } );
99
+
100
+ test( 'setTimeInSeconds', () => {
101
+ expect( () => momentUtil.setTimeInSeconds( 'Sample String', 123123 ) ).toThrowError();
102
+
103
+ const a = moment( '02-28-2010 14:24:40', FORMAT );
104
+ const SECONDS = ( 12.5 ) * 60 * 60;
105
+ const replaced = momentUtil.setTimeInSeconds( a, SECONDS );
106
+ expect( replaced ).toBeInstanceOf( moment );
107
+ expect( replaced.date() ).toEqual( 28 );
108
+ expect( replaced.month() ).toEqual( 1 );
109
+ expect( replaced.year() ).toEqual( 2010 );
110
+ expect( replaced.hour() ).toEqual( 12 );
111
+ expect( replaced.minute() ).toEqual( 30 );
112
+ expect( replaced.seconds() ).toEqual( 0 );
113
+ expect( replaced.milliseconds() ).toEqual( 0 );
114
+
115
+ const test2 = momentUtil.setTimeInSeconds( a, 0 );
116
+ expect( test2.date() ).toEqual( 28 );
117
+ expect( test2.month() ).toEqual( 1 );
118
+ expect( test2.year() ).toEqual( 2010 );
119
+ expect( test2.hour() ).toEqual( 0 );
120
+ expect( test2.minute() ).toEqual( 0 );
121
+ expect( test2.seconds() ).toEqual( 0 );
122
+ expect( test2.milliseconds() ).toEqual( 0 );
123
+ } );
124
+
125
+ test( 'totalSeconds', () => {
126
+ expect( momentUtil.totalSeconds( null ) ).toEqual( 0 );
127
+ expect( momentUtil.totalSeconds( new Date() ) ).toEqual( 0 );
128
+ expect( momentUtil.totalSeconds( moment().startOf( 'day' ) ) ).toEqual( 0 );
129
+ expect( momentUtil.totalSeconds( moment( 'May 23, 2018 12:30 am', 'MMM D, YYYY k:m a' ) ) )
130
+ .toEqual( time.HALF_HOUR_IN_SECONDS );
131
+ } );
132
+
133
+ test( 'toDateTime', () => {
134
+ const converted = momentUtil.toDateTime( moment() );
135
+ expect( typeof converted ).toBe( 'string' );
136
+ const format = momentUtil.toFormat( date.FORMATS.DATABASE.datetime );
137
+ expect( converted ).toBe( moment().format( format ) );
138
+ } );
139
+
140
+ test( 'toDate', () => {
141
+ const converted = momentUtil.toDate( moment() );
142
+ expect( typeof converted ).toBe( 'string' );
143
+ expect( typeof converted ).toBe( 'string' );
144
+ const format = momentUtil.toFormat( date.FORMATS.WP.date );
145
+ expect( converted ).toBe( moment().format( format ) );
146
+ } );
147
+
148
+ test( 'toDateNoYear', () => {
149
+ const converted = momentUtil.toDateNoYear( moment() );
150
+ expect( typeof converted ).toBe( 'string' );
151
+ expect( converted ).toBe( moment().format( 'MMMM D' ) );
152
+ } );
153
+
154
+ test( 'toTime', () => {
155
+ const converted = momentUtil.toTime( moment() );
156
+ expect( typeof converted ).toBe( 'string' );
157
+ expect( converted ).toBe( moment().format( 'h:mm a' ) );
158
+ } );
159
+
160
+ test( 'toTime24Hr', () => {
161
+ const converted = momentUtil.toTime24Hr( moment() );
162
+ expect( typeof converted ).toBe( 'string' );
163
+ expect( converted ).toBe( moment().format( 'HH:mm' ) );
164
+ } );
165
+
166
+ test( 'toDatabaseDate', () => {
167
+ const converted = momentUtil.toDatabaseDate( moment() );
168
+ expect( typeof converted ).toBe( 'string' );
169
+ expect( converted ).toBe( moment().format( 'YYYY-MM-DD' ) );
170
+ } );
171
+
172
+ test( 'toDatabaseTime', () => {
173
+ const converted = momentUtil.toDatabaseTime( moment() );
174
+ expect( typeof converted ).toBe( 'string' );
175
+ expect( converted ).toBe( moment().format( 'HH:mm:ss' ) );
176
+ } );
177
+
178
+ test( 'toDatePicker', () => {
179
+ const converted = momentUtil.toDatePicker( moment() );
180
+ expect( typeof converted ).toBe( 'string' );
181
+ expect( converted ).toBe( moment().format( 'YYYY-MM-DDTHH:mm:ss' ) );
182
+ } );
183
+
184
+ test( 'isSameDay', () => {
185
+ expect( momentUtil.isSameDay() ).toBe( false );
186
+ expect( momentUtil.isSameDay( false, '' ) ).toBe( false );
187
+ expect( momentUtil.isSameDay( 0, null ) ).toBe( false );
188
+ expect( momentUtil.isSameDay( moment(), moment().endOf( 'day' ) ) ).toBeTruthy();
189
+ expect( momentUtil.isSameDay( moment().endOf( 'day' ), moment().endOf( 'day' ) ) ).toBeTruthy();
190
+ expect( momentUtil.isSameDay( moment(), moment().add( 10, 'days' ) ) ).toBeFalsy();
191
+ expect( momentUtil.isSameDay( new Date(), new Date() ) ).toBeTruthy();
192
+ } );
193
+
194
+ test( 'isSameMonth', () => {
195
+ const date = moment( 'October 8, 2018 5:30 pm', 'MMMM D, Y h:mm a' );
196
+
197
+ expect( momentUtil.isSameMonth() ).toBe( false );
198
+ expect( momentUtil.isSameMonth( false, '' ) ).toBe( false );
199
+ expect( momentUtil.isSameMonth( 0, null ) ).toBe( false );
200
+ expect( momentUtil.isSameMonth( date, date.clone().add( 24, 'days' ) ) ).toBe( false );
201
+ expect( momentUtil.isSameMonth( date, date.clone().add( 23, 'days' ) ) ).toBe( true );
202
+ expect( momentUtil.isSameMonth( date, date.clone().endOf( 'month' ) ) ).toBe( true );
203
+ expect( momentUtil.isSameMonth( date.clone().endOf( 'month' ), date.clone().endOf( 'month' ) ) ).toBe( true );
204
+ expect( momentUtil.isSameMonth( date, date.clone().add( 10, 'days' ) ) ).toBe( true );
205
+ expect( momentUtil.isSameMonth( date, date ) ).toBe( true );
206
+ } );
207
+
208
+ test( 'isSameYear', () => {
209
+ expect( momentUtil.isSameYear(
210
+ moment( 'May 23, 2018 12:30 am', 'MMM D, YYYY k:m a' ),
211
+ moment( 'September 15, 2018 5:30 am', 'MMM D, YYYY k:m a' )
212
+ ) ).toBeTruthy();
213
+ expect( momentUtil.isSameYear(
214
+ moment( 'May 23, 2022 12:30 am', 'MMM D, YYYY k:m a' ),
215
+ moment( 'September 15, 2022 5:30 am', 'MMM D, YYYY k:m a' )
216
+ ) ).toBeTruthy();
217
+ expect( momentUtil.isSameYear(
218
+ moment( 'May 23, 2018 12:30 am', 'MMM D, YYYY k:m a' ),
219
+ moment( 'September 15, 2022 5:30 am', 'MMM D, YYYY k:m a' )
220
+ ) ).toBeFalsy();
221
+ } );
222
+
223
+ test( 'toMomentFromDate', () => {
224
+ expect( () => momentUtil.toMomentFromDate( '' ) ).toThrowError();
225
+ expect( () => momentUtil.toMomentFromDate( moment() ) ).toThrowError();
226
+ Date.now = jest.fn( () => '2018-05-04T05:23:19.000Z' );
227
+ const format = 'YYYY-MM-DD HH:mm:ss';
228
+ const now = new Date( 'December 17, 2015 03:24:00' );
229
+ expect( momentUtil.toMomentFromDate( now ) ).toBeInstanceOf( moment );
230
+ const expected = momentUtil.toMomentFromDate( now ).format( format );
231
+ expect( expected ).toBe( '2015-12-17 00:00:00' );
232
+ } );
233
+
234
+ test( 'toFormat', () => {
235
+ expect( momentUtil.toFormat( '' ) ).toEqual( '' );
236
+ expect( momentUtil.toFormat( 'Y-m-d H:i:s' ) ).toEqual( 'YYYY-MM-DD HH:mm:ss' );
237
+ expect( momentUtil.toFormat( 'F j, Y g:i a' ) ).toEqual( 'MMMM D, YYYY h:mm a' );
238
+ expect( momentUtil.toFormat( 'tLBIOPTZcr' ) ).toEqual( '' );
239
+ expect( momentUtil.toFormat( 'd' ) ).toEqual( 'DD' );
240
+ expect( momentUtil.toFormat( 'D' ) ).toEqual( 'ddd' );
241
+ expect( momentUtil.toFormat( 'j' ) ).toEqual( 'D' );
242
+ expect( momentUtil.toFormat( 'l' ) ).toEqual( 'dddd' );
243
+ expect( momentUtil.toFormat( 'N' ) ).toEqual( 'E' );
244
+ expect( momentUtil.toFormat( 'S' ) ).toEqual( 'o' );
245
+ expect( momentUtil.toFormat( 'w' ) ).toEqual( 'e' );
246
+ expect( momentUtil.toFormat( 'z' ) ).toEqual( 'DDD' );
247
+ expect( momentUtil.toFormat( 'W' ) ).toEqual( 'W' );
248
+ expect( momentUtil.toFormat( 'F' ) ).toEqual( 'MMMM' );
249
+ expect( momentUtil.toFormat( 'm' ) ).toEqual( 'MM' );
250
+ expect( momentUtil.toFormat( 'M' ) ).toEqual( 'MMM' );
251
+ expect( momentUtil.toFormat( 'n' ) ).toEqual( 'M' );
252
+ expect( momentUtil.toFormat( 'o' ) ).toEqual( 'YYYY' );
253
+ expect( momentUtil.toFormat( 'Y' ) ).toEqual( 'YYYY' );
254
+ expect( momentUtil.toFormat( 'y' ) ).toEqual( 'YY' );
255
+ expect( momentUtil.toFormat( 'a' ) ).toEqual( 'a' );
256
+ expect( momentUtil.toFormat( 'A' ) ).toEqual( 'A' );
257
+ expect( momentUtil.toFormat( 'g' ) ).toEqual( 'h' );
258
+ expect( momentUtil.toFormat( 'G' ) ).toEqual( 'H' );
259
+ expect( momentUtil.toFormat( 'h' ) ).toEqual( 'hh' );
260
+ expect( momentUtil.toFormat( 'H' ) ).toEqual( 'HH' );
261
+ expect( momentUtil.toFormat( 'i' ) ).toEqual( 'mm' );
262
+ expect( momentUtil.toFormat( 's' ) ).toEqual( 'ss' );
263
+ expect( momentUtil.toFormat( 'u' ) ).toEqual( 'SSS' );
264
+ expect( momentUtil.toFormat( 'e' ) ).toEqual( 'zz' );
265
+ expect( momentUtil.toFormat( 'U' ) ).toEqual( 'X' );
266
+ } );
267
+
268
+ describe( 'parseFormats', () => {
269
+ test( 'Use DB format', () => {
270
+ const format = 'YYYY-MM-DD HH:mm:ss';
271
+ const expected = momentUtil.parseFormats( '2019-11-19 22:32:00' );
272
+ expect( expected.format( format ) ).toBe( '2019-11-19 22:32:00' );
273
+ } );
274
+
275
+ test( 'Use WP datetime format', () => {
276
+ const format = 'MMMM D, YYYY h:mm a';
277
+ const expected = momentUtil.parseFormats( 'November 19, 2019 10:32 pm' );
278
+ expect( expected.format( format ) ).toBe( 'November 19, 2019 10:32 pm' );
279
+ } );
280
+
281
+ test( 'Invalid date', () => {
282
+ Date.now = jest.fn( () => new Date( 'July 1, 2018 00:07:31 UTC' ).toISOString() );
283
+ const format = 'YYYY-MM-DD HH:mm:ss';
284
+ const expected = momentUtil.parseFormats( 'No date!' );
285
+ expect( expected.format( format ) ).toBe( '2018-07-01 00:07:31' );
286
+ expect( window.console.warn ).toHaveBeenCalled();
287
+ } );
288
+ } );
289
+
290
+ describe( 'resetTimes', () => {
291
+ const format = 'YYYY-MM-DD HH:mm:ss';
292
+ it( 'Should add an hour in seconds', () => {
293
+ const startMoment = moment( new Date( 'July 19, 2018 19:30:00 UTC' ).toISOString() );
294
+ const { start, end } = momentUtil.resetTimes( startMoment );
295
+ expect( start.format( format ) ).toBe( '2018-07-19 19:30:00' );
296
+ expect( end.format( format ) ).toBe( '2018-07-19 20:30:00' );
297
+ } );
298
+
299
+ it( 'Should add hour in seconds on start of the day', () => {
300
+ const startMoment = moment( new Date( 'July 19, 2018 00:00:00 UTC' ).toISOString() );
301
+ const { start, end } = momentUtil.resetTimes( startMoment );
302
+ expect( start.format( format ) ).toBe( '2018-07-19 00:00:00' );
303
+ expect( end.format( format ) ).toBe( '2018-07-19 01:00:00' );
304
+ } );
305
+
306
+ it( 'Should prevent overflow to the next day', () => {
307
+ const startMoment = moment( new Date( 'July 19, 2018 23:59:59 UTC' ).toISOString() );
308
+ const { start, end } = momentUtil.resetTimes( startMoment );
309
+ expect( start.format( format ) ).toBe( '2018-07-19 22:59:59' );
310
+ expect( end.format( format ) ).toBe( '2018-07-19 23:59:59' );
311
+ } );
312
+ } );
313
+
314
+ describe( 'adjustStart', () => {
315
+ const format = 'YYYY-MM-DD HH:mm:ss';
316
+ it( 'Should keep the same order when start is before', () => {
317
+ const start = moment( new Date( 'July 10, 2018 14:30:00 UTC' ).toISOString() );
318
+ const end = moment( new Date( 'July 10, 2018 20:35:00 UTC' ).toISOString() );
319
+ const output = momentUtil.adjustStart( start, end );
320
+ expect( output.start.format( format ) ).toBe( '2018-07-10 14:30:00' );
321
+ expect( output.end.format( format ) ).toBe( '2018-07-10 20:35:00' );
322
+ } );
323
+
324
+ it( 'Should adjust the start and end time', () => {
325
+ const start = moment( new Date( 'July 10, 2018 20:35:00 UTC' ).toISOString() );
326
+ const end = moment( new Date( 'July 10, 2018 10:30:00 UTC' ).toISOString() );
327
+ const output = momentUtil.adjustStart( start, end );
328
+ expect( output.start.format( format ) ).toBe( '2018-07-10 20:35:00' );
329
+ expect( output.end.format( format ) ).toBe( '2018-07-10 21:35:00' );
330
+ } );
331
+ } );
332
+ } );
common/src/modules/utils/__tests__/number.test.js ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { percentage } from '@moderntribe/common/utils/number';
5
+
6
+ describe( 'percentage', () => {
7
+ test( 'with default values', () => {
8
+ expect( percentage() ).toBe( 0 );
9
+ } );
10
+
11
+ test( 'with non numbers', () => {
12
+ expect( () => percentage( 'modern', 'tribe' ) ).toThrow();
13
+ } );
14
+
15
+ test( 'limits outside 100 percent', () => {
16
+ expect( percentage( 120, 100 ) ).toBe( 120 );
17
+ expect( percentage( 1220, 100 ) ).toBe( 1220 );
18
+ expect( percentage( 101, 100 ) ).toBe( 101 );
19
+ expect( percentage( 100, 100 ) ).toBe( 100 );
20
+ } );
21
+
22
+ test( 'common cases', () => {
23
+ expect( percentage( 10, 100 ) ).toBe( 10 );
24
+ expect( percentage( 17, 100 ) ).toBe( 17 );
25
+ expect( percentage( 20, 1000 ) ).toBe( 2 );
26
+ expect( percentage( 155, 1000 ) ).toBe( 15.5 );
27
+ expect( percentage( 999, 1000 ) ).toBe( 99.9 );
28
+ expect( percentage( 1000, 1000 ) ).toBe( 100 );
29
+ } );
30
+
31
+ test( 'negative percentages', () => {
32
+ expect( percentage( -10, 100 ) ).toBe( -10 );
33
+ expect( percentage( -80, 100 ) ).toBe( -80 );
34
+ expect( percentage( -200, 100 ) ).toBe( -200 );
35
+ } );
36
+ } );
common/src/modules/utils/__tests__/proptypes.test.js ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { noop } from 'lodash';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import * as proptypes from '@moderntribe/common/utils/proptypes';
10
+
11
+ describe( 'Tests for proptypes utils', () => {
12
+ describe( 'createChainableValidator', () => {
13
+ it( 'should not return an error when prop is undefined and is not required', () => {
14
+ const props = {};
15
+ const chainedValidator = jest.fn( proptypes.createChainableValidator( noop ) );
16
+ chainedValidator( props, 'time', 'component' );
17
+
18
+ expect( chainedValidator ).toHaveReturned();
19
+ expect( chainedValidator ).toHaveReturnedWith( null );
20
+ } );
21
+
22
+ it( 'should return an error when prop is undefined and is required', () => {
23
+ const props = {};
24
+ const chainedValidator = jest.fn( proptypes.createChainableValidator( noop ).isRequired );
25
+ chainedValidator( props, 'time', 'component' );
26
+
27
+ expect( chainedValidator ).toHaveReturned();
28
+ expect( chainedValidator ).toHaveReturnedWith( Error( 'The prop `time` is marked as required in `component`, but its value is `undefined`.' ) );
29
+ } );
30
+
31
+ it( 'should return an error when prop is null and is required', () => {
32
+ const props = {
33
+ time: null,
34
+ };
35
+ const chainedValidator = jest.fn( proptypes.createChainableValidator( noop ).isRequired );
36
+ chainedValidator( props, 'time', 'component' );
37
+
38
+ expect( chainedValidator ).toHaveReturned();
39
+ expect( chainedValidator ).toHaveReturnedWith( Error( 'The prop `time` is marked as required in `component`, but its value is `null`.' ) );
40
+ } );
41
+
42
+ it( 'should call the validator when prop is provided, not undefined or null, and is not required', () => {
43
+ const props = {
44
+ time: '15:34',
45
+ };
46
+ const validator = jest.fn( noop );
47
+ const chainedValidator = jest.fn( proptypes.createChainableValidator( validator ) );
48
+ chainedValidator( props, 'time', 'component' );
49
+
50
+ expect( validator ).toHaveBeenCalled();
51
+ expect( validator ).toHaveBeenCalledTimes( 1 );
52
+ } );
53
+
54
+ it( 'should call the validator when prop is provided, not undefined or null, and is required', () => {
55
+ const props = {
56
+ time: '15:34',
57
+ };
58
+ const validator = jest.fn( noop );
59
+ const chainedValidator = jest.fn( proptypes.createChainableValidator( validator ).isRequired );
60
+ chainedValidator( props, 'time', 'component' );
61
+
62
+ expect( validator ).toHaveBeenCalled();
63
+ expect( validator ).toHaveBeenCalledTimes( 1 );
64
+ } );
65
+ } );
66
+
67
+ describe( 'timeRegex', () => {
68
+ it( 'should return true when provided proper time formatted string', () => {
69
+ expect( proptypes.timeRegex.test( '00:00' ) ).toEqual( true );
70
+ expect( proptypes.timeRegex.test( '23:59' ) ).toEqual( true );
71
+ expect( proptypes.timeRegex.test( '12:42' ) ).toEqual( true );
72
+ expect( proptypes.timeRegex.test( '03:01' ) ).toEqual( true );
73
+ expect( proptypes.timeRegex.test( '19:47' ) ).toEqual( true );
74
+ expect( proptypes.timeRegex.test( '05:56' ) ).toEqual( true );
75
+ expect( proptypes.timeRegex.test( '14:11' ) ).toEqual( true );
76
+ } );
77
+
78
+ it( 'should return false when not provided proper time formatted string', () => {
79
+ expect( proptypes.timeRegex.test( 'random string' ) ).toEqual( false );
80
+ expect( proptypes.timeRegex.test( '-00:00' ) ).toEqual( false );
81
+ expect( proptypes.timeRegex.test( '24:00' ) ).toEqual( false );
82
+ expect( proptypes.timeRegex.test( '00:60' ) ).toEqual( false );
83
+ expect( proptypes.timeRegex.test( '24:60' ) ).toEqual( false );
84
+ expect( proptypes.timeRegex.test( '75:93' ) ).toEqual( false );
85
+ expect( proptypes.timeRegex.test( '90:90' ) ).toEqual( false );
86
+ } );
87
+ } );
88
+
89
+ describe( 'timeFormat', () => {
90
+ it( 'should not return an error when provided proper time formatted string', () => {
91
+ const props = {
92
+ time: '15:34',
93
+ };
94
+ const timeFormat = jest.fn( () => proptypes.timeFormat( props, 'time', 'component' ) );
95
+ timeFormat();
96
+
97
+ expect( timeFormat ).toHaveReturned();
98
+ expect( timeFormat ).toHaveReturnedWith( null );
99
+ } );
100
+
101
+ it( 'should return an error when not provided a string', () => {
102
+ const props = {
103
+ time: true,
104
+ };
105
+ const timeFormat = jest.fn( () => proptypes.timeFormat( props, 'time', 'component' ) );
106
+ timeFormat();
107
+
108
+ expect( timeFormat ).toHaveReturned();
109
+ expect( timeFormat ).toHaveReturnedWith( Error( 'Invalid prop `time` of type `boolean` supplied to `component`, expected `string`.' ) );
110
+ } );
111
+
112
+ it( 'should return an error when not provided proper time format', () => {
113
+ const props = {
114
+ time: 'random string',
115
+ };
116
+ const timeFormat = jest.fn( () => proptypes.timeFormat( props, 'time', 'component' ) );
117
+ timeFormat();
118
+
119
+ expect( timeFormat ).toHaveReturned();
120
+ expect( timeFormat ).toHaveReturnedWith( Error( 'Invalid prop `time` format supplied to `component`, expected `hh:mm`.' ) );
121
+ } );
122
+ } );
123
+
124
+ describe( 'nullType', () => {
125
+ test( 'valid prop types', () => {
126
+ const props = {
127
+ name: null,
128
+ }
129
+ const format = jest.fn( () => proptypes.nullType( props, 'name', 'Test Type' ) );
130
+ format();
131
+ expect( format ).toHaveReturned();
132
+ expect( format ).toHaveReturnedWith( undefined );
133
+ } );
134
+
135
+ test( 'invalid prop types', () => {
136
+ const props = {
137
+ name: 'Modern Tribe',
138
+ }
139
+ const format = jest.fn( () => proptypes.nullType( props, 'name', 'Test Type' ) );
140
+ format();
141
+ expect( format ).toHaveReturned();
142
+ expect( format ).toHaveReturnedWith( Error( 'Invalid prop: `name` supplied to `Test Type`, expect null.') );
143
+ } );
144
+ } );
145
+ } );
common/src/modules/utils/__tests__/range.test.js ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import {
5
+ parseChars,
6
+ extractParts,
7
+ parser,
8
+ isFree,
9
+ } from '@moderntribe/common/utils/range';
10
+
11
+ describe( 'Tests for range.js', () => {
12
+ test( 'parseChars', () => {
13
+ expect( parseChars( '' ) ).toEqual( '' );
14
+ expect( parseChars( '12312312321321 123123123123' ) ).toEqual( '12312312321321 123123123123' );
15
+ expect( parseChars( '1"$!%$&/)=(=?^^Ǩ¨_:;' ) ).toEqual( '1' );
16
+ expect( parseChars( ',.-¨¨*^^?=)(/&%$·"!ª12' ) ).toEqual( ',.-12' );
17
+ expect( parseChars( '1-2-3-!"·$%&4-5-6$,.-' ) ).toEqual( '1-2-3-4-5-6,.-' );
18
+ } );
19
+
20
+ test( 'extractParts', () => {
21
+ expect( extractParts( '' ) ).toEqual( [] );
22
+ expect( extractParts( '12' ) ).toEqual( [ '12' ] );
23
+ expect( extractParts( '12 - 23' ) ).toEqual( [ '12', '23' ] );
24
+ expect( extractParts( '12.23 - ' ) ).toEqual( [ '12.23' ] );
25
+ expect( extractParts( '12.23 - 5,10' ) ).toEqual( [ '12.23', '5.10' ] );
26
+ expect( extractParts( '12.23 - - - - - 5,10' ) ).toEqual( [ '12.23', '5.10' ] );
27
+ expect( extractParts( '- - - - - 12.23 - 5,10' ) ).toEqual( [ '12.23', '5.10' ] );
28
+ expect( extractParts( '......,,,,12.23 - 5,10' ) ).toEqual( [ '12.23', '5.10' ] );
29
+ expect( extractParts( '.12.23 - 5,10.....,,,,' ) ).toEqual( [ '12.23', '5.10' ] );
30
+ expect( extractParts( '12.2.....3 ,-. 5,10' ) ).toEqual( [ '12.20', '5.10' ] );
31
+ expect( extractParts( '1-2-3-!"·$%&4-5-6$,.-' ) ).toEqual( [ '1', '2' ] );
32
+ expect( extractParts( '12.23 ,-. ----5,,,,,,10' ) ).toEqual( [ '12.23', '5' ] );
33
+ } );
34
+
35
+ test( 'parser', () => {
36
+ expect( parser( '' ) ).toEqual( '' );
37
+ expect( parser( 'cupidatat occaecat' ) ).toEqual( '' );
38
+ expect( parser( 'cupidatat 12 occaecat - 1,2' ) ).toEqual( '1.20 - 12' );
39
+ expect( parser( '1,2 cupidatat 12 occaecat' ) ).toEqual( '1.20' );
40
+ expect( parser( '1-2-1-1-1-1' ) ).toEqual( '1 - 2' );
41
+ expect( parser( '......,,,,12.23 - 5,10' ) ).toEqual( '5.10 - 12.23' );
42
+ expect( parser( '2.2.....3 ,-. 2,10' ) ).toEqual( '2.10 - 2.20' );
43
+ expect( parser( '12.23 ,-. ----5,,,,,,10' ) ).toEqual( '5 - 12.23' );
44
+ expect( parser( '1-2-3-!"·$%&4-5-6$,.-' ) ).toEqual( '1 - 2' );
45
+ expect( parser( ',.-¨¨*^^?=)(/&%$·"!ª12' ) ).toEqual( '12' );
46
+ expect( parser( '10 - 10' ) ).toEqual( '10' );
47
+ expect( parser( '0' ) ).toEqual( '' );
48
+ expect( parser( '0.0' ) ).toEqual( '' );
49
+ expect( parser( '0 -' ) ).toEqual( '' );
50
+ expect( parser( '0 - 0' ) ).toEqual( '' );
51
+ expect( parser( '0.0 - 0' ) ).toEqual( '' );
52
+ expect( parser( '0.0 - 0.5' ) ).toEqual( '0.00 - 0.50' );
53
+ } );
54
+
55
+ test( 'isFree', () => {
56
+ expect( isFree( '' ) ).toEqual( false );
57
+ expect( isFree( '0.12' ) ).toEqual( false );
58
+ expect( isFree( '0 - 0.12' ) ).toEqual( false );
59
+ expect( isFree( '0.12 - 0' ) ).toEqual( false );
60
+ expect( isFree( '0' ) ).toEqual( true );
61
+ expect( isFree( '0.0' ) ).toEqual( true );
62
+ expect( isFree( '0 - 0' ) ).toEqual( true );
63
+ expect( isFree( '0.0 - 0.0' ) ).toEqual( true );
64
+ } );
65
+ } );
common/src/modules/utils/__tests__/slide.test.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { checkRequestIds } from '@moderntribe/common/utils/slide';
5
+
6
+ describe( 'Tests for slide.js', () => {
7
+ it( 'should return null for up and down', () => {
8
+ const ids = checkRequestIds( 'test-id' );
9
+ expect( ids.up ).toEqual( null );
10
+ expect( ids.down ).toEqual( null );
11
+ } );
12
+ } );
common/src/modules/utils/__tests__/string.test.js ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import {
5
+ isTruthy,
6
+ isFalsy,
7
+ replaceWithObject,
8
+ getWords,
9
+ wordsAsList,
10
+ normalize,
11
+ toBlockName,
12
+ } from '@moderntribe/common/utils/string';
13
+
14
+ describe( 'Tests for string.js', () => {
15
+ test( 'isTruthy', () => {
16
+ expect( isTruthy( 'Sample string' ) ).toEqual( false );
17
+ expect( isTruthy( '0' ) ).toEqual( false );
18
+ expect( isTruthy( 'false' ) ).toEqual( false );
19
+ expect( isTruthy( '1' ) ).toEqual( true );
20
+ expect( isTruthy( 'yes' ) ).toEqual( true );
21
+ expect( isTruthy( 'true' ) ).toEqual( true );
22
+ } );
23
+
24
+ test( 'isFalsy', () => {
25
+ expect( isFalsy( 'Sample string' ) ).toEqual( false );
26
+ expect( isFalsy( '1' ) ).toEqual( false );
27
+ expect( isFalsy( 'true' ) ).toEqual( false );
28
+ expect( isFalsy( '' ) ).toEqual( true );
29
+ expect( isFalsy( '0' ) ).toEqual( true );
30
+ expect( isFalsy( 'no' ) ).toEqual( true );
31
+ expect( isFalsy( 'false' ) ).toEqual( true );
32
+ } );
33
+
34
+ test( 'replaceWithObject', () => {
35
+ expect( replaceWithObject() ).toEqual( '' );
36
+ expect( replaceWithObject( '', {} ) ).toEqual( '' );
37
+ expect( replaceWithObject( 'abcd' ) ).toEqual( 'abcd' );
38
+ expect( replaceWithObject( 'abcd', { z: 'a' } ) ).toEqual( 'abcd' );
39
+ expect( replaceWithObject( 'abcd', { a: 'b', c: 'd' } ) ).toEqual( 'bbdd' );
40
+ expect( replaceWithObject( 'abcd', { a: '', c: '' } ) ).toEqual( 'bd' );
41
+ } );
42
+
43
+ describe( 'getWords', () => {
44
+ test( 'when strings are formed correctly', () => {
45
+ expect( getWords( 'Modern Tribe' ) ).toEqual( [ 'Modern', 'Tribe' ] );
46
+ expect( getWords( 'The Next Generation of Digital Agency' ) )
47
+ .toEqual( [ 'The', 'Next', 'Generation', 'of', 'Digital', 'Agency' ] );
48
+ expect( getWords( 'A list with numbers: 1, 2, 3' ) )
49
+ .toEqual( [ 'A', 'list', 'with', 'numbers:', '1,', '2,', '3' ] );
50
+ } );
51
+
52
+ test( 'Words with multiple spaces on it', () => {
53
+ expect( getWords( ' Modern Tribe ' ) ).toEqual( [ 'Modern', 'Tribe' ] );
54
+ } );
55
+ } );
56
+
57
+ describe( 'applySeparatorsAsCheckboxList', () => {
58
+ test( 'Single word no separator is applied', () => {
59
+ expect( wordsAsList( [ 'Modern' ] ) ).toEqual( 'Modern' );
60
+ } );
61
+
62
+ test( 'Two words last separator is applied', () => {
63
+ expect( wordsAsList( getWords( 'Modern Tribe' ) ) ).toEqual( 'Modern & Tribe' );
64
+ expect( wordsAsList( getWords( 'Events Calendar' ), ',', ' - ' ) )
65
+ .toEqual( 'Events - Calendar' );
66
+ } );
67
+
68
+ test( 'A large number of words', () => {
69
+ expect(
70
+ wordsAsList( [ 'Dog', 'Cat', 'Hamster', 'Parrot', 'Spider', 'Goldfish' ] ),
71
+ ).toEqual( 'Dog, Cat, Hamster, Parrot, Spider & Goldfish' );
72
+ } );
73
+
74
+ test( 'Custom separators', () => {
75
+ expect(
76
+ wordsAsList( [ 'Dog', 'Cat', 'Hamster', 'Parrot', 'Spider', 'Goldfish' ], ' - ', ' => ' ),
77
+ ).toEqual( 'Dog - Cat - Hamster - Parrot - Spider => Goldfish' );
78
+ } );
79
+ } );
80
+
81
+ describe( 'normalize', () => {
82
+ test( 'single words', () => {
83
+ expect( normalize( 'modern' ) ).toEqual( 'modern' );
84
+ expect( normalize( 'TRIBE' ) ).toEqual( 'tribe' );
85
+ } );
86
+
87
+ test( 'multiple words', () => {
88
+ expect( normalize( 'Modern Tribe' ) ).toEqual( 'modern-tribe' );
89
+ expect( normalize( 'https://theeventscalendar.com/' ) )
90
+ .toEqual( 'httpstheeventscalendarcom' );
91
+ } );
92
+
93
+ test( 'Multiple spaces', () => {
94
+ expect( normalize( ' modern TriBe' ) ).toEqual( 'modern-tribe' );
95
+ } );
96
+
97
+ test( 'non words', () => {
98
+ expect( normalize( ' 12312321-,-.(()=^^ ¨¨:;:_¨¨Ç *¿?=)(/&%$·"!.+' ) ).toEqual( '' );
99
+ } );
100
+
101
+ test( 'non strings types', () => {
102
+ expect( normalize( undefined ) ).toBe( '' );
103
+ expect( normalize( [] ) ).toBe( '' );
104
+ expect( normalize( [] ) ).toBe( '' );
105
+ expect( normalize( null ) ).toBe( '' );
106
+ expect( normalize( 1 ) ).toBe( '' );
107
+ } );
108
+ } );
109
+
110
+ describe( 'toBlockName', () => {
111
+ test( 'words', () => {
112
+ expect( toBlockName( 'modern tribe' ) )
113
+ .toBe( 'moderntribe' );
114
+ expect( toBlockName( 'https://theeventscalendar.com/' ) )
115
+ .toBe( 'httpstheeventscalendarcom' );
116
+ } );
117
+
118
+ test( 'non valid characters of a block', () => {
119
+ expect( toBlockName( '_ecp_custom_2' ) ).toBe( 'ecpcustom2' );
120
+ expect( toBlockName( 'ecp-custom-2' ) ).toBe( 'ecp-custom-2' );
121
+ expect( toBlockName( '„…–~~}][‚|#¢∞ecp-custom-2;:_¨¨Ç¨^^=)(/&%$·"!' ) )
122
+ .toBe( 'ecp-custom-2' );
123
+ expect( toBlockName( '„…– ~~}] [‚|#¢∞ecp-custom-2;:_¨ ¨Ç¨^^=)(/ &%$·"!' ) )
124
+ .toBe( 'ecp-custom-2' );
125
+ } );
126
+
127
+ test( 'non strings types', () => {
128
+ expect( toBlockName( undefined ) ).toBe( '' );
129
+ expect( toBlockName( [] ) ).toBe( '' );
130
+ expect( toBlockName( [] ) ).toBe( '' );
131
+ expect( toBlockName( null ) ).toBe( '' );
132
+ expect( toBlockName( 1 ) ).toBe( '' );
133
+ } );
134
+ } );
135
+ } );
common/src/modules/utils/__tests__/time.test.js ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { time } from '@moderntribe/common/utils';
5
+ import { roundTime } from '../moment';
6
+
7
+ describe( 'Tests for time.js', () => {
8
+ test( 'MINUTE_IN_SECONDS', () => {
9
+ expect( time.MINUTE_IN_SECONDS ).toEqual( 60 );
10
+ } );
11
+
12
+ test( 'HALF_HOUR_IN_SECONDS', () => {
13
+ expect( time.HALF_HOUR_IN_SECONDS ).toEqual( 1800 );
14
+ } );
15
+
16
+ test( 'HOUR_IN_SECONDS', () => {
17
+ expect( time.HOUR_IN_SECONDS ).toEqual( 3600 );
18
+ } );
19
+
20
+ test( 'DAY_IN_SECONDS', () => {
21
+ expect( time.DAY_IN_SECONDS ).toEqual( 86400 );
22
+ } );
23
+
24
+ test( 'roundTime', () => {
25
+ expect( time.roundTime( '01:23:54.423', time.TIME_FORMAT_HH_MM_SS_SSS ) )
26
+ .toEqual( '01:00:00.000' );
27
+ expect( time.roundTime( '14:46:13.042', time.TIME_FORMAT_HH_MM_SS_SSS ) )
28
+ .toEqual( '14:30:00.000' );
29
+ expect( time.roundTime( '01:23:54', time.TIME_FORMAT_HH_MM_SS ) )
30
+ .toEqual( '01:00:00' );
31
+ expect( time.roundTime( '14:46:13', time.TIME_FORMAT_HH_MM_SS ) )
32
+ .toEqual( '14:30:00' );
33
+ expect( time.roundTime( '01:23', time.TIME_FORMAT_HH_MM ) )
34
+ .toEqual( '01:00' );
35
+ expect( time.roundTime( '14:46', time.TIME_FORMAT_HH_MM ) )
36
+ .toEqual( '14:30' );
37
+ expect( time.roundTime( '23:54.423', time.TIME_FORMAT_MM_SS_SSS ) )
38
+ .toEqual( '00:00.000' );
39
+ expect( time.roundTime( '46:13.042', time.TIME_FORMAT_MM_SS_SSS ) )
40
+ .toEqual( '30:00.000' );
41
+ expect( time.roundTime( '23:54', time.TIME_FORMAT_MM_SS ) )
42
+ .toEqual( '00:00' );
43
+ expect( time.roundTime( '46:13', time.TIME_FORMAT_MM_SS ) )
44
+ .toEqual( '30:00' );
45
+ } );
46
+
47
+ test( 'START_OF_DAY', () => {
48
+ expect( time.START_OF_DAY ).toEqual( '00:00' );
49
+ } );
50
+
51
+ test( 'END_OF_DAY', () => {
52
+ expect( time.END_OF_DAY ).toEqual( '23:59' );
53
+ } );
54
+
55
+ test( 'TIME_FORMAT_HH_MM_SS_SSS', () => {
56
+ expect( time.TIME_FORMAT_HH_MM_SS_SSS ).toEqual( 'hh:mm:ss.sss' );
57
+ } );
58
+
59
+ test( 'TIME_FORMAT_HH_MM_SS', () => {
60
+ expect( time.TIME_FORMAT_HH_MM_SS ).toEqual( 'hh:mm:ss' );
61
+ } );
62
+
63
+ test( 'TIME_FORMAT_HH_MM', () => {
64
+ expect( time.TIME_FORMAT_HH_MM ).toEqual( 'hh:mm' );
65
+ } );
66
+
67
+ test( 'TIME_FORMAT_MM_SS_SSS', () => {
68
+ expect( time.TIME_FORMAT_MM_SS_SSS ).toEqual( 'mm:ss.sss' );
69
+ } );
70
+
71
+ test( 'TIME_FORMAT_MM_SS', () => {
72
+ expect( time.TIME_FORMAT_MM_SS ).toEqual( 'mm:ss' );
73
+ } );
74
+
75
+ test( 'HOUR_IN_MS', () => {
76
+ expect( time.HOUR_IN_MS ).toEqual( 3600000 );
77
+ } );
78
+
79
+ test( 'MINUTE_IN_MS', () => {
80
+ expect( time.MINUTE_IN_MS ).toEqual( 60000 );
81
+ } );
82
+
83
+ test( 'SECOND_IN_MS', () => {
84
+ expect( time.SECOND_IN_MS ).toEqual( 1000 );
85
+ } );
86
+
87
+ /**
88
+ * Below are tests copied from the hh-mm-ss library and adjusted to use
89
+ * Jest instead of Tape for testing.
90
+ * Link: https://github.com/Goldob/hh-mm-ss/blob/master/test/index.js
91
+ */
92
+ test( 'fromMilliseconds() test', () => {
93
+ // Basic functionality
94
+ expect( time.fromMilliseconds( 75000 ) ).toEqual( '01:15' );
95
+ expect( time.fromMilliseconds( 442800000 ) ).toEqual( '123:00:00' );
96
+ expect( time.fromMilliseconds( 90576 ) ).toEqual( '01:30.576' );
97
+ expect( time.fromMilliseconds( -157250 ) ).toEqual( '-02:37.250' );
98
+
99
+ // Output formatting
100
+ expect( time.fromMilliseconds( 38000, 'mm:ss.sss' ) ).toEqual( '00:38.000' );
101
+ expect( time.fromMilliseconds( 0, 'hh:mm:ss' ) ).toEqual( '00:00:00' );
102
+ expect( time.fromMilliseconds( 3600000, 'mm:ss' ) ).toEqual( '01:00:00' );
103
+ expect( time.fromMilliseconds( 4500000, 'hh:mm' ) ).toEqual( '01:15' );
104
+ expect( time.fromMilliseconds( -9900000, 'hh:mm' ) ).toEqual( '-02:45' );
105
+
106
+ // Input validation
107
+ expect( () => time.fromMilliseconds( null ) ).toThrowErrorMatchingSnapshot();
108
+ expect( () => time.fromMilliseconds('text') ).toThrowErrorMatchingSnapshot();
109
+ expect( () => time.fromMilliseconds(0, 'mm:hh:ss') ).toThrowErrorMatchingSnapshot();
110
+ } );
111
+
112
+ test( 'fromSeconds() test', () => {
113
+ // Basic functionality
114
+ expect( time.fromSeconds( 75 ) ).toEqual( '01:15' );
115
+ expect( time.fromSeconds( 442800 ) ).toEqual( '123:00:00' );
116
+ expect( time.fromSeconds( -442800 ) ).toEqual( '-123:00:00' );
117
+
118
+ // Output formatting
119
+ expect( time.fromSeconds( 38, 'mm:ss.sss' ) ).toEqual( '00:38.000' );
120
+ expect( time.fromSeconds( 0, 'hh:mm:ss' ) ).toEqual( '00:00:00' );
121
+ expect( time.fromSeconds( 3600, 'mm:ss' ) ).toEqual( '01:00:00' );
122
+ expect( time.fromSeconds( 4500, 'hh:mm' ) ).toEqual( '01:15' );
123
+ expect( time.fromSeconds( -9900, 'hh:mm' ) ).toEqual( '-02:45' );
124
+
125
+ // Input validation
126
+ expect( () => time.fromSeconds( null ) ).toThrowErrorMatchingSnapshot();
127
+ expect( () => time.fromSeconds( 'text' ) ).toThrowErrorMatchingSnapshot();
128
+ expect( () => time.fromSeconds( 0, 'mm:hh:ss' ) ).toThrowErrorMatchingSnapshot();
129
+ } );
130
+
131
+ test( 'toMilliseconds() test', () => {
132
+ // Basic functionality
133
+ expect( time.toMilliseconds( '01:05:17' ) ).toEqual( 3917000 );
134
+ expect( time.toMilliseconds( '137:00:00.0' ) ).toEqual( 493200000 );
135
+ expect( time.toMilliseconds( '00:10.230' ) ).toEqual( 10230 );
136
+ expect( time.toMilliseconds( '00:00:07.10845' ) ).toEqual( 7108 );
137
+ expect( time.toMilliseconds( '-02:07:12' ) ).toEqual( -7632000 );
138
+ expect( time.toMilliseconds( '02:00' ) ).toEqual( 120000 );
139
+ expect( time.toMilliseconds( '02:00', 'hh:mm' ) ).toEqual( 7200000 );
140
+ expect( time.toMilliseconds( '-04:35', 'hh:mm' ) ).toEqual( -16500000 );
141
+ expect( time.toMilliseconds( '00:00:07.10845', 'hh:mm' ) ).toEqual( 7108 );
142
+
143
+ // Input validation
144
+ expect( () => time.toMilliseconds( '13:05:02:11' ) ).toThrowErrorMatchingSnapshot();
145
+ expect( () => time.toMilliseconds( '153' ) ).toThrowErrorMatchingSnapshot();
146
+ expect( () => time.toMilliseconds( null ) ).toThrowErrorMatchingSnapshot();
147
+ expect( () => time.toMilliseconds( '00:62' ) ).toThrowErrorMatchingSnapshot();
148
+ expect( () => time.toMilliseconds( '01:30', 'mm:hh:ss' ) ).toThrowErrorMatchingSnapshot();
149
+ } );
150
+
151
+ test( 'toSeconds() test', () => {
152
+ // Basic functionality
153
+ expect( time.toSeconds( '01:05:17' ) ).toEqual( 3917 );
154
+ expect( time.toSeconds( '137:00:00.0' ) ).toEqual( 493200 );
155
+ expect( time.toSeconds( '00:10.230' ) ).toEqual( 10 );
156
+ expect( time.toSeconds( '00:00:07.10845' ) ).toEqual( 7 );
157
+ expect( time.toSeconds( '-02:07:12' ) ).toEqual( -7632 );
158
+ expect( time.toSeconds( '02:00' ) ).toEqual( 120 );
159
+ expect( time.toSeconds( '02:00', 'hh:mm' ) ).toEqual( 7200 );
160
+ expect( time.toSeconds( '-04:35', 'hh:mm' ) ).toEqual( -16500 );
161
+ expect( time.toSeconds( '00:00:07.10845', 'hh:mm' ) ).toEqual( 7 );
162
+
163
+ // Input validation
164
+ expect( () => time.toSeconds( '13:05:02:11' ) ).toThrowErrorMatchingSnapshot();
165
+ expect( () => time.toSeconds( '153' ) ).toThrowErrorMatchingSnapshot();
166
+ expect( () => time.toSeconds( null ) ).toThrowErrorMatchingSnapshot();
167
+ expect( () => time.toSeconds( '00:62' ) ).toThrowErrorMatchingSnapshot();
168
+ expect( () => time.toSeconds( '01:30', 'mm:hh:ss' ) ).toThrowErrorMatchingSnapshot();
169
+ } );
170
+
171
+ test( 'symmetrical conversion test', () => {
172
+ /*
173
+ * fromMilliseconds() and toMilliseconds() for all formats
174
+ */
175
+
176
+ // 90000ms = 1m 30s
177
+ expect( time.toMilliseconds( time.fromMilliseconds( 90000, 'mm:ss' ), 'mm:ss' ) ).toEqual( 90000 );
178
+ expect( time.toMilliseconds( time.fromMilliseconds( 90000, 'mm:ss.sss' ), 'mm:ss.sss' ) ).toEqual( 90000 );
179
+ expect( time.toMilliseconds( time.fromMilliseconds( 90000, 'hh:mm' ), 'hh:mm' ) ).toEqual( 90000 );
180
+ expect( time.toMilliseconds( time.fromMilliseconds( 90000, 'hh:mm:ss' ), 'hh:mm:ss' ) ).toEqual( 90000 );
181
+ expect( time.toMilliseconds( time.fromMilliseconds( 90000, 'hh:mm:ss.sss' ), 'hh:mm:ss.sss' ) ).toEqual( 90000 );
182
+
183
+ // 7517245ms = 2h 5m 17.245s
184
+ expect( time.toMilliseconds( time.fromMilliseconds( 7517245, 'mm:ss' ), 'mm:ss' ) ).toEqual( 7517245 );
185
+ expect( time.toMilliseconds( time.fromMilliseconds( 7517245, 'mm:ss.sss' ), 'mm:ss.sss' ) ).toEqual( 7517245 );
186
+ expect( time.toMilliseconds( time.fromMilliseconds( 7517245, 'hh:mm' ), 'hh:mm' ) ).toEqual( 7517245 );
187
+ expect( time.toMilliseconds( time.fromMilliseconds( 7517245, 'hh:mm:ss' ), 'hh:mm:ss' ) ).toEqual( 7517245 );
188
+ expect( time.toMilliseconds( time.fromMilliseconds( 7517245, 'hh:mm:ss.sss' ), 'hh:mm:ss.sss' ) ).toEqual( 7517245 );
189
+
190
+ // -10800000ms = -3h
191
+ expect( time.toMilliseconds( time.fromMilliseconds( -10800000, 'mm:ss' ), 'mm:ss' ) ).toEqual( -10800000 );
192
+ expect( time.toMilliseconds( time.fromMilliseconds( -10800000, 'mm:ss.sss' ), 'mm:ss.sss' ) ).toEqual( -10800000 );
193
+ expect( time.toMilliseconds( time.fromMilliseconds( -10800000, 'hh:mm' ), 'hh:mm' ) ).toEqual( -10800000 );
194
+ expect( time.toMilliseconds( time.fromMilliseconds( -10800000, 'hh:mm:ss' ), 'hh:mm:ss' ) ).toEqual( -10800000 );
195
+ expect( time.toMilliseconds( time.fromMilliseconds( -10800000, 'hh:mm:ss.sss' ), 'hh:mm:ss.sss' ) ).toEqual( -10800000 );
196
+
197
+ /*
198
+ * fromSeconds() and toMilliseconds() for all formats
199
+ */
200
+
201
+ // 930s = 15m 30s
202
+ expect( time.toSeconds( time.fromSeconds( 930, 'mm:ss' ), 'mm:ss') ).toEqual( 930 );
203
+ expect( time.toSeconds( time.fromSeconds( 930, 'mm:ss.sss' ), 'mm:ss.sss') ).toEqual( 930 );
204
+ expect( time.toSeconds( time.fromSeconds( 930, 'hh:mm' ), 'hh:mm') ).toEqual( 930 );
205
+ expect( time.toSeconds( time.fromSeconds( 930, 'hh:mm:ss' ), 'hh:mm:ss') ).toEqual( 930 );
206
+ expect( time.toSeconds( time.fromSeconds( 930, 'hh:mm:ss.sss' ), 'hh:mm:ss.sss') ).toEqual( 930 );
207
+
208
+ // 4850s = 1h 20m 50s
209
+ expect( time.toSeconds( time.fromSeconds( 4850, 'mm:ss' ), 'mm:ss') ).toEqual( 4850 );
210
+ expect( time.toSeconds( time.fromSeconds( 4850, 'mm:ss.sss' ), 'mm:ss.sss') ).toEqual( 4850 );
211
+ expect( time.toSeconds( time.fromSeconds( 4850, 'hh:mm' ), 'hh:mm') ).toEqual( 4850 );
212
+ expect( time.toSeconds( time.fromSeconds( 4850, 'hh:mm:ss' ), 'hh:mm:ss') ).toEqual( 4850 );
213
+ expect( time.toSeconds( time.fromSeconds( 4850, 'hh:mm:ss.sss' ), 'hh:mm:ss.sss') ).toEqual( 4850 );
214
+
215
+ // -300s = -5m
216
+ expect( time.toSeconds( time.fromSeconds( -300, 'mm:ss' ), 'mm:ss') ).toEqual( -300 );
217
+ expect( time.toSeconds( time.fromSeconds( -300, 'mm:ss.sss' ), 'mm:ss.sss') ).toEqual( -300 );
218
+ expect( time.toSeconds( time.fromSeconds( -300, 'hh:mm' ), 'hh:mm') ).toEqual( -300 );
219
+ expect( time.toSeconds( time.fromSeconds( -300, 'hh:mm:ss' ), 'hh:mm:ss') ).toEqual( -300 );
220
+ expect( time.toSeconds( time.fromSeconds( -300, 'hh:mm:ss.sss' ), 'hh:mm:ss.sss') ).toEqual( -300 );
221
+ } );
222
+ } );
common/src/modules/utils/api.js ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { rest } from '@moderntribe/common/utils/globals';
2
+ import 'whatwg-fetch';
3
+
4
+ /**
5
+ * Send a request into a wp-json endpoint
6
+ *
7
+ * @param {Object} params An object with the following properties:
8
+ * - path: Path for the endpoint
9
+ * - headers: Array of extra headers for the request
10
+ * - initParams: Params send into the fetch along with headers and credentials
11
+ * - namespace: Endpoint namespace default to `wp/v2`
12
+ *
13
+ * @returns {Promise<Response>} return a fetch promise
14
+ */
15
+ export const wpREST = async ( params ) => {
16
+ const { url = '', nonce = {}, namespaces = {} } = rest();
17
+
18
+ /**
19
+ * @todo refactor this method as for more details look into:
20
+ * - https://github.com/moderntribe/events-gutenberg/pull/346#discussion_r222217138
21
+ */
22
+ const options = {
23
+ path: '',
24
+ headers: {},
25
+ initParams: {},
26
+ namespace: namespaces.core || 'wp/v2',
27
+ ...params,
28
+ };
29
+
30
+ const endpoint = `${ url }${ options.namespace }/${ options.path }`;
31
+
32
+ const headers = {
33
+ 'X-WP-Nonce': nonce.wp_rest || '',
34
+ ...options.headers,
35
+ };
36
+
37
+ try {
38
+ const response = await fetch( endpoint, {
39
+ ...options.initParams,
40
+ credentials: 'include',
41
+ headers,
42
+ } );
43
+
44
+ let data = {};
45
+
46
+ if ( response.ok ) {
47
+ data = await response.json();
48
+ }
49
+
50
+ return {
51
+ response,
52
+ data,
53
+ };
54
+ } catch ( e ) {
55
+ throw e;
56
+ }
57
+ };
common/src/modules/utils/date.js ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { identity } from 'lodash';
5
+ import chrono from 'chrono-node';
6
+
7
+ /**
8
+ * Wordpress dependencies
9
+ */
10
+ import { __ } from '@wordpress/i18n';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import {
16
+ moment as momentUtil,
17
+ timezone as timezoneUtil,
18
+ } from '@moderntribe/common/utils';
19
+ import { dateSettings } from '@moderntribe/common/utils/globals';
20
+
21
+ const formats = dateSettings() && dateSettings().formats ? dateSettings().formats : {};
22
+ const timezone = dateSettings() && dateSettings().formats ? dateSettings().formats : {};
23
+
24
+ export const FORMATS = {
25
+ TIME: 'HH:mm:ss',
26
+ DATE_TIME: 'YYYY-MM-DD HH:mm:ss',
27
+ WP: {
28
+ time: 'g:i a',
29
+ time24Hr: 'H:i',
30
+ date: 'F j, Y',
31
+ datetime: 'F j, Y g:i a',
32
+ dateNoYear: 'F j',
33
+ ...formats,
34
+ },
35
+ TIMEZONE: {
36
+ string: 'UTC',
37
+ ...timezone,
38
+ },
39
+ DATABASE: {
40
+ date: 'Y-m-d',
41
+ datetime: 'Y-m-d H:i:s',
42
+ time: 'H:i:s',
43
+ },
44
+ };
45
+
46
+ export const TODAY = new Date();
47
+
48
+ export const timezonesAsSelectData = () => {
49
+ return timezones().map( ( tzone ) => ( {
50
+ value: tzone.key,
51
+ label: tzone.text,
52
+ } ) );
53
+ };
54
+
55
+ export const timezones = () => {
56
+ return timezoneUtil.getItems()
57
+ .map( ( group ) => group.options || [] )
58
+ .reduce( ( prev, current ) => [ ...prev, ...current ], [] );
59
+ };
60
+
61
+ export const toNaturalLanguage = ( params = {} ) => {
62
+ const options = {
63
+ date: null,
64
+ format: {
65
+ month: 'MMMM',
66
+ day: 'D',
67
+ year: 'YYYY',
68
+ time: momentUtil.toFormat( FORMATS.WP.time ),
69
+ },
70
+ separator: '',
71
+ ...params,
72
+ };
73
+
74
+ const parsed = {
75
+ text: '',
76
+ moment: options.date && momentUtil.toMoment( options.date ),
77
+ detail: {
78
+ day: '',
79
+ month: '',
80
+ year: '',
81
+ time: '',
82
+ },
83
+ isValid: false,
84
+ };
85
+
86
+ parsed.isValid = Boolean( parsed.moment && parsed.moment.isValid() );
87
+
88
+ if ( parsed.isValid ) {
89
+ parsed.detail = {
90
+ month: `${ parsed.moment.format( options.format.month ) }`,
91
+ day: `${ parsed.moment.format( options.format.day ) }`,
92
+ year: `${ parsed.moment.format( options.format.year ) }`,
93
+ time: `${ parsed.moment.format( options.format.time ) }`,
94
+ };
95
+ const { detail } = parsed;
96
+ parsed.text = `${ detail.month } ${ detail.day } ${ detail.year } ${ options.separator } ${ detail.time }`;
97
+ }
98
+ return parsed;
99
+ };
100
+
101
+ export const rangeToNaturalLanguage = ( start = '', end = '', separators = {} ) => {
102
+ const separatorOptions = {
103
+ time: __( 'at', 'tribe-common' ),
104
+ date: ' - ',
105
+ ...separators,
106
+ };
107
+ const from = toNaturalLanguage( { date: start, separator: separatorOptions.time } );
108
+ const to = toNaturalLanguage( { date: end, separator: separatorOptions.time } );
109
+ const parts = [ from.text ];
110
+
111
+ if ( from.isValid && to.isValid ) {
112
+ if ( momentUtil.isSameDay( from.moment, to.moment ) ) {
113
+ /**
114
+ * If both dates are happening on the same day the only relevant thing is the time on the second
115
+ * part of the string (to keep string cleaner).
116
+ *
117
+ * - Current behavior 'Oct 8 2018 at 12:00 pm - Oct 8 2018 at 12:30 pm'
118
+ * - New behavior: 'Oct 8 2018 at 12:00 pm - 12:30 pm'
119
+ */
120
+ parts.push( to.detail.time );
121
+ } else if ( momentUtil.isSameMonth( from.moment, to.moment ) ) {
122
+ /**
123
+ * If both dates are happening on the same month and not on the same day but during the same year
124
+ * we don't need to show the same year twice.
125
+ *
126
+ * - Current Behavior: 'Oct 8 2018 at 12:00 pm - Oct 24 2018 12:30 pm'
127
+ * - New Behavior: 'Oct 8 2018 at 12:00 pm - Oct 24 12:30 pm'
128
+ */
129
+ parts.push( `${ to.detail.month } ${ to.detail.day } ${ separatorOptions.time } ${ to.detail.time }` );
130
+ } else {
131
+ // Otherwise just use the full text
132
+ parts.push( to.text );
133
+ }
134
+ }
135
+
136
+ return parts.filter( identity ).join( separatorOptions.date );
137
+ };
138
+
139
+ export const labelToDate = ( label ) => {
140
+ const [ parsed ] = chrono.parse( label );
141
+ const dates = {
142
+ start: null,
143
+ end: null,
144
+ };
145
+ if ( parsed ) {
146
+ const { start, end } = parsed;
147
+ dates.start = start ? momentUtil.toDateTime( momentUtil.toMoment( start.date() ) ) : null;
148
+ dates.end = end ? momentUtil.toDateTime( momentUtil.toMoment( end.date() ) ) : null;
149
+ }
150
+ return dates;
151
+ };
common/src/modules/utils/dom.js ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { noop } from 'lodash';
5
+
6
+ /**
7
+ * Test if a node element has a class present on it
8
+ *
9
+ * @param {HTMLElement|Element} node The node where to look for the class names
10
+ * @param {array} classNames List of class names as an array of strings
11
+ * @returns {boolean} `true` if has any of the classes or false if does not have any
12
+ */
13
+ export const hasClass = ( node, classNames = [] ) => {
14
+ for ( let i = 0; i < classNames.length; i++ ) {
15
+ if ( node.classList.contains( classNames[ i ] ) ) {
16
+ return true;
17
+ }
18
+ }
19
+ return false;
20
+ };
21
+
22
+ /**
23
+ * Utility to search the parent of a node looking from the current node Up to the highest
24
+ * node on the DOM Tree
25
+ *
26
+ * @param {(DOMElement|object)} node - The DOM node where the search starts
27
+ * @param {function} callback - Is executed on every iteration, it should return a boolean
28
+ * @returns {boolean} Returns tre if the callback returns true with any of the parents.
29
+ */
30
+ export const searchParent = ( node = {}, callback = noop ) => {
31
+ let found = false;
32
+ let testNode = node;
33
+ do {
34
+ if ( testNode ) {
35
+ found = callback( testNode );
36
+ }
37
+ const nextNode = testNode && testNode.parentNode ? testNode.parentNode : null;
38
+ testNode = isRootNode( nextNode ) ? null : nextNode;
39
+ } while ( ! found && testNode !== null );
40
+
41
+ return found;
42
+ };
43
+
44
+ /**
45
+ * Test if a node is the same as the root element or the base node of the document.
46
+ *
47
+ * @param {Element} node A Document Node
48
+ * @returns {boolean} true if node is the root Node Document
49
+ */
50
+ export const isRootNode = ( node ) => node === window.top.document;
common/src/modules/utils/get-hidden-height.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @function getHiddenHeight
3
+ * @desc gets the height of hidden objects.
4
+ */
5
+
6
+ const getHiddenHeight = ( el ) => {
7
+ const width = el.clientWidth;
8
+ const element = el;
9
+
10
+ element.style.visibility = 'hidden';
11
+ element.style.height = 'auto';
12
+ element.style.maxHeight = 'none';
13
+ element.style.position = 'fixed';
14
+ element.style.width = `${width}px`;
15
+
16
+ const tHeight = element.offsetHeight;
17
+
18
+ element.style.visibility = '';
19
+ element.style.height = '';
20
+ element.style.maxHeight = '';
21
+ element.style.width = '';
22
+ element.style.position = '';
23
+ element.style.zIndex = '';
24
+
25
+ return tHeight;
26
+ };
27
+
28
+ export default getHiddenHeight;
common/src/modules/utils/globals.js ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @todo: handle globals in a better way
3
+ */
4
+ export const get = ( key, defaultValue ) => window[ key ] || defaultValue;
5
+ export const google = () => get( 'google' );
6
+
7
+ // Localized Config
8
+ export const config = () => get( 'tribe_editor_config', {} );
9
+
10
+ // Common
11
+ export const common = () => config().common || {};
12
+ export const adminUrl = () => common().adminUrl || '';
13
+ export const rest = () => common().rest || {};
14
+ export const restNonce = () => rest().nonce || {};
15
+ export const dateSettings = () => common().dateSettings || {};
16
+ export const editorConstants = () => common().constants || {};
17
+ export const list = () => ( {
18
+ countries: common().countries || {},
19
+ us_states: common().usStates || {},
20
+ } );
21
+
22
+ // TEC
23
+ export const tec = () => config().events || {};
24
+ export const editor = () => tec().editor || {};
25
+ export const settings = () => tec().settings || {};
26
+ export const mapsAPI = () => tec().googleMap || {};
27
+ export const priceSettings = () => tec().priceSettings || {};
28
+ export const tecDateSettings = () => tec().dateSettings || {};
29
+ export const timezoneHtml = () => tec().timezoneHTML || '';
30
+ export const defaultTimes = () => tec().defaultTimes || {};
31
+
32
+ // PRO
33
+ export const pro = () => config().eventsPRO || {};
34
+ export const editorDefaults = () => pro().defaults || {};
35
+
36
+ // Tickets
37
+ export const tickets = () => config().tickets || {};
common/src/modules/utils/index.js ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as date from './date';
2
+ import * as dom from './dom';
3
+ import * as globals from './globals';
4
+ import * as input from './input';
5
+ import * as moment from './moment';
6
+ import * as range from './range';
7
+ import * as slide from './slide';
8
+ import * as string from './string';
9
+ import * as time from './time';
10
+ import * as timezone from './timezone';
11
+ import * as number from './number';
12
+ import * as api from './api';
13
+
14
+ export { date };
15
+ export { dom };
16
+ export { default as getHiddenHeight } from './get-hidden-height';
17
+ export { globals };
18
+ export { input };
19
+ export { moment };
20
+ export { range };
21
+ export { slide };
22
+ export { string };
23
+ export { time };
24
+ export { timezone };
25
+ export { number };
26
+ export { api };
27
+ export { default as TribePropTypes } from './proptypes';
common/src/modules/utils/input.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Allow to set a function (callback) as the parameter of onChange which will send the value of the
3
+ * event into the callback to avoid arrow functions around props of components.
4
+ *
5
+ * @param {Function} callback executed once the event is fired.
6
+ * @return {Function} Function executed by the event to extract the value
7
+ */
8
+ export const sendValue = ( callback ) => ( event ) => {
9
+ const { target = {} } = event;
10
+ const { value = '' } = target;
11
+ callback( value );
12
+ };
common/src/modules/utils/moment.js ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { isString } from 'lodash';
5
+ import moment, { isMoment } from 'moment';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import {
11
+ date as dateUtil,
12
+ time,
13
+ string,
14
+ } from '@moderntribe/common/utils';
15
+
16
+ export const TIME_FORMAT = 'h:mm a';
17
+
18
+ /**
19
+ * Make sure the format provided matches the spec used by moment.js
20
+ *
21
+ * @param {string} format The format to be converted to a moment format
22
+ * @returns {string} return a moment.js valid format
23
+ */
24
+ export const toFormat = ( format ) => {
25
+ const replacements = {
26
+ d: 'DD',
27
+ D: 'ddd',
28
+ j: 'D',
29
+ l: 'dddd',
30
+ N: 'E',
31
+ S: 'o',
32
+ w: 'e',
33
+ z: 'DDD',
34
+ W: 'W',
35
+ F: 'MMMM',
36
+ m: 'MM',
37
+ M: 'MMM',
38
+ n: 'M',
39
+ t: '', // no equivalent
40
+ L: '', // no equivalent
41
+ o: 'YYYY',
42
+ Y: 'YYYY',
43
+ y: 'YY',
44
+ a: 'a',
45
+ A: 'A',
46
+ B: '', // no equivalent
47
+ g: 'h',
48
+ G: 'H',
49
+ h: 'hh',
50
+ H: 'HH',
51
+ i: 'mm',
52
+ s: 'ss',
53
+ u: 'SSS',
54
+ e: 'zz', // deprecated since version 1.6.0 of moment.js
55
+ I: '', // no equivalent
56
+ O: '', // no equivalent
57
+ P: '', // no equivalent
58
+ T: '', // no equivalent
59
+ Z: '', // no equivalent
60
+ c: '', // no equivalent
61
+ r: '', // no equivalent
62
+ U: 'X',
63
+ };
64
+
65
+ return string.replaceWithObject( format, replacements );
66
+ };
67
+
68
+ /**
69
+ * Round the time of a moment object if the minutes on the date is lower than 30 will set to 0 if
70
+ * is greater will se 30 so is either 30 or 0.
71
+ *
72
+ * @param {moment} date Make sure the date is rounded between 0 or 30 minutes
73
+ * @returns {moment} A moment object
74
+ */
75
+ export const roundTime = ( date ) => {
76
+ if ( ! isMoment( date ) ) {
77
+ return date;
78
+ }
79
+
80
+ let minutes = date.minute();
81
+ if ( minutes >= 30 ) {
82
+ minutes = ( minutes % 30 );
83
+ }
84
+
85
+ return date
86
+ .clone()
87
+ .subtract( minutes, 'm' )
88
+ .seconds( 0 );
89
+ };
90
+
91
+ /**
92
+ * Parse multiple formats in a date to ensure the generated dates are valid
93
+ *
94
+ * @param {string} date The date to be converted
95
+ * @param {array} formats The list of formats used to format
96
+ * @returns {moment} moment Object with the date or current date if is non valid
97
+ */
98
+ export const parseFormats = ( date, formats = [ dateUtil.FORMATS.DATABASE.datetime, dateUtil.FORMATS.WP.datetime ] ) => {
99
+ for ( let i = 0; i < formats.length; i ++ ) {
100
+ const format = formats[ i ];
101
+ const result = toMoment( date, format );
102
+ if ( result.isValid() ) {
103
+ return result;
104
+ }
105
+ }
106
+
107
+ const noFormat = moment( date );
108
+ return noFormat.isValid() ? noFormat : moment();
109
+ };
110
+
111
+ /**
112
+ * Convert a Date() object into a Moment.js object avoiding warnings of different formats
113
+ * used by Date
114
+ *
115
+ * @param {(Date|moment|string)} date The date to be converted.
116
+ * @param {string} format The format of the data to be used
117
+ * @param {bool} Force the parse of the format default to true
118
+ * @returns {moment} A moment object
119
+ */
120
+ export const toMoment = ( date, format = dateUtil.FORMATS.DATABASE.datetime, parseFormat = true ) => {
121
+ if ( isMoment( date ) || date instanceof Date ) {
122
+ return moment( date );
123
+ } else if ( isString( date ) ) {
124
+ return moment( date, parseFormat ? toFormat( format ) : format );
125
+ }
126
+
127
+ return moment();
128
+ };
129
+
130
+ export const toMomentFromDate = ( date ) => {
131
+ if ( ! ( date instanceof Date ) ) {
132
+ throw new Error( 'Make sure your date is an instance of Date' );
133
+ }
134
+
135
+ const year = date.getFullYear();
136
+ const month = date.getMonth();
137
+ const day = date.getDate();
138
+
139
+ return moment()
140
+ .year( year )
141
+ .month( month )
142
+ .date( day )
143
+ .startOf( 'day' );
144
+ };
145
+
146
+ /**
147
+ * Convert a Date() object or date string and time into a moment object
148
+ *
149
+ * @param {(Date|moment|string)} date The date to be converted.
150
+ * @param {string} time The time string in HH:mm format..
151
+ * @returns {moment} A moment object
152
+ */
153
+ export const toMomentFromDateTime = ( date, time ) => {
154
+ const [ hours, minutes ] = time.split( ':' );
155
+ return moment( date ).hours( hours ).minutes( minutes );
156
+ };
157
+
158
+ /**
159
+ * Replace the date of a moment object with another date from another moment object
160
+ *
161
+ * @param {moment} original The moment object where the date is going to be replaced
162
+ * @param {moment} replaced The moment object where the date to be used to replace is located
163
+ * @returns {moment} A moment object where the date is replaced
164
+ */
165
+ export const replaceDate = ( original, replaced ) => {
166
+ if ( ! isMoment( original ) || ! isMoment( replaced ) ) {
167
+ throw new Error( 'Make sure your values are instances of moment' );
168
+ }
169
+
170
+ return original
171
+ .year( replaced.year() )
172
+ .month( replaced.month() )
173
+ .date( replaced.date() );
174
+ };
175
+
176
+ /**
177
+ * Set time in seconds to a moment object
178
+ *
179
+ * @param {moment} original The original moment where the date is going to be set
180
+ * @param {number} seconds Amount of seconds to be set to the moment object.
181
+ * @returns {moment} A moment object with the new date
182
+ */
183
+ export const setTimeInSeconds = ( original, seconds = 0 ) => {
184
+ if ( ! isMoment( original ) ) {
185
+ throw new Error( 'Make sure your values are instances of moment' );
186
+ }
187
+
188
+ if ( seconds < 0 ) {
189
+ return original;
190
+ }
191
+
192
+ return original
193
+ .startOf( 'day' )
194
+ .seconds( seconds || original.seconds() );
195
+ };
196
+
197
+ /**
198
+ * Total seconds of a current date from moment
199
+ *
200
+ * @param {moment} date The date to compare on the current day
201
+ * @returns {int} Total of seconds from start of the day to the current moment,
202
+ */
203
+ export const totalSeconds = ( date ) => {
204
+ if ( ! date || ! isMoment( date ) ) {
205
+ return 0;
206
+ }
207
+ return date.diff( moment( date ).startOf( 'day' ), 'seconds' );
208
+ };
209
+
210
+ /**
211
+ * Convert a moment object into a WP date time format
212
+ *
213
+ * @param {moment} date A moment date object
214
+ * @param {string} format Format used to output the date
215
+ * @returns {string} A date time format
216
+ */
217
+ export const toDateTime = ( date, format = dateUtil.FORMATS.DATABASE.datetime ) => (
218
+ date.format( toFormat( format ) )
219
+ );
220
+
221
+ export const toDate = ( date, format = dateUtil.FORMATS.WP.date ) => (
222
+ date.format( toFormat( format ) )
223
+ );
224
+
225
+ export const toDateNoYear = ( date, format = dateUtil.FORMATS.WP.dateNoYear ) => (
226
+ date.format( toFormat( format ) )
227
+ );
228
+
229
+ export const toTime = ( date, format = dateUtil.FORMATS.WP.time ) => (
230
+ date.format( toFormat( format ) )
231
+ );
232
+
233
+ export const toTime24Hr = ( date, format = dateUtil.FORMATS.WP.time24Hr ) => (
234
+ date.format( toFormat( format ) )
235
+ );
236
+
237
+ export const toDatabaseDate = ( date, format = dateUtil.FORMATS.DATABASE.date ) => (
238
+ date.format( toFormat( format ) )
239
+ );
240
+
241
+ export const toDatabaseTime = ( date, format = dateUtil.FORMATS.DATABASE.time ) => (
242
+ date.format( toFormat( format ) )
243
+ );
244
+
245
+ export const toDatePicker = ( date = moment(), format = 'YYYY-MM-DDTHH:mm:ss' ) => (
246
+ date.format( format )
247
+ );
248
+
249
+ /**
250
+ * Test if the start and end dates are the same day.
251
+ *
252
+ * @param {moment} start The start date
253
+ * @param {(moment|String)} end The end date
254
+ * @returns {boolean} if the start and end dates are the same day
255
+ */
256
+ export const isSameDay = ( start, end ) => {
257
+
258
+ if ( ! start || ! end ) {
259
+ return false;
260
+ }
261
+
262
+ return moment( start ).isSame( end, 'day' );
263
+ };
264
+
265
+ /**
266
+ * Test if two moment objects are in the same month
267
+ *
268
+ * @param {moment} start The start moment
269
+ * @param {moment} end The end moment
270
+ * @returns {boolean} true if start and end are on the same month
271
+ */
272
+ export const isSameMonth = ( start, end ) => {
273
+
274
+ if ( ! start || ! end ) {
275
+ return false;
276
+ }
277
+
278
+ return moment( start ).isSame( end, 'month' );
279
+ };
280
+
281
+ /**
282
+ * Test if the start and end dates have the same year.
283
+ *
284
+ * @param {moment} start The start date
285
+ * @param {(moment|String)} end The end date
286
+ * @returns {boolean} if the start and end dates have the same year
287
+ */
288
+ export const isSameYear = ( start, end ) => (
289
+ toMoment( start ).isSame( toMoment( end ), 'year' )
290
+ );
291
+
292
+ /**
293
+ * Reset the time of an event by creating an object with start and end ensuring the end event is
294
+ * after the start date and both are on the same day if the start is one hour before the end of the
295
+ * day it will remove an hour of the start to ensure both start / end happen on the same day
296
+ *
297
+ * @param {moment} start The start date
298
+ * @returns {{start: {moment}, end: {moment}}} Object with two keys: start, end
299
+ */
300
+ export const resetTimes = ( start ) => {
301
+ const testMoment = start.clone().add( time.HOUR_IN_SECONDS, 'seconds' );
302
+
303
+ // Rollback an hour before adding half an hour as we are on the edge of the day
304
+ if ( ! isSameDay( start, testMoment ) ) {
305
+ start.subtract( time.HOUR_IN_SECONDS, 'seconds' );
306
+ }
307
+
308
+ const end = start.clone().add( time.HOUR_IN_SECONDS, 'seconds' );
309
+
310
+ return {
311
+ start,
312
+ end,
313
+ };
314
+ };
315
+
316
+ /**
317
+ * Make sure the start time is always before the end time
318
+ *
319
+ * @param {moment} start The start date
320
+ * @param {moment} end The end date
321
+ * @returns {{start: {moment}, end: {moment}}} Object with two keys: start, end
322
+ */
323
+ export const adjustStart = ( start, end ) => {
324
+ if ( end.isSameOrBefore( start ) ) {
325
+ return resetTimes( start );
326
+ }
327
+
328
+ return {
329
+ start,
330
+ end,
331
+ };
332
+ };
common/src/modules/utils/number.js ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Calculate the percentage of two numbers
3
+ *
4
+ * @param {number} value Initial value from where to take the percentage
5
+ * @param {number} total Total value to get the percentage relative to this value
6
+ * @returns {number} total percentage value
7
+ */
8
+ export const percentage = ( value = 0, total = 0 ) => {
9
+ if ( total === 0 ) {
10
+ return 0;
11
+ }
12
+
13
+ const result = Number.parseFloat( ( value / total ) * 100 );
14
+
15
+ if ( isNaN( result ) ) {
16
+ throw new RangeError(
17
+ `Make sure ${value} and ${total} are valid numbers, operation result in NaN value`
18
+ );
19
+ }
20
+
21
+ return result;
22
+ };
common/src/modules/utils/proptypes.js ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Create chainable validator with isRequired check
3
+ * @param {function} validator
4
+ */
5
+ export const createChainableValidator = ( validator ) => {
6
+ const createChainedValidator = (
7
+ isRequired,
8
+ props,
9
+ propName,
10
+ componentName,
11
+ ) => {
12
+ const propValue = props[ propName ];
13
+
14
+ if ( propValue == null ) {
15
+ if ( isRequired ) {
16
+ if ( propValue === null ) {
17
+ /* eslint-disable-next-line max-len */
18
+ return new Error( `The prop \`${propName}\` is marked as required in \`${componentName}\`, but its value is \`null\`.` );
19
+ }
20
+ /* eslint-disable-next-line max-len */
21
+ return new Error( `The prop \`${propName}\` is marked as required in \`${componentName}\`, but its value is \`undefined\`.` );
22
+ }
23
+ return null;
24
+ } else {
25
+ return validator( props, propName, componentName );
26
+ }
27
+ };
28
+
29
+ const chainedValidator = createChainedValidator.bind( null, false );
30
+ chainedValidator.isRequired = createChainedValidator.bind( null, true );
31
+
32
+ return chainedValidator;
33
+ }
34
+
35
+ export const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/;
36
+
37
+ /**
38
+ * PropTypes check for type string and time format using 24h clock in hh:mm format
39
+ * e.g. 00:24, 03:57, 21:12
40
+ *
41
+ * @param {object} props
42
+ * @param {string} propName
43
+ * @param {string} componentName
44
+ */
45
+ export const timeFormat = ( props, propName, componentName ) => {
46
+ const propValue = props[ propName ];
47
+
48
+ if ( typeof propValue !== 'string' ) {
49
+ const type = typeof propValue;
50
+ /* eslint-disable-next-line max-len */
51
+ return new Error( `Invalid prop \`${propName}\` of type \`${type}\` supplied to \`${componentName}\`, expected \`string\`.` );
52
+ }
53
+
54
+ if ( ! timeRegex.test( propValue ) ) {
55
+ /* eslint-disable-next-line max-len */
56
+ return new Error( `Invalid prop \`${propName}\` format supplied to \`${componentName}\`, expected \`hh:mm\`.` );
57
+ }
58
+
59
+ return null;
60
+ };
61
+
62
+ export const nullType = ( props, propName, componentName ) => {
63
+ if ( null !== props[ propName ] ) {
64
+ return new Error(
65
+ `Invalid prop: \`${propName}\` supplied to \`${ componentName }\`, expect null.`
66
+ );
67
+ }
68
+ }
69
+
70
+ export default {
71
+ timeFormat: createChainableValidator( timeFormat ),
72
+ nullType: createChainableValidator( nullType ),
73
+ };
common/src/modules/utils/range.js ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { trim, isEmpty, split } from 'lodash';
5
+
6
+ /**
7
+ * Parse a string into a range type of string {a} - {b} where a and b are numbers
8
+ *
9
+ * @param {string} input The original string
10
+ * @returns {string} A formatted range string.
11
+ */
12
+ export const parser = ( input ) => {
13
+ const range = trim( input );
14
+
15
+ if ( isEmpty( range ) ) {
16
+ return range;
17
+ }
18
+
19
+ const chars = parseChars( input );
20
+
21
+ if ( isEmpty( chars ) ) {
22
+ return chars;
23
+ }
24
+
25
+ const [ a, b ] = extractParts( chars );
26
+ const [ num_a, num_b ] = [ parseFloat( a ), parseFloat( b ) ];
27
+
28
+ if ( ! num_b || num_b === num_a ) {
29
+ return num_a === 0 ? '' : trim( a );
30
+ }
31
+
32
+ return num_a >= num_b ? `${ trim( b ) } - ${ trim( a ) }` : `${ trim( a ) } - ${ trim( b ) }`;
33
+ };
34
+
35
+ /**
36
+ * Remove any char that is not a: number, dash, dot or comma.
37
+ *
38
+ * @param {string} input The string from where to extract the chars
39
+ * @returns {string} A string with only valid chars
40
+ */
41
+ export const parseChars = ( input = '' ) => (
42
+ split( input, ' ' )
43
+ .map( ( part ) => {
44
+ // Remove anything that is not a number a period or a dash
45
+ return part.replace( /[^0-9.,-]/g, '' );
46
+ } )
47
+ .join( ' ' )
48
+ .trim()
49
+ );
50
+
51
+ /**
52
+ * Extract only valid numbers from the string
53
+ *
54
+ * @param {string} chars The chars to be split into parts.
55
+ * @returns {array} An array with the parts
56
+ */
57
+ export const extractParts = ( chars ) => (
58
+ split( chars.replace( /,/g, '.' ), '-' )
59
+ // Convert , into . so we can parse into numbers
60
+ .map( ( item ) => {
61
+ const re = /([0-9]+(.[0-9]+)?)/g;
62
+ const result = re.exec( item.trim() );
63
+ return null === result ? '' : result[ 1 ];
64
+ } )
65
+ .filter( ( item ) => ! isEmpty( item ) )
66
+ .map( ( item ) => {
67
+ // If the user input the price with decimals (even .00) we want to keep them
68
+ const decimals = 0 < item.indexOf( '.' ) ? 2 : 0;
69
+ return parseFloat( item ).toFixed( decimals );
70
+ } )
71
+ .filter( ( item ) => ! isNaN( item ) )
72
+ .slice( 0, 2 )
73
+ );
74
+
75
+ /**
76
+ * Test to see if an input range is free of cost
77
+ *
78
+ * @param {string} input Range input
79
+ * @returns {boolean} true if the event has 0 on all parts of the range, false otherwise
80
+ */
81
+ export const isFree = ( input ) => {
82
+ const parts = split( input, '-' );
83
+ const test = parts
84
+ .map( ( item ) => parseFloat( item ) )
85
+ .filter( ( item ) => ! isNaN( item ) )
86
+ .filter( ( item ) => item === 0 );
87
+
88
+ return parts.length === test.length;
89
+ };
common/src/modules/utils/slide.js ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Element to slide gets the following CSS:
3
+ * max-height: 0;
4
+ * overflow: hidden;
5
+ */
6
+ import BezierEasing from 'bezier-easing';
7
+ import getHiddenHeight from './get-hidden-height';
8
+
9
+ const ease = BezierEasing( 0.25, 0.1, 0.25, 1 );
10
+
11
+ const requestIds = {};
12
+
13
+ /**
14
+ * Check that request id exists, if not create an entry
15
+ * @param {string} id Unique ID of animation
16
+ */
17
+ export const checkRequestIds = ( id ) => {
18
+ if ( ! requestIds[ id ] ) {
19
+ requestIds[ id ] = {
20
+ up: null,
21
+ down: null,
22
+ };
23
+ }
24
+
25
+ return requestIds[ id ];
26
+ };
27
+
28
+ /**
29
+ * Cancel animations with request id
30
+ * @param {string} id Unique ID of animation
31
+ */
32
+ const cancelAnimations = ( id ) => {
33
+ if ( requestIds[ id ].up ) {
34
+ window.cancelAnimationFrame( requestIds[ id ].up );
35
+ requestIds[ id ].up = null;
36
+ }
37
+ if ( requestIds[ id ].down ) {
38
+ window.cancelAnimationFrame( requestIds[ id ].down );
39
+ requestIds[ id ].down = null;
40
+ }
41
+ };
42
+
43
+ /**
44
+ * Like jQuery's slideDown function
45
+ * @param {Node} elem Element to show and hide
46
+ * @param {string} id Unique ID of animation
47
+ * @param {int} time Length of animation in ms
48
+ * @param {function} callback Callback function
49
+ */
50
+ export const down = ( elem, id, time = 400, callback = null ) => {
51
+ const startHeight = elem.offsetHeight;
52
+ const endHeight = getHiddenHeight( elem );
53
+ let startTime = null;
54
+ elem.style.maxHeight = '0';
55
+
56
+ checkRequestIds( id );
57
+ cancelAnimations( id );
58
+
59
+ const step = ( timestamp ) => {
60
+ if ( ! startTime ) {
61
+ startTime = timestamp;
62
+ }
63
+ const timeDiff = timestamp - startTime;
64
+ const progress = ease( timeDiff / time );
65
+ const height = ( progress * ( endHeight - startHeight ) ) + startHeight;
66
+ elem.style.maxHeight = `${height}px`;
67
+
68
+ if ( timeDiff < time ) {
69
+ requestIds[ id ].down = window.requestAnimationFrame( step );
70
+ } else {
71
+ requestIds[ id ].down = null;
72
+ elem.style.maxHeight = 'none';
73
+ if ( callback ) {
74
+ callback();
75
+ }
76
+ }
77
+ };
78
+
79
+ requestIds[ id ].down = window.requestAnimationFrame( step );
80
+ };
81
+
82
+ /**
83
+ * Slide element up
84
+ * @param {Node} elem Element to show and hide
85
+ * @param {string} id Unique ID of animation
86
+ * @param {int} time Length of animation in ms
87
+ * @param {function} callback Callback function
88
+ */
89
+ export const up = ( elem, id, time = 400, callback = null ) => {
90
+ const startHeight = elem.offsetHeight;
91
+ const endHeight = 0;
92
+ let startTime = null;
93
+ elem.style.maxHeight = `${startHeight}px`;
94
+
95
+ checkRequestIds( id );
96
+ cancelAnimations( id );
97
+
98
+ const step = ( timestamp ) => {
99
+ if ( ! startTime ) {
100
+ startTime = timestamp;
101
+ }
102
+ const timeDiff = timestamp - startTime;
103
+ const progress = ease( timeDiff / time );
104
+ const height = ( progress * ( endHeight - startHeight ) ) + startHeight;
105
+ elem.style.maxHeight = `${height}px`;
106
+
107
+ if ( timeDiff < time ) {
108
+ requestIds[ id ].up = window.requestAnimationFrame( step );
109
+ } else {
110
+ requestIds[ id ].up = null;
111
+ elem.style.maxHeight = '0';
112
+ if ( callback ) {
113
+ callback();
114
+ }
115
+ }
116
+ };
117
+
118
+ requestIds[ id ].up = window.requestAnimationFrame( step );
119
+ };
common/src/modules/utils/string.js ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { escapeRegExp, isUndefined, isString, identity } from 'lodash';
5
+
6
+ /**
7
+ * Test if a string is equivalent to a true value
8
+ *
9
+ * @param {string} value The value to be tested
10
+ * @returns {boolean} true if the value is a valid "true" value.
11
+ */
12
+ export const isTruthy = ( value ) => {
13
+ const validValues = [
14
+ 'true',
15
+ 'yes',
16
+ '1',
17
+ ];
18
+ return validValues.indexOf( value ) !== -1;
19
+ };
20
+
21
+ /**
22
+ * Test if a string is equivalent to a false value
23
+ *
24
+ * @param {string} value The value to be tested
25
+ * @returns {boolean} true if the value is a valid "false" value.
26
+ */
27
+ export const isFalsy = ( value ) => {
28
+ const validValues = [
29
+ 'false',
30
+ 'no',
31
+ '0',
32
+ '',
33
+ ];
34
+ return validValues.indexOf( value ) !== -1;
35
+ };
36
+
37
+ export const replaceWithObject = ( str = '', pairs = {} ) => {
38
+ const substrs = Object.keys( pairs ).map( escapeRegExp );
39
+ return str.split( RegExp( `(${ substrs.join( '|' ) })` ) )
40
+ .map( part => isUndefined( pairs[ part ] ) ? part : pairs[ part ] )
41
+ .join( '' );
42
+ };
43
+
44
+ /**
45
+ * Extract the words from a string into an array of words removing all the empty spaces.
46
+ *
47
+ * @param {string} text The initial text
48
+ * @returns {array} Return an array with the words
49
+ */
50
+ export const getWords = ( text = '' ) => {
51
+ if ( ! isString( text ) ) {
52
+ return [];
53
+ }
54
+ return text.split( /\s/ ).filter( identity );
55
+ };
56
+
57
+ /**
58
+ * Apply separators specifically for check box style list where if there are more than 2 words the first
59
+ * separators except the last is different from the rest, and if there are only 2 words it only uses
60
+ * the last separator instead
61
+ *
62
+ * @param {array} words The list of words to join
63
+ * @param {string} startSeparator the separator applied if there are more than 2 words between all the words except the last one
64
+ * @param {string} endSeparator separator applied between the last words
65
+ * @returns {string} return a string with custom separators between words
66
+ */
67
+ export const wordsAsList = ( words, startSeparator = ', ', endSeparator = ' & ' ) => {
68
+ if ( words.length <= 1 ) {
69
+ return words.join( '' );
70
+ } else {
71
+ const start = words.slice( 0, words.length - 1 ).join( startSeparator );
72
+ const last = words[ words.length - 1 ];
73
+ return `${ start }${ endSeparator }${ last }`;
74
+ }
75
+ };
76
+
77
+ /**
78
+ * Creates a string that only contains a-z characters, useful specially for keys
79
+ *
80
+ * @param {string} text Then ame to be normalized
81
+ * @returns {string} A formatted string with no spacing and only a-z chars
82
+ */
83
+ export const normalize = ( text = '' ) => {
84
+ if ( ! isString( text ) ) {
85
+ return '';
86
+ }
87
+ return text.toLowerCase()
88
+ // Remove any non word or space
89
+ .replace( /[^a-z\s]/g, '' )
90
+ .trim()
91
+ .replace( /\s+/g, '-' );
92
+ };
93
+
94
+ /**
95
+ * Remove invalid characters from a string that aren't consider as valid for a block name.
96
+ *
97
+ * @since 4.8
98
+ *
99
+ * @param {string} text The text to be formatted as block name
100
+ * @returns {string} The formatted text
101
+ */
102
+ export const toBlockName = ( text = '' ) => {
103
+ if ( ! isString( text ) ) {
104
+ return '';
105
+ }
106
+
107
+ // Remove any non numeric, a-z or - value
108
+ return text.replace(/[^a-zA-Z0-9-]/g, '' );
109
+ }
common/src/modules/utils/time.js ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import zeroFill from 'zero-fill';
5
+
6
+ export const MINUTE_IN_SECONDS = 60;
7
+ export const HALF_HOUR_IN_SECONDS = MINUTE_IN_SECONDS * 30;
8
+ export const HOUR_IN_SECONDS = 60 * MINUTE_IN_SECONDS;
9
+ export const DAY_IN_SECONDS = 24 * HOUR_IN_SECONDS;
10
+
11
+ export const START_OF_DAY = '00:00';
12
+ export const END_OF_DAY = '23:59';
13
+
14
+ /**
15
+ * Round the time of a time string
16
+ * If the minutes is lower than 30, it will round the minutes to 0
17
+ * If the minutes is greater than or equal to 30, it will round the minutes to 30
18
+ *
19
+ * @param {string} time
20
+ * @returns {moment} A moment object
21
+ */
22
+ export const roundTime = ( time, format = TIME_FORMAT_MM_SS ) => {
23
+ const seconds = toSeconds( time, format );
24
+ const overage = seconds % ( MINUTE_IN_SECONDS * 30 );
25
+ const roundedSeconds = seconds - overage;
26
+ return fromSeconds( roundedSeconds, format );
27
+ }
28
+
29
+ /**
30
+ * The code below is copied from the library hh-mm-ss
31
+ * Link: https://www.npmjs.com/package/hh-mm-ss
32
+ * The code has been copied so that Modern Tribe can maintain this library
33
+ * internally and adjust as needed.
34
+ */
35
+ export const TIME_FORMAT_HH_MM_SS_SSS = 'hh:mm:ss.sss';
36
+ export const TIME_FORMAT_HH_MM_SS = 'hh:mm:ss';
37
+ export const TIME_FORMAT_HH_MM = 'hh:mm';
38
+ export const TIME_FORMAT_MM_SS_SSS = 'mm:ss.sss';
39
+ export const TIME_FORMAT_MM_SS = 'mm:ss';
40
+
41
+ export const SECOND_IN_MS = 1000;
42
+ export const MINUTE_IN_MS = MINUTE_IN_SECONDS * SECOND_IN_MS;
43
+ export const HOUR_IN_MS = HOUR_IN_SECONDS * SECOND_IN_MS;
44
+
45
+ /**
46
+ * Converts milliseconds to time in the format provided
47
+ *
48
+ * @param {int} ms Milliseconds to convert from
49
+ * @param {string} format Format of time to convert to
50
+ * @returns {string} Time string equivalent of milliseconds in format provided
51
+ */
52
+ export const fromMilliseconds = ( ms, format = TIME_FORMAT_MM_SS ) => {
53
+ if ( typeof ms !== 'number' || Number.isNaN( ms ) ) {
54
+ /* eslint-disable-next-line max-len */
55
+ throw new Error( 'Argument `ms` provided to `fromMilliseconds` is not a number or is NaN.' );
56
+ }
57
+
58
+ const absMs = Math.abs( ms );
59
+
60
+ const negative = ( ms < 0 );
61
+ const hours = Math.floor( absMs / HOUR_IN_MS );
62
+ const minutes = Math.floor( absMs % HOUR_IN_MS / MINUTE_IN_MS );
63
+ const seconds = Math.floor( absMs % MINUTE_IN_MS / SECOND_IN_MS );
64
+ const miliseconds = Math.floor( absMs % SECOND_IN_MS );
65
+
66
+ return formatTime( {
67
+ negative,
68
+ hours,
69
+ minutes,
70
+ seconds,
71
+ miliseconds,
72
+ }, format );
73
+ };
74
+
75
+ /**
76
+ * Converts seconds to time in the format provided
77
+ *
78
+ * @param {int} s Seconds to convert from
79
+ * @param {string} format Format of time to convert to
80
+ * @returns {string} Time string equivalent of seconds in format provided
81
+ */
82
+ export const fromSeconds = ( s, format = TIME_FORMAT_MM_SS ) => {
83
+ if ( typeof s !== 'number' || Number.isNaN( s ) ) {
84
+ /* eslint-disable-next-line max-len */
85
+ throw new Error( 'Argument `s` provided to `fromSeconds` is not a number or is NaN.' );
86
+ }
87
+
88
+ const ms = s * SECOND_IN_MS;
89
+
90
+ return fromMilliseconds( ms, format );
91
+ };
92
+
93
+ /**
94
+ * Converts time in the format provided to milliseconds
95
+ *
96
+ * @param {string} time Time string to convert from
97
+ * @param {string} format Format of time to convert from
98
+ * @returns {int} Milliseconds equivalent of time string in format provided
99
+ */
100
+ export const toMilliseconds = ( time, format = TIME_FORMAT_MM_SS ) => {
101
+ let re;
102
+
103
+ if ( [
104
+ TIME_FORMAT_HH_MM_SS_SSS,
105
+ TIME_FORMAT_HH_MM_SS,
106
+ TIME_FORMAT_MM_SS_SSS,
107
+ TIME_FORMAT_MM_SS,
108
+ ].includes( format ) ) {
109
+ re = /^(-)?(?:(\d\d+):)?(\d\d):(\d\d)(\.\d+)?$/;
110
+ } else if ( format === TIME_FORMAT_HH_MM ) {
111
+ re = /^(-)?(\d\d):(\d\d)(?::(\d\d)(?:(\.\d+))?)?$/;
112
+ } else {
113
+ /* eslint-disable-next-line max-len */
114
+ throw new Error( 'Argument `format` provided to `toMilliseconds` is not a recognized format.' );
115
+ }
116
+
117
+ const result = re.exec( time );
118
+ if ( ! result ) {
119
+ /* eslint-disable-next-line max-len */
120
+ throw new Error( 'Argument `time` provided to `toMilliseconds` is not a recognized format.' );
121
+ }
122
+
123
+ const negative = result[ 1 ] === '-';
124
+ const hours = result[ 2 ] | 0;
125
+ const minutes = result[ 3 ] | 0;
126
+ const seconds = result[ 4 ] | 0;
127
+ const miliseconds = Math.floor( 1000 * result[ 5 ] | 0 );
128
+
129
+ if ( minutes >= 60 || seconds >= 60 ) {
130
+ /* eslint-disable-next-line max-len */
131
+ throw new Error( 'Argument `time` provided to `toMilliseconds` contains minutes or seconds greater than 59.' );
132
+ }
133
+
134
+ return ( negative ? -1 : 1 ) * (
135
+ hours * HOUR_IN_MS
136
+ + minutes * MINUTE_IN_MS
137
+ + seconds * SECOND_IN_MS
138
+ + miliseconds
139
+ );
140
+ };
141
+
142
+ /**
143
+ * Converts time in the format provided to seconds
144
+ *
145
+ * @param {string} time Time string to convert from
146
+ * @param {string} format Format of time to convert from
147
+ * @returns {int} Seconds equivalent of time string in format provided
148
+ */
149
+ export const toSeconds = ( time, format = TIME_FORMAT_MM_SS ) => {
150
+ const ms = toMilliseconds( time, format );
151
+ return Math.floor( ms / SECOND_IN_MS );
152
+ };
153
+
154
+ /**
155
+ * Formats time object to time string in the format provided
156
+ *
157
+ * @param {object} time Time object to format from
158
+ * @param {string} format Format of time to format to
159
+ * @returns {string} Time string in format provided
160
+ */
161
+ export const formatTime = ( time, format ) => {
162
+ let showMs;
163
+ let showSc;
164
+ let showHr;
165
+
166
+ switch ( format ) {
167
+ case TIME_FORMAT_HH_MM_SS_SSS:
168
+ showMs = true;
169
+ showSc = true;
170
+ showHr = true;
171
+ break;
172
+ case TIME_FORMAT_HH_MM_SS:
173
+ showMs = ! ! time.miliseconds;
174
+ showSc = true;
175
+ showHr = true;
176
+ break;
177
+ case TIME_FORMAT_HH_MM:
178
+ showMs = ! ! time.miliseconds;
179
+ showSc = showMs || ! ! time.seconds;
180
+ showHr = true;
181
+ break;
182
+ case TIME_FORMAT_MM_SS_SSS:
183
+ showMs = true;
184
+ showSc = true;
185
+ showHr = ! ! time.hours;
186
+ break;
187
+ case TIME_FORMAT_MM_SS:
188
+ showMs = ! ! time.miliseconds;
189
+ showSc = true;
190
+ showHr = ! ! time.hours;
191
+ break;
192
+ default:
193
+ /* eslint-disable-next-line max-len */
194
+ throw new Error( 'Argument `format` provided to `formatTime` is not a recognized format.' );
195
+ }
196
+
197
+ const hh = zeroFill( 2, time.hours );
198
+ const mm = zeroFill( 2, time.minutes );
199
+ const ss = zeroFill( 2, time.seconds );
200
+ const sss = zeroFill( 3, time.miliseconds );
201
+
202
+ return ( time.negative ? '-' : '' ) + ( showHr ? (
203
+ showMs ? `${hh}:${mm}:${ss}.${sss}` : showSc ? `${hh}:${mm}:${ss}` : `${hh}:${mm}`