Content Aware Sidebars – Unlimited Widget Areas - Version 3.17.1

Version Description

  • [new] added icons and improved ui when editing conditions
  • [new] performance improvements
  • [fixed] quick-select could in some cases remove edited post from conditions
  • [fixed] php8.1 deprecation warnings
  • [updated] wp-content-aware-engine library
  • [updated] freemius sdk

Pro Plan:

  • [fixed] php8+ error when editing sidebar
Download this release

Release Info

Developer intoxstudio
Plugin Icon 128x128 Content Aware Sidebars – Unlimited Widget Areas
Version 3.17.1
Comparing to
See all releases

Code changes from version 3.17 to 3.17.1

Files changed (103) hide show
  1. admin/admin_bar.php +280 -280
  2. admin/db-updates.php +390 -393
  3. admin/quick_select.php +434 -434
  4. admin/screen_widgets.php +216 -218
  5. admin/settings.php +86 -93
  6. admin/sidebar-edit.php +1005 -1010
  7. admin/sidebar-list-table.php +3 -4
  8. app.php +1 -1
  9. conditions/placeholder.php +101 -115
  10. content-aware-sidebars.php +1 -1
  11. freemius.php +115 -114
  12. lib/freemius/assets/css/admin/account.css +1 -1
  13. lib/freemius/assets/css/admin/account.css.map +1 -0
  14. lib/freemius/assets/css/admin/add-ons.css +1 -2
  15. lib/freemius/assets/css/admin/add-ons.css.map +1 -0
  16. lib/freemius/assets/css/admin/affiliation.css +1 -1
  17. lib/freemius/assets/css/admin/affiliation.css.map +1 -0
  18. lib/freemius/assets/css/admin/checkout.css +1 -1
  19. lib/freemius/assets/css/admin/clone-resolution.css +1 -0
  20. lib/freemius/assets/css/admin/clone-resolution.css.map +1 -0
  21. lib/freemius/assets/css/admin/common.css +1 -2
  22. lib/freemius/assets/css/admin/common.css.map +1 -0
  23. lib/freemius/assets/css/admin/connect.css +1 -1
  24. lib/freemius/assets/css/admin/connect.css.map +1 -0
  25. lib/freemius/assets/css/admin/debug.css +1 -1
  26. lib/freemius/assets/css/admin/debug.css.map +1 -0
  27. lib/freemius/assets/css/admin/gdpr-optin-notice.css +1 -1
  28. lib/freemius/assets/css/admin/gdpr-optin-notice.css.map +1 -0
  29. lib/freemius/assets/css/admin/plugins.css +1 -1
  30. lib/freemius/assets/css/admin/plugins.css.map +1 -0
  31. lib/freemius/assets/css/customizer.css +1 -1
  32. lib/freemius/assets/css/customizer.css.map +1 -0
  33. lib/freemius/config.php +388 -388
  34. lib/freemius/includes/class-freemius.php +538 -146
  35. lib/freemius/includes/class-fs-admin-notices.php +52 -21
  36. lib/freemius/includes/class-fs-api.php +27 -3
  37. lib/freemius/includes/i18n.php +1 -1
  38. lib/freemius/includes/managers/class-fs-admin-notice-manager.php +42 -14
  39. lib/freemius/includes/managers/class-fs-clone-manager.php +1480 -0
  40. lib/freemius/includes/managers/class-fs-key-value-storage.php +10 -0
  41. lib/freemius/includes/managers/class-fs-plugin-manager.php +18 -5
  42. lib/freemius/languages/freemius-cs_CZ.mo +0 -0
  43. lib/freemius/languages/freemius-da_DK.mo +0 -0
  44. lib/freemius/languages/freemius-de_DE.mo +0 -0
  45. lib/freemius/languages/freemius-en.mo +0 -0
  46. lib/freemius/languages/freemius-es_ES.mo +0 -0
  47. lib/freemius/languages/freemius-fr_FR.mo +0 -0
  48. lib/freemius/languages/freemius-he_IL.mo +0 -0
  49. lib/freemius/languages/freemius-hu_HU.mo +0 -0
  50. lib/freemius/languages/freemius-it_IT.mo +0 -0
  51. lib/freemius/languages/freemius-ja.mo +0 -0
  52. lib/freemius/languages/freemius-nl_NL.mo +0 -0
  53. lib/freemius/languages/freemius-ru_RU.mo +0 -0
  54. lib/freemius/languages/freemius-ta.mo +0 -0
  55. lib/freemius/languages/freemius-zh_CN.mo +0 -0
  56. lib/freemius/require.php +1 -0
  57. lib/freemius/start.php +1 -1
  58. lib/freemius/templates/account.php +30 -8
  59. lib/freemius/templates/account/partials/site.php +3 -3
  60. lib/freemius/templates/account/payments.php +10 -11
  61. lib/freemius/templates/admin-notice.php +4 -1
  62. lib/freemius/templates/clone-resolution-js.php +77 -0
  63. lib/freemius/templates/connect.php +11 -3
  64. lib/freemius/templates/debug.php +0 -3
  65. lib/freemius/templates/forms/email-address-update.php +1 -1
  66. lib/freemius/templates/forms/license-activation.php +33 -5
  67. lib/freemius/templates/powered-by.php +7 -7
  68. lib/freemius/templates/pricing.php +1 -1
  69. lib/wp-content-aware-engine/assets/css/condition_groups.css +3 -3
  70. lib/wp-content-aware-engine/assets/js/condition_groups.min.js +2 -2
  71. lib/wp-content-aware-engine/bootstrap.php +2 -2
  72. lib/wp-content-aware-engine/collection.php +158 -0
  73. lib/wp-content-aware-engine/core.php +1227 -1241
  74. lib/wp-content-aware-engine/meta.php +236 -238
  75. lib/wp-content-aware-engine/module/author.php +97 -96
  76. lib/wp-content-aware-engine/module/base.php +376 -363
  77. lib/wp-content-aware-engine/module/bbpress.php +100 -99
  78. lib/wp-content-aware-engine/module/bp_member.php +138 -154
  79. lib/wp-content-aware-engine/module/date.php +72 -77
  80. lib/wp-content-aware-engine/module/page_template.php +2 -1
  81. lib/wp-content-aware-engine/module/pods.php +4 -15
  82. lib/wp-content-aware-engine/module/polylang.php +111 -111
  83. lib/wp-content-aware-engine/module/post_type.php +525 -527
  84. lib/wp-content-aware-engine/module/qtranslate.php +2 -1
  85. lib/wp-content-aware-engine/module/static.php +92 -92
  86. lib/wp-content-aware-engine/module/taxonomy.php +545 -545
  87. lib/wp-content-aware-engine/module/translatepress.php +2 -2
  88. lib/wp-content-aware-engine/module/transposh.php +2 -2
  89. lib/wp-content-aware-engine/module/weglot.php +2 -1
  90. lib/wp-content-aware-engine/module/wpml.php +2 -1
  91. lib/wp-content-aware-engine/modulemanager.php +0 -26
  92. lib/wp-content-aware-engine/objectmanager.php +18 -145
  93. lib/wp-content-aware-engine/typemanager.php +10 -13
  94. lib/wp-content-aware-engine/view.php +1 -1
  95. lib/wp-content-aware-engine/view/condition_options.php +16 -16
  96. lib/wp-content-aware-engine/view/condition_template.php +2 -1
  97. lib/wp-content-aware-engine/view/group_template.php +10 -10
  98. lib/wp-content-aware-engine/view/meta_box.php +1 -1
  99. readme.txt +14 -1
  100. sidebar.php +820 -826
  101. view/meta_box_html.php +53 -53
  102. view/meta_box_submit.php +1 -1
  103. view/sidebars_quick_select.php +42 -42
admin/admin_bar.php CHANGED
@@ -1,66 +1,66 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- class CAS_Admin_Bar
12
- {
13
- const NODE_ROOT = 'wpcas-tool';
14
- const NODE_THEME_AREAS = 'theme-areas';
15
- const NODE_CONDITION_TYPES = 'condition-types';
16
- const NODE_CUSTOM_SIDEBARS = 'custom-sidebars';
17
-
18
- const DOCS_MAP = [
19
- 'author' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/authors/',
20
- 'bb_profile' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/bbpress-user-profiles/',
21
- 'bp_member' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/buddypress-profiles/',
22
- 'bp_group' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/buddypress-groups/',
23
- 'date' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/dates/',
24
- 'language' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/languages/',
25
- 'page_template' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/page-templates/',
26
- 'taxonomy' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/taxonomies/',
27
- 'pods' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/pods-pages/',
28
- 'post_type' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/post-types/',
29
- 'url' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/urls/',
30
- 'referrer' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/urls/',
31
- 'static' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/static-pages/',
32
- ];
33
-
34
- /**
35
- * @var array
36
- */
37
- private $theme_sidebars = [];
38
-
39
- /**
40
- * @var array
41
- */
42
- private $custom_sidebars = [];
43
-
44
- public function __construct()
45
- {
46
- add_action('admin_bar_init', [$this,'initiate']);
47
- }
48
-
49
- public function initiate()
50
- {
51
- if (!$this->authorize_user()) {
52
- return;
53
- }
54
-
55
- add_filter('is_active_sidebar', [$this,'detect_sidebars_via_active'], 10, 2);
56
- add_action('dynamic_sidebar_before', [$this,'detect_sidebars_via_render'], 10, 2);
57
- add_action('admin_bar_menu', [$this,'add_menu'], 99);
58
- add_action('wp_head', [$this,'print_styles']);
59
- }
60
-
61
- public function print_styles()
62
- {
63
- echo '<style type="text/css" media="screen">'."\n"; ?>
64
  #wp-admin-bar-wpcas-tool #wp-admin-bar-wpcas-tool-custom-sidebars {
65
  border-top:4px solid #75d7ef;
66
  }
@@ -82,220 +82,220 @@ class CAS_Admin_Bar
82
  margin-right:0!important;
83
  font-size:14px!important;
84
  }
85
- <?php
86
- echo '</style>';
87
- }
88
-
89
- /**
90
- * @param WP_Admin_Bar $admin_bar
91
- * @param array $args
92
- * @param string $parent
93
- * @return self
94
- */
95
- private function add_node($admin_bar, $args, $parent = null)
96
- {
97
- if ($args['id'] !== self::NODE_ROOT) {
98
- $args['parent'] = self::NODE_ROOT . (!is_null($parent) ? '-'.$parent : '');
99
- $args['id'] = $args['parent'].'-'.$args['id'];
100
- }
101
- $admin_bar->add_node($args);
102
-
103
- return $this;
104
- }
105
-
106
- /**
107
- * @param WP_Admin_Bar $admin_bar
108
- * @return void
109
- */
110
- public function add_menu($admin_bar)
111
- {
112
- global $wp_registered_sidebars;
113
-
114
- $post_type_object = get_post_type_object(CAS_App::TYPE_SIDEBAR);
115
-
116
- $this
117
- ->add_node($admin_bar, [
118
- 'id' => self::NODE_ROOT,
119
- 'title' => '<span class="ab-icon dashicons '.$post_type_object->menu_icon.'"></span>',
120
- 'href' => admin_url('admin.php?page=wpcas'),
121
- 'meta' => [
122
- 'title' => __('Content Aware Sidebars', 'content-aware-sidebars')
123
- ]
124
- ])
125
- ->add_node($admin_bar, [
126
- 'id' => 'add_new',
127
- 'title' => $post_type_object->labels->add_new,
128
- 'href' => admin_url('admin.php?page=wpcas-edit'),
129
- ])
130
- ->add_node($admin_bar, [
131
- 'id' => self::NODE_CONDITION_TYPES,
132
- 'title' => __('Condition Types', 'content-aware-sidebars'),
133
- ])
134
- ->add_node($admin_bar, [
135
- 'id' => self::NODE_THEME_AREAS,
136
- 'title' => __('Theme Areas', 'content-aware-sidebars')
137
- ]);
138
-
139
- $cache = get_option(WPCACore::OPTION_CONDITION_TYPE_CACHE, []);
140
- if (isset($cache[CAS_App::TYPE_SIDEBAR]) && !empty($cache[CAS_App::TYPE_SIDEBAR])) {
141
- $title = __('Cache Active', 'content-aware-sidebars');
142
- $link = null;
143
- $class = 'wpcas-ok';
144
- } else {
145
- $title = __('Activate Cache Now', 'content-aware-sidebars');
146
- $link = wp_nonce_url(admin_url('admin.php?page=wpcas-settings&action=update_condition_type_cache'), 'update_condition_type_cache');
147
- $class = 'wpcas-warn';
148
- }
149
- $this->add_node($admin_bar, [
150
- 'id' => 'condition_cache',
151
- 'title' => $title.' &#9210;',
152
- 'href' => $link,
153
- 'meta' => [
154
- 'class' => $class,
155
- ]
156
- ], self::NODE_CONDITION_TYPES);
157
-
158
- $args = [];
159
- foreach (WPCACore::get_conditional_modules('sidebar') as $module) {
160
- $title = $module->get_name();
161
- $link = '';
162
- if (array_key_exists($module->get_id(), self::DOCS_MAP)) {
163
- $title = '<span class="ab-icon dashicons dashicons-external"></span> '.$title;
164
- $link = self::DOCS_MAP[$module->get_id()].'?utm_source=plugin&amp;utm_medium=admin_bar&amp;utm_campaign=cas';
165
- }
166
- $args[] = [
167
- 'id' => $module->get_id(),
168
- 'title' => $title,
169
- 'href' => $link,
170
- 'meta' => [
171
- 'target' => '_blank',
172
- 'rel' => 'noopener'
173
- ]
174
- ];
175
- }
176
- $this->add_nodes($admin_bar, $args, self::NODE_CONDITION_TYPES);
177
-
178
- $args = [];
179
- foreach ($this->theme_sidebars as $index => $has_widgets) {
180
- $sidebar_name = isset($wp_registered_sidebars[$index]['name'])
181
- ? $wp_registered_sidebars[$index]['name']
182
- : $index;
183
- $args[] = [
184
- 'id' => $index,
185
- 'title' => $sidebar_name . ($has_widgets ? '' : ' ('.__('Hidden').')'),
186
- ];
187
- }
188
- $this->add_nodes($admin_bar, $args, self::NODE_THEME_AREAS);
189
-
190
- $admin_bar->add_group([
191
- 'id' => self::NODE_ROOT.'-'.self::NODE_CUSTOM_SIDEBARS,
192
- 'parent' => self::NODE_ROOT,
193
- 'meta' => [
194
- 'class' => 'ab-sub-secondary'
195
- ]
196
- ]);
197
-
198
- $args = [];
199
- foreach ($this->custom_sidebars as $id => $has_widgets) {
200
- $sidebar = CAS_App::instance()->manager()->sidebars[$id];
201
- $args[] = [
202
- 'id' => $sidebar->ID,
203
- 'title' => $sidebar->post_title,
204
- 'href' => get_edit_post_link($sidebar->ID)
205
- ];
206
- }
207
- $this->add_nodes($admin_bar, $args, self::NODE_CUSTOM_SIDEBARS);
208
-
209
- if (empty($this->custom_sidebars)) {
210
- $args = [
211
- 'id' => 'no_custom_sidebars',
212
- 'title' => $post_type_object->labels->not_found
213
- ];
214
- $this->add_node($admin_bar, $args, self::NODE_CUSTOM_SIDEBARS);
215
- }
216
-
217
- //@todo: show custom sidebars that were not displayed on page
218
- }
219
-
220
- /**
221
- * @param bool $has_widgets
222
- * @param string $index
223
- * @return bool
224
- */
225
- public function detect_sidebars_via_active($has_widgets, $index)
226
- {
227
- $this->detect_sidebar($has_widgets, $index);
228
- return $has_widgets;
229
- }
230
-
231
- /**
232
- * @param string $index
233
- * @param bool $has_widgets
234
- * @return void
235
- */
236
- public function detect_sidebars_via_render($index, $has_widgets)
237
- {
238
- $this->detect_sidebar($has_widgets, $index);
239
- }
240
-
241
- /**
242
- * @param WP_Admin_Bar $admin_bar
243
- * @param array $nodes
244
- * @param string|null $parent
245
- * @return void
246
- */
247
- private function add_nodes($admin_bar, $nodes, $parent = null)
248
- {
249
- usort($nodes, [$this,'sort_nodes']);
250
- foreach ($nodes as $node_args) {
251
- $this->add_node($admin_bar, $node_args, $parent);
252
- }
253
- }
254
-
255
- /**
256
- * @param string $a
257
- * @param string $b
258
- * @return int
259
- */
260
- private function sort_nodes($a, $b)
261
- {
262
- return strcasecmp($a['id'], $b['id']);
263
- }
264
-
265
- /**
266
- * @param bool $has_widgets
267
- * @param string $index
268
- * @return void
269
- */
270
- private function detect_sidebar($has_widgets, $index)
271
- {
272
- if (str_replace(CAS_App::SIDEBAR_PREFIX, '', $index) === $index) {
273
- $this->theme_sidebars[$index] = $has_widgets;
274
- } else {
275
- $this->custom_sidebars[$index] = $has_widgets;
276
- }
277
- $this->detect_sidebar_target($has_widgets, $index);
278
- }
279
-
280
- /**
281
- * @param bool $has_widgets
282
- * @param string $index
283
- * @return void
284
- */
285
- private function detect_sidebar_target($has_widgets, $index)
286
- {
287
- $host_map = CAS_App::instance()->manager()->get_replacement_map();
288
- if (isset($host_map[$index])) {
289
- $this->detect_sidebar($has_widgets, $host_map[$index]);
290
- }
291
- }
292
-
293
- /**
294
- * @return bool
295
- */
296
- private function authorize_user()
297
- {
298
- $post_type_object = get_post_type_object(CAS_App::TYPE_SIDEBAR);
299
- return current_user_can($post_type_object->cap->create_posts);
300
- }
301
- }
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ class CAS_Admin_Bar
12
+ {
13
+ const NODE_ROOT = 'wpcas-tool';
14
+ const NODE_THEME_AREAS = 'theme-areas';
15
+ const NODE_CONDITION_TYPES = 'condition-types';
16
+ const NODE_CUSTOM_SIDEBARS = 'custom-sidebars';
17
+
18
+ const DOCS_MAP = [
19
+ 'author' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/authors/',
20
+ 'bb_profile' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/bbpress-user-profiles/',
21
+ 'bp_member' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/buddypress-profiles/',
22
+ 'bp_group' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/buddypress-groups/',
23
+ 'date' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/dates/',
24
+ 'language' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/languages/',
25
+ 'page_template' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/page-templates/',
26
+ 'taxonomy' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/taxonomies/',
27
+ 'pods' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/pods-pages/',
28
+ 'post_type' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/post-types/',
29
+ 'url' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/urls/',
30
+ 'referrer' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/urls/',
31
+ 'static' => 'https://dev.institute/docs/content-aware-sidebars/sidebar-conditions/static-pages/',
32
+ ];
33
+
34
+ /**
35
+ * @var array
36
+ */
37
+ private $theme_sidebars = [];
38
+
39
+ /**
40
+ * @var array
41
+ */
42
+ private $custom_sidebars = [];
43
+
44
+ public function __construct()
45
+ {
46
+ add_action('admin_bar_init', [$this,'initiate']);
47
+ }
48
+
49
+ public function initiate()
50
+ {
51
+ if (!$this->authorize_user()) {
52
+ return;
53
+ }
54
+
55
+ add_filter('is_active_sidebar', [$this,'detect_sidebars_via_active'], 10, 2);
56
+ add_action('dynamic_sidebar_before', [$this,'detect_sidebars_via_render'], 10, 2);
57
+ add_action('admin_bar_menu', [$this,'add_menu'], 99);
58
+ add_action('wp_head', [$this,'print_styles']);
59
+ }
60
+
61
+ public function print_styles()
62
+ {
63
+ echo '<style type="text/css" media="screen">' . "\n"; ?>
64
  #wp-admin-bar-wpcas-tool #wp-admin-bar-wpcas-tool-custom-sidebars {
65
  border-top:4px solid #75d7ef;
66
  }
82
  margin-right:0!important;
83
  font-size:14px!important;
84
  }
85
+ <?php
86
+ echo '</style>';
87
+ }
88
+
89
+ /**
90
+ * @param WP_Admin_Bar $admin_bar
91
+ * @param array $args
92
+ * @param string $parent
93
+ * @return self
94
+ */
95
+ private function add_node($admin_bar, $args, $parent = null)
96
+ {
97
+ if ($args['id'] !== self::NODE_ROOT) {
98
+ $args['parent'] = self::NODE_ROOT . (!is_null($parent) ? '-' . $parent : '');
99
+ $args['id'] = $args['parent'] . '-' . $args['id'];
100
+ }
101
+ $admin_bar->add_node($args);
102
+
103
+ return $this;
104
+ }
105
+
106
+ /**
107
+ * @param WP_Admin_Bar $admin_bar
108
+ * @return void
109
+ */
110
+ public function add_menu($admin_bar)
111
+ {
112
+ global $wp_registered_sidebars;
113
+
114
+ $post_type_object = get_post_type_object(CAS_App::TYPE_SIDEBAR);
115
+
116
+ $this
117
+ ->add_node($admin_bar, [
118
+ 'id' => self::NODE_ROOT,
119
+ 'title' => '<span class="ab-icon dashicons ' . $post_type_object->menu_icon . '"></span>',
120
+ 'href' => admin_url('admin.php?page=wpcas'),
121
+ 'meta' => [
122
+ 'title' => __('Content Aware Sidebars', 'content-aware-sidebars')
123
+ ]
124
+ ])
125
+ ->add_node($admin_bar, [
126
+ 'id' => 'add_new',
127
+ 'title' => $post_type_object->labels->add_new,
128
+ 'href' => admin_url('admin.php?page=wpcas-edit'),
129
+ ])
130
+ ->add_node($admin_bar, [
131
+ 'id' => self::NODE_CONDITION_TYPES,
132
+ 'title' => __('Condition Types', 'content-aware-sidebars'),
133
+ ])
134
+ ->add_node($admin_bar, [
135
+ 'id' => self::NODE_THEME_AREAS,
136
+ 'title' => __('Theme Areas', 'content-aware-sidebars')
137
+ ]);
138
+
139
+ $cache = get_option(WPCACore::OPTION_CONDITION_TYPE_CACHE, []);
140
+ if (isset($cache[CAS_App::TYPE_SIDEBAR]) && !empty($cache[CAS_App::TYPE_SIDEBAR])) {
141
+ $title = __('Cache Active', 'content-aware-sidebars');
142
+ $link = null;
143
+ $class = 'wpcas-ok';
144
+ } else {
145
+ $title = __('Activate Cache Now', 'content-aware-sidebars');
146
+ $link = wp_nonce_url(admin_url('admin.php?page=wpcas-settings&action=update_condition_type_cache'), 'update_condition_type_cache');
147
+ $class = 'wpcas-warn';
148
+ }
149
+ $this->add_node($admin_bar, [
150
+ 'id' => 'condition_cache',
151
+ 'title' => $title . ' &#9210;',
152
+ 'href' => $link,
153
+ 'meta' => [
154
+ 'class' => $class,
155
+ ]
156
+ ], self::NODE_CONDITION_TYPES);
157
+
158
+ $args = [];
159
+ foreach (WPCACore::get_conditional_modules('sidebar') as $module) {
160
+ $title = $module->get_name();
161
+ $link = '';
162
+ if (array_key_exists($module->get_id(), self::DOCS_MAP)) {
163
+ $title = '<span class="ab-icon dashicons dashicons-external"></span> ' . $title;
164
+ $link = self::DOCS_MAP[$module->get_id()] . '?utm_source=plugin&amp;utm_medium=admin_bar&amp;utm_campaign=cas';
165
+ }
166
+ $args[] = [
167
+ 'id' => $module->get_id(),
168
+ 'title' => $title,
169
+ 'href' => $link,
170
+ 'meta' => [
171
+ 'target' => '_blank',
172
+ 'rel' => 'noopener'
173
+ ]
174
+ ];
175
+ }
176
+ $this->add_nodes($admin_bar, $args, self::NODE_CONDITION_TYPES);
177
+
178
+ $args = [];
179
+ foreach ($this->theme_sidebars as $index => $has_widgets) {
180
+ $sidebar_name = isset($wp_registered_sidebars[$index]['name'])
181
+ ? $wp_registered_sidebars[$index]['name']
182
+ : $index;
183
+ $args[] = [
184
+ 'id' => $index,
185
+ 'title' => $sidebar_name . ($has_widgets ? '' : ' (' . __('Hidden') . ')'),
186
+ ];
187
+ }
188
+ $this->add_nodes($admin_bar, $args, self::NODE_THEME_AREAS);
189
+
190
+ $admin_bar->add_group([
191
+ 'id' => self::NODE_ROOT . '-' . self::NODE_CUSTOM_SIDEBARS,
192
+ 'parent' => self::NODE_ROOT,
193
+ 'meta' => [
194
+ 'class' => 'ab-sub-secondary'
195
+ ]
196
+ ]);
197
+
198
+ $args = [];
199
+ foreach ($this->custom_sidebars as $id => $has_widgets) {
200
+ $sidebar = CAS_App::instance()->manager()->sidebars[$id];
201
+ $args[] = [
202
+ 'id' => $sidebar->ID,
203
+ 'title' => $sidebar->post_title,
204
+ 'href' => get_edit_post_link($sidebar->ID)
205
+ ];
206
+ }
207
+ $this->add_nodes($admin_bar, $args, self::NODE_CUSTOM_SIDEBARS);
208
+
209
+ if (empty($this->custom_sidebars)) {
210
+ $args = [
211
+ 'id' => 'no_custom_sidebars',
212
+ 'title' => $post_type_object->labels->not_found
213
+ ];
214
+ $this->add_node($admin_bar, $args, self::NODE_CUSTOM_SIDEBARS);
215
+ }
216
+
217
+ //@todo: show custom sidebars that were not displayed on page
218
+ }
219
+
220
+ /**
221
+ * @param bool $has_widgets
222
+ * @param string $index
223
+ * @return bool
224
+ */
225
+ public function detect_sidebars_via_active($has_widgets, $index)
226
+ {
227
+ $this->detect_sidebar($has_widgets, $index);
228
+ return $has_widgets;
229
+ }
230
+
231
+ /**
232
+ * @param string $index
233
+ * @param bool $has_widgets
234
+ * @return void
235
+ */
236
+ public function detect_sidebars_via_render($index, $has_widgets)
237
+ {
238
+ $this->detect_sidebar($has_widgets, $index);
239
+ }
240
+
241
+ /**
242
+ * @param WP_Admin_Bar $admin_bar
243
+ * @param array $nodes
244
+ * @param string|null $parent
245
+ * @return void
246
+ */
247
+ private function add_nodes($admin_bar, $nodes, $parent = null)
248
+ {
249
+ usort($nodes, [$this,'sort_nodes']);
250
+ foreach ($nodes as $node_args) {
251
+ $this->add_node($admin_bar, $node_args, $parent);
252
+ }
253
+ }
254
+
255
+ /**
256
+ * @param array $a
257
+ * @param array $b
258
+ * @return int
259
+ */
260
+ private function sort_nodes($a, $b)
261
+ {
262
+ return strcasecmp($a['id'], $b['id']);
263
+ }
264
+
265
+ /**
266
+ * @param bool $has_widgets
267
+ * @param string $index
268
+ * @return void
269
+ */
270
+ private function detect_sidebar($has_widgets, $index)
271
+ {
272
+ if (str_replace(CAS_App::SIDEBAR_PREFIX, '', $index) === $index) {
273
+ $this->theme_sidebars[$index] = $has_widgets;
274
+ } else {
275
+ $this->custom_sidebars[$index] = $has_widgets;
276
+ }
277
+ $this->detect_sidebar_target($has_widgets, $index);
278
+ }
279
+
280
+ /**
281
+ * @param bool $has_widgets
282
+ * @param string $index
283
+ * @return void
284
+ */
285
+ private function detect_sidebar_target($has_widgets, $index)
286
+ {
287
+ $host_map = CAS_App::instance()->manager()->get_replacement_map();
288
+ if (isset($host_map[$index])) {
289
+ $this->detect_sidebar($has_widgets, $host_map[$index]);
290
+ }
291
+ }
292
+
293
+ /**
294
+ * @return bool
295
+ */
296
+ private function authorize_user()
297
+ {
298
+ $post_type_object = get_post_type_object(CAS_App::TYPE_SIDEBAR);
299
+ return current_user_can($post_type_object->cap->create_posts);
300
+ }
301
+ }
admin/db-updates.php CHANGED
@@ -1,393 +1,390 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- $cas_db_updater = CAS_App::instance()->get_updater();
12
- $cas_db_updater->register_version_update('1.1', 'cas_update_to_11');
13
- $cas_db_updater->register_version_update('2.0', 'cas_update_to_20');
14
- $cas_db_updater->register_version_update('3.0', 'cas_update_to_30');
15
- $cas_db_updater->register_version_update('3.1', 'cas_update_to_31');
16
- $cas_db_updater->register_version_update('3.4', 'cas_update_to_34');
17
- $cas_db_updater->register_version_update('3.5.1', 'cas_update_to_351');
18
- $cas_db_updater->register_version_update('3.8', 'cas_update_to_38');
19
- $cas_db_updater->register_version_update('3.15.2', 'cas_update_to_3152');
20
- $cas_db_updater->register_version_update('3.16.1', 'cas_update_to_3161');
21
-
22
-
23
- /**
24
- * Enable legacy date module and
25
- * negated conditions if in use
26
- *
27
- * Clear condition type cache
28
- *
29
- * @since 3.16.1
30
- *
31
- * @return bool
32
- */
33
- function cas_update_to_3161()
34
- {
35
- update_option('_ca_condition_type_cache', []);
36
-
37
- global $wpdb;
38
-
39
- $types = WPCACore::types()->get_all();
40
-
41
- $options = [
42
- 'legacy.date_module' => [],
43
- 'legacy.negated_conditions' => []
44
- ];
45
-
46
- $options['legacy.date_module'] = array_flip((array)$wpdb->get_col("
47
- SELECT p.post_type FROM $wpdb->posts p
48
- INNER JOIN $wpdb->posts c on p.ID = c.post_parent
49
- INNER JOIN $wpdb->postmeta m on c.ID = m.post_id
50
- WHERE c.post_type = 'condition_group' AND m.meta_key = '_ca_date'
51
- "));
52
-
53
- $options['legacy.negated_conditions'] = array_flip((array)$wpdb->get_col("
54
- SELECT p.post_type FROM $wpdb->posts p
55
- INNER JOIN $wpdb->posts c on p.ID = c.post_parent
56
- WHERE c.post_type = 'condition_group' AND c.post_status = 'negated'
57
- "));
58
-
59
- foreach ($types as $type => $val) {
60
- foreach ($options as $option => $post_types) {
61
- if (isset($post_types[$type])) {
62
- WPCACore::save_option($type, $option, true);
63
- } elseif (WPCACore::get_option($type, $option, false)) {
64
- WPCACore::save_option($type, $option, false);
65
- }
66
- }
67
- }
68
-
69
- return true;
70
- }
71
-
72
- /**
73
- * Add -1 to condition groups with select terms
74
- *
75
- * @since 3.15.2
76
- *
77
- * @return bool
78
- */
79
- function cas_update_to_3152()
80
- {
81
- $taxonomies = array_map(function ($value) {
82
- return "'" . esc_sql($value) . "'";
83
- }, get_taxonomies(['public' => true]));
84
-
85
- if (empty($taxonomies)) {
86
- return true;
87
- }
88
-
89
- global $wpdb;
90
-
91
- $condition_group_ids = array_unique((array)$wpdb->get_col("
92
- SELECT p.ID FROM $wpdb->posts p
93
- INNER JOIN $wpdb->term_relationships r ON r.object_id = p.ID
94
- INNER JOIN $wpdb->term_taxonomy t ON t.term_taxonomy_id = r.term_taxonomy_id
95
- WHERE p.post_type = 'condition_group'
96
- AND t.taxonomy IN (".implode(',', $taxonomies).')
97
- '));
98
-
99
- foreach ($condition_group_ids as $id) {
100
- add_post_meta($id, '_ca_taxonomy', '-1');
101
- }
102
-
103
- return true;
104
- }
105
-
106
- /**
107
- * Update to version 3.8
108
- *
109
- * @since 3.8
110
- * @return boolean
111
- */
112
- function cas_update_to_38()
113
- {
114
- global $wpdb;
115
-
116
- $time = time();
117
-
118
- $wpdb->query("
119
- UPDATE $wpdb->usermeta AS t
120
- INNER JOIN $wpdb->usermeta AS r ON t.user_id = r.user_id
121
- SET t.meta_value = '{$time}'
122
- WHERE t.meta_key = '{$wpdb->prefix}_ca_cas_tour'
123
- AND r.meta_key = '{$wpdb->prefix}_ca_cas_review'
124
- AND r.meta_value != '1'
125
- AND CAST(r.meta_value AS DECIMAL) <= 1522540800
126
- ");
127
-
128
- $wpdb->query("
129
- DELETE FROM $wpdb->usermeta
130
- WHERE meta_key = '{$wpdb->prefix}_ca_cas_review'
131
- AND meta_value != '1'
132
- AND CAST(meta_value AS DECIMAL) <= 1522540800
133
- ");
134
-
135
- return true;
136
- }
137
-
138
- /**
139
- * Update to version 3.5.1
140
- * Simplify auto select option
141
- *
142
- * @since 3.5.1
143
- * @return boolean
144
- */
145
- function cas_update_to_351()
146
- {
147
- global $wpdb;
148
-
149
- $group_ids = $wpdb->get_col("SELECT post_id FROM $wpdb->postmeta WHERE meta_value LIKE '_ca_sub_%'");
150
- foreach ($group_ids as $group_id) {
151
- add_post_meta($group_id, '_ca_autoselect', 1, true);
152
- }
153
-
154
- $wpdb->query("
155
- DELETE FROM $wpdb->postmeta
156
- WHERE meta_value LIKE '_ca_sub_%'
157
- ");
158
-
159
- return true;
160
- }
161
-
162
- /**
163
- * Version 3.3.3 -> 3.4
164
- * Inherit condition exposure from sidebar
165
- * Remove sidebar exposure
166
- *
167
- * @since 3.4
168
- * @return boolean
169
- */
170
- function cas_update_to_34()
171
- {
172
- global $wpdb;
173
-
174
- $wpdb->query("
175
- UPDATE $wpdb->posts AS c
176
- INNER JOIN $wpdb->posts AS s ON s.ID = c.post_parent
177
- INNER JOIN $wpdb->postmeta AS e ON e.post_id = s.ID
178
- SET c.menu_order = e.meta_value
179
- WHERE c.post_type = 'condition_group'
180
- AND e.meta_key = '_ca_exposure'
181
- ");
182
-
183
- $wpdb->query("
184
- DELETE FROM $wpdb->postmeta
185
- WHERE meta_key = '_ca_exposure'
186
- ");
187
-
188
- wp_cache_flush();
189
-
190
- return true;
191
- }
192
-
193
- /**
194
- * Version 3.0 -> 3.1
195
- * Remove flag about plugin tour for all users
196
- *
197
- * @since 3.1
198
- * @return boolean
199
- */
200
- function cas_update_to_31()
201
- {
202
- global $wpdb;
203
- $wpdb->query("
204
- DELETE FROM $wpdb->usermeta
205
- WHERE meta_key = '{$wpdb->prefix}_ca_cas_tour'
206
- ");
207
- return true;
208
- }
209
-
210
- /**
211
- * Version 2.0 -> 3.0
212
- * Data key prefices will use that from WP Content Aware Engine
213
- * Condition group post type made generic
214
- * Module id convention made consistent
215
- *
216
- * @since 3.0
217
- * @return boolean
218
- */
219
- function cas_update_to_30()
220
- {
221
- global $wpdb;
222
-
223
- // Get all sidebars
224
- $posts = get_posts([
225
- 'numberposts' => -1,
226
- 'post_type' => 'sidebar',
227
- 'post_status' => 'publish,pending,draft,future,private,trash'
228
- ]);
229
-
230
- if (!empty($posts)) {
231
- $wpdb->query("
232
- UPDATE $wpdb->posts
233
- SET post_type = 'condition_group', post_status = 'publish'
234
- WHERE post_type = 'sidebar_group'
235
- ");
236
-
237
- $metadata = [
238
- 'post_types' => 'post_type',
239
- 'taxonomies' => 'taxonomy',
240
- 'authors' => 'author',
241
- 'page_templates' => 'page_template',
242
- 'static' => 'static',
243
- 'bb_profile' => 'bb_profile',
244
- 'bp_member' => 'bp_member',
245
- 'date' => 'date',
246
- 'language' => 'language',
247
- 'exposure' => 'exposure',
248
- 'handle' => 'handle',
249
- 'host' => 'host',
250
- 'merge-pos' => 'merge_pos'
251
- ];
252
-
253
- foreach ($metadata as $old_key => $new_key) {
254
- $wpdb->query("
255
- UPDATE $wpdb->postmeta
256
- SET meta_key = '_ca_".$new_key."'
257
- WHERE meta_key = '_cas_".$old_key."'
258
- ");
259
- switch ($new_key) {
260
- case 'author':
261
- case 'page_template':
262
- $wpdb->query("
263
- UPDATE $wpdb->postmeta
264
- SET meta_value = '".$new_key."'
265
- WHERE meta_key = '_ca_".$new_key."'
266
- AND meta_value = '".$old_key."'
267
- ");
268
- break;
269
- case 'post_type':
270
- case 'taxonomy':
271
- $wpdb->query("
272
- UPDATE $wpdb->postmeta
273
- SET meta_value = REPLACE(meta_value, '_cas_sub_', '_ca_sub_')
274
- WHERE meta_key = '_ca_".$new_key."'
275
- AND meta_value LIKE '_cas_sub_%'
276
- ");
277
- break;
278
- default:
279
- break;
280
- }
281
- }
282
-
283
- // Clear cache for new meta keys
284
- wp_cache_flush();
285
- }
286
-
287
- return true;
288
- }
289
-
290
- /**
291
- * Version 1.1 -> 2.0
292
- * Moves module data for each sidebar to a condition group
293
- *
294
- * @author Joachim Jensen <jv@intox.dk>
295
- * @since 2.0
296
- * @return boolean
297
- */
298
- function cas_update_to_20()
299
- {
300
- global $wpdb;
301
-
302
- $module_keys = [
303
- 'static',
304
- 'post_types',
305
- 'authors',
306
- 'page_templates',
307
- 'taxonomies',
308
- 'language',
309
- 'bb_profile',
310
- 'bp_member'
311
- ];
312
-
313
- // Get all sidebars
314
- $posts = get_posts([
315
- 'numberposts' => -1,
316
- 'post_type' => 'sidebar',
317
- 'post_status' => 'publish,pending,draft,future,private,trash'
318
- ]);
319
- if (!empty($posts)) {
320
- foreach ($posts as $post) {
321
-
322
- //Create new condition group
323
- $group_id = wp_insert_post([
324
- 'post_status' => $post->post_status,
325
- 'post_type' => 'sidebar_group',
326
- 'post_author' => $post->post_author,
327
- 'post_parent' => $post->ID,
328
- ]);
329
-
330
- if ($group_id) {
331
-
332
- //Move module data to condition group
333
- $wpdb->query("
334
- UPDATE $wpdb->postmeta
335
- SET post_id = '".$group_id."'
336
- WHERE meta_key IN ('_cas_".implode("','_cas_", $module_keys)."')
337
- AND post_id = '".$post->ID."'
338
- ");
339
-
340
- //Move term data to condition group
341
- $wpdb->query("
342
- UPDATE $wpdb->term_relationships
343
- SET object_id = '".$group_id."'
344
- WHERE object_id = '".$post->ID."'
345
- ");
346
- }
347
- }
348
- }
349
-
350
- return true;
351
- }
352
-
353
- /**
354
- * Version 0.8 -> 1.1
355
- * Serialized metadata gets their own rows
356
- *
357
- * @return boolean
358
- */
359
- function cas_update_to_11()
360
- {
361
- $moduledata = [
362
- 'static',
363
- 'post_types',
364
- 'authors',
365
- 'page_templates',
366
- 'taxonomies',
367
- 'language'
368
- ];
369
-
370
- // Get all sidebars
371
- $posts = get_posts([
372
- 'numberposts' => -1,
373
- 'post_type' => 'sidebar',
374
- 'post_status' => 'publish,pending,draft,future,private,trash'
375
- ]);
376
-
377
- if (!empty($posts)) {
378
- foreach ($posts as $post) {
379
- foreach ($moduledata as $field) {
380
- // Remove old serialized data and insert it again properly
381
- $old = get_post_meta($post->ID, '_cas_'.$field, true);
382
- if ($old != '') {
383
- delete_post_meta($post->ID, '_cas_'.$field, $old);
384
- foreach ((array)$old as $new_single) {
385
- add_post_meta($post->ID, '_cas_'.$field, $new_single);
386
- }
387
- }
388
- }
389
- }
390
- }
391
-
392
- return true;
393
- }
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ $cas_db_updater = CAS_App::instance()->get_updater();
12
+ $cas_db_updater->register_version_update('1.1', 'cas_update_to_11');
13
+ $cas_db_updater->register_version_update('2.0', 'cas_update_to_20');
14
+ $cas_db_updater->register_version_update('3.0', 'cas_update_to_30');
15
+ $cas_db_updater->register_version_update('3.1', 'cas_update_to_31');
16
+ $cas_db_updater->register_version_update('3.4', 'cas_update_to_34');
17
+ $cas_db_updater->register_version_update('3.5.1', 'cas_update_to_351');
18
+ $cas_db_updater->register_version_update('3.8', 'cas_update_to_38');
19
+ $cas_db_updater->register_version_update('3.15.2', 'cas_update_to_3152');
20
+ $cas_db_updater->register_version_update('3.17.1', 'cas_update_to_3171');
21
+
22
+ /**
23
+ * Enable legacy date module and
24
+ * negated conditions if in use
25
+ *
26
+ * Clear condition type cache
27
+ *
28
+ * @since 3.17.1
29
+ *
30
+ * @return bool
31
+ */
32
+ function cas_update_to_3171()
33
+ {
34
+ update_option('_ca_condition_type_cache', []);
35
+
36
+ global $wpdb;
37
+
38
+ $types = WPCACore::types()->all();
39
+
40
+ $options = [
41
+ 'legacy.date_module' => [],
42
+ 'legacy.negated_conditions' => []
43
+ ];
44
+
45
+ $options['legacy.date_module'] = array_flip((array)$wpdb->get_col("
46
+ SELECT p.post_type FROM $wpdb->posts p
47
+ INNER JOIN $wpdb->posts c on p.ID = c.post_parent
48
+ INNER JOIN $wpdb->postmeta m on c.ID = m.post_id
49
+ WHERE c.post_type = 'condition_group' AND m.meta_key = '_ca_date'
50
+ "));
51
+
52
+ $options['legacy.negated_conditions'] = array_flip((array)$wpdb->get_col("
53
+ SELECT p.post_type FROM $wpdb->posts p
54
+ INNER JOIN $wpdb->posts c on p.ID = c.post_parent
55
+ WHERE c.post_type = 'condition_group' AND c.post_status = 'negated'
56
+ "));
57
+
58
+ foreach ($types as $type => $val) {
59
+ foreach ($options as $option => $post_types) {
60
+ if (isset($post_types[$type])) {
61
+ WPCACore::save_option($type, $option, true);
62
+ } elseif (WPCACore::get_option($type, $option, false)) {
63
+ WPCACore::save_option($type, $option, false);
64
+ }
65
+ }
66
+ }
67
+
68
+ return true;
69
+ }
70
+
71
+ /**
72
+ * Add -1 to condition groups with select terms
73
+ *
74
+ * @since 3.15.2
75
+ *
76
+ * @return bool
77
+ */
78
+ function cas_update_to_3152()
79
+ {
80
+ $taxonomies = array_map(function ($value) {
81
+ return "'" . esc_sql($value) . "'";
82
+ }, get_taxonomies(['public' => true]));
83
+
84
+ if (empty($taxonomies)) {
85
+ return true;
86
+ }
87
+
88
+ global $wpdb;
89
+
90
+ $condition_group_ids = array_unique((array)$wpdb->get_col("
91
+ SELECT p.ID FROM $wpdb->posts p
92
+ INNER JOIN $wpdb->term_relationships r ON r.object_id = p.ID
93
+ INNER JOIN $wpdb->term_taxonomy t ON t.term_taxonomy_id = r.term_taxonomy_id
94
+ WHERE p.post_type = 'condition_group'
95
+ AND t.taxonomy IN (" . implode(',', $taxonomies) . ')
96
+ '));
97
+
98
+ foreach ($condition_group_ids as $id) {
99
+ add_post_meta($id, '_ca_taxonomy', '-1');
100
+ }
101
+
102
+ return true;
103
+ }
104
+
105
+ /**
106
+ * Update to version 3.8
107
+ *
108
+ * @since 3.8
109
+ * @return boolean
110
+ */
111
+ function cas_update_to_38()
112
+ {
113
+ global $wpdb;
114
+
115
+ $time = time();
116
+
117
+ $wpdb->query("
118
+ UPDATE $wpdb->usermeta AS t
119
+ INNER JOIN $wpdb->usermeta AS r ON t.user_id = r.user_id
120
+ SET t.meta_value = '$time'
121
+ WHERE t.meta_key = '{$wpdb->prefix}_ca_cas_tour'
122
+ AND r.meta_key = '{$wpdb->prefix}_ca_cas_review'
123
+ AND r.meta_value != '1'
124
+ AND CAST(r.meta_value AS DECIMAL) <= 1522540800
125
+ ");
126
+
127
+ $wpdb->query("
128
+ DELETE FROM $wpdb->usermeta
129
+ WHERE meta_key = '{$wpdb->prefix}_ca_cas_review'
130
+ AND meta_value != '1'
131
+ AND CAST(meta_value AS DECIMAL) <= 1522540800
132
+ ");
133
+
134
+ return true;
135
+ }
136
+
137
+ /**
138
+ * Update to version 3.5.1
139
+ * Simplify auto select option
140
+ *
141
+ * @since 3.5.1
142
+ * @return boolean
143
+ */
144
+ function cas_update_to_351()
145
+ {
146
+ global $wpdb;
147
+
148
+ $group_ids = $wpdb->get_col("SELECT post_id FROM $wpdb->postmeta WHERE meta_value LIKE '_ca_sub_%'");
149
+ foreach ($group_ids as $group_id) {
150
+ add_post_meta($group_id, '_ca_autoselect', 1, true);
151
+ }
152
+
153
+ $wpdb->query("
154
+ DELETE FROM $wpdb->postmeta
155
+ WHERE meta_value LIKE '_ca_sub_%'
156
+ ");
157
+
158
+ return true;
159
+ }
160
+
161
+ /**
162
+ * Version 3.3.3 -> 3.4
163
+ * Inherit condition exposure from sidebar
164
+ * Remove sidebar exposure
165
+ *
166
+ * @since 3.4
167
+ * @return boolean
168
+ */
169
+ function cas_update_to_34()
170
+ {
171
+ global $wpdb;
172
+
173
+ $wpdb->query("
174
+ UPDATE $wpdb->posts AS c
175
+ INNER JOIN $wpdb->posts AS s ON s.ID = c.post_parent
176
+ INNER JOIN $wpdb->postmeta AS e ON e.post_id = s.ID
177
+ SET c.menu_order = e.meta_value
178
+ WHERE c.post_type = 'condition_group'
179
+ AND e.meta_key = '_ca_exposure'
180
+ ");
181
+
182
+ $wpdb->query("
183
+ DELETE FROM $wpdb->postmeta
184
+ WHERE meta_key = '_ca_exposure'
185
+ ");
186
+
187
+ wp_cache_flush();
188
+
189
+ return true;
190
+ }
191
+
192
+ /**
193
+ * Version 3.0 -> 3.1
194
+ * Remove flag about plugin tour for all users
195
+ *
196
+ * @since 3.1
197
+ * @return boolean
198
+ */
199
+ function cas_update_to_31()
200
+ {
201
+ global $wpdb;
202
+ $wpdb->query("
203
+ DELETE FROM $wpdb->usermeta
204
+ WHERE meta_key = '{$wpdb->prefix}_ca_cas_tour'
205
+ ");
206
+ return true;
207
+ }
208
+
209
+ /**
210
+ * Version 2.0 -> 3.0
211
+ * Data key prefices will use that from WP Content Aware Engine
212
+ * Condition group post type made generic
213
+ * Module id convention made consistent
214
+ *
215
+ * @since 3.0
216
+ * @return boolean
217
+ */
218
+ function cas_update_to_30()
219
+ {
220
+ global $wpdb;
221
+
222
+ // Get all sidebars
223
+ $posts = get_posts([
224
+ 'numberposts' => -1,
225
+ 'post_type' => 'sidebar',
226
+ 'post_status' => 'publish,pending,draft,future,private,trash'
227
+ ]);
228
+
229
+ if (!empty($posts)) {
230
+ $wpdb->query("
231
+ UPDATE $wpdb->posts
232
+ SET post_type = 'condition_group', post_status = 'publish'
233
+ WHERE post_type = 'sidebar_group'
234
+ ");
235
+
236
+ $metadata = [
237
+ 'post_types' => 'post_type',
238
+ 'taxonomies' => 'taxonomy',
239
+ 'authors' => 'author',
240
+ 'page_templates' => 'page_template',
241
+ 'static' => 'static',
242
+ 'bb_profile' => 'bb_profile',
243
+ 'bp_member' => 'bp_member',
244
+ 'date' => 'date',
245
+ 'language' => 'language',
246
+ 'exposure' => 'exposure',
247
+ 'handle' => 'handle',
248
+ 'host' => 'host',
249
+ 'merge-pos' => 'merge_pos'
250
+ ];
251
+
252
+ foreach ($metadata as $old_key => $new_key) {
253
+ $wpdb->query("
254
+ UPDATE $wpdb->postmeta
255
+ SET meta_key = '_ca_" . $new_key . "'
256
+ WHERE meta_key = '_cas_" . $old_key . "'
257
+ ");
258
+ switch ($new_key) {
259
+ case 'author':
260
+ case 'page_template':
261
+ $wpdb->query("
262
+ UPDATE $wpdb->postmeta
263
+ SET meta_value = '" . $new_key . "'
264
+ WHERE meta_key = '_ca_" . $new_key . "'
265
+ AND meta_value = '" . $old_key . "'
266
+ ");
267
+ break;
268
+ case 'post_type':
269
+ case 'taxonomy':
270
+ $wpdb->query("
271
+ UPDATE $wpdb->postmeta
272
+ SET meta_value = REPLACE(meta_value, '_cas_sub_', '_ca_sub_')
273
+ WHERE meta_key = '_ca_" . $new_key . "'
274
+ AND meta_value LIKE '_cas_sub_%'
275
+ ");
276
+ break;
277
+ default:
278
+ break;
279
+ }
280
+ }
281
+
282
+ // Clear cache for new meta keys
283
+ wp_cache_flush();
284
+ }
285
+
286
+ return true;
287
+ }
288
+
289
+ /**
290
+ * Version 1.1 -> 2.0
291
+ * Moves module data for each sidebar to a condition group
292
+ *
293
+ * @author Joachim Jensen <jv@intox.dk>
294
+ * @since 2.0
295
+ * @return boolean
296
+ */
297
+ function cas_update_to_20()
298
+ {
299
+ global $wpdb;
300
+
301
+ $module_keys = [
302
+ 'static',
303
+ 'post_types',
304
+ 'authors',
305
+ 'page_templates',
306
+ 'taxonomies',
307
+ 'language',
308
+ 'bb_profile',
309
+ 'bp_member'
310
+ ];
311
+
312
+ // Get all sidebars
313
+ $posts = get_posts([
314
+ 'numberposts' => -1,
315
+ 'post_type' => 'sidebar',
316
+ 'post_status' => 'publish,pending,draft,future,private,trash'
317
+ ]);
318
+ if (!empty($posts)) {
319
+ foreach ($posts as $post) {
320
+ //Create new condition group
321
+ $group_id = wp_insert_post([
322
+ 'post_status' => $post->post_status,
323
+ 'post_type' => 'sidebar_group',
324
+ 'post_author' => $post->post_author,
325
+ 'post_parent' => $post->ID,
326
+ ]);
327
+
328
+ if ($group_id) {
329
+ //Move module data to condition group
330
+ $wpdb->query("
331
+ UPDATE $wpdb->postmeta
332
+ SET post_id = '" . $group_id . "'
333
+ WHERE meta_key IN ('_cas_" . implode("','_cas_", $module_keys) . "')
334
+ AND post_id = '" . $post->ID . "'
335
+ ");
336
+
337
+ //Move term data to condition group
338
+ $wpdb->query("
339
+ UPDATE $wpdb->term_relationships
340
+ SET object_id = '" . $group_id . "'
341
+ WHERE object_id = '" . $post->ID . "'
342
+ ");
343
+ }
344
+ }
345
+ }
346
+
347
+ return true;
348
+ }
349
+
350
+ /**
351
+ * Version 0.8 -> 1.1
352
+ * Serialized metadata gets their own rows
353
+ *
354
+ * @return boolean
355
+ */
356
+ function cas_update_to_11()
357
+ {
358
+ $moduledata = [
359
+ 'static',
360
+ 'post_types',
361
+ 'authors',
362
+ 'page_templates',
363
+ 'taxonomies',
364
+ 'language'
365
+ ];
366
+
367
+ // Get all sidebars
368
+ $posts = get_posts([
369
+ 'numberposts' => -1,
370
+ 'post_type' => 'sidebar',
371
+ 'post_status' => 'publish,pending,draft,future,private,trash'
372
+ ]);
373
+
374
+ if (!empty($posts)) {
375
+ foreach ($posts as $post) {
376
+ foreach ($moduledata as $field) {
377
+ // Remove old serialized data and insert it again properly
378
+ $old = get_post_meta($post->ID, '_cas_' . $field, true);
379
+ if ($old != '') {
380
+ delete_post_meta($post->ID, '_cas_' . $field, $old);
381
+ foreach ((array)$old as $new_single) {
382
+ add_post_meta($post->ID, '_cas_' . $field, $new_single);
383
+ }
384
+ }
385
+ }
386
+ }
387
+ }
388
+
389
+ return true;
390
+ }
 
 
 
admin/quick_select.php CHANGED
@@ -1,434 +1,434 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- class CAS_Quick_Select
12
- {
13
- const MODULE_NAME = 'post_type';
14
- const NONCE = '_cas_nonce';
15
-
16
- protected static $_theme_sidebars = [];
17
-
18
- public function __construct()
19
- {
20
- new CAS_Post_Type_Sidebar();
21
- add_action(
22
- 'current_screen',
23
- [__CLASS__,'load_screen']
24
- );
25
- }
26
-
27
- /**
28
- * Load Quick Select on post screen
29
- *
30
- * @since 3.7
31
- * @param WP_Screen $screen
32
- * @return void
33
- */
34
- public static function load_screen($screen)
35
- {
36
- //We are on the post edit screen
37
- if ($screen->base == 'post' && $screen->post_type) {
38
- $module = WPCACore::types()->get(CAS_App::TYPE_SIDEBAR)->get(self::MODULE_NAME);
39
- if (!$module) {
40
- return;
41
- }
42
-
43
- $legacy_removal = !has_action('admin_init', ['CAS_Post_Type_Sidebar','initiate']);
44
-
45
- if ($legacy_removal) {
46
- _deprecated_hook(
47
- "remove_action('admin_init', array('CAS_Post_Type_Sidebar', 'initiate'))",
48
- '3.7',
49
- "add_filter('cas/module/quick_select', '__return_false')"
50
- );
51
- }
52
-
53
- $enable = apply_filters(
54
- 'cas/module/quick_select',
55
- !$legacy_removal,
56
- $screen->post_type
57
- );
58
-
59
- if (!$enable) {
60
- return;
61
- }
62
-
63
- $post_types = $module->post_types();
64
- self::get_theme_sidebars();
65
- if (isset($post_types[$screen->post_type]) && self::$_theme_sidebars) {
66
- add_action(
67
- 'add_meta_boxes_'.$screen->post_type,
68
- [__CLASS__,'create_meta_boxes']
69
- );
70
- add_action(
71
- 'save_post_'.$screen->post_type,
72
- [__CLASS__,'save_post_sidebars'],
73
- 10,
74
- 2
75
- );
76
- add_action(
77
- 'admin_enqueue_scripts',
78
- [__CLASS__,'register_scripts'],
79
- 8
80
- );
81
- add_action(
82
- 'admin_enqueue_scripts',
83
- [__CLASS__,'enqueue_scripts'],
84
- 11
85
- );
86
- }
87
- }
88
- }
89
-
90
- /**
91
- * Gather theme sidebars for later use
92
- *
93
- * @since 3.3
94
- * @return void
95
- */
96
- public static function get_theme_sidebars()
97
- {
98
- global $wp_registered_sidebars;
99
-
100
- $cas_sidebars = CAS_App::instance()->manager()->sidebars;
101
-
102
- foreach ($wp_registered_sidebars as $sidebar) {
103
- if (!isset($cas_sidebars[$sidebar['id']])) {
104
- self::$_theme_sidebars[$sidebar['id']] = [
105
- 'label' => $sidebar['name'],
106
- 'options' => []
107
- ];
108
- }
109
- }
110
- }
111
-
112
- /**
113
- * Save sidebars and conditions for post
114
- *
115
- * @since 3.3
116
- * @param int $post_id
117
- * @param WP_Post $post
118
- * @return void
119
- */
120
- public static function save_post_sidebars($post_id, $post)
121
- {
122
- if (in_array($post_id, self::get_special_post_ids())) {
123
- return;
124
- }
125
-
126
- if (!(isset($_POST[self::NONCE])
127
- && wp_verify_nonce($_POST[self::NONCE], self::NONCE.$post_id))) {
128
- return;
129
- }
130
-
131
- // Check post permissions
132
- if (!current_user_can('edit_post', $post_id)) {
133
- return;
134
- }
135
-
136
- if ($post->post_status == 'auto-draft') {
137
- return;
138
- }
139
-
140
- // Check autosave
141
- if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
142
- return;
143
- }
144
-
145
- $meta_key = WPCACore::PREFIX . self::MODULE_NAME;
146
- $new = isset($_POST['cas_sidebars']) ? $_POST['cas_sidebars'] : [];
147
-
148
- $relations = self::_get_content_sidebars($post);
149
- $user_can_create_sidebar = current_user_can(CAS_App::CAPABILITY);
150
-
151
- foreach ($new as $host => $sidebar_ids) {
152
- foreach ($sidebar_ids as $sidebar_id) {
153
- //Post has sidebar already
154
- if (isset($relations[$sidebar_id])) {
155
- unset($relations[$sidebar_id]);
156
- //Discard empty
157
- } elseif ($sidebar_id) {
158
- $condition_group_id = 0;
159
-
160
- //New sidebar
161
- //check permissions here
162
- if ($sidebar_id[0] == '_') {
163
- if ($user_can_create_sidebar) {
164
- $id = wp_insert_post([
165
- 'post_title' => str_replace('__', ',', substr($sidebar_id, 1)),
166
- 'post_status' => CAS_App::STATUS_INACTIVE,
167
- 'post_type' => CAS_App::TYPE_SIDEBAR
168
- ]);
169
- if ($id) {
170
- //wp_insert_post does not handle meta before WP4.4
171
- add_post_meta($id, WPCACore::PREFIX.'host', $host);
172
- $condition_group_id = WPCACore::add_condition_group($id);
173
- }
174
- }
175
- } else {
176
- //Add post to group with other posts
177
- $id = intval($sidebar_id);
178
- $condition_groups = get_posts([
179
- 'posts_per_page' => 1,
180
- 'meta_key' => $meta_key,
181
- 'meta_value' => $post->post_type,
182
- 'meta_compare' => '!=',
183
- 'post_parent' => $id,
184
- 'post_type' => WPCACore::TYPE_CONDITION_GROUP,
185
- 'post_status' => WPCACore::STATUS_PUBLISHED
186
- ]);
187
- if ($condition_groups) {
188
- $condition_group_id = $condition_groups[0]->ID;
189
- } else {
190
- $condition_group_id = WPCACore::add_condition_group($id);
191
- }
192
- }
193
-
194
- if ($condition_group_id) {
195
- add_post_meta($condition_group_id, $meta_key, $post_id);
196
- }
197
- }
198
- }
199
- }
200
-
201
- //remove old relations
202
- //todo: sanity check if post is added to several groups?
203
- $sidebars = CAS_App::instance()->manager()->sidebars;
204
- $host_meta = CAS_App::instance()->manager()->metadata()->get('host');
205
- foreach ($relations as $sidebar_id => $group_id) {
206
- if (isset($sidebars[CAS_App::SIDEBAR_PREFIX.$sidebar_id])) {
207
- $host_ids = $host_meta->get_data($sidebar_id, false, false);
208
- foreach ($host_ids as $host_id) {
209
- if (!isset(self::$_theme_sidebars[$host_id])) {
210
- continue 2;
211
- }
212
- }
213
- }
214
-
215
- //group with no post_type meta will be removed
216
- //even if it has other meta (unlikely)
217
- $group_meta = get_post_meta($group_id, $meta_key);
218
- if (count($group_meta) <= 1) {
219
- wp_delete_post($group_id);
220
- } else {
221
- delete_post_meta($group_id, $meta_key, $post_id);
222
- }
223
- }
224
- }
225
-
226
- /**
227
- * Create sidebar meta box for post types
228
- *
229
- * @since 3.3
230
- * @param WP_Post $post
231
- * @return void
232
- */
233
- public static function create_meta_boxes($post)
234
- {
235
- if (in_array($post->ID, self::get_special_post_ids())) {
236
- return;
237
- }
238
-
239
- $post_sidebars = self::_get_content_sidebars($post);
240
-
241
- $manager = CAS_App::instance()->manager();
242
- $host_meta = $manager->metadata()->get('host');
243
- foreach ($manager->sidebars as $sidebar) {
244
- $host_ids = $host_meta->get_data($sidebar->ID, false, false);
245
- foreach ($host_ids as $host_id) {
246
- if (isset(self::$_theme_sidebars[$host_id])) {
247
- self::$_theme_sidebars[$host_id]['options'][$sidebar->ID] = [
248
- 'id' => $sidebar->ID,
249
- 'text' => $sidebar->post_title.self::sidebar_states($sidebar)
250
- ];
251
- if (isset($post_sidebars[$sidebar->ID])) {
252
- self::$_theme_sidebars[$host_id]['options'][$sidebar->ID]['select'] = 1;
253
- }
254
- }
255
- }
256
- }
257
-
258
- $post_type = get_post_type_object($post->post_type);
259
- $content = [
260
- __('Author')
261
- ];
262
- if ($post_type->hierarchical) {
263
- $content[] = __('Child Page', 'content-aware-sidebars');
264
- }
265
- if ($post_type->name == 'page') {
266
- $content[] = __('Page Template', 'content-aware-sidebars');
267
- }
268
- $taxonomies = get_object_taxonomies($post, 'objects');
269
- if ($taxonomies) {
270
- foreach ($taxonomies as $tax) {
271
- $content[] = $tax->labels->singular_name;
272
- }
273
- }
274
- $content[] = __('Archive Page', 'content-aware-sidebars');
275
-
276
- $path = plugin_dir_path(dirname(__FILE__)).'view/';
277
- $view = WPCAView::make($path.'sidebars_quick_select.php', [
278
- 'post' => $post,
279
- 'sidebars' => self::$_theme_sidebars,
280
- 'limit' => 3,
281
- 'content' => $content,
282
- 'singular' => $post_type->labels->singular_name,
283
- 'nonce' => wp_nonce_field(self::NONCE.$post->ID, self::NONCE, false, false)
284
- ]);
285
-
286
- add_meta_box(
287
- 'cas-content-sidebars',
288
- __('Sidebars - Quick Select', 'content-aware-sidebars'),
289
- [$view, 'render'],
290
- $post->post_type,
291
- 'side',
292
- 'default'
293
- );
294
- }
295
-
296
- /**
297
- * Get sidebar status for display
298
- *
299
- * @since 3.4.1
300
- * @param WP_Post $post
301
- * @return string
302
- */
303
- public static function sidebar_states($post)
304
- {
305
- switch ($post->post_status) {
306
- case CAS_App::STATUS_ACTIVE:
307
- $status = '';
308
- break;
309
- case CAS_App::STATUS_SCHEDULED:
310
- $status = ' ('.__('Scheduled').')';
311
- break;
312
- default:
313
- $status = ' ('.__('Inactive', 'content-aware-sidebars').')';
314
- break;
315
- }
316
- return $status;
317
- }
318
-
319
- /**
320
- * Register scripts and styles
321
- * We register early to make sure our select2 comes first
322
- *
323
- * @since 3.5.2
324
- * @param string $hook
325
- * @return void
326
- */
327
- public static function register_scripts($hook)
328
- {
329
- wp_register_script(
330
- 'select2',
331
- plugins_url('lib/wp-content-aware-engine/assets/js/select2.min.js', dirname(__FILE__)),
332
- ['jquery'],
333
- '4.0.3',
334
- false
335
- );
336
- wp_register_script('cas/sidebars/suggest', plugins_url('assets/js/suggest-sidebars.min.js', dirname(__FILE__)), ['select2'], CAS_App::PLUGIN_VERSION, true);
337
- }
338
-
339
- /**
340
- * Enqueue scripts and styles
341
- * We enqueue later to make sure our CSS takes precedence
342
- *
343
- * @since 3.5.2
344
- * @param string $hook
345
- * @return void
346
- */
347
- public static function enqueue_scripts($hook)
348
- {
349
- wp_enqueue_style(CAS_App::META_PREFIX.'condition-groups');
350
- wp_enqueue_script('cas/sidebars/suggest');
351
-
352
- $labels = [
353
- 'createNew' => __('Create New', 'content-aware-sidebars'),
354
- 'labelNew' => __('New', 'content-aware-sidebars')
355
- ];
356
- if (current_user_can(CAS_App::CAPABILITY)) {
357
- $labels['notFound'] = __('Type to Add New Sidebar', 'content-aware-sidebars');
358
- } else {
359
- $labels['notFound'] = __('No sidebars found', 'content-aware-sidebars');
360
- }
361
- wp_localize_script('cas/sidebars/suggest', 'CAS', $labels);
362
- }
363
-
364
- /**
365
- * @since 3.10.1
366
- *
367
- * @return array
368
- */
369
- protected static function get_special_post_ids()
370
- {
371
- $special_post_ids = [
372
- get_option('page_on_front'),
373
- get_option('page_for_posts'),
374
- ];
375
-
376
- if (defined('WC_VERSION')) {
377
- $special_post_ids[] = get_option('woocommerce_shop_page_id');
378
- }
379
-
380
- return $special_post_ids;
381
- }
382
-
383
- /**
384
- * Get sidebars for select post types
385
- *
386
- * @since 3.3
387
- * @param WP_Post $post
388
- * @return array
389
- */
390
- protected static function _get_content_sidebars($post)
391
- {
392
- $sidebars = [];
393
- if ($post) {
394
- global $wpdb;
395
- $query = $wpdb->get_results($wpdb->prepare(
396
- "SELECT s.ID, g.ID as group_id
397
- FROM $wpdb->posts s
398
- INNER JOIN $wpdb->posts g ON g.post_parent = s.ID
399
- INNER JOIN $wpdb->postmeta gm ON gm.post_id = g.ID AND gm.meta_key = '".WPCACore::PREFIX.self::MODULE_NAME."'
400
- WHERE s.post_status <> 'trash'
401
- AND s.post_type = '".CAS_App::TYPE_SIDEBAR."'
402
- AND g.post_status IN ('".WPCACore::STATUS_PUBLISHED."','".WPCACore::STATUS_OR."')
403
- AND gm.meta_value = %d
404
- ORDER BY s.post_title ASC",
405
- $post->ID
406
- ));
407
- if ($query) {
408
- foreach ($query as $sidebar) {
409
- $sidebars[$sidebar->ID] = $sidebar->group_id;
410
- }
411
- }
412
- }
413
- return $sidebars;
414
- }
415
- }
416
-
417
- /**
418
- * Backwards compat for users disabling quick select
419
- * with remove_action('admin_init', array('CAS_Post_Type_Sidebar', 'initiate'))
420
- *
421
- * @deprecated 3.7
422
- * @see add_filter('cas/module/quick_select', ...)
423
- */
424
- class CAS_Post_Type_Sidebar
425
- {
426
- public function __construct()
427
- {
428
- add_action('admin_init', [__CLASS__,'initiate']);
429
- }
430
-
431
- public static function initiate()
432
- {
433
- }
434
- }
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ class CAS_Quick_Select
12
+ {
13
+ const MODULE_NAME = 'post_type';
14
+ const NONCE = '_cas_nonce';
15
+
16
+ protected static $_theme_sidebars = [];
17
+
18
+ public function __construct()
19
+ {
20
+ new CAS_Post_Type_Sidebar();
21
+ add_action(
22
+ 'current_screen',
23
+ [__CLASS__,'load_screen']
24
+ );
25
+ }
26
+
27
+ /**
28
+ * Load Quick Select on post screen
29
+ *
30
+ * @since 3.7
31
+ * @param WP_Screen $screen
32
+ * @return void
33
+ */
34
+ public static function load_screen($screen)
35
+ {
36
+ //We are on the post edit screen
37
+ if ($screen->base == 'post' && $screen->post_type) {
38
+ $module = WPCACore::types()->get(CAS_App::TYPE_SIDEBAR)->get(self::MODULE_NAME);
39
+ if (!$module) {
40
+ return;
41
+ }
42
+
43
+ $legacy_removal = !has_action('admin_init', ['CAS_Post_Type_Sidebar','initiate']);
44
+
45
+ if ($legacy_removal) {
46
+ _deprecated_hook(
47
+ "remove_action('admin_init', array('CAS_Post_Type_Sidebar', 'initiate'))",
48
+ '3.7',
49
+ "add_filter('cas/module/quick_select', '__return_false')"
50
+ );
51
+ }
52
+
53
+ $enable = apply_filters(
54
+ 'cas/module/quick_select',
55
+ !$legacy_removal,
56
+ $screen->post_type
57
+ );
58
+
59
+ if (!$enable) {
60
+ return;
61
+ }
62
+
63
+ $post_types = $module->post_types();
64
+ self::get_theme_sidebars();
65
+ if (isset($post_types[$screen->post_type]) && self::$_theme_sidebars) {
66
+ add_action(
67
+ 'add_meta_boxes_' . $screen->post_type,
68
+ [__CLASS__,'create_meta_boxes']
69
+ );
70
+ add_action(
71
+ 'save_post_' . $screen->post_type,
72
+ [__CLASS__,'save_post_sidebars'],
73
+ 10,
74
+ 2
75
+ );
76
+ add_action(
77
+ 'admin_enqueue_scripts',
78
+ [__CLASS__,'register_scripts'],
79
+ 8
80
+ );
81
+ add_action(
82
+ 'admin_enqueue_scripts',
83
+ [__CLASS__,'enqueue_scripts'],
84
+ 11
85
+ );
86
+ }
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Gather theme sidebars for later use
92
+ *
93
+ * @since 3.3
94
+ * @return void
95
+ */
96
+ public static function get_theme_sidebars()
97
+ {
98
+ global $wp_registered_sidebars;
99
+
100
+ $cas_sidebars = CAS_App::instance()->manager()->sidebars;
101
+
102
+ foreach ($wp_registered_sidebars as $sidebar) {
103
+ if (!isset($cas_sidebars[$sidebar['id']])) {
104
+ self::$_theme_sidebars[$sidebar['id']] = [
105
+ 'label' => $sidebar['name'],
106
+ 'options' => []
107
+ ];
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Save sidebars and conditions for post
114
+ *
115
+ * @since 3.3
116
+ * @param int $post_id
117
+ * @param WP_Post $post
118
+ * @return void
119
+ */
120
+ public static function save_post_sidebars($post_id, $post)
121
+ {
122
+ if (in_array($post_id, self::get_special_post_ids())) {
123
+ return;
124
+ }
125
+
126
+ if (!(isset($_POST[self::NONCE])
127
+ && wp_verify_nonce($_POST[self::NONCE], self::NONCE . $post_id))) {
128
+ return;
129
+ }
130
+
131
+ // Check post permissions
132
+ if (!current_user_can('edit_post', $post_id)) {
133
+ return;
134
+ }
135
+
136
+ if ($post->post_status == 'auto-draft') {
137
+ return;
138
+ }
139
+
140
+ // Check autosave
141
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
142
+ return;
143
+ }
144
+
145
+ $meta_key = WPCACore::PREFIX . self::MODULE_NAME;
146
+ $new = isset($_POST['cas_sidebars']) ? $_POST['cas_sidebars'] : [];
147
+
148
+ $relations = self::_get_content_sidebars($post);
149
+ $user_can_create_sidebar = current_user_can(CAS_App::CAPABILITY);
150
+
151
+ foreach ($new as $host => $sidebar_ids) {
152
+ foreach ($sidebar_ids as $sidebar_id) {
153
+ //Post has sidebar already
154
+ if (isset($relations[$sidebar_id])) {
155
+ unset($relations[$sidebar_id]);
156
+ //Discard empty
157
+ } elseif ($sidebar_id) {
158
+ $condition_group_id = 0;
159
+
160
+ //New sidebar
161
+ //check permissions here
162
+ if ($sidebar_id[0] == '_') {
163
+ if ($user_can_create_sidebar) {
164
+ $id = wp_insert_post([
165
+ 'post_title' => str_replace('__', ',', substr($sidebar_id, 1)),
166
+ 'post_status' => CAS_App::STATUS_INACTIVE,
167
+ 'post_type' => CAS_App::TYPE_SIDEBAR
168
+ ]);
169
+ if ($id) {
170
+ //wp_insert_post does not handle meta before WP4.4
171
+ add_post_meta($id, WPCACore::PREFIX . 'host', $host);
172
+ $condition_group_id = WPCACore::add_condition_group($id);
173
+ }
174
+ }
175
+ } else {
176
+ //Add post to group with other posts
177
+ $id = intval($sidebar_id);
178
+ $condition_groups = get_posts([
179
+ 'posts_per_page' => 1,
180
+ 'meta_key' => $meta_key,
181
+ 'meta_value' => $post->post_type,
182
+ 'meta_compare' => '!=',
183
+ 'post_parent' => $id,
184
+ 'post_type' => WPCACore::TYPE_CONDITION_GROUP,
185
+ 'post_status' => WPCACore::STATUS_PUBLISHED
186
+ ]);
187
+ if ($condition_groups) {
188
+ $condition_group_id = $condition_groups[0]->ID;
189
+ } else {
190
+ $condition_group_id = WPCACore::add_condition_group($id);
191
+ }
192
+ }
193
+
194
+ if ($condition_group_id) {
195
+ add_post_meta($condition_group_id, $meta_key, $post_id);
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ //remove old relations
202
+ //todo: sanity check if post is added to several groups?
203
+ $sidebars = CAS_App::instance()->manager()->sidebars;
204
+ $host_meta = CAS_App::instance()->manager()->metadata()->get('host');
205
+ foreach ($relations as $sidebar_id => $group_id) {
206
+ if (isset($sidebars[CAS_App::SIDEBAR_PREFIX . $sidebar_id])) {
207
+ $host_ids = $host_meta->get_data($sidebar_id, false, false);
208
+ foreach ($host_ids as $host_id) {
209
+ if (!isset(self::$_theme_sidebars[$host_id])) {
210
+ continue 2;
211
+ }
212
+ }
213
+ }
214
+
215
+ //group with no post_type meta will be removed
216
+ //even if it has other meta (unlikely)
217
+ $group_meta = get_post_meta($group_id, $meta_key);
218
+ if (count($group_meta) <= 1) {
219
+ wp_delete_post($group_id);
220
+ } else {
221
+ delete_post_meta($group_id, $meta_key, $post_id);
222
+ }
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Create sidebar meta box for post types
228
+ *
229
+ * @since 3.3
230
+ * @param WP_Post $post
231
+ * @return void
232
+ */
233
+ public static function create_meta_boxes($post)
234
+ {
235
+ if (in_array($post->ID, self::get_special_post_ids())) {
236
+ return;
237
+ }
238
+
239
+ $post_sidebars = self::_get_content_sidebars($post);
240
+
241
+ $manager = CAS_App::instance()->manager();
242
+ $host_meta = $manager->metadata()->get('host');
243
+ foreach ($manager->sidebars as $sidebar) {
244
+ $host_ids = $host_meta->get_data($sidebar->ID, false, false);
245
+ foreach ($host_ids as $host_id) {
246
+ if (isset(self::$_theme_sidebars[$host_id])) {
247
+ self::$_theme_sidebars[$host_id]['options'][$sidebar->ID] = [
248
+ 'id' => $sidebar->ID,
249
+ 'text' => $sidebar->post_title . self::sidebar_states($sidebar)
250
+ ];
251
+ if (isset($post_sidebars[$sidebar->ID])) {
252
+ self::$_theme_sidebars[$host_id]['options'][$sidebar->ID]['select'] = 1;
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ $post_type = get_post_type_object($post->post_type);
259
+ $content = [
260
+ __('Author')
261
+ ];
262
+ if ($post_type->hierarchical) {
263
+ $content[] = __('Child Page', 'content-aware-sidebars');
264
+ }
265
+ if ($post_type->name == 'page') {
266
+ $content[] = __('Page Template', 'content-aware-sidebars');
267
+ }
268
+ $taxonomies = get_object_taxonomies($post, 'objects');
269
+ if ($taxonomies) {
270
+ foreach ($taxonomies as $tax) {
271
+ $content[] = $tax->labels->singular_name;
272
+ }
273
+ }
274
+ $content[] = __('Archive Page', 'content-aware-sidebars');
275
+
276
+ $path = plugin_dir_path(dirname(__FILE__)) . 'view/';
277
+ $view = WPCAView::make($path . 'sidebars_quick_select.php', [
278
+ 'post' => $post,
279
+ 'sidebars' => self::$_theme_sidebars,
280
+ 'limit' => 3,
281
+ 'content' => $content,
282
+ 'singular' => $post_type->labels->singular_name,
283
+ 'nonce' => wp_nonce_field(self::NONCE . $post->ID, self::NONCE, false, false)
284
+ ]);
285
+
286
+ add_meta_box(
287
+ 'cas-content-sidebars',
288
+ __('Sidebars - Quick Select', 'content-aware-sidebars'),
289
+ [$view, 'render'],
290
+ $post->post_type,
291
+ 'side'
292
+ );
293
+ }
294
+
295
+ /**
296
+ * Get sidebar status for display
297
+ *
298
+ * @since 3.4.1
299
+ * @param WP_Post $post
300
+ * @return string
301
+ */
302
+ public static function sidebar_states($post)
303
+ {
304
+ switch ($post->post_status) {
305
+ case CAS_App::STATUS_ACTIVE:
306
+ $status = '';
307
+ break;
308
+ case CAS_App::STATUS_SCHEDULED:
309
+ $status = ' (' . __('Scheduled') . ')';
310
+ break;
311
+ default:
312
+ $status = ' (' . __('Inactive', 'content-aware-sidebars') . ')';
313
+ break;
314
+ }
315
+ return $status;
316
+ }
317
+
318
+ /**
319
+ * Register scripts and styles
320
+ * We register early to make sure our select2 comes first
321
+ *
322
+ * @since 3.5.2
323
+ * @param string $hook
324
+ * @return void
325
+ */
326
+ public static function register_scripts($hook)
327
+ {
328
+ wp_register_script(
329
+ 'select2',
330
+ plugins_url('lib/wp-content-aware-engine/assets/js/select2.min.js', dirname(__FILE__)),
331
+ ['jquery'],
332
+ '4.0.3'
333
+ );
334
+ wp_register_script('cas/sidebars/suggest', plugins_url('assets/js/suggest-sidebars.min.js', dirname(__FILE__)), ['select2'], CAS_App::PLUGIN_VERSION, true);
335
+ }
336
+
337
+ /**
338
+ * Enqueue scripts and styles
339
+ * We enqueue later to make sure our CSS takes precedence
340
+ *
341
+ * @since 3.5.2
342
+ * @param string $hook
343
+ * @return void
344
+ */
345
+ public static function enqueue_scripts($hook)
346
+ {
347
+ wp_enqueue_style(CAS_App::META_PREFIX . 'condition-groups');
348
+ wp_enqueue_script('cas/sidebars/suggest');
349
+
350
+ $labels = [
351
+ 'createNew' => __('Create New', 'content-aware-sidebars'),
352
+ 'labelNew' => __('New', 'content-aware-sidebars')
353
+ ];
354
+ if (current_user_can(CAS_App::CAPABILITY)) {
355
+ $labels['notFound'] = __('Type to Add New Sidebar', 'content-aware-sidebars');
356
+ } else {
357
+ $labels['notFound'] = __('No sidebars found', 'content-aware-sidebars');
358
+ }
359
+ wp_localize_script('cas/sidebars/suggest', 'CAS', $labels);
360
+ }
361
+
362
+ /**
363
+ * @since 3.10.1
364
+ *
365
+ * @return array
366
+ */
367
+ protected static function get_special_post_ids()
368
+ {
369
+ $special_post_ids = [
370
+ get_option('page_on_front'),
371
+ get_option('page_for_posts'),
372
+ ];
373
+
374
+ if (defined('WC_VERSION')) {
375
+ $special_post_ids[] = get_option('woocommerce_shop_page_id');
376
+ }
377
+
378
+ return $special_post_ids;
379
+ }
380
+
381
+ /**
382
+ * Get sidebars for select post types
383
+ *
384
+ * @since 3.3
385
+ * @param WP_Post $post
386
+ * @return array
387
+ */
388
+ protected static function _get_content_sidebars($post)
389
+ {
390
+ $sidebars = [];
391
+ if ($post) {
392
+ global $wpdb;
393
+ $query = $wpdb->get_results($wpdb->prepare(
394
+ "SELECT s.ID, g.ID as group_id
395
+ FROM $wpdb->posts s
396
+ INNER JOIN $wpdb->posts g ON g.post_parent = s.ID
397
+ INNER JOIN $wpdb->postmeta sm ON sm.post_id = s.ID AND sm.meta_key = '" . WPCACore::PREFIX . "handle'
398
+ INNER JOIN $wpdb->postmeta gm ON gm.post_id = g.ID AND gm.meta_key = '" . WPCACore::PREFIX . self::MODULE_NAME . "'
399
+ WHERE s.post_status <> 'trash'
400
+ AND s.post_type = '" . CAS_App::TYPE_SIDEBAR . "'
401
+ AND sm.meta_value IN (" . CAS_App::ACTION_REPLACE . ',' . CAS_App::ACTION_MERGE . ',' . CAS_App::ACTION_REPLACE_FORCED . ")
402
+ AND g.post_status IN ('" . WPCACore::STATUS_PUBLISHED . "','" . WPCACore::STATUS_OR . "')
403
+ AND gm.meta_value = %d
404
+ ORDER BY s.post_title ASC",
405
+ $post->ID
406
+ ));
407
+ if ($query) {
408
+ foreach ($query as $sidebar) {
409
+ $sidebars[$sidebar->ID] = $sidebar->group_id;
410
+ }
411
+ }
412
+ }
413
+ return $sidebars;
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Backwards compat for users disabling quick select
419
+ * with remove_action('admin_init', array('CAS_Post_Type_Sidebar', 'initiate'))
420
+ *
421
+ * @deprecated 3.7
422
+ * @see add_filter('cas/module/quick_select', ...)
423
+ */
424
+ class CAS_Post_Type_Sidebar
425
+ {
426
+ public function __construct()
427
+ {
428
+ add_action('admin_init', [__CLASS__,'initiate']);
429
+ }
430
+
431
+ public static function initiate()
432
+ {
433
+ }
434
+ }
admin/screen_widgets.php CHANGED
@@ -1,218 +1,216 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- class CAS_Admin_Screen_Widgets extends CAS_Admin
12
- {
13
-
14
- /**
15
- * Get current screen
16
- *
17
- * @since 3.4
18
- * @return string
19
- */
20
- public function get_screen()
21
- {
22
- return 'widgets.php';
23
- }
24
-
25
- /**
26
- * Authorize user for screen
27
- *
28
- * @since 3.5
29
- * @return boolean
30
- */
31
- public function authorize_user()
32
- {
33
- return true;
34
- }
35
-
36
- /**
37
- * Prepare screen load
38
- *
39
- * @since 3.4
40
- * @return void
41
- */
42
- public function prepare_screen()
43
- {
44
- $this->add_action('dynamic_sidebar_before', 'render_sidebar_controls');
45
- $this->add_filter('admin_body_class', 'widget_manager_class');
46
-
47
- global $wp_registered_sidebars;
48
-
49
- $manager = CAS_App::instance()->manager();
50
- $manager->populate_metadata();
51
-
52
- $has_host = [0 => 1,1 => 1,3 => 1];
53
-
54
- foreach ($manager->sidebars as $id => $post) {
55
- $handle_meta = $manager->metadata()->get('handle');
56
-
57
- $args = [];
58
- $args['description'] = $handle_meta->get_list_data($post->ID, true);
59
-
60
- if (isset($has_host[$handle_meta->get_data($post->ID)])) {
61
- $hosts = $manager->metadata()->get('host')->get_data($post->ID, false, false);
62
- if ($hosts) {
63
- $list = $manager->metadata()->get('host')->get_input_list();
64
- $data = [];
65
- foreach ($hosts as $host) {
66
- if (!isset($list[$host])) {
67
- $data[] = __('Target not found', 'content-aware-sidebars');
68
- } else {
69
- $data[] = $list[$host];
70
- }
71
- }
72
- $args['description'] .= ': ' . implode(', ', $data);
73
- }
74
- }
75
-
76
- $wp_registered_sidebars[$id] = array_merge($wp_registered_sidebars[$id], $args);
77
- }
78
- }
79
-
80
- /**
81
- * Add filters and actions for admin dashboard
82
- * e.g. AJAX calls
83
- *
84
- * @since 3.5
85
- * @return void
86
- */
87
- public function admin_hooks()
88
- {
89
- $this->add_action('wp_ajax_cas_sidebar_status', 'ajax_set_sidebar_status');
90
- }
91
-
92
- /**
93
- * Set post type status on AJAX
94
- *
95
- * @since 3.5
96
- * @return void
97
- */
98
- public function ajax_set_sidebar_status()
99
- {
100
- if (!isset($_POST['sidebar_id'],$_POST['status'],$_POST['token'])) {
101
- wp_send_json_error('400 Bad Request');
102
- }
103
-
104
- $sidebar_id = $_POST['sidebar_id'];
105
-
106
- if (!wp_verify_nonce($_POST['token'], CAS_Admin::NONCE_PREFIX_1CLICK.$sidebar_id)) {
107
- wp_send_json_error('403 Forbidden');
108
- }
109
-
110
- if (!current_user_can(CAS_App::CAPABILITY, $sidebar_id)) {
111
- wp_send_json_error('401 Unauthorized');
112
- }
113
-
114
- $data = [];
115
- $status = filter_var($_POST['status'], FILTER_VALIDATE_BOOLEAN);
116
- if ($status) {
117
- $data = [
118
- 'ID' => $sidebar_id,
119
- 'post_status' => CAS_App::STATUS_ACTIVE,
120
- 'post_date' => current_time('mysql'),
121
- 'post_date_gmt' => current_time('mysql', true)
122
- ];
123
- } else {
124
- $data = [
125
- 'ID' => $sidebar_id,
126
- 'post_status' => CAS_App::STATUS_INACTIVE
127
- ];
128
- }
129
-
130
- if (!wp_update_post($data)) {
131
- wp_send_json_error('409 Conflict');
132
- }
133
-
134
- $data['title'] = $status ? __('Active', 'content-aware-sidebars') : __('Inactive', 'content-aware-sidebars');
135
- //$data['message'] = sprintf(__('Status set to %s'),$data['title']);
136
-
137
- wp_send_json_success($data);
138
- }
139
-
140
- /**
141
- * Add body class to enable widget manager
142
- *
143
- * @since 3.6
144
- * @param string $classes
145
- * @return string
146
- */
147
- public function widget_manager_class($classes)
148
- {
149
- $enhanced_enabled = apply_filters('cas/module/widget_manager', true);
150
- if ($enhanced_enabled && version_compare(get_bloginfo('version'), '4.7', '>=')) {
151
- $classes .= ' cas-widget-manager ';
152
- }
153
- return $classes;
154
- }
155
-
156
- /**
157
- * Render controls for custom sidebars
158
- *
159
- * @since 3.3
160
- * @param string $index
161
- * @return void
162
- */
163
- public function render_sidebar_controls($index)
164
- {
165
- //trashed custom sidebars not included
166
- $sidebars = CAS_App::instance()->manager()->sidebars;
167
- if (isset($sidebars[$index])) {
168
- $sidebar = $sidebars[$index];
169
- $link = admin_url('post.php?post='.$sidebar->ID);
170
- $edit_link = admin_url('admin.php?page=wpcas-edit&sidebar_id='.$sidebar->ID);
171
- $more_link = esc_url('https://dev.institute/wordpress-sidebars/pricing/?utm_source=plugin&utm_medium=popup&utm_content=widget-revisions&utm_campaign=cas');
172
-
173
- switch ($sidebar->post_status) {
174
- case CAS_App::STATUS_ACTIVE:
175
- $status = __('Active', 'content-aware-sidebars');
176
- break;
177
- case CAS_App::STATUS_SCHEDULED:
178
- $status = __('Scheduled');
179
- break;
180
- default:
181
- $status = __('Inactive', 'content-aware-sidebars');
182
- }
183
-
184
- echo '<div class="cas-settings">';
185
- echo '<div class="sidebar-status">';
186
- echo '<input type="checkbox" class="sidebar-status-input sidebar-status-'.$sidebar->post_status.'"
187
- id="cas-status-'.$sidebar->ID.'" data-nonce="'.wp_create_nonce(CAS_Admin::NONCE_PREFIX_1CLICK.$sidebar->ID).'"
188
- value="'.$sidebar->ID.'" '.checked($sidebar->post_status, CAS_App::STATUS_ACTIVE, false).'>';
189
- echo '<label title="'.$status.'" class="sidebar-status-label" for="cas-status-'.$sidebar->ID.'">';
190
- echo '</label>';
191
- echo '</div>';
192
- echo '<a title="'.esc_attr__('Widget Revisions', 'content-aware-sidebars').'" class="js-cas-pro-notice cas-sidebar-link dashicons dashicons-backup" data-url="'.$more_link.'" href="'.add_query_arg('action', 'cas-revisions', $link).'"></a>';
193
- echo '<a title="'.esc_attr__('Sidebar Conditions', 'content-aware-sidebars').'" class="dashicons dashicons-visibility cas-sidebar-link" href="'.$edit_link.'"></a>';
194
- echo '<a title="'.esc_attr__('Schedule Sidebar', 'content-aware-sidebars').'" class="dashicons dashicons-calendar cas-sidebar-link" href="'.$edit_link.'#top#section-schedule"></a>';
195
- echo '<a title="'.esc_attr__('Design Sidebar', 'content-aware-sidebars').'" class="dashicons dashicons-admin-appearance cas-sidebar-link" href="'.$edit_link.'#top#section-design"></a>';
196
- echo '<a title="'.esc_attr__('Edit Sidebar', 'content-aware-sidebars').'" class="dashicons dashicons-admin-generic cas-sidebar-link" href="'.$edit_link.'"></a>';
197
- echo '</div>';
198
- }
199
- }
200
-
201
- /**
202
- * Register and enqueue scripts styles
203
- * for screen
204
- *
205
- * @since 3.4
206
- */
207
- public function add_scripts_styles()
208
- {
209
- $this->enqueue_script('cas/admin/widgets', 'widgets', ['jquery'], '', true);
210
- wp_localize_script('cas/admin/widgets', 'CASAdmin', [
211
- 'addNew' => $this->get_sidebar_type()->labels->add_new_item,
212
- 'collapse' => __('Collapse', 'content-aware-sidebars'),
213
- 'expand' => __('Expand', 'content-aware-sidebars'),
214
- 'filterSidebars' => __('Search Sidebars', 'content-aware-sidebars'),
215
- 'filterWidgets' => __('Search Widgets', 'content-aware-sidebars')
216
- ]);
217
- }
218
- }
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ class CAS_Admin_Screen_Widgets extends CAS_Admin
12
+ {
13
+ /**
14
+ * Get current screen
15
+ *
16
+ * @since 3.4
17
+ * @return string
18
+ */
19
+ public function get_screen()
20
+ {
21
+ return 'widgets.php';
22
+ }
23
+
24
+ /**
25
+ * Authorize user for screen
26
+ *
27
+ * @since 3.5
28
+ * @return boolean
29
+ */
30
+ public function authorize_user()
31
+ {
32
+ return true;
33
+ }
34
+
35
+ /**
36
+ * Prepare screen load
37
+ *
38
+ * @since 3.4
39
+ * @return void
40
+ */
41
+ public function prepare_screen()
42
+ {
43
+ $this->add_action('dynamic_sidebar_before', 'render_sidebar_controls');
44
+ $this->add_filter('admin_body_class', 'widget_manager_class');
45
+
46
+ global $wp_registered_sidebars;
47
+
48
+ $manager = CAS_App::instance()->manager();
49
+ $manager->populate_metadata();
50
+
51
+ $has_host = [0 => 1,1 => 1,3 => 1];
52
+
53
+ foreach ($manager->sidebars as $id => $post) {
54
+ $handle_meta = $manager->metadata()->get('handle');
55
+
56
+ $args = [];
57
+ $args['description'] = $handle_meta->get_list_data($post->ID, true);
58
+
59
+ if (isset($has_host[$handle_meta->get_data($post->ID)])) {
60
+ $hosts = $manager->metadata()->get('host')->get_data($post->ID, false, false);
61
+ if ($hosts) {
62
+ $list = $manager->metadata()->get('host')->get_input_list();
63
+ $data = [];
64
+ foreach ($hosts as $host) {
65
+ if (!isset($list[$host])) {
66
+ $data[] = __('Target not found', 'content-aware-sidebars');
67
+ } else {
68
+ $data[] = $list[$host];
69
+ }
70
+ }
71
+ $args['description'] .= ': ' . implode(', ', $data);
72
+ }
73
+ }
74
+
75
+ $wp_registered_sidebars[$id] = array_merge($wp_registered_sidebars[$id], $args);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Add filters and actions for admin dashboard
81
+ * e.g. AJAX calls
82
+ *
83
+ * @since 3.5
84
+ * @return void
85
+ */
86
+ public function admin_hooks()
87
+ {
88
+ $this->add_action('wp_ajax_cas_sidebar_status', 'ajax_set_sidebar_status');
89
+ }
90
+
91
+ /**
92
+ * Set post type status on AJAX
93
+ *
94
+ * @since 3.5
95
+ * @return void
96
+ */
97
+ public function ajax_set_sidebar_status()
98
+ {
99
+ if (!isset($_POST['sidebar_id'],$_POST['status'],$_POST['token'])) {
100
+ wp_send_json_error('400 Bad Request');
101
+ }
102
+
103
+ $sidebar_id = $_POST['sidebar_id'];
104
+
105
+ if (!wp_verify_nonce($_POST['token'], CAS_Admin::NONCE_PREFIX_1CLICK . $sidebar_id)) {
106
+ wp_send_json_error('403 Forbidden');
107
+ }
108
+
109
+ if (!current_user_can(CAS_App::CAPABILITY, $sidebar_id)) {
110
+ wp_send_json_error('401 Unauthorized');
111
+ }
112
+
113
+ $status = filter_var($_POST['status'], FILTER_VALIDATE_BOOLEAN);
114
+ if ($status) {
115
+ $data = [
116
+ 'ID' => $sidebar_id,
117
+ 'post_status' => CAS_App::STATUS_ACTIVE,
118
+ 'post_date' => current_time('mysql'),
119
+ 'post_date_gmt' => current_time('mysql', true)
120
+ ];
121
+ } else {
122
+ $data = [
123
+ 'ID' => $sidebar_id,
124
+ 'post_status' => CAS_App::STATUS_INACTIVE
125
+ ];
126
+ }
127
+
128
+ if (!wp_update_post($data)) {
129
+ wp_send_json_error('409 Conflict');
130
+ }
131
+
132
+ $data['title'] = $status ? __('Active', 'content-aware-sidebars') : __('Inactive', 'content-aware-sidebars');
133
+ //$data['message'] = sprintf(__('Status set to %s'),$data['title']);
134
+
135
+ wp_send_json_success($data);
136
+ }
137
+
138
+ /**
139
+ * Add body class to enable widget manager
140
+ *
141
+ * @since 3.6
142
+ * @param string $classes
143
+ * @return string
144
+ */
145
+ public function widget_manager_class($classes)
146
+ {
147
+ $enhanced_enabled = apply_filters('cas/module/widget_manager', true);
148
+ if ($enhanced_enabled && version_compare(get_bloginfo('version'), '4.7', '>=')) {
149
+ $classes .= ' cas-widget-manager ';
150
+ }
151
+ return $classes;
152
+ }
153
+
154
+ /**
155
+ * Render controls for custom sidebars
156
+ *
157
+ * @since 3.3
158
+ * @param string $index
159
+ * @return void
160
+ */
161
+ public function render_sidebar_controls($index)
162
+ {
163
+ //trashed custom sidebars not included
164
+ $sidebars = CAS_App::instance()->manager()->sidebars;
165
+ if (isset($sidebars[$index])) {
166
+ $sidebar = $sidebars[$index];
167
+ $link = admin_url('post.php?post=' . $sidebar->ID);
168
+ $edit_link = admin_url('admin.php?page=wpcas-edit&sidebar_id=' . $sidebar->ID);
169
+ $more_link = esc_url('https://dev.institute/wordpress-sidebars/pricing/?utm_source=plugin&utm_medium=popup&utm_content=widget-revisions&utm_campaign=cas');
170
+
171
+ switch ($sidebar->post_status) {
172
+ case CAS_App::STATUS_ACTIVE:
173
+ $status = __('Active', 'content-aware-sidebars');
174
+ break;
175
+ case CAS_App::STATUS_SCHEDULED:
176
+ $status = __('Scheduled');
177
+ break;
178
+ default:
179
+ $status = __('Inactive', 'content-aware-sidebars');
180
+ }
181
+
182
+ echo '<div class="cas-settings">';
183
+ echo '<div class="sidebar-status">';
184
+ echo '<input type="checkbox" class="sidebar-status-input sidebar-status-' . $sidebar->post_status . '"
185
+ id="cas-status-' . $sidebar->ID . '" data-nonce="' . wp_create_nonce(CAS_Admin::NONCE_PREFIX_1CLICK . $sidebar->ID) . '"
186
+ value="' . $sidebar->ID . '" ' . checked($sidebar->post_status, CAS_App::STATUS_ACTIVE, false) . '>';
187
+ echo '<label title="' . $status . '" class="sidebar-status-label" for="cas-status-' . $sidebar->ID . '">';
188
+ echo '</label>';
189
+ echo '</div>';
190
+ echo '<a title="' . esc_attr__('Widget Revisions', 'content-aware-sidebars') . '" class="js-cas-pro-notice cas-sidebar-link dashicons dashicons-backup" data-url="' . $more_link . '" href="' . add_query_arg('action', 'cas-revisions', $link) . '"></a>';
191
+ echo '<a title="' . esc_attr__('Sidebar Conditions', 'content-aware-sidebars') . '" class="dashicons dashicons-visibility cas-sidebar-link" href="' . $edit_link . '"></a>';
192
+ echo '<a title="' . esc_attr__('Schedule Sidebar', 'content-aware-sidebars') . '" class="dashicons dashicons-calendar cas-sidebar-link" href="' . $edit_link . '#top#section-schedule"></a>';
193
+ echo '<a title="' . esc_attr__('Design Sidebar', 'content-aware-sidebars') . '" class="dashicons dashicons-admin-appearance cas-sidebar-link" href="' . $edit_link . '#top#section-design"></a>';
194
+ echo '<a title="' . esc_attr__('Edit Sidebar', 'content-aware-sidebars') . '" class="dashicons dashicons-admin-generic cas-sidebar-link" href="' . $edit_link . '"></a>';
195
+ echo '</div>';
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Register and enqueue scripts styles
201
+ * for screen
202
+ *
203
+ * @since 3.4
204
+ */
205
+ public function add_scripts_styles()
206
+ {
207
+ $this->enqueue_script('cas/admin/widgets', 'widgets', ['jquery'], '', true);
208
+ wp_localize_script('cas/admin/widgets', 'CASAdmin', [
209
+ 'addNew' => $this->get_sidebar_type()->labels->add_new_item,
210
+ 'collapse' => __('Collapse', 'content-aware-sidebars'),
211
+ 'expand' => __('Expand', 'content-aware-sidebars'),
212
+ 'filterSidebars' => __('Search Sidebars', 'content-aware-sidebars'),
213
+ 'filterWidgets' => __('Search Widgets', 'content-aware-sidebars')
214
+ ]);
215
+ }
216
+ }
 
 
admin/settings.php CHANGED
@@ -1,93 +1,86 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- class CAS_Admin_Settings extends CAS_Admin
12
- {
13
- /**
14
- * @inheritDoc
15
- */
16
- public function admin_hooks()
17
- {
18
- }
19
-
20
- /**
21
- * @inheritDoc
22
- */
23
- public function get_screen()
24
- {
25
- $screen = add_submenu_page(
26
- CAS_App::BASE_SCREEN.'-bogus', //dont add menu item
27
- null,
28
- null,
29
- $this->authorize_user(),
30
- CAS_App::BASE_SCREEN.'-settings',
31
- [$this,'render_screen']
32
- );
33
- return $screen;
34
- }
35
-
36
- /**
37
- * @inheritDoc
38
- */
39
- public function authorize_user()
40
- {
41
- $post_type_object = $this->get_sidebar_type();
42
- return $post_type_object->cap->edit_posts;
43
- }
44
-
45
- /**
46
- * @inheritDoc
47
- */
48
- public function prepare_screen()
49
- {
50
- $this->process_actions();
51
- }
52
-
53
- /**
54
- * @inheritDoc
55
- */
56
- public function process_actions()
57
- {
58
- $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
59
-
60
- if (!$action) {
61
- return;
62
- }
63
-
64
- check_admin_referer($action);
65
-
66
- $sendback = wp_get_referer();
67
-
68
- switch ($action) {
69
- case 'update_condition_type_cache':
70
- WPCACore::cache_condition_types();
71
- break;
72
- default:
73
- break;
74
- }
75
-
76
- wp_safe_redirect($sendback);
77
- exit();
78
- }
79
-
80
- /**
81
- * @inheritDoc
82
- */
83
- public function render_screen()
84
- {
85
- }
86
-
87
- /**
88
- * @inheritDoc
89
- */
90
- public function add_scripts_styles()
91
- {
92
- }
93
- }
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ class CAS_Admin_Settings extends CAS_Admin
12
+ {
13
+ /**
14
+ * @inheritDoc
15
+ */
16
+ public function admin_hooks()
17
+ {
18
+ }
19
+
20
+ /**
21
+ * @inheritDoc
22
+ */
23
+ public function get_screen()
24
+ {
25
+ return add_submenu_page(
26
+ CAS_App::BASE_SCREEN . '-bogus', //dont add menu item
27
+ null,
28
+ null,
29
+ $this->authorize_user(),
30
+ CAS_App::BASE_SCREEN . '-settings',
31
+ [$this,'render_screen']
32
+ );
33
+ }
34
+
35
+ /**
36
+ * @inheritDoc
37
+ */
38
+ public function authorize_user()
39
+ {
40
+ $post_type_object = $this->get_sidebar_type();
41
+ return $post_type_object->cap->edit_posts;
42
+ }
43
+
44
+ /**
45
+ * @inheritDoc
46
+ */
47
+ public function prepare_screen()
48
+ {
49
+ $this->process_actions();
50
+ }
51
+
52
+ public function process_actions()
53
+ {
54
+ $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
55
+
56
+ if (!$action) {
57
+ return;
58
+ }
59
+
60
+ check_admin_referer($action);
61
+
62
+ $sendback = wp_get_referer();
63
+
64
+ switch ($action) {
65
+ case 'update_condition_type_cache':
66
+ WPCACore::cache_condition_types();
67
+ break;
68
+ default:
69
+ break;
70
+ }
71
+
72
+ wp_safe_redirect($sendback);
73
+ exit();
74
+ }
75
+
76
+ public function render_screen()
77
+ {
78
+ }
79
+
80
+ /**
81
+ * @inheritDoc
82
+ */
83
+ public function add_scripts_styles()
84
+ {
85
+ }
86
+ }
 
 
 
 
 
 
 
admin/sidebar-edit.php CHANGED
@@ -1,1010 +1,1005 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- final class CAS_Sidebar_Edit extends CAS_Admin
12
- {
13
-
14
- /**
15
- * Intro tour manager
16
- * @var WP_Pointer_Tour
17
- */
18
- private $_tour_manager;
19
-
20
- /**
21
- * Add filters and actions for admin dashboard
22
- * e.g. AJAX calls
23
- *
24
- * @since 3.5
25
- * @return void
26
- */
27
- public function admin_hooks()
28
- {
29
- $this->_tour_manager = new WP_Pointer_Tour(CAS_App::META_PREFIX.'cas_tour');
30
-
31
- $this->add_action('delete_post', 'remove_sidebar_widgets');
32
- $this->add_action('save_post_'.CAS_App::TYPE_SIDEBAR, 'save_post', 10, 2);
33
-
34
- $this->add_filter('wp_insert_post_data', 'add_duplicate_title_suffix', 99, 2);
35
-
36
- if (!cas_fs()->can_use_premium_code()) {
37
- $this->add_action('wpca/modules/init', 'add_modules');
38
- }
39
- }
40
-
41
- /**
42
- * Set up admin menu and get current screen
43
- *
44
- * @since 3.4
45
- * @return string
46
- */
47
- public function get_screen()
48
- {
49
- $post_type_object = $this->get_sidebar_type();
50
- return add_submenu_page(
51
- CAS_App::BASE_SCREEN,
52
- $post_type_object->labels->add_new_item,
53
- $post_type_object->labels->add_new,
54
- $post_type_object->cap->edit_posts,
55
- CAS_App::BASE_SCREEN.'-edit',
56
- [$this,'render_screen']
57
- );
58
- }
59
-
60
- /**
61
- * @since 3.5
62
- *
63
- * @return bool
64
- */
65
- public function authorize_user()
66
- {
67
- return true;
68
- }
69
-
70
- /**
71
- * @since 3.4
72
- *
73
- * @return void
74
- */
75
- public function prepare_screen()
76
- {
77
- $this->add_action('cas/admin/add_meta_boxes', 'create_meta_boxes');
78
-
79
- global $post, $title, $active_post_lock;
80
-
81
- $post_type = CAS_App::TYPE_SIDEBAR;
82
- $post_type_object = $this->get_sidebar_type();
83
- $post_id = isset($_REQUEST['sidebar_id']) ? $_REQUEST['sidebar_id'] : 0;
84
-
85
- /**
86
- * Edit mode
87
- */
88
- if ($post_id) {
89
- $this->process_actions($post_id);
90
-
91
- $post = get_post($post_id, OBJECT, 'edit');
92
-
93
- if (! $post) {
94
- wp_die(__('The sidebar no longer exists.'));
95
- }
96
- if (! current_user_can($post_type_object->cap->edit_post, $post_id)) {
97
- wp_die(__('You are not allowed to edit this sidebar.'));
98
- }
99
- if ('trash' == $post->post_status) {
100
- wp_die(__('You cannot edit this sidebar because it is in the Trash. Please restore it and try again.'));
101
- }
102
-
103
- if (! empty($_GET['get-post-lock'])) {
104
- check_admin_referer('lock-post_' . $post_id);
105
- wp_set_post_lock($post_id);
106
- wp_redirect(get_edit_post_link($post_id, 'url'));
107
- exit();
108
- }
109
-
110
- if (! wp_check_post_lock($post->ID)) {
111
- $active_post_lock = wp_set_post_lock($post->ID);
112
- }
113
-
114
- $title = $post_type_object->labels->edit_item;
115
-
116
- /**
117
- * New Mode
118
- */
119
- } else {
120
- if (! (current_user_can($post_type_object->cap->edit_posts) || current_user_can($post_type_object->cap->create_posts))) {
121
- wp_die(
122
- '<p>' . __('You are not allowed to create sidebars.', 'content-aware-sidebars') . '</p>',
123
- 403
124
- );
125
- }
126
-
127
- $post = get_default_post_to_edit($post_type, true);
128
-
129
- $title = $post_type_object->labels->add_new_item;
130
- }
131
-
132
- do_action('cas/admin/add_meta_boxes', $post);
133
- }
134
-
135
- /**
136
- * @since 3.9
137
- * @param WPCATypeManager $types
138
- *
139
- * @return void
140
- */
141
- public function add_modules($types)
142
- {
143
- if (!$types->has(CAS_App::TYPE_SIDEBAR)) {
144
- return;
145
- }
146
-
147
- $pro_label = '(Pro)';
148
- $type = $types->get(CAS_App::TYPE_SIDEBAR);
149
- $path = plugin_dir_path(dirname(__FILE__));
150
-
151
- require($path.'conditions/placeholder.php');
152
-
153
- if (!WPCACore::get_option(CAS_App::TYPE_SIDEBAR, 'legacy.date_module', false)) {
154
- $module = new CASConditionPlaceholder('cas_date', __('Dates', 'content-aware-sidebars').' '.$pro_label);
155
- $type->add($module, 'cas_date');
156
- }
157
-
158
- $module = new CASConditionPlaceholder('cas_url', __('URLs', 'content-aware-sidebars').' '.$pro_label);
159
- $type->add($module, 'cas_url');
160
- $module = new CASConditionPlaceholder('cas_ref_url', __('Referrer URLs', 'content-aware-sidebars').' '.$pro_label);
161
- $type->add($module, 'cas_ref_url');
162
-
163
- if (function_exists('bp_is_active')) {
164
- $module = new CASConditionPlaceholder('cas_bbp', __('BuddyPress Groups', 'content-aware-sidebars').' '.$pro_label, '', '', 'plugins');
165
- $type->add($module, 'cas_bbp');
166
- }
167
-
168
- if (defined('ACF')) {
169
- $module = new CASConditionPlaceholder('cas_acf', __('Advanced Custom Fields', 'content-aware-sidebars').' '.$pro_label, '', '', 'plugins');
170
- $type->add($module, 'cas_acf');
171
- }
172
- }
173
-
174
- /**
175
- * Process actions
176
- *
177
- * @since 3.4
178
- * @param int $post_id
179
- * @return void
180
- */
181
- public function process_actions($post_id)
182
- {
183
- $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
184
- if (isset($_POST['deletepost'])) {
185
- $action = 'delete';
186
- }
187
-
188
- if ($action && $post_id) {
189
- $sendback = wp_get_referer();
190
- $sendback = remove_query_arg(
191
- ['action','trashed', 'untrashed', 'deleted', 'ids'],
192
- $sendback
193
- );
194
- if (isset($_REQUEST['_cas_section']) && $_REQUEST['_cas_section']) {
195
- $sendback .= $_REQUEST['_cas_section'];
196
- }
197
-
198
- $post = get_post($post_id);
199
- if (! $post) {
200
- wp_die(__('The sidebar no longer exists.', 'content-aware-sidebars'));
201
- }
202
-
203
- check_admin_referer($action . '-post_' . $post_id);
204
-
205
- switch ($action) {
206
- case 'update':
207
-
208
- $post_id = $this->update_sidebar_type();
209
-
210
- // Session cookie flag that the post was saved
211
- if (isset($_COOKIE['wp-saving-post']) && $_COOKIE['wp-saving-post'] === $post_id . '-check') {
212
- setcookie('wp-saving-post', $post_id . '-saved', time() + DAY_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl());
213
- }
214
-
215
- $status = get_post_status($post_id);
216
- if (isset($_POST['original_post_status']) && $_POST['original_post_status'] == $status) {
217
- $message = 1;
218
- } else {
219
- switch ($status) {
220
- case CAS_App::STATUS_SCHEDULED:
221
- //gets scheduled
222
- $message = 9;
223
- break;
224
- case CAS_App::STATUS_INACTIVE:
225
- //gets deactivated
226
- $message = 10;
227
- break;
228
- case CAS_App::STATUS_ACTIVE:
229
- //gets activated
230
- $message = 6;
231
- break;
232
- default:
233
- $message = 1;
234
- }
235
- }
236
-
237
- $sendback = add_query_arg([
238
- 'sidebar_id' => $post_id,
239
- 'message' => $message,
240
- 'page' => 'wpcas-edit'
241
- ], $sendback);
242
- wp_safe_redirect($sendback);
243
- exit();
244
- case 'trash':
245
-
246
- if (! current_user_can('delete_post', $post_id)) {
247
- wp_die(__('You are not allowed to move this sidebar to the Trash.', 'content-aware-sidebars'));
248
- }
249
-
250
- if ($user_id = wp_check_post_lock($post_id)) {
251
- $user = get_userdata($user_id);
252
- wp_die(sprintf(__('You cannot move this sidebar to the Trash. %s is currently editing.', 'content-aware-sidebars'), $user->display_name));
253
- }
254
-
255
- if (! wp_trash_post($post_id)) {
256
- wp_die(__('Error in moving to Trash.'));
257
- }
258
-
259
- $sendback = remove_query_arg('sidebar_id', $sendback);
260
-
261
- wp_safe_redirect(add_query_arg(
262
- [
263
- 'page' => 'wpcas',
264
- 'trashed' => 1,
265
- 'ids' => $post_id
266
- ],
267
- $sendback
268
- ));
269
- exit();
270
- case 'untrash':
271
-
272
- if (! current_user_can('delete_post', $post_id)) {
273
- wp_die(__('You are not allowed to restore this sidebar from the Trash.', 'content-aware-sidebars'));
274
- }
275
-
276
- if (! wp_untrash_post($post_id)) {
277
- wp_die(__('Error in restoring from Trash.'));
278
- }
279
-
280
- wp_safe_redirect(add_query_arg('untrashed', 1, $sendback));
281
- exit();
282
- case 'delete':
283
-
284
- if (! current_user_can('delete_post', $post_id)) {
285
- wp_die(__('You are not allowed to delete this sidebar.', 'content-aware-sidebars'));
286
- }
287
-
288
- if (! wp_delete_post($post_id, true)) {
289
- wp_die(__('Error in deleting.'));
290
- }
291
-
292
- $sendback = remove_query_arg('sidebar_id', $sendback);
293
- wp_safe_redirect(add_query_arg([
294
- 'page' => 'wpcas',
295
- 'deleted' => 1
296
- ], $sendback));
297
- exit();
298
- default:
299
- do_action('cas/admin/action', $action, $post);
300
- break;
301
- }
302
- }
303
- }
304
-
305
- /**
306
- * Render screen
307
- *
308
- * @since 3.4
309
- * @return void
310
- */
311
- public function render_screen()
312
- {
313
- global $post, $active_post_lock;
314
-
315
- $post_type_object = get_post_type_object($post->post_type);
316
- $post_id = isset($_REQUEST['sidebar_id']) ? $_REQUEST['sidebar_id'] : 0;
317
-
318
- $form_extra = '';
319
- if ('auto-draft' == $post->post_status) {
320
- if (isset($_REQUEST['sidebar_id'])) {
321
- $post->post_title = '';
322
- }
323
- $form_extra .= "<input type='hidden' id='auto_draft' name='auto_draft' value='1' />";
324
- }
325
-
326
- if ($post_id) {
327
- $title = __('Edit');
328
- } else {
329
- $title = $post_type_object->labels->new_item;
330
- }
331
-
332
- echo '<div class="wrap">';
333
- echo '<h1>';
334
- echo '<a href="'.admin_url('admin.php?page=wpcas').'">'.$post_type_object->labels->all_items.'</a> &raquo; ';
335
- echo esc_html($title);
336
- if (isset($_REQUEST['sidebar_id']) && current_user_can('edit_theme_options')) {
337
- echo ' <a href="' . esc_url(admin_url('widgets.php#'.CAS_App::SIDEBAR_PREFIX.$post->ID)) . '" class="page-title-action add-new-h2">' . __('Manage Widgets', 'content-aware-sidebars') . '</a>';
338
- }
339
-
340
- echo '</h1>';
341
-
342
- $this->sidebar_updated_messages($post);
343
-
344
- echo '<form name="post" action="admin.php?page=wpcas-edit" method="post" id="post">';
345
- $referer = wp_get_referer();
346
- wp_nonce_field('update-post_' . $post->ID);
347
- echo '<input type="hidden" id="user-id" name="user_ID" value="'.(int)get_current_user_id().'" />';
348
- echo '<input type="hidden" id="_cas_section" name="_cas_section" value="" />';
349
- echo '<input type="hidden" id="hiddenaction" name="action" value="update" />';
350
- echo '<input type="hidden" id="post_author" name="post_author" value="'.esc_attr($post->post_author).'" />';
351
- echo '<input type="hidden" id="original_post_status" name="original_post_status" value="'.esc_attr($post->post_status).'" />';
352
- echo '<input type="hidden" id="referredby" name="referredby" value="'.($referer ? esc_url($referer) : '').'" />';
353
- echo '<input type="hidden" id="post_ID" name="sidebar_id" value="'.esc_attr($post->ID).'" />';
354
- if (! empty($active_post_lock)) {
355
- echo '<input type="hidden" id="active_post_lock" value="'.esc_attr(implode(':', $active_post_lock)).'" />';
356
- }
357
- if (get_post_status($post) != CAS_App::STATUS_INACTIVE) {
358
- wp_original_referer_field(true, 'previous');
359
- }
360
- echo $form_extra;
361
-
362
- $nav_tabs = [
363
- 'conditions' => __('Conditions', 'content-aware-sidebars'),
364
- 'action' => __('Action', 'content-aware-sidebars'),
365
- 'design' => __('Design', 'content-aware-sidebars'),
366
- 'schedule' => __('Schedule', 'content-aware-sidebars'),
367
- 'advanced' => __('Options', 'content-aware-sidebars')
368
- ];
369
- $nav_tabs = apply_filters('cas/admin/nav-tabs', $nav_tabs);
370
-
371
- echo '<div id="poststuff">';
372
- echo '<div id="post-body" class="cas-metabox-holder metabox-holder columns-2">';
373
- echo '<div id="post-body-content">';
374
- echo '<div id="titlediv">';
375
- echo '<div id="titlewrap">';
376
- echo '<label class="screen-reader-text" id="title-prompt-text" for="title">'.__('Enter title here').'</label>';
377
- echo '<input type="text" name="post_title" size="30" value="'.esc_attr($post->post_title).'" id="title" spellcheck="true" autocomplete="off" />';
378
- echo '</div></div>';
379
- $this->render_section_nav($nav_tabs);
380
- echo '</div>';
381
- $this->render_sections($nav_tabs, $post, $post->post_type);
382
- echo '</div>';
383
- echo '<br class="clear" />';
384
- echo '</div></form></div>';
385
- }
386
-
387
- /**
388
- * Render tab navigation
389
- *
390
- * @since 3.4
391
- * @param array $tabs
392
- * @return void
393
- */
394
- public function render_section_nav($tabs)
395
- {
396
- echo '<h2 class="nav-tab-wrapper js-cas-tabs hide-if-no-js " style="padding-bottom:0;">';
397
- foreach ($tabs as $id => $label) {
398
- echo '<a class="js-nav-link nav-tab nav-tab-section-'.$id.'" href="#top#section-'.$id.'">'.$label.'</a>';
399
- }
400
- echo '</h2>';
401
- }
402
-
403
- /**
404
- * Render meta box sections
405
- *
406
- * @since 3.4
407
- * @param array $tabs
408
- * @param WP_Post $post
409
- * @param string $post_type
410
- * @return void
411
- */
412
- public function render_sections($tabs, $post, $post_type)
413
- {
414
- echo '<div id="postbox-container-1" class="postbox-container">';
415
- do_meta_boxes(CAS_App::BASE_SCREEN.'-edit', 'side', $post);
416
- echo '</div>';
417
- echo '<div id="postbox-container-2" class="postbox-container">';
418
- foreach ($tabs as $id => $label) {
419
- $name = 'section-'.$id;
420
- echo '<div id="'.$name.'" class="cas-section">';
421
- do_meta_boxes(CAS_App::BASE_SCREEN.'-edit', $name, $post);
422
- echo '</div>';
423
- }
424
- //boxes across sections
425
- do_meta_boxes(CAS_App::BASE_SCREEN.'-edit', 'normal', $post);
426
- echo '</div>';
427
- }
428
-
429
- /**
430
- * Update sidebar post type
431
- *
432
- * @since 3.4
433
- * @return int
434
- */
435
- public function update_sidebar_type()
436
- {
437
- global $wpdb;
438
-
439
- $post_ID = (int) $_POST['sidebar_id'];
440
- $post = get_post($post_ID);
441
- $post_data['post_type'] = CAS_App::TYPE_SIDEBAR;
442
- $post_data['ID'] = (int) $post_ID;
443
- $post_data['post_title'] = $_POST['post_title'];
444
- $post_data['comment_status'] = 'closed';
445
- $post_data['ping_status'] = 'closed';
446
- $post_data['post_author'] = get_current_user_id();
447
- $post_data['menu_order'] = intval($_POST['menu_order']);
448
-
449
- $ptype = get_post_type_object($post_data['post_type']);
450
-
451
- if (!current_user_can('edit_post', $post_ID)) {
452
- wp_die(__('You are not allowed to edit this sidebar.', 'content-aware-sidebars'));
453
- } elseif (! current_user_can($ptype->cap->create_posts)) {
454
- return new WP_Error('edit_others_posts', __('You are not allowed to create sidebars.', 'content-aware-sidebars'));
455
- } elseif ($post_data['post_author'] != $_POST['post_author']
456
- && ! current_user_can($ptype->cap->edit_others_posts)) {
457
- return new WP_Error('edit_others_posts', __('You are not allowed to edit this sidebar.', 'content-aware-sidebars'));
458
- }
459
-
460
- if (isset($_POST['post_status'])) {
461
- $post_data['post_status'] = CAS_App::STATUS_ACTIVE;
462
- //if sidebar has been future before, we need to reset date
463
- if ($_POST['post_status'] != $_POST['original_post_status']) {
464
- $post_data['post_date'] = current_time('mysql');
465
- }
466
- } elseif ($_POST['sidebar_activate']) {
467
- $_POST['post_status'] = CAS_App::STATUS_SCHEDULED; //yoast seo expects this
468
- $post_data['post_status'] = CAS_App::STATUS_SCHEDULED;
469
- $post_data['post_date'] = $_POST['sidebar_activate'];
470
- } else {
471
- $_POST['post_status'] = CAS_App::STATUS_INACTIVE;
472
- $post_data['post_status'] = CAS_App::STATUS_INACTIVE;
473
- }
474
-
475
- if ($post_data['post_status'] != CAS_App::STATUS_INACTIVE
476
- && $_POST['sidebar_deactivate']) {
477
- $this->reschedule_deactivation($post_ID, $_POST['sidebar_deactivate']);
478
- } else {
479
- $this->reschedule_deactivation($post_ID);
480
- }
481
-
482
- if (isset($post_data['post_date'])) {
483
- $post_data['post_date_gmt'] = get_gmt_from_date($post_data['post_date']);
484
- }
485
-
486
- if (post_type_supports(CAS_App::TYPE_SIDEBAR, 'revisions')) {
487
- $revisions = wp_get_post_revisions($post_ID, [
488
- 'order' => 'ASC',
489
- 'posts_per_page' => 1
490
- ]);
491
- $revision = current($revisions);
492
- // Check if the revisions have been upgraded
493
- if ($revisions && _wp_get_post_revision_version($revision) < 1) {
494
- _wp_upgrade_revisions_of_post($post, wp_get_post_revisions($post_ID));
495
- }
496
- }
497
-
498
- update_post_meta($post_ID, '_edit_last', $post_data['post_author']);
499
- wp_update_post($post_data);
500
- wp_set_post_lock($post_ID);
501
-
502
- return $post_ID;
503
- }
504
-
505
- /**
506
- * Handle schedule for deactivation
507
- *
508
- * @since 3.4
509
- * @param int $post_id
510
- * @param string $time
511
- * @return void
512
- */
513
- public function reschedule_deactivation($post_id, $time = false)
514
- {
515
- $name = 'cas/event/deactivate';
516
- if (wp_next_scheduled($name, [$post_id]) !== false) {
517
- wp_clear_scheduled_hook($name, [$post_id]);
518
- }
519
-
520
- if ($time) {
521
- //Requires to be in GMT
522
- $utime = get_gmt_from_date($time, 'U');
523
- wp_schedule_single_event($utime, $name, [$post_id]);
524
- update_post_meta($post_id, CAS_App::META_PREFIX.'deactivate_time', $time);
525
- } else {
526
- delete_post_meta($post_id, CAS_App::META_PREFIX.'deactivate_time');
527
- }
528
- }
529
-
530
- /**
531
- * Create update messages
532
- *
533
- * @param WP_Post $post
534
- *
535
- * @return void
536
- */
537
- public function sidebar_updated_messages($post)
538
- {
539
- $message_number = isset($_GET['message']) ? absint($_GET['message']) : null;
540
-
541
- if (is_null($message_number)) {
542
- return;
543
- }
544
-
545
- $manage_widgets = sprintf(' <a href="%1$s">%2$s</a>', esc_url(admin_url('widgets.php#'.CAS_App::SIDEBAR_PREFIX.$post->ID)), __('Manage widgets', 'content-aware-sidebars'));
546
- $messages = [
547
- 1 => __('Sidebar updated.', 'content-aware-sidebars').$manage_widgets,
548
- 6 => __('Sidebar activated.', 'content-aware-sidebars').$manage_widgets,
549
- 9 => sprintf(
550
- __('Sidebar scheduled for: <strong>%1$s</strong>.', 'content-aware-sidebars'),
551
- // translators: Publish box date format, see http://php.net/date
552
- date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date))
553
- ).$manage_widgets,
554
- 10 => __('Sidebar deactivated.', 'content-aware-sidebars').$manage_widgets,
555
- ];
556
- $messages = apply_filters('cas/admin/messages', $messages, $post);
557
-
558
- if (isset($messages[$message_number])) {
559
- echo '<div id="message" class="updated notice notice-success is-dismissible"><p>'.$messages[$message_number].'</p></div>';
560
- }
561
- }
562
-
563
- /**
564
- * Set pointers for tour and enqueue script
565
- *
566
- * @since 3.3
567
- * @return void
568
- */
569
- private function create_pointers()
570
- {
571
- if ($this->_tour_manager->user_has_finished_tour()) {
572
- return;
573
- }
574
-
575
- $this->_tour_manager->set_pointers([
576
- [
577
- 'content' => sprintf(
578
- '<h3>%s</h3>%s',
579
- __('Get Started in 60 Seconds', 'content-aware-sidebars'),
580
- '<p>'.__('Welcome to Content Aware Sidebars!', 'content-aware-sidebars').'</p>'.
581
- '<p>'.__('This interactive guide will show you just how easy it is to create a widget area and control where, how, and when to display it.', 'content-aware-sidebars').'</p>'
582
- ),
583
- 'ref_id' => '#titlediv',
584
- 'position' => [
585
- 'edge' => 'top',
586
- 'align' => 'center'
587
- ],
588
- 'pointerWidth' => 400,
589
- 'next' => __('Start Quick Tour', 'content-aware-sidebars'),
590
- 'dismiss' => __('Skip - I know what to do', 'content-aware-sidebars')
591
- ],
592
- [
593
- 'content' => sprintf(
594
- '<h3>%s</h3>%s',
595
- '1/5 '.__('Where to display', 'content-aware-sidebars'),
596
- '<p>'.__('Choose from the extensive Display Conditions with built-in support for other plugins. You will never be asked to enter widget logic PHP code!', 'content-aware-sidebars').'</p>'.
597
- '<p>'.__('Select anything to continue the tour. You can change it later.', 'content-aware-sidebars').'</p>'
598
- ),
599
- 'ref_id' => '.cas-group-new',
600
- 'position' => [
601
- 'edge' => 'top',
602
- 'align' => 'center'
603
- ],
604
- 'prev' => false,
605
- 'next' => '.js-wpca-add-or, .js-wpca-add-quick',
606
- 'nextEvent' => 'select2:select click',
607
- 'dismiss' => false
608
- ],
609
- [
610
- 'content' => sprintf(
611
- '<h3>%s</h3>%s',
612
- '2/5 '.__('Where to display', 'content-aware-sidebars'),
613
- '<p>'.__('Click on the input field and select the content you want - just type to search. Changes are saved automatically!', 'content-aware-sidebars').'</p>'.
614
- '<p>'.__('You can add multiple content types to the same group. Try e.g. "All Posts" and an Author to display on all posts written by that author.', 'content-aware-sidebars').'</p>'.
615
- '<p>'.sprintf('<a href="%s" target="_blank" rel="noopener">'.__('Learn more about AND vs OR conditions', 'content-aware-sidebars').'</a>', 'https://dev.institute/docs/content-aware-sidebars/getting-started/display-sidebar-advanced/').'</p>'
616
- ),
617
- 'ref_id' => '#cas-groups > ul',
618
- 'position' => [
619
- 'edge' => 'top',
620
- 'align' => 'center'
621
- ],
622
- 'dismiss' => __('Close Tour', 'content-aware-sidebars')
623
- ],
624
- [
625
- 'content' => sprintf(
626
- '<h3>%s</h3>%s',
627
- '3/5 '.__('How to display', 'content-aware-sidebars'),
628
- '<p>'.__('Replace any sidebar or widget area in your theme, or add widgets by merging with them.', 'content-aware-sidebars').'</p>'.
629
- '<p>'.__('You can also use the shortcode to display widgets inside a page or post.', 'content-aware-sidebars').'</p>'
630
- ),
631
- 'ref_id' => '.nav-tab-wrapper.js-cas-tabs .nav-tab-section-action',
632
- 'position' => [
633
- 'edge' => 'left',
634
- 'align' => 'left'
635
- ],
636
- 'dismiss' => __('Close Tour', 'content-aware-sidebars')
637
- ],
638
- [
639
- 'content' => sprintf(
640
- '<h3>%s</h3>%s',
641
- '4/5 '.__('When to activate', 'content-aware-sidebars'),
642
- '<p>'.__('Create a widget area and manage its widgets today, then publish it when you are ready.', 'content-aware-sidebars').'</p>'.
643
- '<p>'.__('To schedule automatic activation or deactivation, just pick a date and time!', 'content-aware-sidebars').'</p>'.
644
- '<p>'.__('By default, new widget areas will be activated when created.', 'content-aware-sidebars').'</p>'
645
- ),
646
- 'ref_id' => '.nav-tab-wrapper.js-cas-tabs .nav-tab-section-schedule',
647
- 'position' => [
648
- 'edge' => 'left',
649
- 'align' => 'left'
650
- ],
651
- 'dismiss' => __('Close Tour', 'content-aware-sidebars')
652
- ],
653
- [
654
- 'content' => sprintf(
655
- '<h3>%s</h3>%s',
656
- '5/5 '.__('How to look', 'content-aware-sidebars'),
657
- '<p>'.__('Personalize the styling without writing any code!', 'content-aware-sidebars').'</p>'.
658
- '<p>'.__('You can modify the HTML and CSS classes of the widget area itself, each widget, as well as widget titles.', 'content-aware-sidebars').'</p>'
659
- ),
660
- 'ref_id' => '.nav-tab-wrapper.js-cas-tabs .nav-tab-section-design',
661
- 'position' => [
662
- 'edge' => 'left',
663
- 'align' => 'left'
664
- ],
665
- 'next' => __('Finish Tour', 'content-aware-sidebars')
666
- ],
667
- [
668
- 'content' => sprintf(
669
- '<h3>%s</h3>%s',
670
- __("That's it", 'content-aware-sidebars'),
671
- '<p>'.__('Hit the Create button to save your first custom widget area.', 'content-aware-sidebars').'</p>'.
672
- '<p>'.__('If you need more help, check out the links below.', 'content-aware-sidebars').'</p>'
673
- ),
674
- 'ref_id' => '#submitdiv',
675
- 'position' => [
676
- 'edge' => 'right',
677
- 'align' => 'top'
678
- ],
679
- 'dismiss' => __('Close', 'content-aware-sidebars')
680
- ]
681
- ]);
682
- $this->_tour_manager->enqueue_scripts();
683
- }
684
-
685
- /**
686
- * Meta boxes for sidebar edit
687
- * @global object $post
688
- * @return void
689
- */
690
- public function create_meta_boxes($post)
691
- {
692
- $this->create_pointers();
693
- CAS_App::instance()->manager()->populate_metadata();
694
- $path = plugin_dir_path(dirname(__FILE__)).'view/';
695
-
696
- $boxes = [];
697
- $boxes[] = [
698
- 'id' => 'submitdiv',
699
- 'title' => __('Publish'),
700
- 'view' => 'submit',
701
- 'context' => 'side',
702
- 'priority' => 'high'
703
- ];
704
- $boxes[] = [
705
- 'id' => 'cas-options',
706
- 'title' => __('How to display', 'content-aware-sidebars'),
707
- 'view' => 'action',
708
- 'context' => 'section-action',
709
- ];
710
- $boxes[] = [
711
- 'id' => 'cas-status',
712
- 'title' => __('Status', 'content-aware-sidebars'),
713
- 'view' => 'status',
714
- 'context' => 'section-schedule',
715
- ];
716
- $boxes[] = [
717
- 'id' => 'cas-widget-html',
718
- 'title' => __('Styles', 'content-aware-sidebars'),
719
- 'view' => 'html',
720
- 'context' => 'section-design',
721
- ];
722
- $boxes[] = [
723
- 'id' => 'cas-advanced',
724
- 'title' => __('Options', 'content-aware-sidebars'),
725
- 'view' => 'advanced',
726
- 'context' => 'section-advanced',
727
- ];
728
- $boxes[] = [
729
- 'id' => 'cas-plugin-links',
730
- 'title' => __('Recommendations', 'content-aware-sidebars'),
731
- 'view' => 'support',
732
- 'context' => 'side',
733
- ];
734
- $boxes[] = [
735
- 'id' => 'cas-schedule',
736
- 'title' => __('Time Schedule', 'content-aware-sidebars').' <span class="cas-pro-label">'.__('Pro', 'content-aware-sidebars').'</span>',
737
- 'view' => 'schedule',
738
- 'context' => 'section-schedule',
739
- ];
740
- $boxes[] = [
741
- 'id' => 'cas-design',
742
- 'title' => __('Design', 'content-aware-sidebars').' <span class="cas-pro-label">'.__('Pro', 'content-aware-sidebars').'</span>',
743
- 'view' => 'design',
744
- 'context' => 'section-design',
745
- ];
746
-
747
- foreach ($boxes as $box) {
748
- $view = WPCAView::make($path.'meta_box_'.$box['view'].'.php', [
749
- 'post' => $post
750
- ]);
751
-
752
- add_meta_box(
753
- $box['id'],
754
- $box['title'],
755
- [$view,'render'],
756
- CAS_App::BASE_SCREEN.'-edit',
757
- $box['context'],
758
- isset($box['priority']) ? $box['priority'] : 'default'
759
- );
760
- }
761
-
762
- //todo: refactor add of meta box
763
- //with new bootstrapper, legacy core might be loaded
764
- if (method_exists('WPCACore', 'render_group_meta_box')) {
765
- WPCACore::render_group_meta_box($post, CAS_App::BASE_SCREEN.'-edit', 'section-conditions', 'default');
766
- }
767
- }
768
-
769
- /**
770
- * Create form field for metadata
771
- * @global object $post
772
- * @param array $setting
773
- * @return void
774
- */
775
- public static function form_field($id, $class = '', $icon = '')
776
- {
777
- $setting = CAS_App::instance()->manager()->metadata()->get($id);
778
- $current = $setting->get_data(get_the_ID(), true, $setting->get_input_type() != 'multi');
779
- $icon = $icon ? '<span class="'.$icon.'"></span> ' : '';
780
-
781
- echo '<div class="'.$class.'">'.$icon.'<strong>' . $setting->get_title() . '</strong>';
782
- echo '<p>';
783
- switch ($setting->get_input_type()) {
784
- case 'select':
785
- echo '<select style="width:250px;" name="' . $id . '" class="js-cas-'.$id.'">' . "\n";
786
- foreach ($setting->get_input_list() as $key => $value) {
787
- $disabled = '';
788
- if (is_string($key) && strpos($key, '__') === 0) {
789
- $disabled = ' disabled="disabled"';
790
- }
791
- echo '<option value="' . $key . '"' . selected($current, $key, false) . $disabled . '>' . $value . '</option>' . "\n";
792
- }
793
- echo '</select>' . "\n";
794
- break;
795
- case 'checkbox':
796
- echo '<ul>' . "\n";
797
- foreach ($setting->get_input_list() as $key => $value) {
798
- echo '<li><label><input type="checkbox" name="' . $id . '[]" class="js-cas-'.$id.'" value="' . $key . '"' . (in_array($key, $current) ? ' checked="checked"' : '') . ' /> ' . $value . '</label></li>' . "\n";
799
- }
800
- echo '</ul>' . "\n";
801
- break;
802
- case 'multi':
803
- echo '<div><select style="width:100%;" class="js-cas-'.$id.'" multiple="multiple" name="' . $id . '[]" data-value="'.implode(',', $current).'"></select></div>';
804
- break;
805
- case 'text':
806
- default:
807
- echo '<input style="width:200px;" type="text" name="' . $id . '" value="' . $current . '" />' . "\n";
808
- break;
809
- }
810
- echo '</p></div>';
811
- }
812
-
813
- /**
814
- * Save meta values for post
815
- * @param int $post_id
816
- * @return void
817
- */
818
- public function save_post($post_id, $post)
819
- {
820
-
821
- //Verify nonce, check_admin_referer dies on false
822
- if (!(isset($_POST[WPCACore::NONCE])
823
- && wp_verify_nonce($_POST[WPCACore::NONCE], WPCACore::PREFIX.$post_id))) {
824
- return;
825
- }
826
-
827
- // Check permissions
828
- if (!current_user_can(CAS_App::CAPABILITY, $post_id)) {
829
- return;
830
- }
831
-
832
- // Check autosave
833
- if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
834
- return;
835
- }
836
-
837
- // Save metadata
838
- // todo: wrap this in metadata manager?
839
- foreach (CAS_App::instance()->manager()->metadata() as $field) {
840
- $field->save($post_id);
841
- }
842
- }
843
-
844
- /**
845
- * Add suffix when creating sidebar with existing name
846
- * Does not stop duplicate titles on update
847
- *
848
- * @since 3.4.3
849
- * @param array $insert_data
850
- * @param array $data
851
- * @return array
852
- */
853
- public function add_duplicate_title_suffix($insert_data, $data)
854
- {
855
- if ($data['post_type'] == CAS_App::TYPE_SIDEBAR && !$data['ID']) {
856
- $sidebars = CAS_App::instance()->manager()->sidebars;
857
- $sidebar_titles = [];
858
- foreach ($sidebars as $sidebar) {
859
- $sidebar_titles[$sidebar->post_title] = 1;
860
- }
861
- //if title exists, add a suffix
862
- $i = 0;
863
- $title = wp_unslash($insert_data['post_title']);
864
- $new_title = $title;
865
- while (isset($sidebar_titles[$new_title])) {
866
- $new_title = $title.' ('.++$i.')';
867
- }
868
- if ($i) {
869
- $insert_data['post_title'] = wp_slash($new_title);
870
- }
871
- }
872
- return $insert_data;
873
- }
874
-
875
- /**
876
- * Remove widget when its sidebar is removed
877
- * @param int $post_id
878
- * @return void
879
- */
880
- public function remove_sidebar_widgets($post_id)
881
- {
882
-
883
- // Authenticate and only continue on sidebar post type
884
- if (!current_user_can(CAS_App::CAPABILITY) || get_post_type($post_id) != CAS_App::TYPE_SIDEBAR) {
885
- return;
886
- }
887
-
888
- $id = CAS_App::SIDEBAR_PREFIX . $post_id;
889
-
890
- //Get widgets
891
- $sidebars_widgets = wp_get_sidebars_widgets();
892
-
893
- // Check if sidebar exists in database
894
- if (!isset($sidebars_widgets[$id])) {
895
- return;
896
- }
897
-
898
- // Remove widgets settings from sidebar
899
- foreach ($sidebars_widgets[$id] as $widget_id) {
900
- $widget_type = preg_replace('/-[0-9]+$/', '', $widget_id);
901
- $widget_settings = get_option('widget_' . $widget_type);
902
- $widget_id = substr($widget_id, strpos($widget_id, '-') + 1);
903
- if ($widget_settings && isset($widget_settings[$widget_id])) {
904
- unset($widget_settings[$widget_id]);
905
- update_option('widget_' . $widget_type, $widget_settings);
906
- }
907
- }
908
-
909
- // Remove sidebar
910
- unset($sidebars_widgets[$id]);
911
- wp_set_sidebars_widgets($sidebars_widgets);
912
- }
913
-
914
- /**
915
- * Register and enqueue scripts styles
916
- * for screen
917
- *
918
- * @since 3.4
919
- */
920
- public function add_scripts_styles()
921
- {
922
- if (is_multisite()) {
923
- add_action('admin_footer', '_admin_notice_post_locked');
924
- } else {
925
- $check_users = get_users([ 'fields' => 'ID', 'number' => 2 ]);
926
- if (count($check_users) > 1) {
927
- add_action('admin_footer', '_admin_notice_post_locked');
928
- }
929
- }
930
-
931
- wp_enqueue_script('wp-a11y');
932
-
933
- if (wp_is_mobile()) {
934
- wp_enqueue_script('jquery-touch-punch');
935
- }
936
-
937
- WPCACore::enqueue_scripts_styles('');
938
-
939
- $this->register_script('flatpickr', 'flatpickr', [], '3.0.6');
940
- $this->register_script('cas/admin/edit', 'cas_admin', ['jquery','flatpickr','wp-color-picker']);
941
-
942
- $this->enqueue_style('flatpickr', 'flatpickr.dark.min', [], '3.0.6');
943
- wp_enqueue_style('wp-color-picker');
944
-
945
- $metadata = CAS_App::instance()->manager()->metadata();
946
- $visibility = [];
947
- $target = [];
948
- foreach ($metadata->get('visibility')->get_input_list() as $category_key => $category) {
949
-
950
- //legacy format
951
- if (!is_array($category)) {
952
- $visibility[] = [
953
- 'id' => $category_key,
954
- 'text' => $category
955
- ];
956
- continue;
957
- }
958
-
959
- $data = [
960
- 'text' => $category['label'],
961
- 'children' => []
962
- ];
963
- foreach ($category['options'] as $key => $value) {
964
- $data['children'][] = [
965
- 'id' => $key,
966
- 'text' => $value
967
- ];
968
- }
969
- $visibility[] = $data;
970
- }
971
- foreach ($metadata->get('host')->get_input_list() as $value => $label) {
972
- $target[] = [
973
- 'id' => $value,
974
- 'text' => $label
975
- ];
976
- }
977
-
978
- if (!cas_fs()->can_use_premium_code()) {
979
- $visibility[] = [
980
- 'text' => __('Upgrade to Pro for more options', 'content-aware-sidebars'),
981
- 'children' => []
982
- ];
983
- }
984
-
985
- global $wp_locale;
986
-
987
- wp_enqueue_script('cas/admin/edit');
988
- wp_localize_script('cas/admin/edit', 'CASAdmin', [
989
- 'allVisibility' => __('All Users', 'content-aware-sidebars'),
990
- 'visibility' => $visibility,
991
- 'target' => $target,
992
- 'weekdays' => [
993
- 'shorthand' => array_values($wp_locale->weekday_abbrev),
994
- 'longhand' => array_values($wp_locale->weekday)
995
- ],
996
- 'months' => [
997
- 'shorthand' => array_values($wp_locale->month_abbrev),
998
- 'longhand' => array_values($wp_locale->month)
999
- ],
1000
- 'weekStart' => get_option('start_of_week', 0),
1001
- 'timeFormat' => get_option('time_format'),
1002
- 'dateFormat' => __('F j, Y') //default long date
1003
- ]);
1004
-
1005
- //badgeos compat
1006
- //todo: check that developers respond with a fix soon
1007
- wp_register_script('badgeos-select2', '');
1008
- wp_register_style('badgeos-select2-css', '');
1009
- }
1010
- }
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ final class CAS_Sidebar_Edit extends CAS_Admin
12
+ {
13
+ /**
14
+ * Intro tour manager
15
+ * @var WP_Pointer_Tour
16
+ */
17
+ private $_tour_manager;
18
+
19
+ /**
20
+ * Add filters and actions for admin dashboard
21
+ * e.g. AJAX calls
22
+ *
23
+ * @since 3.5
24
+ * @return void
25
+ */
26
+ public function admin_hooks()
27
+ {
28
+ $this->_tour_manager = new WP_Pointer_Tour(CAS_App::META_PREFIX . 'cas_tour');
29
+
30
+ $this->add_action('delete_post', 'remove_sidebar_widgets');
31
+ $this->add_action('save_post_' . CAS_App::TYPE_SIDEBAR, 'save_post', 10, 2);
32
+
33
+ $this->add_filter('wp_insert_post_data', 'add_duplicate_title_suffix', 99, 2);
34
+
35
+ if (!cas_fs()->can_use_premium_code()) {
36
+ $this->add_action('wpca/modules/init', 'add_modules');
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Set up admin menu and get current screen
42
+ *
43
+ * @since 3.4
44
+ * @return string
45
+ */
46
+ public function get_screen()
47
+ {
48
+ $post_type_object = $this->get_sidebar_type();
49
+ return add_submenu_page(
50
+ CAS_App::BASE_SCREEN,
51
+ $post_type_object->labels->add_new_item,
52
+ $post_type_object->labels->add_new,
53
+ $post_type_object->cap->edit_posts,
54
+ CAS_App::BASE_SCREEN . '-edit',
55
+ [$this,'render_screen']
56
+ );
57
+ }
58
+
59
+ /**
60
+ * @since 3.5
61
+ *
62
+ * @return bool
63
+ */
64
+ public function authorize_user()
65
+ {
66
+ return true;
67
+ }
68
+
69
+ /**
70
+ * @since 3.4
71
+ *
72
+ * @return void
73
+ */
74
+ public function prepare_screen()
75
+ {
76
+ $this->add_action('cas/admin/add_meta_boxes', 'create_meta_boxes');
77
+
78
+ global $post, $title, $active_post_lock;
79
+
80
+ $post_type = CAS_App::TYPE_SIDEBAR;
81
+ $post_type_object = $this->get_sidebar_type();
82
+ $post_id = isset($_REQUEST['sidebar_id']) ? $_REQUEST['sidebar_id'] : 0;
83
+
84
+ /**
85
+ * Edit mode
86
+ */
87
+ if ($post_id) {
88
+ $this->process_actions($post_id);
89
+
90
+ $post = get_post($post_id, OBJECT, 'edit');
91
+
92
+ if (!$post) {
93
+ wp_die(__('The sidebar no longer exists.'));
94
+ }
95
+ if (!current_user_can($post_type_object->cap->edit_post, $post_id)) {
96
+ wp_die(__('You are not allowed to edit this sidebar.'));
97
+ }
98
+ if ('trash' == $post->post_status) {
99
+ wp_die(__('You cannot edit this sidebar because it is in the Trash. Please restore it and try again.'));
100
+ }
101
+
102
+ if (!empty($_GET['get-post-lock'])) {
103
+ check_admin_referer('lock-post_' . $post_id);
104
+ wp_set_post_lock($post_id);
105
+ wp_redirect(get_edit_post_link($post_id, 'url'));
106
+ exit();
107
+ }
108
+
109
+ if (!wp_check_post_lock($post->ID)) {
110
+ $active_post_lock = wp_set_post_lock($post->ID);
111
+ }
112
+
113
+ $title = $post_type_object->labels->edit_item;
114
+
115
+ /**
116
+ * New Mode
117
+ */
118
+ } else {
119
+ if (!(current_user_can($post_type_object->cap->edit_posts) || current_user_can($post_type_object->cap->create_posts))) {
120
+ wp_die(
121
+ '<p>' . __('You are not allowed to create sidebars.', 'content-aware-sidebars') . '</p>',
122
+ 403
123
+ );
124
+ }
125
+
126
+ $post = get_default_post_to_edit($post_type, true);
127
+
128
+ $title = $post_type_object->labels->add_new_item;
129
+ }
130
+
131
+ do_action('cas/admin/add_meta_boxes', $post);
132
+ }
133
+
134
+ /**
135
+ * @since 3.9
136
+ * @param WPCATypeManager $types
137
+ *
138
+ * @return void
139
+ */
140
+ public function add_modules($types)
141
+ {
142
+ if (!$types->has(CAS_App::TYPE_SIDEBAR)) {
143
+ return;
144
+ }
145
+
146
+ $pro_label = '(Pro)';
147
+ $type = $types->get(CAS_App::TYPE_SIDEBAR);
148
+ $path = plugin_dir_path(dirname(__FILE__));
149
+
150
+ require $path . 'conditions/placeholder.php';
151
+
152
+ if (!WPCACore::get_option(CAS_App::TYPE_SIDEBAR, 'legacy.date_module', false)) {
153
+ $module = new CASConditionPlaceholder('cas_date', __('Dates', 'content-aware-sidebars') . ' ' . $pro_label);
154
+ $type->add($module, 'cas_date');
155
+ }
156
+
157
+ $module = new CASConditionPlaceholder('cas_url', __('URLs', 'content-aware-sidebars') . ' ' . $pro_label);
158
+ $type->add($module, 'cas_url');
159
+ $module = new CASConditionPlaceholder('cas_ref_url', __('Referrer URLs', 'content-aware-sidebars') . ' ' . $pro_label);
160
+ $type->add($module, 'cas_ref_url');
161
+
162
+ if (function_exists('bp_is_active')) {
163
+ $module = new CASConditionPlaceholder('cas_bbp', __('BuddyPress Groups', 'content-aware-sidebars') . ' ' . $pro_label, '', '', 'plugins');
164
+ $type->add($module, 'cas_bbp');
165
+ }
166
+
167
+ if (defined('ACF')) {
168
+ $module = new CASConditionPlaceholder('cas_acf', __('Advanced Custom Fields', 'content-aware-sidebars') . ' ' . $pro_label, '', '', 'plugins');
169
+ $type->add($module, 'cas_acf');
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Process actions
175
+ *
176
+ * @since 3.4
177
+ * @param int $post_id
178
+ * @return void
179
+ */
180
+ public function process_actions($post_id)
181
+ {
182
+ $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
183
+ if (isset($_POST['deletepost'])) {
184
+ $action = 'delete';
185
+ }
186
+
187
+ if ($action && $post_id) {
188
+ $sendback = wp_get_referer();
189
+ $sendback = remove_query_arg(
190
+ ['action','trashed', 'untrashed', 'deleted', 'ids'],
191
+ $sendback
192
+ );
193
+ if (isset($_REQUEST['_cas_section']) && $_REQUEST['_cas_section']) {
194
+ $sendback .= $_REQUEST['_cas_section'];
195
+ }
196
+
197
+ $post = get_post($post_id);
198
+ if (!$post) {
199
+ wp_die(__('The sidebar no longer exists.', 'content-aware-sidebars'));
200
+ }
201
+
202
+ check_admin_referer($action . '-post_' . $post_id);
203
+
204
+ switch ($action) {
205
+ case 'update':
206
+
207
+ $post_id = $this->update_sidebar_type();
208
+
209
+ // Session cookie flag that the post was saved
210
+ if (isset($_COOKIE['wp-saving-post']) && $_COOKIE['wp-saving-post'] === $post_id . '-check') {
211
+ setcookie('wp-saving-post', $post_id . '-saved', time() + DAY_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl());
212
+ }
213
+
214
+ $status = get_post_status($post_id);
215
+ if (isset($_POST['original_post_status']) && $_POST['original_post_status'] == $status) {
216
+ $message = 1;
217
+ } else {
218
+ switch ($status) {
219
+ case CAS_App::STATUS_SCHEDULED:
220
+ //gets scheduled
221
+ $message = 9;
222
+ break;
223
+ case CAS_App::STATUS_INACTIVE:
224
+ //gets deactivated
225
+ $message = 10;
226
+ break;
227
+ case CAS_App::STATUS_ACTIVE:
228
+ //gets activated
229
+ $message = 6;
230
+ break;
231
+ default:
232
+ $message = 1;
233
+ }
234
+ }
235
+
236
+ $sendback = add_query_arg([
237
+ 'sidebar_id' => $post_id,
238
+ 'message' => $message,
239
+ 'page' => 'wpcas-edit'
240
+ ], $sendback);
241
+ wp_safe_redirect($sendback);
242
+ exit();
243
+ case 'trash':
244
+
245
+ if (!current_user_can('delete_post', $post_id)) {
246
+ wp_die(__('You are not allowed to move this sidebar to the Trash.', 'content-aware-sidebars'));
247
+ }
248
+
249
+ if ($user_id = wp_check_post_lock($post_id)) {
250
+ $user = get_userdata($user_id);
251
+ wp_die(sprintf(__('You cannot move this sidebar to the Trash. %s is currently editing.', 'content-aware-sidebars'), $user->display_name));
252
+ }
253
+
254
+ if (!wp_trash_post($post_id)) {
255
+ wp_die(__('Error in moving to Trash.'));
256
+ }
257
+
258
+ $sendback = remove_query_arg('sidebar_id', $sendback);
259
+
260
+ wp_safe_redirect(add_query_arg(
261
+ [
262
+ 'page' => 'wpcas',
263
+ 'trashed' => 1,
264
+ 'ids' => $post_id
265
+ ],
266
+ $sendback
267
+ ));
268
+ exit();
269
+ case 'untrash':
270
+
271
+ if (!current_user_can('delete_post', $post_id)) {
272
+ wp_die(__('You are not allowed to restore this sidebar from the Trash.', 'content-aware-sidebars'));
273
+ }
274
+
275
+ if (!wp_untrash_post($post_id)) {
276
+ wp_die(__('Error in restoring from Trash.'));
277
+ }
278
+
279
+ wp_safe_redirect(add_query_arg('untrashed', 1, $sendback));
280
+ exit();
281
+ case 'delete':
282
+
283
+ if (!current_user_can('delete_post', $post_id)) {
284
+ wp_die(__('You are not allowed to delete this sidebar.', 'content-aware-sidebars'));
285
+ }
286
+
287
+ if (!wp_delete_post($post_id, true)) {
288
+ wp_die(__('Error in deleting.'));
289
+ }
290
+
291
+ $sendback = remove_query_arg('sidebar_id', $sendback);
292
+ wp_safe_redirect(add_query_arg([
293
+ 'page' => 'wpcas',
294
+ 'deleted' => 1
295
+ ], $sendback));
296
+ exit();
297
+ default:
298
+ do_action('cas/admin/action', $action, $post);
299
+ break;
300
+ }
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Render screen
306
+ *
307
+ * @since 3.4
308
+ * @return void
309
+ */
310
+ public function render_screen()
311
+ {
312
+ global $post, $active_post_lock;
313
+
314
+ $post_type_object = get_post_type_object($post->post_type);
315
+ $post_id = isset($_REQUEST['sidebar_id']) ? $_REQUEST['sidebar_id'] : 0;
316
+
317
+ $form_extra = '';
318
+ if ('auto-draft' == $post->post_status) {
319
+ if (isset($_REQUEST['sidebar_id'])) {
320
+ $post->post_title = '';
321
+ }
322
+ $form_extra .= "<input type='hidden' id='auto_draft' name='auto_draft' value='1' />";
323
+ }
324
+
325
+ if ($post_id) {
326
+ $title = __('Edit');
327
+ } else {
328
+ $title = $post_type_object->labels->new_item;
329
+ }
330
+
331
+ echo '<div class="wrap">';
332
+ echo '<h1>';
333
+ echo '<a href="' . admin_url('admin.php?page=wpcas') . '">' . $post_type_object->labels->all_items . '</a> &raquo; ';
334
+ echo esc_html($title);
335
+ if (isset($_REQUEST['sidebar_id']) && current_user_can('edit_theme_options')) {
336
+ echo ' <a href="' . esc_url(admin_url('widgets.php#' . CAS_App::SIDEBAR_PREFIX . $post->ID)) . '" class="page-title-action add-new-h2">' . __('Manage Widgets', 'content-aware-sidebars') . '</a>';
337
+ }
338
+
339
+ echo '</h1>';
340
+
341
+ $this->sidebar_updated_messages($post);
342
+
343
+ echo '<form name="post" action="admin.php?page=wpcas-edit" method="post" id="post">';
344
+ $referer = wp_get_referer();
345
+ wp_nonce_field('update-post_' . $post->ID);
346
+ echo '<input type="hidden" id="user-id" name="user_ID" value="' . get_current_user_id() . '" />';
347
+ echo '<input type="hidden" id="_cas_section" name="_cas_section" value="" />';
348
+ echo '<input type="hidden" id="hiddenaction" name="action" value="update" />';
349
+ echo '<input type="hidden" id="post_author" name="post_author" value="' . esc_attr($post->post_author) . '" />';
350
+ echo '<input type="hidden" id="original_post_status" name="original_post_status" value="' . esc_attr($post->post_status) . '" />';
351
+ echo '<input type="hidden" id="referredby" name="referredby" value="' . ($referer ? esc_url($referer) : '') . '" />';
352
+ echo '<input type="hidden" id="post_ID" name="sidebar_id" value="' . esc_attr($post->ID) . '" />';
353
+ if (!empty($active_post_lock)) {
354
+ echo '<input type="hidden" id="active_post_lock" value="' . esc_attr(implode(':', $active_post_lock)) . '" />';
355
+ }
356
+ if (get_post_status($post) != CAS_App::STATUS_INACTIVE) {
357
+ wp_original_referer_field(true, 'previous');
358
+ }
359
+ echo $form_extra;
360
+
361
+ $nav_tabs = [
362
+ 'conditions' => __('Conditions', 'content-aware-sidebars'),
363
+ 'action' => __('Action', 'content-aware-sidebars'),
364
+ 'design' => __('Design', 'content-aware-sidebars'),
365
+ 'schedule' => __('Schedule', 'content-aware-sidebars'),
366
+ 'advanced' => __('Options', 'content-aware-sidebars')
367
+ ];
368
+ $nav_tabs = apply_filters('cas/admin/nav-tabs', $nav_tabs);
369
+
370
+ echo '<div id="poststuff">';
371
+ echo '<div id="post-body" class="cas-metabox-holder metabox-holder columns-2">';
372
+ echo '<div id="post-body-content">';
373
+ echo '<div id="titlediv">';
374
+ echo '<div id="titlewrap">';
375
+ echo '<label class="screen-reader-text" id="title-prompt-text" for="title">' . __('Enter title here') . '</label>';
376
+ echo '<input type="text" name="post_title" size="30" value="' . esc_attr($post->post_title) . '" id="title" spellcheck="true" autocomplete="off" />';
377
+ echo '</div></div>';
378
+ $this->render_section_nav($nav_tabs);
379
+ echo '</div>';
380
+ $this->render_sections($nav_tabs, $post, $post->post_type);
381
+ echo '</div>';
382
+ echo '<br class="clear" />';
383
+ echo '</div></form></div>';
384
+ }
385
+
386
+ /**
387
+ * Render tab navigation
388
+ *
389
+ * @since 3.4
390
+ * @param array $tabs
391
+ * @return void
392
+ */
393
+ public function render_section_nav($tabs)
394
+ {
395
+ echo '<h2 class="nav-tab-wrapper js-cas-tabs hide-if-no-js " style="padding-bottom:0;">';
396
+ foreach ($tabs as $id => $label) {
397
+ echo '<a class="js-nav-link nav-tab nav-tab-section-' . $id . '" href="#top#section-' . $id . '">' . $label . '</a>';
398
+ }
399
+ echo '</h2>';
400
+ }
401
+
402
+ /**
403
+ * Render meta box sections
404
+ *
405
+ * @since 3.4
406
+ * @param array $tabs
407
+ * @param WP_Post $post
408
+ * @param string $post_type
409
+ * @return void
410
+ */
411
+ public function render_sections($tabs, $post, $post_type)
412
+ {
413
+ echo '<div id="postbox-container-1" class="postbox-container">';
414
+ do_meta_boxes(CAS_App::BASE_SCREEN . '-edit', 'side', $post);
415
+ echo '</div>';
416
+ echo '<div id="postbox-container-2" class="postbox-container">';
417
+ foreach ($tabs as $id => $label) {
418
+ $name = 'section-' . $id;
419
+ echo '<div id="' . $name . '" class="cas-section">';
420
+ do_meta_boxes(CAS_App::BASE_SCREEN . '-edit', $name, $post);
421
+ echo '</div>';
422
+ }
423
+ //boxes across sections
424
+ do_meta_boxes(CAS_App::BASE_SCREEN . '-edit', 'normal', $post);
425
+ echo '</div>';
426
+ }
427
+
428
+ /**
429
+ * Update sidebar post type
430
+ *
431
+ * @since 3.4
432
+ * @return int|WP_Error
433
+ */
434
+ public function update_sidebar_type()
435
+ {
436
+ $post_ID = (int) $_POST['sidebar_id'];
437
+ $post = get_post($post_ID);
438
+ $post_data['post_type'] = CAS_App::TYPE_SIDEBAR;
439
+ $post_data['ID'] = $post_ID;
440
+ $post_data['post_title'] = $_POST['post_title'];
441
+ $post_data['comment_status'] = 'closed';
442
+ $post_data['ping_status'] = 'closed';
443
+ $post_data['post_author'] = get_current_user_id();
444
+ $post_data['menu_order'] = intval($_POST['menu_order']);
445
+
446
+ $ptype = get_post_type_object($post_data['post_type']);
447
+
448
+ if (!current_user_can('edit_post', $post_ID)) {
449
+ wp_die(__('You are not allowed to edit this sidebar.', 'content-aware-sidebars'));
450
+ } elseif (!current_user_can($ptype->cap->create_posts)) {
451
+ return new WP_Error('edit_others_posts', __('You are not allowed to create sidebars.', 'content-aware-sidebars'));
452
+ } elseif ($post_data['post_author'] != $_POST['post_author']
453
+ && !current_user_can($ptype->cap->edit_others_posts)) {
454
+ return new WP_Error('edit_others_posts', __('You are not allowed to edit this sidebar.', 'content-aware-sidebars'));
455
+ }
456
+
457
+ if (isset($_POST['post_status'])) {
458
+ $post_data['post_status'] = CAS_App::STATUS_ACTIVE;
459
+ //if sidebar has been future before, we need to reset date
460
+ if ($_POST['post_status'] != $_POST['original_post_status']) {
461
+ $post_data['post_date'] = current_time('mysql');
462
+ }
463
+ } elseif ($_POST['sidebar_activate']) {
464
+ $_POST['post_status'] = CAS_App::STATUS_SCHEDULED; //yoast seo expects this
465
+ $post_data['post_status'] = CAS_App::STATUS_SCHEDULED;
466
+ $post_data['post_date'] = $_POST['sidebar_activate'];
467
+ } else {
468
+ $_POST['post_status'] = CAS_App::STATUS_INACTIVE;
469
+ $post_data['post_status'] = CAS_App::STATUS_INACTIVE;
470
+ }
471
+
472
+ if ($post_data['post_status'] != CAS_App::STATUS_INACTIVE
473
+ && $_POST['sidebar_deactivate']) {
474
+ $this->reschedule_deactivation($post_ID, $_POST['sidebar_deactivate']);
475
+ } else {
476
+ $this->reschedule_deactivation($post_ID);
477
+ }
478
+
479
+ if (isset($post_data['post_date'])) {
480
+ $post_data['post_date_gmt'] = get_gmt_from_date($post_data['post_date']);
481
+ }
482
+
483
+ if (post_type_supports(CAS_App::TYPE_SIDEBAR, 'revisions')) {
484
+ $revisions = wp_get_post_revisions($post_ID, [
485
+ 'order' => 'ASC',
486
+ 'posts_per_page' => 1
487
+ ]);
488
+ $revision = current($revisions);
489
+ // Check if the revisions have been upgraded
490
+ if ($revisions && _wp_get_post_revision_version($revision) < 1) {
491
+ _wp_upgrade_revisions_of_post($post, wp_get_post_revisions($post_ID));
492
+ }
493
+ }
494
+
495
+ update_post_meta($post_ID, '_edit_last', $post_data['post_author']);
496
+ wp_update_post($post_data);
497
+ wp_set_post_lock($post_ID);
498
+
499
+ return $post_ID;
500
+ }
501
+
502
+ /**
503
+ * Handle schedule for deactivation
504
+ *
505
+ * @since 3.4
506
+ * @param int $post_id
507
+ * @param string $time
508
+ * @return void
509
+ */
510
+ public function reschedule_deactivation($post_id, $time = false)
511
+ {
512
+ $name = 'cas/event/deactivate';
513
+ if (wp_next_scheduled($name, [$post_id]) !== false) {
514
+ wp_clear_scheduled_hook($name, [$post_id]);
515
+ }
516
+
517
+ if ($time) {
518
+ //Requires to be in GMT
519
+ $utime = get_gmt_from_date($time, 'U');
520
+ wp_schedule_single_event($utime, $name, [$post_id]);
521
+ update_post_meta($post_id, CAS_App::META_PREFIX . 'deactivate_time', $time);
522
+ } else {
523
+ delete_post_meta($post_id, CAS_App::META_PREFIX . 'deactivate_time');
524
+ }
525
+ }
526
+
527
+ /**
528
+ * Create update messages
529
+ *
530
+ * @param WP_Post $post
531
+ *
532
+ * @return void
533
+ */
534
+ public function sidebar_updated_messages($post)
535
+ {
536
+ $message_number = isset($_GET['message']) ? absint($_GET['message']) : null;
537
+
538
+ if (is_null($message_number)) {
539
+ return;
540
+ }
541
+
542
+ $manage_widgets = sprintf(' <a href="%1$s">%2$s</a>', esc_url(admin_url('widgets.php#' . CAS_App::SIDEBAR_PREFIX . $post->ID)), __('Manage widgets', 'content-aware-sidebars'));
543
+ $messages = [
544
+ 1 => __('Sidebar updated.', 'content-aware-sidebars') . $manage_widgets,
545
+ 6 => __('Sidebar activated.', 'content-aware-sidebars') . $manage_widgets,
546
+ 9 => sprintf(
547
+ __('Sidebar scheduled for: <strong>%1$s</strong>.', 'content-aware-sidebars'),
548
+ // translators: Publish box date format, see http://php.net/date
549
+ date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date))
550
+ ) . $manage_widgets,
551
+ 10 => __('Sidebar deactivated.', 'content-aware-sidebars') . $manage_widgets,
552
+ ];
553
+ $messages = apply_filters('cas/admin/messages', $messages, $post);
554
+
555
+ if (isset($messages[$message_number])) {
556
+ echo '<div id="message" class="updated notice notice-success is-dismissible"><p>' . $messages[$message_number] . '</p></div>';
557
+ }
558
+ }
559
+
560
+ /**
561
+ * Set pointers for tour and enqueue script
562
+ *
563
+ * @since 3.3
564
+ * @return void
565
+ */
566
+ private function create_pointers()
567
+ {
568
+ if ($this->_tour_manager->user_has_finished_tour()) {
569
+ return;
570
+ }
571
+
572
+ $this->_tour_manager->set_pointers([
573
+ [
574
+ 'content' => sprintf(
575
+ '<h3>%s</h3>%s',
576
+ __('Get Started in 60 Seconds', 'content-aware-sidebars'),
577
+ '<p>' . __('Welcome to Content Aware Sidebars!', 'content-aware-sidebars') . '</p>' .
578
+ '<p>' . __('This interactive guide will show you just how easy it is to create a widget area and control where, how, and when to display it.', 'content-aware-sidebars') . '</p>'
579
+ ),
580
+ 'ref_id' => '#titlediv',
581
+ 'position' => [
582
+ 'edge' => 'top',
583
+ 'align' => 'center'
584
+ ],
585
+ 'pointerWidth' => 400,
586
+ 'next' => __('Start Quick Tour', 'content-aware-sidebars'),
587
+ 'dismiss' => __('Skip - I know what to do', 'content-aware-sidebars')
588
+ ],
589
+ [
590
+ 'content' => sprintf(
591
+ '<h3>%s</h3>%s',
592
+ '1/5 ' . __('Where to display', 'content-aware-sidebars'),
593
+ '<p>' . __('Choose from the extensive Display Conditions with built-in support for other plugins. You will never be asked to enter widget logic PHP code!', 'content-aware-sidebars') . '</p>' .
594
+ '<p>' . __('Select anything to continue the tour. You can change it later.', 'content-aware-sidebars') . '</p>'
595
+ ),
596
+ 'ref_id' => '.cas-group-new',
597
+ 'position' => [
598
+ 'edge' => 'top',
599
+ 'align' => 'center'
600
+ ],
601
+ 'prev' => false,
602
+ 'next' => '.js-wpca-add-or, .js-wpca-add-quick',
603
+ 'nextEvent' => 'select2:select click',
604
+ 'dismiss' => false
605
+ ],
606
+ [
607
+ 'content' => sprintf(
608
+ '<h3>%s</h3>%s',
609
+ '2/5 ' . __('Where to display', 'content-aware-sidebars'),
610
+ '<p>' . __('Click on the input field and select the content you want - just type to search. Changes are saved automatically!', 'content-aware-sidebars') . '</p>' .
611
+ '<p>' . __('You can add multiple content types to the same group. Try e.g. "All Posts" and an Author to display on all posts written by that author.', 'content-aware-sidebars') . '</p>' .
612
+ '<p>' . sprintf('<a href="%s" target="_blank" rel="noopener">' . __('Learn more about AND vs OR conditions', 'content-aware-sidebars') . '</a>', 'https://dev.institute/docs/content-aware-sidebars/getting-started/display-sidebar-advanced/') . '</p>'
613
+ ),
614
+ 'ref_id' => '#cas-groups > ul',
615
+ 'position' => [
616
+ 'edge' => 'top',
617
+ 'align' => 'center'
618
+ ],
619
+ 'dismiss' => __('Close Tour', 'content-aware-sidebars')
620
+ ],
621
+ [
622
+ 'content' => sprintf(
623
+ '<h3>%s</h3>%s',
624
+ '3/5 ' . __('How to display', 'content-aware-sidebars'),
625
+ '<p>' . __('Replace any sidebar or widget area in your theme, or add widgets by merging with them.', 'content-aware-sidebars') . '</p>' .
626
+ '<p>' . __('You can also use the shortcode to display widgets inside a page or post.', 'content-aware-sidebars') . '</p>'
627
+ ),
628
+ 'ref_id' => '.nav-tab-wrapper.js-cas-tabs .nav-tab-section-action',
629
+ 'position' => [
630
+ 'edge' => 'left',
631
+ 'align' => 'left'
632
+ ],
633
+ 'dismiss' => __('Close Tour', 'content-aware-sidebars')
634
+ ],
635
+ [
636
+ 'content' => sprintf(
637
+ '<h3>%s</h3>%s',
638
+ '4/5 ' . __('When to activate', 'content-aware-sidebars'),
639
+ '<p>' . __('Create a widget area and manage its widgets today, then publish it when you are ready.', 'content-aware-sidebars') . '</p>' .
640
+ '<p>' . __('To schedule automatic activation or deactivation, just pick a date and time!', 'content-aware-sidebars') . '</p>' .
641
+ '<p>' . __('By default, new widget areas will be activated when created.', 'content-aware-sidebars') . '</p>'
642
+ ),
643
+ 'ref_id' => '.nav-tab-wrapper.js-cas-tabs .nav-tab-section-schedule',
644
+ 'position' => [
645
+ 'edge' => 'left',
646
+ 'align' => 'left'
647
+ ],
648
+ 'dismiss' => __('Close Tour', 'content-aware-sidebars')
649
+ ],
650
+ [
651
+ 'content' => sprintf(
652
+ '<h3>%s</h3>%s',
653
+ '5/5 ' . __('How to look', 'content-aware-sidebars'),
654
+ '<p>' . __('Personalize the styling without writing any code!', 'content-aware-sidebars') . '</p>' .
655
+ '<p>' . __('You can modify the HTML and CSS classes of the widget area itself, each widget, as well as widget titles.', 'content-aware-sidebars') . '</p>'
656
+ ),
657
+ 'ref_id' => '.nav-tab-wrapper.js-cas-tabs .nav-tab-section-design',
658
+ 'position' => [
659
+ 'edge' => 'left',
660
+ 'align' => 'left'
661
+ ],
662
+ 'next' => __('Finish Tour', 'content-aware-sidebars')
663
+ ],
664
+ [
665
+ 'content' => sprintf(
666
+ '<h3>%s</h3>%s',
667
+ __("That's it", 'content-aware-sidebars'),
668
+ '<p>' . __('Hit the Create button to save your first custom widget area.', 'content-aware-sidebars') . '</p>' .
669
+ '<p>' . __('If you need more help, check out the links below.', 'content-aware-sidebars') . '</p>'
670
+ ),
671
+ 'ref_id' => '#submitdiv',
672
+ 'position' => [
673
+ 'edge' => 'right',
674
+ 'align' => 'top'
675
+ ],
676
+ 'dismiss' => __('Close', 'content-aware-sidebars')
677
+ ]
678
+ ]);
679
+ $this->_tour_manager->enqueue_scripts();
680
+ }
681
+
682
+ /**
683
+ * Meta boxes for sidebar edit
684
+ * @global object $post
685
+ * @return void
686
+ */
687
+ public function create_meta_boxes($post)
688
+ {
689
+ $this->create_pointers();
690
+ CAS_App::instance()->manager()->populate_metadata();
691
+ $path = plugin_dir_path(dirname(__FILE__)) . 'view/';
692
+
693
+ $boxes = [];
694
+ $boxes[] = [
695
+ 'id' => 'submitdiv',
696
+ 'title' => __('Publish'),
697
+ 'view' => 'submit',
698
+ 'context' => 'side',
699
+ 'priority' => 'high'
700
+ ];
701
+ $boxes[] = [
702
+ 'id' => 'cas-options',
703
+ 'title' => __('How to display', 'content-aware-sidebars'),
704
+ 'view' => 'action',
705
+ 'context' => 'section-action',
706
+ ];
707
+ $boxes[] = [
708
+ 'id' => 'cas-status',
709
+ 'title' => __('Status', 'content-aware-sidebars'),
710
+ 'view' => 'status',
711
+ 'context' => 'section-schedule',
712
+ ];
713
+ $boxes[] = [
714
+ 'id' => 'cas-widget-html',
715
+ 'title' => __('Styles', 'content-aware-sidebars'),
716
+ 'view' => 'html',
717
+ 'context' => 'section-design',
718
+ ];
719
+ $boxes[] = [
720
+ 'id' => 'cas-advanced',
721
+ 'title' => __('Options', 'content-aware-sidebars'),
722
+ 'view' => 'advanced',
723
+ 'context' => 'section-advanced',
724
+ ];
725
+ $boxes[] = [
726
+ 'id' => 'cas-plugin-links',
727
+ 'title' => __('Recommendations', 'content-aware-sidebars'),
728
+ 'view' => 'support',
729
+ 'context' => 'side',
730
+ ];
731
+ $boxes[] = [
732
+ 'id' => 'cas-schedule',
733
+ 'title' => __('Time Schedule', 'content-aware-sidebars') . ' <span class="cas-pro-label">' . __('Pro', 'content-aware-sidebars') . '</span>',
734
+ 'view' => 'schedule',
735
+ 'context' => 'section-schedule',
736
+ ];
737
+ $boxes[] = [
738
+ 'id' => 'cas-design',
739
+ 'title' => __('Design', 'content-aware-sidebars') . ' <span class="cas-pro-label">' . __('Pro', 'content-aware-sidebars') . '</span>',
740
+ 'view' => 'design',
741
+ 'context' => 'section-design',
742
+ ];
743
+
744
+ foreach ($boxes as $box) {
745
+ $view = WPCAView::make($path . 'meta_box_' . $box['view'] . '.php', [
746
+ 'post' => $post
747
+ ]);
748
+
749
+ add_meta_box(
750
+ $box['id'],
751
+ $box['title'],
752
+ [$view,'render'],
753
+ CAS_App::BASE_SCREEN . '-edit',
754
+ $box['context'],
755
+ isset($box['priority']) ? $box['priority'] : 'default'
756
+ );
757
+ }
758
+
759
+ //todo: refactor add of meta box
760
+ //with new bootstrapper, legacy core might be loaded
761
+ if (method_exists('WPCACore', 'render_group_meta_box')) {
762
+ WPCACore::render_group_meta_box($post, CAS_App::BASE_SCREEN . '-edit', 'section-conditions');
763
+ }
764
+ }
765
+
766
+ /**
767
+ * Create form field for metadata
768
+ *
769
+ * @param string $id
770
+ * @param string $class
771
+ * @param string $icon
772
+ */
773
+ public static function form_field($id, $class = '', $icon = '')
774
+ {
775
+ $setting = CAS_App::instance()->manager()->metadata()->get($id);
776
+ $current = $setting->get_data(get_the_ID(), true, $setting->get_input_type() != 'multi');
777
+ $icon = $icon ? '<span class="' . $icon . '"></span> ' : '';
778
+
779
+ echo '<div class="' . $class . '">' . $icon . '<strong>' . $setting->get_title() . '</strong>';
780
+ echo '<p>';
781
+ switch ($setting->get_input_type()) {
782
+ case 'select':
783
+ echo '<select style="width:250px;" name="' . $id . '" class="js-cas-' . $id . '">' . "\n";
784
+ foreach ($setting->get_input_list() as $key => $value) {
785
+ $disabled = '';
786
+ if (is_string($key) && strpos($key, '__') === 0) {
787
+ $disabled = ' disabled="disabled"';
788
+ }
789
+ echo '<option value="' . $key . '"' . selected($current, $key, false) . $disabled . '>' . $value . '</option>' . "\n";
790
+ }
791
+ echo '</select>' . "\n";
792
+ break;
793
+ case 'checkbox':
794
+ echo '<ul>' . "\n";
795
+ foreach ($setting->get_input_list() as $key => $value) {
796
+ echo '<li><label><input type="checkbox" name="' . $id . '[]" class="js-cas-' . $id . '" value="' . $key . '"' . (in_array($key, $current) ? ' checked="checked"' : '') . ' /> ' . $value . '</label></li>' . "\n";
797
+ }
798
+ echo '</ul>' . "\n";
799
+ break;
800
+ case 'multi':
801
+ echo '<div><select style="width:100%;" class="js-cas-' . $id . '" multiple="multiple" name="' . $id . '[]" data-value="' . implode(',', $current) . '"></select></div>';
802
+ break;
803
+ case 'text':
804
+ default:
805
+ echo '<input style="width:200px;" type="text" name="' . $id . '" value="' . $current . '" />' . "\n";
806
+ break;
807
+ }
808
+ echo '</p></div>';
809
+ }
810
+
811
+ /**
812
+ * Save meta values for post
813
+ * @param int $post_id
814
+ * @return void
815
+ */
816
+ public function save_post($post_id, $post)
817
+ {
818
+ //Verify nonce, check_admin_referer dies on false
819
+ if (!(isset($_POST[WPCACore::NONCE])
820
+ && wp_verify_nonce($_POST[WPCACore::NONCE], WPCACore::PREFIX . $post_id))) {
821
+ return;
822
+ }
823
+
824
+ // Check permissions
825
+ if (!current_user_can(CAS_App::CAPABILITY, $post_id)) {
826
+ return;
827
+ }
828
+
829
+ // Check autosave
830
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
831
+ return;
832
+ }
833
+
834
+ // Save metadata
835
+ // todo: wrap this in metadata manager?
836
+ foreach (CAS_App::instance()->manager()->metadata() as $field) {
837
+ $field->save($post_id);
838
+ }
839
+ }
840
+
841
+ /**
842
+ * Add suffix when creating sidebar with existing name
843
+ * Does not stop duplicate titles on update
844
+ *
845
+ * @since 3.4.3
846
+ * @param array $insert_data
847
+ * @param array $data
848
+ * @return array
849
+ */
850
+ public function add_duplicate_title_suffix($insert_data, $data)
851
+ {
852
+ if ($data['post_type'] == CAS_App::TYPE_SIDEBAR && !$data['ID']) {
853
+ $sidebars = CAS_App::instance()->manager()->sidebars;
854
+ $sidebar_titles = [];
855
+ foreach ($sidebars as $sidebar) {
856
+ $sidebar_titles[$sidebar->post_title] = 1;
857
+ }
858
+ //if title exists, add a suffix
859
+ $i = 0;
860
+ $title = wp_unslash($insert_data['post_title']);
861
+ $new_title = $title;
862
+ while (isset($sidebar_titles[$new_title])) {
863
+ $new_title = $title . ' (' . ++$i . ')';
864
+ }
865
+ if ($i) {
866
+ $insert_data['post_title'] = wp_slash($new_title);
867
+ }
868
+ }
869
+ return $insert_data;
870
+ }
871
+
872
+ /**
873
+ * Remove widget when its sidebar is removed
874
+ * @param int $post_id
875
+ * @return void
876
+ */
877
+ public function remove_sidebar_widgets($post_id)
878
+ {
879
+ // Authenticate and only continue on sidebar post type
880
+ if (!current_user_can(CAS_App::CAPABILITY) || get_post_type($post_id) != CAS_App::TYPE_SIDEBAR) {
881
+ return;
882
+ }
883
+
884
+ $id = CAS_App::SIDEBAR_PREFIX . $post_id;
885
+
886
+ //Get widgets
887
+ $sidebars_widgets = wp_get_sidebars_widgets();
888
+
889
+ // Check if sidebar exists in database
890
+ if (!isset($sidebars_widgets[$id])) {
891
+ return;
892
+ }
893
+
894
+ // Remove widgets settings from sidebar
895
+ foreach ($sidebars_widgets[$id] as $widget_id) {
896
+ $widget_type = preg_replace('/-[0-9]+$/', '', $widget_id);
897
+ $widget_settings = get_option('widget_' . $widget_type);
898
+ $widget_id = substr($widget_id, strpos($widget_id, '-') + 1);
899
+ if ($widget_settings && isset($widget_settings[$widget_id])) {
900
+ unset($widget_settings[$widget_id]);
901
+ update_option('widget_' . $widget_type, $widget_settings);
902
+ }
903
+ }
904
+
905
+ // Remove sidebar
906
+ unset($sidebars_widgets[$id]);
907
+ wp_set_sidebars_widgets($sidebars_widgets);
908
+ }
909
+
910
+ /**
911
+ * Register and enqueue scripts styles
912
+ * for screen
913
+ *
914
+ * @since 3.4
915
+ */
916
+ public function add_scripts_styles()
917
+ {
918
+ if (is_multisite()) {
919
+ add_action('admin_footer', '_admin_notice_post_locked');
920
+ } else {
921
+ $check_users = get_users(['fields' => 'ID', 'number' => 2]);
922
+ if (count($check_users) > 1) {
923
+ add_action('admin_footer', '_admin_notice_post_locked');
924
+ }
925
+ }
926
+
927
+ wp_enqueue_script('wp-a11y');
928
+
929
+ if (wp_is_mobile()) {
930
+ wp_enqueue_script('jquery-touch-punch');
931
+ }
932
+
933
+ WPCACore::enqueue_scripts_styles();
934
+
935
+ $this->register_script('flatpickr', 'flatpickr', [], '3.0.6');
936
+ $this->register_script('cas/admin/edit', 'cas_admin', ['jquery','flatpickr','wp-color-picker']);
937
+
938
+ $this->enqueue_style('flatpickr', 'flatpickr.dark.min', [], '3.0.6');
939
+ wp_enqueue_style('wp-color-picker');
940
+
941
+ $metadata = CAS_App::instance()->manager()->metadata();
942
+ $visibility = [];
943
+ $target = [];
944
+ foreach ($metadata->get('visibility')->get_input_list() as $category_key => $category) {
945
+ //legacy format
946
+ if (!is_array($category)) {
947
+ $visibility[] = [
948
+ 'id' => $category_key,
949
+ 'text' => $category
950
+ ];
951
+ continue;
952
+ }
953
+
954
+ $data = [
955
+ 'text' => $category['label'],
956
+ 'children' => []
957
+ ];
958
+ foreach ($category['options'] as $key => $value) {
959
+ $data['children'][] = [
960
+ 'id' => $key,
961
+ 'text' => $value
962
+ ];
963
+ }
964
+ $visibility[] = $data;
965
+ }
966
+ foreach ($metadata->get('host')->get_input_list() as $value => $label) {
967
+ $target[] = [
968
+ 'id' => $value,
969
+ 'text' => $label
970
+ ];
971
+ }
972
+
973
+ if (!cas_fs()->can_use_premium_code()) {
974
+ $visibility[] = [
975
+ 'text' => __('Upgrade to Pro for more options', 'content-aware-sidebars'),
976
+ 'children' => []
977
+ ];
978
+ }
979
+
980
+ global $wp_locale;
981
+
982
+ wp_enqueue_script('cas/admin/edit');
983
+ wp_localize_script('cas/admin/edit', 'CASAdmin', [
984
+ 'allVisibility' => __('All Users', 'content-aware-sidebars'),
985
+ 'visibility' => $visibility,
986
+ 'target' => $target,
987
+ 'weekdays' => [
988
+ 'shorthand' => array_values($wp_locale->weekday_abbrev),
989
+ 'longhand' => array_values($wp_locale->weekday)
990
+ ],
991
+ 'months' => [
992
+ 'shorthand' => array_values($wp_locale->month_abbrev),
993
+ 'longhand' => array_values($wp_locale->month)
994
+ ],
995
+ 'weekStart' => get_option('start_of_week', 0),
996
+ 'timeFormat' => get_option('time_format'),
997
+ 'dateFormat' => __('F j, Y') //default long date
998
+ ]);
999
+
1000
+ //badgeos compat
1001
+ //todo: check that developers respond with a fix soon
1002
+ wp_register_script('badgeos-select2', '');
1003
+ wp_register_style('badgeos-select2-css', '');
1004
+ }
1005
+ }
 
 
 
 
 
admin/sidebar-list-table.php CHANGED
@@ -46,7 +46,7 @@ class CAS_Sidebar_List_Table extends WP_List_Table
46
 
47
  $avail_post_stati = get_available_post_statuses(CAS_App::TYPE_SIDEBAR);
48
 
49
- $per_page = $this->get_items_per_page('cas_sidebars_per_page', 20);
50
  $current_page = $this->get_pagenum();
51
 
52
  $args = [
@@ -396,7 +396,7 @@ class CAS_Sidebar_List_Table extends WP_List_Table
396
  protected function _column_title($post, $classes, $data, $primary)
397
  {
398
  echo '<td class="' . $classes . ' page-title" ', $data, '>';
399
- echo $this->column_title($post);
400
  echo '</td>';
401
  }
402
 
@@ -652,7 +652,6 @@ class CAS_Sidebar_List_Table extends WP_List_Table
652
  return '';
653
  }
654
 
655
- $post_type_object = get_post_type_object($post->post_type);
656
  $actions = [];
657
  $title = _draft_or_post_title();
658
  $cas_fs = cas_fs();
@@ -681,7 +680,7 @@ class CAS_Sidebar_List_Table extends WP_List_Table
681
  if ($post->post_status == 'trash') {
682
  $actions['untrash'] = sprintf(
683
  '<a href="%s" aria-label="%s">%s</a>',
684
- wp_nonce_url(get_edit_post_link($post->ID, 'display') . '&amp;action=untrash', 'untrash-post_' . $post->ID),
685
  /* translators: %s: post title */
686
  esc_attr(sprintf(__('Restore &#8220;%s&#8221; from the Trash'), $title)),
687
  __('Restore')
46
 
47
  $avail_post_stati = get_available_post_statuses(CAS_App::TYPE_SIDEBAR);
48
 
49
+ $per_page = $this->get_items_per_page('cas_sidebars_per_page');
50
  $current_page = $this->get_pagenum();
51
 
52
  $args = [
396
  protected function _column_title($post, $classes, $data, $primary)
397
  {
398
  echo '<td class="' . $classes . ' page-title" ', $data, '>';
399
+ $this->column_title($post);
400
  echo '</td>';
401
  }
402
 
652
  return '';
653
  }
654
 
 
655
  $actions = [];
656
  $title = _draft_or_post_title();
657
  $cas_fs = cas_fs();
680
  if ($post->post_status == 'trash') {
681
  $actions['untrash'] = sprintf(
682
  '<a href="%s" aria-label="%s">%s</a>',
683
+ wp_nonce_url(get_edit_post_link($post->ID) . '&amp;action=untrash', 'untrash-post_' . $post->ID),
684
  /* translators: %s: post title */
685
  esc_attr(sprintf(__('Restore &#8220;%s&#8221; from the Trash'), $title)),
686
  __('Restore')
app.php CHANGED
@@ -11,7 +11,7 @@ defined('ABSPATH') || exit;
11
  final class CAS_App
12
  {
13
  const PLUGIN_VERSION_KEY = 'cas_db_version';
14
- const PLUGIN_VERSION = '3.17';
15
 
16
  /**
17
  * Prefix for sidebar id
11
  final class CAS_App
12
  {
13
  const PLUGIN_VERSION_KEY = 'cas_db_version';
14
+ const PLUGIN_VERSION = '3.17.1';
15
 
16
  /**
17
  * Prefix for sidebar id
conditions/placeholder.php CHANGED
@@ -1,115 +1,101 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- final class CASConditionPlaceholder extends WPCAModule_Base
12
- {
13
- public function __construct($id, $title, $description = '', $placeholder = '', $category = 'general')
14
- {
15
- parent::__construct($id, $title, $description, $placeholder);
16
- $this->category = $category;
17
- }
18
-
19
- /**
20
- * @since 3.9
21
- *
22
- * @return void
23
- */
24
- public function initiate()
25
- {
26
- }
27
-
28
- /**
29
- * @since 3.9
30
- * @param array $list
31
- *
32
- * @return array
33
- */
34
- public function list_module($list)
35
- {
36
- $list[] = [
37
- 'id' => $this->id,
38
- 'text' => $this->name,
39
- 'placeholder' => $this->placeholder,
40
- 'default_value' => $this->default_value,
41
- 'disabled' => true,
42
- ];
43
- return $list;
44
- }
45
-
46
- /**
47
- * @since 3.9
48
- *
49
- * @return string
50
- */
51
- public function db_join()
52
- {
53
- return '';
54
- }
55
-
56
- /**
57
- * @since 3.9
58
- * @param int $post_id
59
- *
60
- * @return void
61
- */
62
- public function save_data($post_id)
63
- {
64
- }
65
-
66
- /**
67
- * @since 3.9
68
- * @param array $group_data
69
- * @param int $post_id
70
- *
71
- * @return array
72
- */
73
- public function get_group_data($group_data, $post_id)
74
- {
75
- return $group_data;
76
- }
77
-
78
- /**
79
- * @since 3.9
80
- * @param array $args
81
- *
82
- * @return void
83
- */
84
- protected function _get_content($args = [])
85
- {
86
- return [];
87
- }
88
-
89
- /**
90
- * @since 1.0
91
- *
92
- * @return boolean
93
- */
94
- public function in_context()
95
- {
96
- return false;
97
- }
98
-
99
- /**
100
- * @since 3.9
101
- *
102
- * @return array
103
- */
104
- public function get_context_data()
105
- {
106
- return [];
107
- }
108
-
109
- /**
110
- * @since 3.9
111
- */
112
- public function __destruct()
113
- {
114
- }
115
- }
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ final class CASConditionPlaceholder extends WPCAModule_Base
12
+ {
13
+ /**
14
+ * @param string $id
15
+ * @param string $title
16
+ * @param string $description
17
+ * @param string $placeholder
18
+ * @param string $category
19
+ */
20
+ public function __construct($id, $title, $description = '', $placeholder = '', $category = 'general')
21
+ {
22
+ parent::__construct($id, $title, $description, $placeholder);
23
+ $this->category = $category;
24
+ }
25
+
26
+ /**
27
+ * @inheritDoc
28
+ */
29
+ public function initiate()
30
+ {
31
+ }
32
+
33
+ /**
34
+ * @inheritDoc
35
+ */
36
+ public function list_module($list)
37
+ {
38
+ $list[] = [
39
+ 'id' => $this->id,
40
+ 'text' => $this->name,
41
+ 'placeholder' => $this->placeholder,
42
+ 'default_value' => $this->default_value,
43
+ 'disabled' => true,
44
+ ];
45
+ return $list;
46
+ }
47
+
48
+ /**
49
+ * @inheritDoc
50
+ */
51
+ public function db_join()
52
+ {
53
+ return '';
54
+ }
55
+
56
+ /**
57
+ * @inheritDoc
58
+ */
59
+ public function save_data($post_id)
60
+ {
61
+ }
62
+
63
+ /**
64
+ * @inheritDoc
65
+ */
66
+ public function get_group_data($group_data, $post_id)
67
+ {
68
+ return $group_data;
69
+ }
70
+
71
+ /**
72
+ * @inheritDoc
73
+ */
74
+ protected function _get_content($args = [])
75
+ {
76
+ return [];
77
+ }
78
+
79
+ /**
80
+ * @inheritDoc
81
+ */
82
+ public function in_context()
83
+ {
84
+ return false;
85
+ }
86
+
87
+ /**
88
+ * @inheritDoc
89
+ */
90
+ public function get_context_data()
91
+ {
92
+ return [];
93
+ }
94
+
95
+ /**
96
+ * @inheritDoc
97
+ */
98
+ public function __destruct()
99
+ {
100
+ }
101
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
content-aware-sidebars.php CHANGED
@@ -8,7 +8,7 @@
8
  * Plugin Name: Content Aware Sidebars
9
  * Plugin URI: https://dev.institute/wordpress-sidebars/
10
  * Description: Unlimited custom sidebars and widget areas for any post, page, category etc.
11
- * Version: 3.17
12
  * Author: Joachim Jensen - DEV Institute
13
  * Author URI: https://dev.institute
14
  * Requires at least: 5.0
8
  * Plugin Name: Content Aware Sidebars
9
  * Plugin URI: https://dev.institute/wordpress-sidebars/
10
  * Description: Unlimited custom sidebars and widget areas for any post, page, category etc.
11
+ * Version: 3.17.1
12
  * Author: Joachim Jensen - DEV Institute
13
  * Author URI: https://dev.institute
14
  * Requires at least: 5.0
freemius.php CHANGED
@@ -1,115 +1,116 @@
1
- <?php
2
-
3
- /**
4
- * @package Content Aware Sidebars
5
- * @author Joachim Jensen <joachim@dev.institute>
6
- * @license GPLv3
7
- * @copyright 2022 by Joachim Jensen
8
- */
9
- defined( 'ABSPATH' ) || exit;
10
- // Create a helper function for easy SDK access.
11
- function cas_fs()
12
- {
13
- global $cas_fs ;
14
-
15
- if ( !isset( $cas_fs ) ) {
16
- // Include Freemius SDK.
17
- require_once dirname( __FILE__ ) . '/lib/freemius/start.php';
18
- $cas_fs = fs_dynamic_init( [
19
- 'id' => '259',
20
- 'slug' => 'content-aware-sidebars',
21
- 'type' => 'plugin',
22
- 'public_key' => 'pk_75513325effa77f024565ef74c9d6',
23
- 'is_premium' => false,
24
- 'premium_suffix' => 'Pro',
25
- 'has_addons' => false,
26
- 'has_paid_plans' => true,
27
- 'has_affiliation' => 'selected',
28
- 'menu' => [
29
- 'slug' => 'wpcas',
30
- 'support' => false,
31
- 'contact' => false,
32
- 'affiliation' => false,
33
- ],
34
- 'is_live' => true,
35
- ] );
36
- }
37
-
38
- return $cas_fs;
39
- }
40
-
41
- // Init Freemius.
42
- cas_fs();
43
- global $cas_fs ;
44
- function cas_fs_connect_message_update(
45
- $message,
46
- $user_first_name,
47
- $plugin_title,
48
- $user_login,
49
- $site_link,
50
- $freemius_link
51
- )
52
- {
53
- return sprintf(
54
- __( 'Hey %1$s' ) . ',<br>' . __( 'Please help us improve %2$s by securely sharing some usage data with %5$s. If you skip this, that\'s okay! %2$s will still work just fine.', 'content-aware-sidebars' ),
55
- $user_first_name,
56
- '<b>' . $plugin_title . '</b>',
57
- '<b>' . $user_login . '</b>',
58
- $site_link,
59
- $freemius_link
60
- );
61
- }
62
-
63
- function cas_fs_get_plugin_icon()
64
- {
65
- return dirname( __FILE__ ) . '/assets/img/icon.png';
66
- }
67
-
68
- $cas_fs->add_filter(
69
- 'connect_message_on_update',
70
- 'cas_fs_connect_message_update',
71
- 10,
72
- 6
73
- );
74
- $cas_fs->add_filter(
75
- 'connect_message',
76
- 'cas_fs_connect_message_update',
77
- 10,
78
- 6
79
- );
80
- $cas_fs->add_filter( 'show_affiliate_program_notice', '__return_false' );
81
- $cas_fs->add_filter( 'plugin_icon', 'cas_fs_get_plugin_icon' );
82
- $cas_fs->add_filter( 'permission_extensions_default', '__return_true' );
83
- function cas_fs_upgrade()
84
- {
85
- global $cas_fs ;
86
- $flag = 'cas_pro';
87
- $upgrade_flag = (int) $cas_fs->can_use_premium_code();
88
- if ( $upgrade_flag != (int) get_option( $flag, 0 ) ) {
89
- if ( !$upgrade_flag ) {
90
- //downgrade
91
- update_option( $flag, $upgrade_flag );
92
- }
93
- }
94
- }
95
-
96
- add_action( 'admin_init', 'cas_fs_upgrade', 999 );
97
-
98
- if ( !$cas_fs->can_use_premium_code() ) {
99
- function cas_fs_uninstall()
100
- {
101
- require plugin_dir_path( __FILE__ ) . '/cas_uninstall.php';
102
- }
103
-
104
-
105
- if ( $cas_fs->is_on() ) {
106
- $cas_fs->add_action( 'after_uninstall', 'cas_fs_uninstall' );
107
- } elseif ( is_admin() ) {
108
- //after_uninstall is only run for new users
109
- register_uninstall_hook( plugin_dir_path( __FILE__ ) . 'content-aware-sidebars.php', 'cas_fs_uninstall' );
110
- }
111
-
112
- }
113
-
114
- // Signal that SDK was initiated.
 
115
  do_action( 'cas_fs_loaded' );
1
+ <?php
2
+
3
+ /**
4
+ * @package Content Aware Sidebars
5
+ * @author Joachim Jensen <joachim@dev.institute>
6
+ * @license GPLv3
7
+ * @copyright 2022 by Joachim Jensen
8
+ */
9
+ defined( 'ABSPATH' ) || exit;
10
+ // Create a helper function for easy SDK access.
11
+ function cas_fs()
12
+ {
13
+ global $cas_fs ;
14
+
15
+ if ( !isset( $cas_fs ) ) {
16
+ // Include Freemius SDK.
17
+ require_once dirname( __FILE__ ) . '/lib/freemius/start.php';
18
+ $cas_fs = fs_dynamic_init( [
19
+ 'id' => '259',
20
+ 'slug' => 'content-aware-sidebars',
21
+ 'type' => 'plugin',
22
+ 'public_key' => 'pk_75513325effa77f024565ef74c9d6',
23
+ 'is_premium' => false,
24
+ 'premium_suffix' => 'Pro',
25
+ 'has_addons' => false,
26
+ 'has_paid_plans' => true,
27
+ 'has_affiliation' => 'selected',
28
+ 'menu' => [
29
+ 'slug' => 'wpcas',
30
+ 'support' => false,
31
+ 'contact' => false,
32
+ 'affiliation' => false,
33
+ ],
34
+ 'is_live' => true,
35
+ ] );
36
+ }
37
+
38
+ return $cas_fs;
39
+ }
40
+
41
+ // Init Freemius.
42
+ cas_fs();
43
+ global $cas_fs ;
44
+ function cas_fs_connect_message_update(
45
+ $message,
46
+ $user_first_name,
47
+ $plugin_title,
48
+ $user_login,
49
+ $site_link,
50
+ $freemius_link
51
+ )
52
+ {
53
+ return sprintf(
54
+ __( 'Hey %1$s' ) . ',<br>' . __( 'Please help us improve %2$s by securely sharing some usage data with %5$s. If you skip this, that\'s okay! %2$s will still work just fine.', 'content-aware-sidebars' ),
55
+ $user_first_name,
56
+ '<b>' . $plugin_title . '</b>',
57
+ '<b>' . $user_login . '</b>',
58
+ $site_link,
59
+ $freemius_link
60
+ );
61
+ }
62
+
63
+ function cas_fs_get_plugin_icon()
64
+ {
65
+ return dirname( __FILE__ ) . '/assets/img/icon.png';
66
+ }
67
+
68
+ $cas_fs->add_filter(
69
+ 'connect_message_on_update',
70
+ 'cas_fs_connect_message_update',
71
+ 10,
72
+ 6
73
+ );
74
+ $cas_fs->add_filter(
75
+ 'connect_message',
76
+ 'cas_fs_connect_message_update',
77
+ 10,
78
+ 6
79
+ );
80
+ $cas_fs->add_filter( 'show_affiliate_program_notice', '__return_false' );
81
+ $cas_fs->add_filter( 'plugin_icon', 'cas_fs_get_plugin_icon' );
82
+ $cas_fs->add_filter( 'permission_extensions_default', '__return_true' );
83
+ $cas_fs->add_filter( 'hide_freemius_powered_by', '__return_true' );
84
+ function cas_fs_upgrade()
85
+ {
86
+ global $cas_fs ;
87
+ $flag = 'cas_pro';
88
+ $upgrade_flag = (int) $cas_fs->can_use_premium_code();
89
+ if ( $upgrade_flag != (int) get_option( $flag, 0 ) ) {
90
+ if ( !$upgrade_flag ) {
91
+ //downgrade
92
+ update_option( $flag, $upgrade_flag );
93
+ }
94
+ }
95
+ }
96
+
97
+ add_action( 'admin_init', 'cas_fs_upgrade', 999 );
98
+
99
+ if ( !$cas_fs->can_use_premium_code() ) {
100
+ function cas_fs_uninstall()
101
+ {
102
+ require plugin_dir_path( __FILE__ ) . '/cas_uninstall.php';
103
+ }
104
+
105
+
106
+ if ( $cas_fs->is_on() ) {
107
+ $cas_fs->add_action( 'after_uninstall', 'cas_fs_uninstall' );
108
+ } elseif ( is_admin() ) {
109
+ //after_uninstall is only run for new users
110
+ register_uninstall_hook( plugin_dir_path( __FILE__ ) . 'content-aware-sidebars.php', 'cas_fs_uninstall' );
111
+ }
112
+
113
+ }
114
+
115
+ // Signal that SDK was initiated.
116
  do_action( 'cas_fs_loaded' );
lib/freemius/assets/css/admin/account.css CHANGED
@@ -1 +1 @@
1
- label.fs-tag,span.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.fs-notice[data-id="license_not_whitelabeled"].success,.fs-notice[data-id="license_whitelabeled"].success{color:inherit;border-left-color:#00a0d2}.fs-notice[data-id="license_not_whitelabeled"].success label.fs-plugin-title,.fs-notice[data-id="license_whitelabeled"].success label.fs-plugin-title{display:none}#fs_account .postbox,#fs_account .widefat{max-width:800px}#fs_account h3{font-size:1.3em;padding:12px 15px;margin:0 0 12px 0;line-height:1.4;border-bottom:1px solid #F1F1F1}#fs_account h3 .dashicons{width:26px;height:26px;font-size:1.3em}#fs_account i.dashicons{font-size:1.2em;height:1.2em;width:1.2em}#fs_account .dashicons{vertical-align:middle}#fs_account .fs-header-actions{position:absolute;top:17px;right:15px;font-size:0.9em}#fs_account .fs-header-actions ul{margin:0}#fs_account .fs-header-actions li{float:left}#fs_account .fs-header-actions li form{display:inline-block}#fs_account .fs-header-actions li a{text-decoration:none}#fs_account_details .button-group{float:right}.rtl #fs_account .fs-header-actions{left:15px;right:auto}.fs-key-value-table{width:100%}.fs-key-value-table form{display:inline-block}.fs-key-value-table tr td:first-child{text-align:right}.fs-key-value-table tr td:first-child nobr{font-weight:bold}.fs-key-value-table tr td:first-child form{display:block}.fs-key-value-table tr td.fs-right{text-align:right}.fs-key-value-table tr.fs-odd{background:#ebebeb}.fs-key-value-table td,.fs-key-value-table th{padding:10px}.fs-key-value-table code{line-height:28px}.fs-key-value-table var,.fs-key-value-table code,.fs-key-value-table input[type="text"]{color:#0073AA;font-size:16px;background:none}.fs-key-value-table input[type="text"]{width:100%;font-weight:bold}.fs-field-beta_program label{margin-left:7px}label.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error{background:#dc3232}#fs_sites .fs-scrollable-table .fs-table-body{max-height:200px;overflow:auto;border:1px solid #e5e5e5}#fs_sites .fs-scrollable-table .fs-table-body>table.widefat{border:none !important}#fs_sites .fs-scrollable-table .fs-main-column{width:100%}#fs_sites .fs-scrollable-table .fs-site-details td:first-of-type{text-align:right;color:grey;width:1px}#fs_sites .fs-scrollable-table .fs-site-details td:last-of-type{text-align:right}#fs_sites .fs-scrollable-table .fs-install-details table tr td{width:1px;white-space:nowrap}#fs_sites .fs-scrollable-table .fs-install-details table tr td:last-of-type{width:auto}#fs_addons h3{border:none;margin-bottom:0;padding:4px 5px}#fs_addons td{vertical-align:middle}#fs_addons thead{white-space:nowrap}#fs_addons td:first-child,#fs_addons th:first-child{text-align:left;font-weight:bold}#fs_addons td:last-child,#fs_addons th:last-child{text-align:right}#fs_addons th{font-weight:bold}#fs_billing_address{width:100%}#fs_billing_address tr td{width:50%;padding:5px}#fs_billing_address tr:first-of-type td{padding-top:0}#fs_billing_address span{font-weight:bold}#fs_billing_address input,#fs_billing_address select{display:block;width:100%;margin-top:5px}#fs_billing_address input::-moz-placeholder,#fs_billing_address select::-moz-placeholder{color:transparent;opacity:1}#fs_billing_address input:-ms-input-placeholder,#fs_billing_address select:-ms-input-placeholder{color:transparent}#fs_billing_address input::-webkit-input-placeholder,#fs_billing_address select::-webkit-input-placeholder{color:transparent}#fs_billing_address input.fs-read-mode,#fs_billing_address select.fs-read-mode{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode td span{display:none}#fs_billing_address.fs-read-mode input,#fs_billing_address.fs-read-mode select{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode input::-moz-placeholder,#fs_billing_address.fs-read-mode select::-moz-placeholder{color:#ccc;opacity:1}#fs_billing_address.fs-read-mode input:-ms-input-placeholder,#fs_billing_address.fs-read-mode select:-ms-input-placeholder{color:#ccc}#fs_billing_address.fs-read-mode input::-webkit-input-placeholder,#fs_billing_address.fs-read-mode select::-webkit-input-placeholder{color:#ccc}#fs_billing_address button{display:block;width:100%}
1
+ label.fs-tag,span.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.fs-notice[data-id=license_not_whitelabeled].success,.fs-notice[data-id=license_whitelabeled].success{color:inherit;border-left-color:#00a0d2}.fs-notice[data-id=license_not_whitelabeled].success label.fs-plugin-title,.fs-notice[data-id=license_whitelabeled].success label.fs-plugin-title{display:none}#fs_account .postbox,#fs_account .widefat{max-width:800px}#fs_account h3{font-size:1.3em;padding:12px 15px;margin:0 0 12px 0;line-height:1.4;border-bottom:1px solid #f1f1f1}#fs_account h3 .dashicons{width:26px;height:26px;font-size:1.3em}#fs_account i.dashicons{font-size:1.2em;height:1.2em;width:1.2em}#fs_account .dashicons{vertical-align:middle}#fs_account .fs-header-actions{position:absolute;top:17px;right:15px;font-size:.9em}#fs_account .fs-header-actions ul{margin:0}#fs_account .fs-header-actions li{float:left}#fs_account .fs-header-actions li form{display:inline-block}#fs_account .fs-header-actions li a{text-decoration:none}#fs_account_details .button-group{float:right}.rtl #fs_account .fs-header-actions{left:15px;right:auto}.fs-key-value-table{width:100%}.fs-key-value-table form{display:inline-block}.fs-key-value-table tr td:first-child{text-align:right}.fs-key-value-table tr td:first-child nobr{font-weight:bold}.fs-key-value-table tr td:first-child form{display:block}.fs-key-value-table tr td.fs-right{text-align:right}.fs-key-value-table tr.fs-odd{background:#ebebeb}.fs-key-value-table td,.fs-key-value-table th{padding:10px}.fs-key-value-table code{line-height:28px}.fs-key-value-table var,.fs-key-value-table code,.fs-key-value-table input[type=text]{color:#0073aa;font-size:16px;background:none}.fs-key-value-table input[type=text]{width:100%;font-weight:bold}.fs-field-beta_program label{margin-left:7px}label.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error{background:#dc3232}#fs_sites .fs-scrollable-table .fs-table-body{max-height:200px;overflow:auto;border:1px solid #e5e5e5}#fs_sites .fs-scrollable-table .fs-table-body>table.widefat{border:none !important}#fs_sites .fs-scrollable-table .fs-main-column{width:100%}#fs_sites .fs-scrollable-table .fs-site-details td:first-of-type{text-align:right;color:gray;width:1px}#fs_sites .fs-scrollable-table .fs-site-details td:last-of-type{text-align:right}#fs_sites .fs-scrollable-table .fs-install-details table tr td{width:1px;white-space:nowrap}#fs_sites .fs-scrollable-table .fs-install-details table tr td:last-of-type{width:auto}#fs_addons h3{border:none;margin-bottom:0;padding:4px 5px}#fs_addons td{vertical-align:middle}#fs_addons thead{white-space:nowrap}#fs_addons td:first-child,#fs_addons th:first-child{text-align:left;font-weight:bold}#fs_addons td:last-child,#fs_addons th:last-child{text-align:right}#fs_addons th{font-weight:bold}#fs_billing_address{width:100%}#fs_billing_address tr td{width:50%;padding:5px}#fs_billing_address tr:first-of-type td{padding-top:0}#fs_billing_address span{font-weight:bold}#fs_billing_address input,#fs_billing_address select{display:block;width:100%;margin-top:5px}#fs_billing_address input::-moz-placeholder,#fs_billing_address select::-moz-placeholder{color:transparent;opacity:1}#fs_billing_address input:-ms-input-placeholder,#fs_billing_address select:-ms-input-placeholder{color:transparent}#fs_billing_address input::-webkit-input-placeholder,#fs_billing_address select::-webkit-input-placeholder{color:transparent}#fs_billing_address input.fs-read-mode,#fs_billing_address select.fs-read-mode{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode td span{display:none}#fs_billing_address.fs-read-mode input,#fs_billing_address.fs-read-mode select{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode input::-moz-placeholder,#fs_billing_address.fs-read-mode select::-moz-placeholder{color:#ccc;opacity:1}#fs_billing_address.fs-read-mode input:-ms-input-placeholder,#fs_billing_address.fs-read-mode select:-ms-input-placeholder{color:#ccc}#fs_billing_address.fs-read-mode input::-webkit-input-placeholder,#fs_billing_address.fs-read-mode select::-webkit-input-placeholder{color:#ccc}#fs_billing_address button{display:block;width:100%}@media screen and (max-width: 639px){#fs_account .fs-header-actions{position:static;padding:0 15px 12px 15px;margin:0 0 12px 0}#fs_account .fs-header-actions li{float:none;display:inline-block}#fs_account #fs_account_details{display:block}#fs_account #fs_account_details tbody,#fs_account #fs_account_details tr,#fs_account #fs_account_details td,#fs_account #fs_account_details th{display:block}#fs_account #fs_account_details tr td:first-child{text-align:left}#fs_account #fs_account_details tr td:nth-child(2){padding:0 12px}#fs_account #fs_account_details tr td:nth-child(2) code{margin:0;padding:0}#fs_account #fs_account_details tr td:nth-child(2) label{margin-left:0}#fs_account #fs_account_details tr td:nth-child(3){text-align:left}#fs_account #fs_account_details tr.fs-field-plan td:nth-child(2) .button-group{float:none;margin:12px 0}}/*# sourceMappingURL=account.css.map */
lib/freemius/assets/css/admin/account.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../../scss/admin/_tag.scss","../../scss/admin/account.scss","../../scss/_mixins.scss"],"names":[],"mappings":"AAAA,yBAEI,mBACA,WACA,qBACA,kBACA,YACA,eACA,iBACA,wBAEA,yCAEI,mBAEJ,yCAEI,mBAEJ,+CAEI,mBAEJ,2CAEI,mBCrBJ,sGACI,cACA,0BAEA,kJACI,aAOR,0CAGI,gBAGJ,eAEI,gBACA,kBACA,kBACA,gBACA,gCAEA,0BACI,WACA,YACA,gBAIR,wBAEI,gBACA,aACA,YAGJ,uBAEI,sBAGJ,+BAEI,kBACA,SACA,WACA,eAEA,kCAEI,SAGJ,kCAOI,WALA,uCAEI,qBAIJ,oCAEI,qBAMhB,kCACI,YAGJ,oCAEI,UACA,WAGJ,oBAEI,WAEA,yBAEI,qBAKA,sCAOI,iBALA,2CAEI,iBAKJ,2CAEI,cAIR,mCAEI,iBAGJ,8BAEI,mBAIR,8CAEI,aAGJ,yBACI,iBAGJ,sFAEI,cACA,eACA,gBAGJ,qCACI,WACA,iBAIR,6BACI,gBAGJ,aAEI,mBACA,WACA,qBACA,kBACA,YACA,eACA,iBACA,wBAEA,qBAEI,mBAEJ,wBAEI,mBAEJ,sBAEI,mBAQA,8CACI,iBACA,cACA,yBAEA,4DACI,uBAIR,+CACI,WAKA,iEAEI,iBACA,WACA,UAGJ,gEAEI,iBAMJ,+DAEI,UACA,mBAEA,4EAEI,WAShB,cAEI,YACA,gBACA,gBAGJ,cAEI,sBAGJ,iBACI,mBAGJ,oDAGI,gBACA,iBAEJ,kDAGI,iBAEJ,cAEI,iBAIR,oBACI,WAGI,0BACI,UACA,YAIA,wCACI,cAaZ,yBACI,iBAGJ,qDAGI,cACA,WACA,eC7BJ,yFACI,MDwBqB,YCvBrB,UAEJ,uGDqByB,YCpBzB,iHDoByB,YAMrB,+EAlBA,yBACA,WACA,8BACA,eACA,gBAqBA,yCACI,aAGJ,+EA7BA,yBACA,WACA,8BACA,eACA,gBCjBJ,mHACI,MD4CyB,KC3CzB,UAEJ,iIDyC6B,KCxC7B,2IDwC6B,KAK7B,2BACI,cACA,WAOR,qCAGQ,+BACI,gBACA,yBACA,kBAEA,kCACI,WACA,qBAKR,gCACI,cAEA,+IACI,cAKI,kDACI,gBAGJ,mDACI,eACA,wDACI,SACA,UAGJ,yDACI,cAIR,mDACI,gBAMJ,+EACI,WACA","file":"account.css"}
lib/freemius/assets/css/admin/add-ons.css CHANGED
@@ -1,2 +1 @@
1
- .fs-badge{position:absolute;top:10px;right:0;background:#71ae00;color:white;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);box-shadow:0 2px 1px -1px rgba(0,0,0,0.3)}#fs_addons .fs-cards-list{list-style:none}#fs_addons .fs-cards-list .fs-card{float:left;height:152px;width:310px;padding:0;margin:0 0 30px 30px;font-size:14px;list-style:none;border:1px solid #ddd;cursor:pointer;position:relative}#fs_addons .fs-cards-list .fs-card .fs-overlay{position:absolute;left:0;right:0;bottom:0;top:0;z-index:9}#fs_addons .fs-cards-list .fs-card .fs-inner{background-color:#fff;overflow:hidden;height:100%;position:relative}#fs_addons .fs-cards-list .fs-card .fs-inner>ul{-moz-transition:all,0.15s;-o-transition:all,0.15s;-ms-transition:all,0.15s;-webkit-transition:all,0.15s;transition:all,0.15s;left:0;right:0;top:0;position:absolute}#fs_addons .fs-cards-list .fs-card .fs-inner>ul>li{list-style:none;line-height:18px;padding:0 15px;width:100%;display:block;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner{padding:0;margin:0;line-height:0;display:block;height:100px;background-repeat:repeat-x;background-size:100% 100%;-moz-transition:all,0.15s;-o-transition:all,0.15s;-ms-transition:all,0.15s;-webkit-transition:all,0.15s;transition:all,0.15s}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner .fs-badge.fs-installed-addon-badge{font-size:1.02em;line-height:1.3em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-title{margin:10px 0 0 0;height:18px;overflow:hidden;color:#000;white-space:nowrap;text-overflow:ellipsis;font-weight:bold}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-offer{font-size:0.9em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-description{background-color:#f9f9f9;padding:10px 15px 100px 15px;border-top:1px solid #eee;margin:0 0 10px 0;color:#777}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-tag{position:absolute;top:10px;right:0px;background:greenyellow;display:block;padding:2px 10px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.3);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.3);box-shadow:1px 1px 1px rgba(0,0,0,0.3);text-transform:uppercase;font-size:0.9em;font-weight:bold}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button,#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button-group{position:absolute;top:112px;right:10px}@media screen and (min-width: 960px){#fs_addons .fs-cards-list .fs-card:hover .fs-overlay{border:2px solid #29abe1;margin-left:-1px;margin-top:-1px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner ul{top:-100px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-title,#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-offer{color:#29abe1}}
2
- #TB_window,#TB_window iframe{width:821px !important}#plugin-information .fyi{width:266px !important}#plugin-information #section-holder{margin-right:299px}#plugin-information #section-description h2,#plugin-information #section-description h3,#plugin-information #section-description p,#plugin-information #section-description b,#plugin-information #section-description i,#plugin-information #section-description blockquote,#plugin-information #section-description li,#plugin-information #section-description ul,#plugin-information #section-description ol{clear:none}#plugin-information #section-description iframe{max-width:100%}#plugin-information #section-description .fs-selling-points{padding-bottom:10px;border-bottom:1px solid #ddd}#plugin-information #section-description .fs-selling-points ul{margin:0}#plugin-information #section-description .fs-selling-points ul li{padding:0;list-style:none outside none}#plugin-information #section-description .fs-selling-points ul li i.dashicons{color:#71ae00;font-size:3em;vertical-align:middle;line-height:30px;float:left;margin:0 0 0 -15px}#plugin-information #section-description .fs-selling-points ul li h3{margin:1em 30px !important}#plugin-information #section-description .fs-screenshots:after{content:"";display:table;clear:both}#plugin-information #section-description .fs-screenshots ul{list-style:none;margin:0}#plugin-information #section-description .fs-screenshots ul li{width:225px;height:225px;float:left;margin-bottom:20px;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}#plugin-information #section-description .fs-screenshots ul li a{display:block;width:100%;height:100%;border:1px solid;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.2);box-shadow:1px 1px 1px rgba(0,0,0,0.2);background-size:cover}#plugin-information #section-description .fs-screenshots ul li.odd{margin-right:20px}#plugin-information .plugin-information-pricing{margin:-16px;border-bottom:1px solid #ddd}#plugin-information .plugin-information-pricing .fs-plan h3{margin-top:0;padding:20px;font-size:16px}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper{border-bottom:1px solid #ddd}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab{cursor:pointer;position:relative;padding:0 10px;font-size:0.9em}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab label{text-transform:uppercase;color:green;background:greenyellow;position:absolute;left:-1px;right:-1px;bottom:100%;border:1px solid darkgreen;padding:2px;text-align:center;font-size:0.9em;line-height:1em}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab.nav-tab-active{cursor:default;background:#fffeec;border-bottom-color:#fffeec}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle h3{background:#fffeec;margin:0;padding-bottom:0;color:#0073aa}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .nav-tab-wrapper,#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .fs-billing-frequency{display:none}#plugin-information .plugin-information-pricing .fs-plan .fs-pricing-body{background:#fffeec;padding:20px}#plugin-information .plugin-information-pricing .fs-plan .button{width:100%;text-align:center;font-weight:bold;text-transform:uppercase;font-size:1.1em}#plugin-information .plugin-information-pricing .fs-plan label{white-space:nowrap}#plugin-information .plugin-information-pricing .fs-plan var{font-style:normal}#plugin-information .plugin-information-pricing .fs-plan .fs-billing-frequency,#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount{text-align:center;display:block;font-weight:bold;margin-bottom:10px;text-transform:uppercase;background:#F3F3F3;padding:2px;border:1px solid #ccc}#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount{text-transform:none;color:green;background:greenyellow}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms{font-size:0.9em}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms i{float:left;margin:0 0 0 -15px}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms li{margin:10px 0 0 0}#plugin-information #section-features .fs-features{margin:-20px -26px}#plugin-information #section-features table{width:100%;border-spacing:0;border-collapse:separate}#plugin-information #section-features table thead th{padding:10px 0}#plugin-information #section-features table thead .fs-price{color:#71ae00;font-weight:normal;display:block;text-align:center}#plugin-information #section-features table tbody td{border-top:1px solid #ccc;padding:10px 0;text-align:center;width:100px;color:#71ae00}#plugin-information #section-features table tbody td:first-child{text-align:left;width:auto;color:inherit;padding-left:26px}#plugin-information #section-features table tbody tr.fs-odd td{background:#fefefe}#plugin-information #section-features .dashicons-yes{width:30px;height:30px;font-size:30px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button,#plugin-information .fs-dropdown .button-group .button{position:relative;width:auto;top:0;right:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button:focus,#plugin-information .fs-dropdown .button-group .button:focus{z-index:10}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .fs-dropdown-arrow,#plugin-information .fs-dropdown .button-group .fs-dropdown-arrow{border-top:6px solid white;border-right:4px solid transparent;border-left:4px solid transparent;top:12px;position:relative}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button){border-bottom-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button{border-bottom-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button){border-top-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active.up .fs-dropdown-arrow-button{border-top-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list,#plugin-information .fs-dropdown .fs-dropdown-list{position:absolute;right:-1px;top:100%;margin-left:auto;padding:3px 0;border:1px solid #bfbfbf;background-color:#fff;z-index:1;width:230px;text-align:left;-moz-box-shadow:0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12);-webkit-box-shadow:0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12);box-shadow:0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li,#plugin-information .fs-dropdown .fs-dropdown-list li{margin:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li a,#plugin-information .fs-dropdown .fs-dropdown-list li a{display:block;padding:5px 10px;text-decoration:none;text-shadow:none}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover,#plugin-information .fs-dropdown .fs-dropdown-list li:hover{background-color:#0074a3;color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover a,#plugin-information .fs-dropdown .fs-dropdown-list li:hover a{color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown:not(.up) .fs-dropdown-list,#plugin-information .fs-dropdown:not(.up) .fs-dropdown-list{-moz-border-radius:3px 0 3px 3px;-webkit-border-radius:3px 0 3px 3px;border-radius:3px 0 3px 3px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.up .fs-dropdown-list,#plugin-information .fs-dropdown.up .fs-dropdown-list{-moz-border-radius:3px 3px 0 3px;-webkit-border-radius:3px 3px 0 3px;border-radius:3px 3px 0 3px}#plugin-information .fs-dropdown .button-group{width:100%}#plugin-information .fs-dropdown .button-group .button{float:none;font-size:14px;font-weight:normal;text-transform:none}#plugin-information .fs-dropdown .fs-dropdown-list{margin-top:1px}#plugin-information .fs-dropdown.up .fs-dropdown-list{top:auto;bottom:100%;margin-bottom:2px}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group{text-align:center;display:table}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button{display:table-cell}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button:not(.fs-dropdown-arrow-button){left:1px;width:100%}#plugin-information-footer>.button,#plugin-information-footer .fs-dropdown{position:relative;top:3px}#plugin-information-footer>.button.left,#plugin-information-footer .fs-dropdown.left{float:left}#plugin-information-footer>.right,#plugin-information-footer .fs-dropdown{float:right}@media screen and (max-width: 961px){#fs_addons .fs-cards-list .fs-card{height:265px}}
1
+ .fs-badge{position:absolute;top:10px;right:0;background:#71ae00;color:#fff;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,.3);box-shadow:0 2px 1px -1px rgba(0,0,0,.3)}#fs_addons .fs-cards-list{list-style:none}#fs_addons .fs-cards-list .fs-card{float:left;height:152px;width:310px;padding:0;margin:0 0 30px 30px;font-size:14px;list-style:none;border:1px solid #ddd;cursor:pointer;position:relative}#fs_addons .fs-cards-list .fs-card .fs-overlay{position:absolute;left:0;right:0;bottom:0;top:0;z-index:9}#fs_addons .fs-cards-list .fs-card .fs-inner{background-color:#fff;overflow:hidden;height:100%;position:relative}#fs_addons .fs-cards-list .fs-card .fs-inner>ul{-moz-transition:all,.15s;-o-transition:all,.15s;-ms-transition:all,.15s;-webkit-transition:all,.15s;transition:all,.15s;left:0;right:0;top:0;position:absolute}#fs_addons .fs-cards-list .fs-card .fs-inner>ul>li{list-style:none;line-height:18px;padding:0 15px;width:100%;display:block;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner{padding:0;margin:0;line-height:0;display:block;height:100px;background-repeat:repeat-x;background-size:100% 100%;-moz-transition:all,.15s;-o-transition:all,.15s;-ms-transition:all,.15s;-webkit-transition:all,.15s;transition:all,.15s}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner .fs-badge.fs-installed-addon-badge{font-size:1.02em;line-height:1.3em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-title{margin:10px 0 0 0;height:18px;overflow:hidden;color:#000;white-space:nowrap;text-overflow:ellipsis;font-weight:bold}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-offer{font-size:.9em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-description{background-color:#f9f9f9;padding:10px 15px 100px 15px;border-top:1px solid #eee;margin:0 0 10px 0;color:#777}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-tag{position:absolute;top:10px;right:0px;background:#adff2f;display:block;padding:2px 10px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.3);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.3);box-shadow:1px 1px 1px rgba(0,0,0,.3);text-transform:uppercase;font-size:.9em;font-weight:bold}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button,#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button-group{position:absolute;top:112px;right:10px}@media screen and (min-width: 960px){#fs_addons .fs-cards-list .fs-card:hover .fs-overlay{border:2px solid #29abe1;margin-left:-1px;margin-top:-1px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner ul{top:-100px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-title,#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-offer{color:#29abe1}}#TB_window,#TB_window iframe{width:821px !important}#plugin-information .fyi{width:266px !important}#plugin-information #section-holder{margin-right:299px}#plugin-information #section-description h2,#plugin-information #section-description h3,#plugin-information #section-description p,#plugin-information #section-description b,#plugin-information #section-description i,#plugin-information #section-description blockquote,#plugin-information #section-description li,#plugin-information #section-description ul,#plugin-information #section-description ol{clear:none}#plugin-information #section-description iframe{max-width:100%}#plugin-information #section-description .fs-selling-points{padding-bottom:10px;border-bottom:1px solid #ddd}#plugin-information #section-description .fs-selling-points ul{margin:0}#plugin-information #section-description .fs-selling-points ul li{padding:0;list-style:none outside none}#plugin-information #section-description .fs-selling-points ul li i.dashicons{color:#71ae00;font-size:3em;vertical-align:middle;line-height:30px;float:left;margin:0 0 0 -15px}#plugin-information #section-description .fs-selling-points ul li h3{margin:1em 30px !important}#plugin-information #section-description .fs-screenshots:after{content:"";display:table;clear:both}#plugin-information #section-description .fs-screenshots ul{list-style:none;margin:0}#plugin-information #section-description .fs-screenshots ul li{width:225px;height:225px;float:left;margin-bottom:20px;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}#plugin-information #section-description .fs-screenshots ul li a{display:block;width:100%;height:100%;border:1px solid;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.2);box-shadow:1px 1px 1px rgba(0,0,0,.2);background-size:cover}#plugin-information #section-description .fs-screenshots ul li.odd{margin-right:20px}#plugin-information .plugin-information-pricing{margin:-16px;border-bottom:1px solid #ddd}#plugin-information .plugin-information-pricing .fs-plan h3{margin-top:0;padding:20px;font-size:16px}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper{border-bottom:1px solid #ddd}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab{cursor:pointer;position:relative;padding:0 10px;font-size:.9em}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab label{text-transform:uppercase;color:green;background:#adff2f;position:absolute;left:-1px;right:-1px;bottom:100%;border:1px solid #006400;padding:2px;text-align:center;font-size:.9em;line-height:1em}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab.nav-tab-active{cursor:default;background:#fffeec;border-bottom-color:#fffeec}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle h3{background:#fffeec;margin:0;padding-bottom:0;color:#0073aa}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .nav-tab-wrapper,#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .fs-billing-frequency{display:none}#plugin-information .plugin-information-pricing .fs-plan .fs-pricing-body{background:#fffeec;padding:20px}#plugin-information .plugin-information-pricing .fs-plan .button{width:100%;text-align:center;font-weight:bold;text-transform:uppercase;font-size:1.1em}#plugin-information .plugin-information-pricing .fs-plan label{white-space:nowrap}#plugin-information .plugin-information-pricing .fs-plan var{font-style:normal}#plugin-information .plugin-information-pricing .fs-plan .fs-billing-frequency,#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount{text-align:center;display:block;font-weight:bold;margin-bottom:10px;text-transform:uppercase;background:#f3f3f3;padding:2px;border:1px solid #ccc}#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount{text-transform:none;color:green;background:#adff2f}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms{font-size:.9em}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms i{float:left;margin:0 0 0 -15px}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms li{margin:10px 0 0 0}#plugin-information #section-features .fs-features{margin:-20px -26px}#plugin-information #section-features table{width:100%;border-spacing:0;border-collapse:separate}#plugin-information #section-features table thead th{padding:10px 0}#plugin-information #section-features table thead .fs-price{color:#71ae00;font-weight:normal;display:block;text-align:center}#plugin-information #section-features table tbody td{border-top:1px solid #ccc;padding:10px 0;text-align:center;width:100px;color:#71ae00}#plugin-information #section-features table tbody td:first-child{text-align:left;width:auto;color:inherit;padding-left:26px}#plugin-information #section-features table tbody tr.fs-odd td{background:#fefefe}#plugin-information #section-features .dashicons-yes{width:30px;height:30px;font-size:30px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button,#plugin-information .fs-dropdown .button-group .button{position:relative;width:auto;top:0;right:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button:focus,#plugin-information .fs-dropdown .button-group .button:focus{z-index:10}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .fs-dropdown-arrow,#plugin-information .fs-dropdown .button-group .fs-dropdown-arrow{border-top:6px solid #fff;border-right:4px solid transparent;border-left:4px solid transparent;top:12px;position:relative}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button){border-bottom-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button{border-bottom-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button){border-top-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active.up .fs-dropdown-arrow-button{border-top-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list,#plugin-information .fs-dropdown .fs-dropdown-list{position:absolute;right:-1px;top:100%;margin-left:auto;padding:3px 0;border:1px solid #bfbfbf;background-color:#fff;z-index:1;width:230px;text-align:left;-moz-box-shadow:0px 2px 4px -1px rgba(0,0,0,.2),0px 4px 5px 0px rgba(0,0,0,.14),0px 1px 10px 0px rgba(0,0,0,.12);-webkit-box-shadow:0px 2px 4px -1px rgba(0,0,0,.2),0px 4px 5px 0px rgba(0,0,0,.14),0px 1px 10px 0px rgba(0,0,0,.12);box-shadow:0px 2px 4px -1px rgba(0,0,0,.2),0px 4px 5px 0px rgba(0,0,0,.14),0px 1px 10px 0px rgba(0,0,0,.12)}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li,#plugin-information .fs-dropdown .fs-dropdown-list li{margin:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li a,#plugin-information .fs-dropdown .fs-dropdown-list li a{display:block;padding:5px 10px;text-decoration:none;text-shadow:none}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover,#plugin-information .fs-dropdown .fs-dropdown-list li:hover{background-color:#0074a3;color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover a,#plugin-information .fs-dropdown .fs-dropdown-list li:hover a{color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown:not(.up) .fs-dropdown-list,#plugin-information .fs-dropdown:not(.up) .fs-dropdown-list{-moz-border-radius:3px 0 3px 3px;-webkit-border-radius:3px 0 3px 3px;border-radius:3px 0 3px 3px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.up .fs-dropdown-list,#plugin-information .fs-dropdown.up .fs-dropdown-list{-moz-border-radius:3px 3px 0 3px;-webkit-border-radius:3px 3px 0 3px;border-radius:3px 3px 0 3px}#plugin-information .fs-dropdown .button-group{width:100%}#plugin-information .fs-dropdown .button-group .button{float:none;font-size:14px;font-weight:normal;text-transform:none}#plugin-information .fs-dropdown .fs-dropdown-list{margin-top:1px}#plugin-information .fs-dropdown.up .fs-dropdown-list{top:auto;bottom:100%;margin-bottom:2px}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group{text-align:center;display:table}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button{display:table-cell}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button:not(.fs-dropdown-arrow-button){left:1px;width:100%}#plugin-information-footer>.button,#plugin-information-footer .fs-dropdown{position:relative;top:3px}#plugin-information-footer>.button.left,#plugin-information-footer .fs-dropdown.left{float:left}#plugin-information-footer>.right,#plugin-information-footer .fs-dropdown{float:right}@media screen and (max-width: 961px){#fs_addons .fs-cards-list .fs-card{height:265px}}/*# sourceMappingURL=add-ons.css.map */
 
lib/freemius/assets/css/admin/add-ons.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../../scss/admin/_badge.scss","../../scss/_colors.scss","../../scss/_mixins.scss","../../scss/admin/add-ons.scss"],"names":[],"mappings":"AAAA,UAEI,kBACA,SACA,QACA,WCckB,QDblB,WACA,yBACA,iBEuCC,mBFtCD,YEuCF,sBFvCE,YEwCM,cFxCN,YACA,iBACA,eE6CC,gBF5CD,8BE6CF,mBF7CE,8BE8CM,WF9CN,8BGPA,0BAEI,gBAEA,mCAEI,WAEA,aACA,YACA,UACA,qBACA,eACA,gBACA,sBACA,eACA,kBAEA,+CAEI,kBACA,OACA,QACA,SACA,MACA,UAGJ,6CAEI,sBACA,gBACA,YACA,kBAEA,gDDsIX,gBCpIe,SDqIb,cCrIa,SDsId,eCtIc,SDuIlB,mBCvIkB,SDwIV,WCxIU,SACA,OACA,QACA,MACA,kBAEA,mDAEI,gBACA,iBACA,eACA,WACA,cDoBnB,gBCnBuC,WDoB1C,mBCpB0C,WDqBlC,WCrBkC,WAI5B,6DAEI,UACA,SACA,cACA,cACA,aACA,2BACA,0BD2Gf,gBC1Ge,SD2Gb,cC3Ga,SD4Gd,eC5Gc,SD6GlB,mBC7GkB,SD8GV,WC9GU,SAEA,gGACI,iBACA,kBAIR,uDAEI,kBACA,YACA,gBACA,WACA,mBACA,uBACA,iBAGJ,uDAEI,eAGJ,6DAEI,yBACA,6BACA,0BACA,kBACA,WAGJ,qDAEI,kBACA,SACA,UACA,mBACA,cACA,iBDpDf,gBCqDe,2BDpDlB,mBCoDkB,2BDnDV,WCmDU,2BACA,yBACA,eACA,iBAKA,gIAEI,kBACA,UACA,WAKZ,qCAGQ,qDAEI,yBACA,iBACA,gBAKA,sDAEI,WAQJ,0HAGI,MFnJX,SE8Jb,6BAEI,uBAMJ,yBACI,uBAGJ,oCACI,mBAKA,iZAEI,WAGJ,gDAEI,eAGJ,4DAEI,oBACA,6BAEA,+DAEI,SAEA,kEAEI,UACA,6BAEA,8EAEI,MF3LF,QE4LE,cACA,sBACA,iBACA,WACA,mBAGJ,qEAEI,2BDqCpB,+DACI,WACA,cACA,WC/BI,4DAEI,gBACA,SAEA,+DAEI,YACA,aACA,WACA,mBDjKf,gBCkKmC,YDjKtC,mBCiKsC,YDhK9B,WCgK8B,YAEpB,iEAEI,cACA,WACA,YACA,iBD3LnB,gBC4LmB,2BD3LtB,mBC2LsB,2BD1Ld,WC0Lc,2BACA,sBAGJ,mEAEI,kBAOpB,gDAII,aAEA,6BAKI,4DAEI,aACA,aACA,eAGJ,0EAEI,6BAEA,mFAEI,eACA,kBACA,eACA,eAEA,yFAEI,yBACA,YACA,mBACA,kBACA,UACA,WACA,YACA,yBACA,YACA,kBACA,eACA,gBAGJ,kGAEI,eACA,WA9CA,QA+CA,oBA/CA,QAsDR,4EAEI,WAxDI,QAyDJ,SACA,iBACA,cAGJ,yLAGI,aAIR,0EAEI,WAvEQ,QAwER,aAGJ,iEAEI,WACA,kBACA,iBACA,yBACA,gBAGJ,+DAEI,mBAGJ,6DACI,kBAGJ,4JAGI,kBACA,cACA,iBACA,mBACA,yBACA,mBACA,YACA,sBAGJ,6EAEI,oBACA,YACA,mBAGJ,2EAEI,eAEA,6EAEI,WACA,mBAGJ,8EAEI,kBAQZ,mDAEI,mBAGJ,4CAEI,WACA,iBACA,yBAII,qDAEI,eAGJ,4DAEI,MFzYE,QE0YF,mBACA,cACA,kBAMJ,qDAEI,0BACA,eACA,kBACA,YACA,MFxZE,QE0ZF,iEAEI,gBACA,WACA,cACA,kBAKJ,+DAEI,mBAMhB,qDAEI,WACA,YACA,eAQN,uIACE,kBACA,WACA,MACA,QAEA,mJACE,WAIJ,6JACE,0BACA,mCACA,kCACA,SACA,kBAKF,yMACE,4BAGF,+KACE,6BAKF,6LACE,yBAGF,mKACE,0BAIJ,+HACE,kBACA,WACA,SACA,iBACA,cACA,yBACA,sBACA,UACA,YACA,gBDrcD,gBCscC,iGDrcJ,mBCqcI,iGDpcI,WCocJ,iGAEA,qIACE,SAEA,yIACE,cACA,iBACA,qBACA,iBAGF,iJACE,iBF9dU,QE+dV,WAEA,qJACE,WAON,iJDveD,mBCweG,cDveN,sBCueM,cDteE,cCseF,cAKF,qID7eD,mBC8eG,cD7eN,sBC6eM,cD5eE,cC4eF,cAMJ,+CACE,WAEA,uDACE,WACA,eACA,mBACA,oBAIJ,mDACE,eAIA,sDACE,SACA,YACA,kBAMR,2EACE,kBAEA,cAEA,mFACE,mBAEA,kHACE,SACA,WAMJ,2EACE,kBACA,QAEA,qFACE,WAIJ,0EACE,YAIJ,qCAKY,mCAEI","file":"add-ons.css"}
lib/freemius/assets/css/admin/affiliation.css CHANGED
@@ -1 +1 @@
1
- @charset "UTF-8";#fs_affiliation_content_wrapper #messages{margin-top:25px}#fs_affiliation_content_wrapper h3{font-size:24px;padding:0;margin-left:0}#fs_affiliation_content_wrapper ul li{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;list-style-type:none}#fs_affiliation_content_wrapper ul li:before{content:'';margin-right:10px;font-weight:bold}#fs_affiliation_content_wrapper p:not(.description),#fs_affiliation_content_wrapper li,#fs_affiliation_content_wrapper label{font-size:16px !important;line-height:26px !important}#fs_affiliation_content_wrapper .button{margin-top:20px;margin-bottom:7px;line-height:35px;height:40px;font-size:16px}#fs_affiliation_content_wrapper .button#cancel_button{margin-right:5px}#fs_affiliation_content_wrapper form .input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form .input-container .input-label{font-weight:bold;display:block;width:100%}#fs_affiliation_content_wrapper form .input-container.input-container-text label,#fs_affiliation_content_wrapper form .input-container.input-container-text input,#fs_affiliation_content_wrapper form .input-container.input-container-text textarea{display:block}#fs_affiliation_content_wrapper form .input-container #add_domain,#fs_affiliation_content_wrapper form .input-container .remove-domain{text-decoration:none;display:inline-block;margin-top:3px}#fs_affiliation_content_wrapper form .input-container #add_domain:focus,#fs_affiliation_content_wrapper form .input-container .remove-domain:focus{box-shadow:none}#fs_affiliation_content_wrapper form .input-container #add_domain.disabled,#fs_affiliation_content_wrapper form .input-container .remove-domain.disabled{color:#aaa;cursor:default}#fs_affiliation_content_wrapper form #extra_domains_container .description{margin-top:0;position:relative;top:-4px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain{display:inline-block;margin-right:5px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain:last-of-type{margin-bottom:0}
1
+ #fs_affiliation_content_wrapper #messages{margin-top:25px}#fs_affiliation_content_wrapper h3{font-size:24px;padding:0;margin-left:0}#fs_affiliation_content_wrapper ul li{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;list-style-type:none}#fs_affiliation_content_wrapper ul li:before{content:"";margin-right:10px;font-weight:bold}#fs_affiliation_content_wrapper p:not(.description),#fs_affiliation_content_wrapper li,#fs_affiliation_content_wrapper label{font-size:16px !important;line-height:26px !important}#fs_affiliation_content_wrapper .button{margin-top:20px;margin-bottom:7px;line-height:35px;height:40px;font-size:16px}#fs_affiliation_content_wrapper .button#cancel_button{margin-right:5px}#fs_affiliation_content_wrapper form .input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form .input-container .input-label{font-weight:bold;display:block;width:100%}#fs_affiliation_content_wrapper form .input-container.input-container-text label,#fs_affiliation_content_wrapper form .input-container.input-container-text input,#fs_affiliation_content_wrapper form .input-container.input-container-text textarea{display:block}#fs_affiliation_content_wrapper form .input-container #add_domain,#fs_affiliation_content_wrapper form .input-container .remove-domain{text-decoration:none;display:inline-block;margin-top:3px}#fs_affiliation_content_wrapper form .input-container #add_domain:focus,#fs_affiliation_content_wrapper form .input-container .remove-domain:focus{box-shadow:none}#fs_affiliation_content_wrapper form .input-container #add_domain.disabled,#fs_affiliation_content_wrapper form .input-container .remove-domain.disabled{color:#aaa;cursor:default}#fs_affiliation_content_wrapper form #extra_domains_container .description{margin-top:0;position:relative;top:-4px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain{display:inline-block;margin-right:5px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain:last-of-type{margin-bottom:0}/*# sourceMappingURL=affiliation.css.map */
lib/freemius/assets/css/admin/affiliation.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../../scss/admin/affiliation.scss","../../scss/_mixins.scss"],"names":[],"mappings":"CAGE,0CACE,gBAGF,mCACE,eACA,UACA,cAIA,sCC4DC,gBD3DqB,WC4DxB,mBD5DwB,WC6DhB,WD7DgB,WACpB,qBAEA,6CACE,YACA,kBACA,iBAKN,6HACE,0BACA,4BAGF,wCACE,gBACA,kBACA,iBACA,YACA,eAEA,sDACE,iBAKF,sDAaE,mBAZA,mEACE,iBACA,cACA,WAIA,sPACE,cAMJ,uIACE,qBACA,qBACA,eAEA,mJACE,gBAGF,yJACE,WACA,eAMJ,2EACE,aACA,kBACA,SAGF,4FACE,mBAEA,oGACE,qBACA,iBAEA,iHACE","file":"affiliation.css"}
lib/freemius/assets/css/admin/checkout.css CHANGED
@@ -1 +1 @@
1
- @media screen and (max-width: 782px){#wpbody-content{padding-bottom:0 !important}}
1
+ @media screen and (max-width: 782px){#wpbody-content{padding-bottom:0 !important}}/*# sourceMappingURL=checkout.css.map */
lib/freemius/assets/css/admin/clone-resolution.css ADDED
@@ -0,0 +1 @@
 
1
+ .fs-notice[data-id^=clone_resolution_options_notice]{padding:0;color:inherit !important}.fs-notice[data-id^=clone_resolution_options_notice] .fs-notice-body{padding:0;margin-bottom:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-notice-header{padding:5px 10px}.fs-notice[data-id^=clone_resolution_options_notice] ol{margin-top:0;margin-bottom:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-options-container{display:flex;flex-direction:row;padding:0 10px 10px}@media(max-width: 750px){.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-options-container{flex-direction:column}}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option{border:1px solid #ccc;padding:10px 10px 15px 10px;flex:auto;margin:5px}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option:first-child{margin-left:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option:last-child{margin-right:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option strong{font-size:1.2em;padding:2px;line-height:1.5em}.fs-notice[data-id^=clone_resolution_options_notice] a{text-decoration:none}.fs-notice[data-id^=clone_resolution_options_notice] .button{margin-right:10px}.rtl .fs-notice[data-id^=clone_resolution_options_notice] .button{margin-right:0;margin-left:10px}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-documentation-container{padding:0 10px 15px}.fs-notice[data-id=temporary_duplicate_notice] #fs_clone_resolution_error_message{border:1px solid #d3135a;background:#fee;color:#d3135a;padding:10px}.fs-notice[data-id=temporary_duplicate_notice] ol{margin-top:0}/*# sourceMappingURL=clone-resolution.css.map */
lib/freemius/assets/css/admin/clone-resolution.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../../scss/admin/clone-resolution.scss","../../scss/_colors.scss"],"names":[],"mappings":"AAEA,qDACI,UACA,yBAEA,qEACI,UACA,gBAGJ,uEACI,iBAGJ,wDACI,aACA,gBAGJ,4FACI,aACA,mBACA,oBAEA,yBALJ,4FAMQ,uBAIR,iFACI,sBACA,4BACA,UACA,WAEA,6FACI,cAGJ,4FACI,eAGJ,wFACI,gBACA,YACA,kBAIR,uDACI,qBAGJ,6DACI,kBAEA,kEACI,eACA,iBAIR,uFACI,oBAKJ,kFACI,yBACA,gBACA,MCrDgB,QDsDhB,aAGJ,kDACI","file":"clone-resolution.css"}
lib/freemius/assets/css/admin/common.css CHANGED
@@ -1,2 +1 @@
1
- .fs-badge{position:absolute;top:10px;right:0;background:#71ae00;color:white;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);box-shadow:0 2px 1px -1px rgba(0,0,0,0.3)}.theme-browser .theme .fs-premium-theme-badge-container{position:absolute;right:0;top:0}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge{position:relative;top:0;margin-top:10px;text-align:center}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-premium-theme-badge{font-size:1.1em}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-beta-theme-badge{background:#00a0d2}.fs-switch{position:relative;display:inline-block;color:#ccc;text-shadow:0 1px 1px rgba(255,255,255,0.8);height:18px;padding:6px 6px 5px 6px;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);background:#ececec;box-shadow:0 0 4px rgba(0,0,0,0.1),inset 0 1px 3px 0 rgba(0,0,0,0.1);cursor:pointer}.fs-switch span{display:inline-block;width:35px;text-transform:uppercase}.fs-switch .fs-toggle{position:absolute;top:1px;width:37px;height:25px;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.3);border-radius:4px;background:#fff;background-color:#fff;background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #ececec), color-stop(1, #fff));background-image:-webkit-linear-gradient(top, #ececec, #fff);background-image:-moz-linear-gradient(top, #ececec, #fff);background-image:-ms-linear-gradient(top, #ececec, #fff);background-image:-o-linear-gradient(top, #ececec, #fff);background-image:linear-gradient(top, bottom, #ececec, #fff);box-shadow:inset 0 1px 0 0 rgba(255,255,255,0.5);z-index:999;-moz-transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1);-o-transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1);-ms-transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1);-webkit-transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1);transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1)}.fs-switch.fs-off .fs-toggle{left:2%}.fs-switch.fs-on .fs-toggle{left:54%}.fs-switch.fs-round{top:8px;padding:4px 25px;-moz-border-radius:24px;-webkit-border-radius:24px;border-radius:24px}.fs-switch.fs-round .fs-toggle{top:0;width:24px;height:24px;-moz-border-radius:24px;-webkit-border-radius:24px;border-radius:24px}.fs-switch.fs-round.fs-off .fs-toggle{left:-1px}.fs-switch.fs-round.fs-on{background:#0085ba}.fs-switch.fs-round.fs-on .fs-toggle{left:25px}.fs-switch.fs-small.fs-round{padding:1px 19px}.fs-switch.fs-small.fs-round .fs-toggle{top:0;width:18px;height:18px;-moz-border-radius:18px;-webkit-border-radius:18px;border-radius:18px}.fs-switch.fs-small.fs-round.fs-on .fs-toggle{left:19px}.fs-switch-feedback{margin-left:10px}.fs-switch-feedback.success{color:#71ae00}.rtl .fs-switch-feedback{margin-left:0;margin-right:10px}#fs_frame{line-height:0;font-size:0}.fs-full-size-wrapper{margin:40px 0 -65px -20px}@media (max-width: 600px){.fs-full-size-wrapper{margin:0 0 -65px -10px}}
2
- .fs-notice{position:relative}.fs-notice.fs-has-title{margin-bottom:30px !important}.fs-notice.success{color:green}.fs-notice.promotion{border-color:#00a0d2 !important;background-color:#f2fcff !important}.fs-notice .fs-notice-body{margin:.5em 0;padding:2px}.fs-notice .fs-close{cursor:pointer;color:#aaa;float:right}.fs-notice .fs-close:hover{color:#666}.fs-notice .fs-close>*{margin-top:7px;display:inline-block}.fs-notice label.fs-plugin-title{background:rgba(0,0,0,0.3);color:#fff;padding:2px 10px;position:absolute;top:100%;bottom:auto;right:auto;-moz-border-radius:0 0 3px 3px;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;left:10px;font-size:12px;font-weight:bold;cursor:auto}div.fs-notice.updated,div.fs-notice.success,div.fs-notice.promotion{display:block !important}.rtl .fs-notice .fs-close{float:left}.fs-secure-notice{position:fixed;top:32px;left:160px;right:0;background:#ebfdeb;padding:10px 20px;color:green;z-index:9999;-moz-box-shadow:0 2px 2px rgba(6,113,6,0.3);-webkit-box-shadow:0 2px 2px rgba(6,113,6,0.3);box-shadow:0 2px 2px rgba(6,113,6,0.3);opacity:0.95;filter:alpha(opacity=95)}.fs-secure-notice:hover{opacity:1;filter:alpha(opacity=100)}.fs-secure-notice a.fs-security-proof{color:green;text-decoration:none}@media screen and (max-width: 960px){.fs-secure-notice{left:36px}}@media screen and (max-width: 600px){.fs-secure-notice{display:none}}@media screen and (max-width: 1250px){#fs_promo_tab{display:none}}@media screen and (max-width: 782px){.fs-secure-notice{left:0;top:46px;text-align:center}}span.fs-submenu-item.fs-sub:before{content:'\21B3';padding:0 5px}.rtl span.fs-submenu-item.fs-sub:before{content:'\21B2'}.fs-submenu-item.pricing.upgrade-mode{color:greenyellow}.fs-submenu-item.pricing.trial-mode{color:#83e2ff}#adminmenu .update-plugins.fs-trial{background-color:#00b9eb}.fs-ajax-spinner{border:0;width:20px;height:20px;margin-right:5px;vertical-align:sub;display:inline-block;background:url("/wp-admin/images/wpspin_light-2x.gif");background-size:contain;margin-bottom:-2px}.wrap.fs-section h2{text-align:left}.plugins p.fs-upgrade-notice{border:0;background-color:#d54e21;padding:10px;color:#f9f9f9;margin-top:10px}
1
+ .fs-badge{position:absolute;top:10px;right:0;background:#71ae00;color:#fff;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,.3);box-shadow:0 2px 1px -1px rgba(0,0,0,.3)}.theme-browser .theme .fs-premium-theme-badge-container{position:absolute;right:0;top:0}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge{position:relative;top:0;margin-top:10px;text-align:center}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-premium-theme-badge{font-size:1.1em}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-beta-theme-badge{background:#00a0d2}.fs-switch{position:relative;display:inline-block;color:#ccc;text-shadow:0 1px 1px rgba(255,255,255,.8);height:18px;padding:6px 6px 5px 6px;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);background:#ececec;box-shadow:0 0 4px rgba(0,0,0,.1),inset 0 1px 3px 0 rgba(0,0,0,.1);cursor:pointer}.fs-switch span{display:inline-block;width:35px;text-transform:uppercase}.fs-switch .fs-toggle{position:absolute;top:1px;width:37px;height:25px;border:1px solid #ccc;border:1px solid rgba(0,0,0,.3);border-radius:4px;background:#fff;background-color:#fff;background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #ececec), color-stop(1, #fff));background-image:-webkit-linear-gradient(top, #ececec, #fff);background-image:-moz-linear-gradient(top, #ececec, #fff);background-image:-ms-linear-gradient(top, #ececec, #fff);background-image:-o-linear-gradient(top, #ececec, #fff);background-image:linear-gradient(top, bottom, #ececec, #fff);box-shadow:inset 0 1px 0 0 rgba(255,255,255,.5);z-index:999;-moz-transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1);-o-transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1);-ms-transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1);-webkit-transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1);transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1)}.fs-switch.fs-off .fs-toggle{left:2%}.fs-switch.fs-on .fs-toggle{left:54%}.fs-switch.fs-round{top:8px;padding:4px 25px;-moz-border-radius:24px;-webkit-border-radius:24px;border-radius:24px}.fs-switch.fs-round .fs-toggle{top:0;width:24px;height:24px;-moz-border-radius:24px;-webkit-border-radius:24px;border-radius:24px}.fs-switch.fs-round.fs-off .fs-toggle{left:-1px}.fs-switch.fs-round.fs-on{background:#0085ba}.fs-switch.fs-round.fs-on .fs-toggle{left:25px}.fs-switch.fs-small.fs-round{padding:1px 19px}.fs-switch.fs-small.fs-round .fs-toggle{top:0;width:18px;height:18px;-moz-border-radius:18px;-webkit-border-radius:18px;border-radius:18px}.fs-switch.fs-small.fs-round.fs-on .fs-toggle{left:19px}.fs-switch-feedback{margin-left:10px}.fs-switch-feedback.success{color:#71ae00}.rtl .fs-switch-feedback{margin-left:0;margin-right:10px}#fs_frame{line-height:0;font-size:0}.fs-full-size-wrapper{margin:40px 0 -65px -20px}@media(max-width: 600px){.fs-full-size-wrapper{margin:0 0 -65px -10px}}.fs-notice{position:relative}.fs-notice.fs-has-title{margin-bottom:30px !important}.fs-notice.success{color:green}.fs-notice.promotion{border-color:#00a0d2 !important;background-color:#f2fcff !important}.fs-notice .fs-notice-body{margin:.5em 0;padding:2px}.fs-notice .fs-close{cursor:pointer;color:#aaa;float:right}.fs-notice .fs-close:hover{color:#666}.fs-notice .fs-close>*{margin-top:7px;display:inline-block}.fs-notice label.fs-plugin-title{background:rgba(0,0,0,.3);color:#fff;padding:2px 10px;position:absolute;top:100%;bottom:auto;right:auto;-moz-border-radius:0 0 3px 3px;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;left:10px;font-size:12px;font-weight:bold;cursor:auto}div.fs-notice.updated,div.fs-notice.success,div.fs-notice.promotion{display:block !important}.rtl .fs-notice .fs-close{float:left}.fs-secure-notice{position:fixed;top:32px;left:160px;right:0;background:#ebfdeb;padding:10px 20px;color:green;z-index:9999;-moz-box-shadow:0 2px 2px rgba(6,113,6,.3);-webkit-box-shadow:0 2px 2px rgba(6,113,6,.3);box-shadow:0 2px 2px rgba(6,113,6,.3);opacity:.95;filter:alpha(opacity=95)}.fs-secure-notice:hover{opacity:1;filter:alpha(opacity=100)}.fs-secure-notice a.fs-security-proof{color:green;text-decoration:none}@media screen and (max-width: 960px){.fs-secure-notice{left:36px}}@media screen and (max-width: 600px){.fs-secure-notice{display:none}}@media screen and (max-width: 1250px){#fs_promo_tab{display:none}}@media screen and (max-width: 782px){.fs-secure-notice{left:0;top:46px;text-align:center}}span.fs-submenu-item.fs-sub:before{content:"↳";padding:0 5px}.rtl span.fs-submenu-item.fs-sub:before{content:"↲"}.fs-submenu-item.pricing.upgrade-mode{color:#adff2f}.fs-submenu-item.pricing.trial-mode{color:#83e2ff}#adminmenu .update-plugins.fs-trial{background-color:#00b9eb}.fs-ajax-spinner{border:0;width:20px;height:20px;margin-right:5px;vertical-align:sub;display:inline-block;background:url("/wp-admin/images/wpspin_light-2x.gif");background-size:contain;margin-bottom:-2px}.wrap.fs-section h2{text-align:left}.plugins p.fs-upgrade-notice{border:0;background-color:#d54e21;padding:10px;color:#f9f9f9;margin-top:10px}/*# sourceMappingURL=common.css.map */
 
lib/freemius/assets/css/admin/common.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../../scss/admin/_badge.scss","../../scss/_colors.scss","../../scss/_mixins.scss","../../scss/admin/_themes.scss","../../scss/admin/_switch.scss","../../scss/admin/common.scss","../../scss/admin/_plugin-upgrade-notice.scss"],"names":[],"mappings":"CAAA,UAEI,kBACA,SACA,QACA,WCckB,QDblB,WACA,yBACA,iBEuCC,mBFtCD,YEuCF,sBFvCE,YEwCM,cFxCN,YACA,iBACA,eE6CC,gBF5CD,8BE6CF,mBF7CE,8BE8CM,WF9CN,8BGNI,wDACI,kBACA,QACA,MAEA,kEACI,kBACA,MACA,gBACA,kBAEA,yFACI,gBAGJ,sFACI,mBClBpB,WAEI,kBACA,qBAGA,WACA,2CACA,YACA,wBACA,sBACA,gCAEA,mBACA,mEACA,eAEA,gBAEI,qBACA,WACA,yBAGJ,sBAEI,kBACA,QACA,WACA,YACA,sBACA,gCACA,kBACA,gBFoDS,iBEnDyB,KFqDxC,8GACA,6DACA,0DACA,yDACA,wDACA,6DEzDM,gDACA,YFsIH,gBErIG,oCFsID,cEtIC,oCFuIF,eEvIE,oCFwIN,mBExIM,oCFyIE,WEzIF,oCAGJ,6BAEI,QAEJ,4BAEI,SAIJ,oBAEI,QACA,iBFVH,mBEWG,KFVN,sBEUM,KFTE,cESF,KAEA,+BAEI,MACA,MA/DE,KAgEF,OAhEE,KF+CT,mBEkBO,KFjBV,sBEiBU,KFhBF,cEgBE,KAGJ,sCAEI,UAGJ,0BAEI,WHjByB,QGmBzB,qCAEI,UAOR,6BAEI,iBAEA,wCAEI,MACA,MA5FI,KA6FJ,OA7FI,KF8Cf,mBEgDW,KF/Cd,sBE+Cc,KF9CN,cE8CM,KAGJ,8CAEI,UAMhB,oBACI,iBAEA,4BACI,MH3Fc,QG8FlB,yBAEI,cACA,kBChHR,UAEI,cACA,YAGJ,sBAEI,0BAEA,yBAJJ,sBAKQ,wBAIR,WAEI,kBAEA,wBAEI,8BAGJ,mBAEI,YAIJ,qBAEI,gCACA,oCAGJ,2BAEI,cACA,YAGJ,qBASI,eACA,WACA,YAEA,2BAEI,WAIJ,uBAEI,eACA,qBAIR,iCAEI,0BACA,WACA,iBACA,kBACA,SACA,YACA,WHjCH,mBGkCG,YHjCN,sBGiCM,YHhCE,cGgCF,YACA,UACA,eACA,iBACA,YAMJ,oEAII,yBAMJ,0BAMI,WAIR,kBAEI,eACA,SACA,WACA,QACA,mBACA,kBACA,YACA,aHhEC,gBGiED,2BHhEF,mBGgEE,2BH/DM,WG+DN,2BHuHA,QGtHiB,IHwHjB,yBGtHA,wBHoHA,QGlHqB,EHoHrB,0BGjHA,sCAEI,YACA,qBAIR,qCACI,kBAEI,WAIR,qCACI,kBAEI,cAIR,sCACI,cAEI,cAIR,qCACI,kBAEI,OACA,SACA,mBAIR,mCAGI,YACA,cAKA,wCAGI,YAQA,sCAEI,cAGJ,oCAEI,cAKZ,oCAEI,yBAEJ,iBAEI,SACA,WACA,YACA,iBACA,mBACA,qBACA,uDACA,wBACA,mBAIA,oBACI,gBCzNR,6BAEI,SACA,yBACA,aACA,cACA","file":"common.css"}
lib/freemius/assets/css/admin/connect.css CHANGED
@@ -1 +1 @@
1
- #fs_connect{width:480px;-moz-box-shadow:0px 1px 2px rgba(0,0,0,0.3);-webkit-box-shadow:0px 1px 2px rgba(0,0,0,0.3);box-shadow:0px 1px 2px rgba(0,0,0,0.3);margin:20px 0}@media screen and (max-width: 479px){#fs_connect{-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none;width:auto;margin:0 0 0 -10px}}#fs_connect .fs-content{background:#fff;padding:15px 20px}#fs_connect .fs-content .fs-error{background:snow;color:#d3135a;border:1px solid #d3135a;-moz-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);text-align:center;padding:5px;margin-bottom:10px}#fs_connect .fs-content p{margin:0;padding:0;font-size:1.2em}#fs_connect .fs-license-key-container{position:relative;width:280px;margin:10px auto 0 auto}#fs_connect .fs-license-key-container input{width:100%}#fs_connect .fs-license-key-container .dashicons{position:absolute;top:5px;right:5px}#fs_connect.require-license-key .fs-sites-list-container td{cursor:pointer}#fs_connect #delegate_to_site_admins{margin-right:15px;float:right;height:26px;vertical-align:middle;line-height:37px;font-weight:bold;border-bottom:1px dashed;text-decoration:none}#fs_connect #delegate_to_site_admins.rtl{margin-left:15px;margin-right:0}#fs_connect .fs-actions{padding:10px 20px;background:#C0C7CA}#fs_connect .fs-actions .button{padding:0 10px 1px;line-height:35px;height:37px;font-size:16px;margin-bottom:0}#fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}#fs_connect .fs-actions .button.button-primary{padding-right:15px;padding-left:15px}#fs_connect .fs-actions .button.button-primary:after{content:' \279C'}#fs_connect .fs-actions .button.button-primary.fs-loading:after{content:''}#fs_connect .fs-actions .button.button-secondary{float:right}#fs_connect.fs-anonymous-disabled .fs-actions .button.button-primary{width:100%}#fs_connect .fs-permissions{padding:10px 20px;background:#FEFEFE;-moz-transition:background 0.5s ease;-o-transition:background 0.5s ease;-ms-transition:background 0.5s ease;-webkit-transition:background 0.5s ease;transition:background 0.5s ease}#fs_connect .fs-permissions .fs-license-sync-disclaimer{text-align:center;margin-top:0}#fs_connect .fs-permissions>.fs-trigger{font-size:0.9em;text-decoration:none;text-align:center;display:block}#fs_connect .fs-permissions ul{height:0;overflow:hidden;margin:0}#fs_connect .fs-permissions ul li{margin-bottom:12px}#fs_connect .fs-permissions ul li:last-child{margin-bottom:0}#fs_connect .fs-permissions ul li>i.dashicons{float:left;font-size:40px;width:40px;height:40px}#fs_connect .fs-permissions ul li .fs-switch{float:right}#fs_connect .fs-permissions ul li .fs-permission-description{margin-left:55px}#fs_connect .fs-permissions ul li .fs-permission-description span{font-weight:bold;text-transform:uppercase;color:#23282d}#fs_connect .fs-permissions ul li .fs-permission-description p{margin:2px 0 0 0}#fs_connect .fs-permissions.fs-open{background:#fff}#fs_connect .fs-permissions.fs-open ul{overflow:initial;height:auto;margin:20px 20px 10px 20px}@media screen and (max-width: 479px){#fs_connect .fs-permissions{background:#fff}#fs_connect .fs-permissions .fs-trigger{display:none}#fs_connect .fs-permissions ul{height:auto;margin:20px}}#fs_connect .fs-freemium-licensing{padding:8px;background:#777;color:#fff}#fs_connect .fs-freemium-licensing p{text-align:center;display:block;margin:0;padding:0}#fs_connect .fs-freemium-licensing a{color:#C2EEFF;text-decoration:underline}#fs_connect .fs-visual{padding:12px;line-height:0;background:#fafafa;height:80px;position:relative}#fs_connect .fs-visual .fs-site-icon{position:absolute;left:20px;top:10px}#fs_connect .fs-visual .fs-connect-logo{position:absolute;right:20px;top:10px}#fs_connect .fs-visual .fs-plugin-icon{position:absolute;top:10px;left:50%;margin-left:-40px}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-site-icon,#fs_connect .fs-visual img,#fs_connect .fs-visual object{width:80px;height:80px}#fs_connect .fs-visual .dashicons-wordpress{font-size:64px;background:#01749a;color:#fff;width:64px;height:64px;padding:8px}#fs_connect .fs-visual .dashicons-plus{position:absolute;top:50%;font-size:30px;margin-top:-10px;color:#bbb}#fs_connect .fs-visual .dashicons-plus.fs-first{left:28%}#fs_connect .fs-visual .dashicons-plus.fs-second{left:65%}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-connect-logo,#fs_connect .fs-visual .fs-site-icon{border:1px solid #ccc;padding:1px;background:#fff}#fs_connect .fs-terms{text-align:center;font-size:0.85em;padding:5px;background:rgba(0,0,0,0.05)}#fs_connect .fs-terms,#fs_connect .fs-terms a{color:#999}#fs_connect .fs-terms a{text-decoration:none}.fs-multisite-options-container{margin-top:10px;border:1px solid #ccc;padding:5px}.fs-multisite-options-container a{text-decoration:none}.fs-multisite-options-container a:focus{box-shadow:none}.fs-multisite-options-container a.selected{font-weight:bold}.fs-multisite-options-container.fs-apply-on-all-sites{border:0 none;padding:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options{border-spacing:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options td:not(:first-child){display:none}.fs-multisite-options-container .fs-sites-list-container{display:none;overflow:auto}.fs-multisite-options-container .fs-sites-list-container table td{border-top:1px solid #ccc;padding:4px 2px}.fs-tooltip-trigger{position:relative}.fs-tooltip-trigger:not(a){cursor:help}.fs-tooltip-trigger .fs-tooltip{opacity:0;visibility:hidden;-moz-transition:opacity 0.3s ease-in-out;-o-transition:opacity 0.3s ease-in-out;-ms-transition:opacity 0.3s ease-in-out;-webkit-transition:opacity 0.3s ease-in-out;transition:opacity 0.3s ease-in-out;position:absolute;background:rgba(0,0,0,0.8);color:#fff !important;font-family:'arial', serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:-17px;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.2);box-shadow:1px 1px 1px rgba(0,0,0,0.2);line-height:1.3em;font-weight:bold;text-align:left;text-transform:none !important}.rtl .fs-tooltip-trigger .fs-tooltip{text-align:right;left:auto;right:-17px}.fs-tooltip-trigger .fs-tooltip::after{content:' ';display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:rgba(0,0,0,0.8) transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl .fs-tooltip-trigger .fs-tooltip::after{right:21px;left:auto}.fs-tooltip-trigger:hover .fs-tooltip{visibility:visible;opacity:1}#fs_marketing_optin{display:none;margin-top:10px;border:1px solid #ccc;padding:10px;line-height:1.5em}#fs_marketing_optin .fs-message{display:block;margin-bottom:5px;font-size:1.05em;font-weight:600}#fs_marketing_optin.error{border:1px solid #d3135a;background:#fee}#fs_marketing_optin.error .fs-message{color:#d3135a}#fs_marketing_optin .fs-input-container{margin-top:5px}#fs_marketing_optin .fs-input-container label{margin-top:5px;display:block}#fs_marketing_optin .fs-input-container label input{float:left;margin:1px 0 0 0}#fs_marketing_optin .fs-input-container label:first-child{display:block;margin-bottom:2px}#fs_marketing_optin .fs-input-label{display:block;margin-left:20px}#fs_marketing_optin .fs-input-label .underlined{text-decoration:underline}.rtl #fs_marketing_optin .fs-input-container label input{float:right}.rtl #fs_marketing_optin .fs-input-label{margin-left:0;margin-right:20px}.rtl #fs_connect .fs-actions{padding:10px 20px;background:#C0C7CA}.rtl #fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}.rtl #fs_connect .fs-actions .button.button-primary:after{content:' \000bb'}.rtl #fs_connect .fs-actions .button.button-primary.fs-loading:after{content:''}.rtl #fs_connect .fs-actions .button.button-secondary{float:left}.rtl #fs_connect .fs-permissions ul li .fs-permission-description{margin-right:55px;margin-left:0}.rtl #fs_connect .fs-permissions ul li .fs-switch{float:left}.rtl #fs_connect .fs-permissions ul li i.dashicons{float:right}.rtl #fs_connect .fs-visual .fs-site-icon{right:20px;left:auto}.rtl #fs_connect .fs-visual .fs-connect-logo{right:auto;left:20px}#fs_theme_connect_wrapper{position:fixed;top:0;height:100%;width:100%;z-index:99990;background:rgba(0,0,0,0.75);text-align:center;overflow-y:auto}#fs_theme_connect_wrapper:before{content:"";display:inline-block;vertical-align:middle;height:100%}#fs_theme_connect_wrapper>button.close{color:white;cursor:pointer;height:40px;width:40px;position:absolute;right:0;border:0;background-color:transparent;top:32px}#fs_theme_connect_wrapper #fs_connect{top:0;text-align:left;display:inline-block;vertical-align:middle;margin-top:52px;margin-bottom:20px}#fs_theme_connect_wrapper #fs_connect .fs-terms{background:rgba(140,140,140,0.64)}#fs_theme_connect_wrapper #fs_connect .fs-terms,#fs_theme_connect_wrapper #fs_connect .fs-terms a{color:#c5c5c5}.wp-pointer-content #fs_connect{margin:0;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none}.fs-opt-in-pointer .wp-pointer-content{padding:0}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow{border-bottom-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow-inner{border-bottom-color:#fafafa}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow{border-top-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow-inner{border-top-color:#fafafa}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow{border-right-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow-inner{border-right-color:#fafafa}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow{border-left-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow-inner{border-left-color:#fafafa}#license_issues_link{display:block;text-align:center;font-size:0.9em;margin-top:10px}
1
+ #fs_connect{width:480px;-moz-box-shadow:0px 1px 2px rgba(0,0,0,.3);-webkit-box-shadow:0px 1px 2px rgba(0,0,0,.3);box-shadow:0px 1px 2px rgba(0,0,0,.3);margin:20px 0}@media screen and (max-width: 479px){#fs_connect{-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none;width:auto;margin:0 0 0 -10px}}#fs_connect .fs-content{background:#fff;padding:15px 20px}#fs_connect .fs-content .fs-error{background:snow;color:#d3135a;border:1px solid #d3135a;-moz-box-shadow:0 1px 1px 0 rgba(0,0,0,.1);-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,.1);box-shadow:0 1px 1px 0 rgba(0,0,0,.1);text-align:center;padding:5px;margin-bottom:10px}#fs_connect .fs-content p{margin:0;padding:0;font-size:1.2em}#fs_connect .fs-license-key-container{position:relative;width:280px;margin:10px auto 0 auto}#fs_connect .fs-license-key-container input{width:100%}#fs_connect .fs-license-key-container .dashicons{position:absolute;top:5px;right:5px}#fs_connect.require-license-key .fs-sites-list-container td{cursor:pointer}#fs_connect #delegate_to_site_admins{margin-right:15px;float:right;height:26px;vertical-align:middle;line-height:37px;font-weight:bold;border-bottom:1px dashed;text-decoration:none}#fs_connect #delegate_to_site_admins.rtl{margin-left:15px;margin-right:0}#fs_connect .fs-actions{padding:10px 20px;background:#c0c7ca}#fs_connect .fs-actions .button{padding:0 10px 1px;line-height:35px;height:37px;font-size:16px;margin-bottom:0}#fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}#fs_connect .fs-actions .button.button-primary{padding-right:15px;padding-left:15px}#fs_connect .fs-actions .button.button-primary:after{content:" ➜"}#fs_connect .fs-actions .button.button-primary.fs-loading:after{content:""}#fs_connect .fs-actions .button.button-secondary{float:right}#fs_connect.fs-anonymous-disabled .fs-actions .button.button-primary{width:100%}#fs_connect .fs-permissions{padding:10px 20px;background:#fefefe;-moz-transition:background .5s ease;-o-transition:background .5s ease;-ms-transition:background .5s ease;-webkit-transition:background .5s ease;transition:background .5s ease}#fs_connect .fs-permissions .fs-license-sync-disclaimer{text-align:center;margin-top:0}#fs_connect .fs-permissions>.fs-trigger{font-size:.9em;text-decoration:none;text-align:center;display:block}#fs_connect .fs-permissions ul{height:0;overflow:hidden;margin:0}#fs_connect .fs-permissions ul li{margin-bottom:12px}#fs_connect .fs-permissions ul li:last-child{margin-bottom:0}#fs_connect .fs-permissions ul li>i.dashicons{float:left;font-size:40px;width:40px;height:40px}#fs_connect .fs-permissions ul li .fs-switch{float:right}#fs_connect .fs-permissions ul li .fs-permission-description{margin-left:55px}#fs_connect .fs-permissions ul li .fs-permission-description span{font-weight:bold;text-transform:uppercase;color:#23282d}#fs_connect .fs-permissions ul li .fs-permission-description p{margin:2px 0 0 0}#fs_connect .fs-permissions.fs-open{background:#fff}#fs_connect .fs-permissions.fs-open ul{overflow:initial;height:auto;margin:20px 20px 10px 20px}@media screen and (max-width: 479px){#fs_connect .fs-permissions{background:#fff}#fs_connect .fs-permissions .fs-trigger{display:none}#fs_connect .fs-permissions ul{height:auto;margin:20px}}#fs_connect .fs-freemium-licensing{padding:8px;background:#777;color:#fff}#fs_connect .fs-freemium-licensing p{text-align:center;display:block;margin:0;padding:0}#fs_connect .fs-freemium-licensing a{color:#c2eeff;text-decoration:underline}#fs_connect .fs-visual{padding:12px;line-height:0;background:#fafafa;height:80px;position:relative}#fs_connect .fs-visual .fs-site-icon{position:absolute;left:20px;top:10px}#fs_connect .fs-visual .fs-connect-logo{position:absolute;right:20px;top:10px}#fs_connect .fs-visual .fs-plugin-icon{position:absolute;top:10px;left:50%;margin-left:-40px}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-site-icon,#fs_connect .fs-visual img,#fs_connect .fs-visual object{width:80px;height:80px}#fs_connect .fs-visual .dashicons-wordpress{font-size:64px;background:#01749a;color:#fff;width:64px;height:64px;padding:8px}#fs_connect .fs-visual .dashicons-plus{position:absolute;top:50%;font-size:30px;margin-top:-10px;color:#bbb}#fs_connect .fs-visual .dashicons-plus.fs-first{left:28%}#fs_connect .fs-visual .dashicons-plus.fs-second{left:65%}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-connect-logo,#fs_connect .fs-visual .fs-site-icon{border:1px solid #ccc;padding:1px;background:#fff}#fs_connect .fs-terms{text-align:center;font-size:.85em;padding:5px;background:rgba(0,0,0,.05)}#fs_connect .fs-terms,#fs_connect .fs-terms a{color:#999}#fs_connect .fs-terms a{text-decoration:none}.fs-multisite-options-container{margin-top:10px;border:1px solid #ccc;padding:5px}.fs-multisite-options-container a{text-decoration:none}.fs-multisite-options-container a:focus{box-shadow:none}.fs-multisite-options-container a.selected{font-weight:bold}.fs-multisite-options-container.fs-apply-on-all-sites{border:0 none;padding:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options{border-spacing:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options td:not(:first-child){display:none}.fs-multisite-options-container .fs-sites-list-container{display:none;overflow:auto}.fs-multisite-options-container .fs-sites-list-container table td{border-top:1px solid #ccc;padding:4px 2px}.fs-tooltip-trigger{position:relative}.fs-tooltip-trigger:not(a){cursor:help}.fs-tooltip-trigger .fs-tooltip{opacity:0;visibility:hidden;-moz-transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;-ms-transition:opacity .3s ease-in-out;-webkit-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;position:absolute;background:rgba(0,0,0,.8);color:#fff !important;font-family:"arial",serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:-17px;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.2);box-shadow:1px 1px 1px rgba(0,0,0,.2);line-height:1.3em;font-weight:bold;text-align:left;text-transform:none !important}.rtl .fs-tooltip-trigger .fs-tooltip{text-align:right;left:auto;right:-17px}.fs-tooltip-trigger .fs-tooltip::after{content:" ";display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:rgba(0,0,0,.8) transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl .fs-tooltip-trigger .fs-tooltip::after{right:21px;left:auto}.fs-tooltip-trigger:hover .fs-tooltip{visibility:visible;opacity:1}#fs_marketing_optin{display:none;margin-top:10px;border:1px solid #ccc;padding:10px;line-height:1.5em}#fs_marketing_optin .fs-message{display:block;margin-bottom:5px;font-size:1.05em;font-weight:600}#fs_marketing_optin.error{border:1px solid #d3135a;background:#fee}#fs_marketing_optin.error .fs-message{color:#d3135a}#fs_marketing_optin .fs-input-container{margin-top:5px}#fs_marketing_optin .fs-input-container label{margin-top:5px;display:block}#fs_marketing_optin .fs-input-container label input{float:left;margin:1px 0 0 0}#fs_marketing_optin .fs-input-container label:first-child{display:block;margin-bottom:2px}#fs_marketing_optin .fs-input-label{display:block;margin-left:20px}#fs_marketing_optin .fs-input-label .underlined{text-decoration:underline}.rtl #fs_marketing_optin .fs-input-container label input{float:right}.rtl #fs_marketing_optin .fs-input-label{margin-left:0;margin-right:20px}.rtl #fs_connect .fs-actions{padding:10px 20px;background:#c0c7ca}.rtl #fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}.rtl #fs_connect .fs-actions .button.button-primary:after{content:" »"}.rtl #fs_connect .fs-actions .button.button-primary.fs-loading:after{content:""}.rtl #fs_connect .fs-actions .button.button-secondary{float:left}.rtl #fs_connect .fs-permissions ul li .fs-permission-description{margin-right:55px;margin-left:0}.rtl #fs_connect .fs-permissions ul li .fs-switch{float:left}.rtl #fs_connect .fs-permissions ul li i.dashicons{float:right}.rtl #fs_connect .fs-visual .fs-site-icon{right:20px;left:auto}.rtl #fs_connect .fs-visual .fs-connect-logo{right:auto;left:20px}#fs_theme_connect_wrapper{position:fixed;top:0;height:100%;width:100%;z-index:99990;background:rgba(0,0,0,.75);text-align:center;overflow-y:auto}#fs_theme_connect_wrapper:before{content:"";display:inline-block;vertical-align:middle;height:100%}#fs_theme_connect_wrapper>button.close{color:#fff;cursor:pointer;height:40px;width:40px;position:absolute;right:0;border:0;background-color:transparent;top:32px}#fs_theme_connect_wrapper #fs_connect{top:0;text-align:left;display:inline-block;vertical-align:middle;margin-top:52px;margin-bottom:20px}#fs_theme_connect_wrapper #fs_connect .fs-terms{background:rgba(140,140,140,.64)}#fs_theme_connect_wrapper #fs_connect .fs-terms,#fs_theme_connect_wrapper #fs_connect .fs-terms a{color:#c5c5c5}.wp-pointer-content #fs_connect{margin:0;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none}.fs-opt-in-pointer .wp-pointer-content{padding:0}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow{border-bottom-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow-inner{border-bottom-color:#fafafa}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow{border-top-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow-inner{border-top-color:#fafafa}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow{border-right-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow-inner{border-right-color:#fafafa}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow{border-left-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow-inner{border-left-color:#fafafa}#license_issues_link{display:block;text-align:center;font-size:.9em;margin-top:10px}/*# sourceMappingURL=connect.css.map */
lib/freemius/assets/css/admin/connect.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../../scss/admin/connect.scss","../../scss/_mixins.scss","../../scss/_colors.scss","../../scss/admin/_multisite-options.scss","../../scss/admin/_tooltip.scss","../../scss/admin/_gdpr-consent.scss"],"names":[],"mappings":"CAIA,YAEI,MAJS,MCsDR,gBDjDD,2BCkDF,mBDlDE,2BCmDM,WDnDN,2BACA,cAEA,qCANJ,YCoDK,gBD7CG,KC8CN,mBD9CM,KC+CE,WD/CF,KACA,WACA,oBAGJ,wBAEI,gBACA,kBAEA,kCACI,gBACA,MEHY,QFIZ,yBCgCP,gBD/BO,2BCgCV,mBDhCU,2BCiCF,WDjCE,2BACA,kBACA,YACA,mBAGJ,0BAEI,SACA,UACA,gBAIR,sCACI,kBACA,YACA,wBAEA,4CACI,WAGJ,iDACI,kBACA,QACA,UAMA,4DACI,eAKZ,qCACI,kBACA,YACA,YACA,sBACA,iBACA,iBACA,yBACA,qBAEA,yCACI,iBACA,eAIR,wBAEI,kBACA,mBAEA,gCAEI,mBACA,iBACA,YACA,eACA,gBAEA,2CAEI,eACA,iBACA,kBAGJ,+CAEI,mBACA,kBAEA,qDAEI,aAKA,gEAEI,WAKZ,iDAEI,YAiBJ,qEAEI,WAKZ,4BAEI,kBACA,mBC2BH,gBDzBG,oBC0BD,cD1BC,oBC2BF,eD3BE,oBC4BN,mBD5BM,oBC6BE,WD7BF,oBAEA,wDACI,kBACA,aAGJ,wCAEI,eACA,qBACA,kBACA,cAGJ,+BAEI,SACA,gBACA,SAEA,kCAEI,mBAEA,6CAEI,gBAGJ,8CAEI,WACA,eACA,WACA,YAGJ,6CACI,YAGJ,6DAEI,iBAEA,kEAEI,iBACA,yBACA,cAGJ,+DAEI,iBAMhB,oCAEI,gBAEA,uCAEI,iBACA,YACA,2BAIR,qCA9EJ,4BA+EQ,gBAEA,wCAEI,aAGJ,+BAEI,YACA,aAKZ,mCACI,YAEA,gBACA,WAEA,qCACI,kBACA,cACA,SACA,UAGJ,qCACI,cACA,0BAQR,uBAEI,aACA,cACA,mBACA,OATQ,KAUR,kBAEA,qCAEI,kBACA,UACA,IAdI,KAiBR,wCAEI,kBACA,WACA,IArBI,KAwBR,uCAEI,kBACA,IA3BI,KA4BJ,SACA,kBAGJ,qIAKI,MAvCI,KAwCJ,OAxCI,KA2CR,4CAEI,eACA,WE/OM,QFgPN,WACA,WACA,YACA,QAjDU,IAoDd,uCAEI,kBACA,QACA,eACA,iBACA,WAEA,gDAEI,SAEJ,iDAEI,SAIR,oHAII,sBACA,YACA,gBAIR,sBAEI,kBACA,gBACA,YACA,2BAEA,8CAEI,WAGJ,wBAEI,qBGhWZ,gCACI,gBACA,sBACA,YAEA,kCACI,qBAEA,wCACI,gBAGJ,2CACI,iBAIR,sDACI,cACA,UAEA,4EACI,iBAEA,iGACI,aAKZ,yDACI,aACA,cAEA,kEACI,0BACA,gBCpCZ,oBAOI,kBALA,2BAEI,YAKJ,gCAEI,UACA,kBHkKH,gBGjKG,wBHkKD,cGlKC,wBHmKF,eGnKE,wBHoKN,mBGpKM,wBHqKE,WGrKF,wBACA,kBACA,WFqEY,eEpEZ,sBACA,0BACA,eACA,aACA,eACA,YACA,kBACA,WACA,QHuBH,mBGtBG,IHuBN,sBGvBM,IHwBE,cGxBF,IH+BH,gBG9BG,2BH+BN,mBG/BM,2BHgCE,WGhCF,2BACA,kBACA,iBACA,gBACA,+BAEA,qCAEI,iBACA,UACA,YAGJ,uCAEI,YACA,cACA,QACA,SACA,mBACA,2BACA,gEACA,kBACA,SACA,UAEA,4CAEI,WACA,UAOR,sCAEI,mBACA,UCjEZ,oBAEI,aACA,gBACA,sBACA,aACA,kBAEA,gCAEI,cACA,kBACA,iBACA,gBAGJ,0BAEI,yBACA,gBAEA,sCAEI,MHHY,QGOpB,wCAEI,eAEA,8CAEI,eACA,cAEA,oDAEI,WACA,iBAGJ,0DAEI,cACA,kBAKZ,oCAEI,cACA,iBAEA,gDAEI,0BAWA,yDAEI,YAIR,yCAEI,cACA,kBLgSJ,6BAEI,kBACA,mBAII,gDAEI,eACA,iBACA,kBAKA,0DAEI,aAKA,qEAEI,WAKZ,sDAEI,WAWA,kEAEI,kBACA,cAGJ,kDACI,WAGJ,mDAEI,YASZ,0CAEI,WACA,UAGJ,6CAEI,WACA,UAMhB,0BACI,eACA,MACA,YACA,WACA,cACA,2BACA,kBACA,gBAEA,iCACI,WACA,qBACA,sBACA,YAGJ,uCACI,WACA,eACA,YACA,WACA,kBACA,QACA,SACA,6BACA,SAGJ,sCACI,MACA,gBACA,qBACA,sBACA,gBACA,mBAEA,gDAEI,iCAEA,kGAEI,cAQZ,gCAEI,SCzbH,gBD0bG,KCzbN,mBDybM,KCxbE,WDwbF,KAMJ,uCAEI,UAKA,oDAEI,4BAEJ,0DAEI,4BAMJ,uDAEI,yBAEJ,6DAEI,yBAMJ,qDAEI,2BAEJ,2DAEI,2BAMJ,sDAEI,0BAEJ,4DAEI,0BAKZ,qBACI,cACA,kBACA,eACA","file":"connect.css"}
lib/freemius/assets/css/admin/debug.css CHANGED
@@ -1 +1 @@
1
- .fs-switch-label{font-size:20px;line-height:31px;margin:0 5px}#fs_log_book table{font-family:Consolas,Monaco,monospace;font-size:12px}#fs_log_book table th{color:#ccc}#fs_log_book table tr{background:#232525}#fs_log_book table tr.alternate{background:#2b2b2b}#fs_log_book table tr td.fs-col--logger{color:#5a7435}#fs_log_book table tr td.fs-col--type{color:#ffc861}#fs_log_book table tr td.fs-col--function{color:#a7b7b1;font-weight:bold}#fs_log_book table tr td.fs-col--message,#fs_log_book table tr td.fs-col--message a{color:#9a73ac !important}#fs_log_book table tr td.fs-col--file{color:#d07922}#fs_log_book table tr td.fs-col--timestamp{color:#6596be}
1
+ .fs-switch-label{font-size:20px;line-height:31px;margin:0 5px}#fs_log_book table{font-family:Consolas,Monaco,monospace;font-size:12px}#fs_log_book table th{color:#ccc}#fs_log_book table tr{background:#232525}#fs_log_book table tr.alternate{background:#2b2b2b}#fs_log_book table tr td.fs-col--logger{color:#5a7435}#fs_log_book table tr td.fs-col--type{color:#ffc861}#fs_log_book table tr td.fs-col--function{color:#a7b7b1;font-weight:bold}#fs_log_book table tr td.fs-col--message,#fs_log_book table tr td.fs-col--message a{color:#9a73ac !important}#fs_log_book table tr td.fs-col--file{color:#d07922}#fs_log_book table tr td.fs-col--timestamp{color:#6596be}/*# sourceMappingURL=debug.css.map */
lib/freemius/assets/css/admin/debug.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../../scss/admin/debug.scss"],"names":[],"mappings":"AAEA,iBAEI,eACA,iBACA,aAIA,mBACI,sCACA,eAEA,sBACI,WAGJ,sBACI,mBAEA,gCACI,mBAIA,wCACI,cAEJ,sCACI,cAEJ,0CACI,cACA,iBAGA,oFAEI,yBAGR,sCACI,cAEJ,2CACI","file":"debug.css"}
lib/freemius/assets/css/admin/gdpr-optin-notice.css CHANGED
@@ -1 +1 @@
1
- .fs-notice[data-id^="gdpr_optin_actions"] .underlined{text-decoration:underline}.fs-notice[data-id^="gdpr_optin_actions"] ul .button,.fs-notice[data-id^="gdpr_optin_actions"] ul .action-description{vertical-align:middle}.fs-notice[data-id^="gdpr_optin_actions"] ul .action-description{display:inline-block;margin-left:3px}
1
+ .fs-notice[data-id^=gdpr_optin_actions] .underlined{text-decoration:underline}.fs-notice[data-id^=gdpr_optin_actions] ul .button,.fs-notice[data-id^=gdpr_optin_actions] ul .action-description{vertical-align:middle}.fs-notice[data-id^=gdpr_optin_actions] ul .action-description{display:inline-block;margin-left:3px}/*# sourceMappingURL=gdpr-optin-notice.css.map */
lib/freemius/assets/css/admin/gdpr-optin-notice.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../../scss/admin/gdpr-optin-notice.scss"],"names":[],"mappings":"AAEI,oDACI,0BAIA,kHACI,sBAGJ,+DACI,qBACA","file":"gdpr-optin-notice.css"}
lib/freemius/assets/css/admin/plugins.css CHANGED
@@ -1 +1 @@
1
- label.fs-tag,span.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.wp-list-table.plugins .plugin-title span.fs-tag{display:inline-block;margin-left:5px;line-height:10px}
1
+ label.fs-tag,span.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.wp-list-table.plugins .plugin-title span.fs-tag{display:inline-block;margin-left:5px;line-height:10px}/*# sourceMappingURL=plugins.css.map */
lib/freemius/assets/css/admin/plugins.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../../scss/admin/_tag.scss","../../scss/admin/plugins.scss"],"names":[],"mappings":"AAAA,yBAEI,mBACA,WACA,qBACA,kBACA,YACA,eACA,iBACA,wBAEA,yCAEI,mBAEJ,yCAEI,mBAEJ,+CAEI,mBAEJ,2CAEI,mBCpBA,iDACI,qBACA,gBACA","file":"plugins.css"}
lib/freemius/assets/css/customizer.css CHANGED
@@ -1 +1 @@
1
- #fs_customizer_upsell .fs-customizer-plan{padding:10px 20px 20px 20px;border-radius:3px;background:#fff}#fs_customizer_upsell .fs-customizer-plan h2{position:relative;margin:0;line-height:2em;text-transform:uppercase}#fs_customizer_upsell .fs-customizer-plan h2 .button-link{top:-2px}#fs_customizer_upsell .fs-feature{position:relative}#fs_customizer_upsell .dashicons-yes{color:#0085ba;font-size:2em;vertical-align:bottom;margin-left:-7px;margin-right:10px}.rtl #fs_customizer_upsell .dashicons-yes{margin-left:10px;margin-right:-7px}#fs_customizer_upsell .dashicons-editor-help{color:#bbb;cursor:help}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{opacity:0;visibility:hidden;-moz-transition:opacity 0.3s ease-in-out;-o-transition:opacity 0.3s ease-in-out;-ms-transition:opacity 0.3s ease-in-out;-webkit-transition:opacity 0.3s ease-in-out;transition:opacity 0.3s ease-in-out;position:absolute;background:#000;color:#fff;font-family:'arial', serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:0;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.2);box-shadow:1px 1px 1px rgba(0,0,0,0.2);line-height:1.3em;font-weight:bold;text-align:left}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{text-align:right}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{content:' ';display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:#000 transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{right:21px;left:auto}#fs_customizer_upsell .dashicons-editor-help:hover .fs-feature-desc{visibility:visible;opacity:1}#fs_customizer_upsell .button-primary{display:block;text-align:center;margin-top:10px}#fs_customizer_support{display:block !important}#fs_customizer_support .button{float:right}#fs_customizer_support .button-group{width:100%;display:block;margin-top:10px}#fs_customizer_support .button-group .button{float:none;width:50%;text-align:center}#customize-theme-controls #accordion-section-freemius_upsell{border-top:1px solid #0085ba !important;border-bottom:1px solid #0085ba !important}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title{color:#fff;background-color:#0085ba;border-left:4px solid #0085ba;transition:.15s background-color ease-in-out, .15s border-color ease-in-out;outline:none;border-bottom:none !important}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title:hover{background-color:#008ec2;border-left-color:#0073aa}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title:after{color:#fff}#customize-theme-controls #accordion-section-freemius_upsell .rtl h3.accordion-section-title{border-left:none;border-right:4px solid #0085ba}#customize-theme-controls #accordion-section-freemius_upsell .rtl h3.accordion-section-title:hover{border-right-color:#0073aa}
1
+ #fs_customizer_upsell .fs-customizer-plan{padding:10px 20px 20px 20px;border-radius:3px;background:#fff}#fs_customizer_upsell .fs-customizer-plan h2{position:relative;margin:0;line-height:2em;text-transform:uppercase}#fs_customizer_upsell .fs-customizer-plan h2 .button-link{top:-2px}#fs_customizer_upsell .fs-feature{position:relative}#fs_customizer_upsell .dashicons-yes{color:#0085ba;font-size:2em;vertical-align:bottom;margin-left:-7px;margin-right:10px}.rtl #fs_customizer_upsell .dashicons-yes{margin-left:10px;margin-right:-7px}#fs_customizer_upsell .dashicons-editor-help{color:#bbb;cursor:help}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{opacity:0;visibility:hidden;-moz-transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;-ms-transition:opacity .3s ease-in-out;-webkit-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;position:absolute;background:#000;color:#fff;font-family:"arial",serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:0;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.2);box-shadow:1px 1px 1px rgba(0,0,0,.2);line-height:1.3em;font-weight:bold;text-align:left}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{text-align:right}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{content:" ";display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:#000 transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{right:21px;left:auto}#fs_customizer_upsell .dashicons-editor-help:hover .fs-feature-desc{visibility:visible;opacity:1}#fs_customizer_upsell .button-primary{display:block;text-align:center;margin-top:10px}#fs_customizer_support{display:block !important}#fs_customizer_support .button{float:right}#fs_customizer_support .button-group{width:100%;display:block;margin-top:10px}#fs_customizer_support .button-group .button{float:none;width:50%;text-align:center}#customize-theme-controls #accordion-section-freemius_upsell{border-top:1px solid #0085ba !important;border-bottom:1px solid #0085ba !important}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title{color:#fff;background-color:#0085ba;border-left:4px solid #0085ba;transition:.15s background-color ease-in-out,.15s border-color ease-in-out;outline:none;border-bottom:none !important}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title:hover{background-color:#008ec2;border-left-color:#0073aa}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title:after{color:#fff}#customize-theme-controls #accordion-section-freemius_upsell .rtl h3.accordion-section-title{border-left:none;border-right:4px solid #0085ba}#customize-theme-controls #accordion-section-freemius_upsell .rtl h3.accordion-section-title:hover{border-right-color:#0073aa}/*# sourceMappingURL=customizer.css.map */
lib/freemius/assets/css/customizer.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sourceRoot":"","sources":["../scss/customizer.scss","../scss/_mixins.scss","../scss/_colors.scss"],"names":[],"mappings":"AAGI,0CACI,4BACA,kBACA,gBAEA,6CACI,kBACA,SACA,gBACA,yBAEA,0DACI,SAKZ,kCACI,kBAGJ,qCACI,cACA,cACA,sBACA,iBACA,kBAEA,0CACI,iBACA,kBAIR,6CAEI,WACA,YAIA,8DACI,UACA,kBCgIP,gBD/HO,wBCgIL,cDhIK,wBCiIN,eDjIM,wBCkIV,mBDlIU,wBCmIF,WDnIE,wBAEA,kBACA,WARY,KASZ,WACA,0BACA,eACA,aACA,eACA,YACA,kBACA,OACA,QCZP,mBDaO,ICZV,sBDYU,ICXF,cDWE,ICJP,gBDKO,2BCJV,mBDIU,2BCHF,WDGE,2BACA,kBACA,iBACA,gBAEA,mEAEI,iBAGJ,qEACI,YACA,cACA,QACA,SACA,mBACA,2BACA,sDACA,kBACA,SACA,UAEA,0EACI,WACA,UAMR,oEACI,mBACA,UAKZ,sCACI,cACA,kBACA,gBAIR,uBAEI,yBAEA,+BACI,YAGJ,qCACI,WACA,cACA,gBAEA,6CACI,WACA,UACA,kBAKZ,6DAEI,wCACA,2CAEA,wFAEI,ME5EkB,KF6ElB,iBE5E6B,QF6E7B,8BACA,2EACA,aACA,8BAEA,8FAEI,iBEnF+B,QFoF/B,kBElFgC,QFqFpC,8FACI,ME1Fc,KFgGlB,6FAEI,iBACA,+BAEA,mGAEI,mBEnG4B","file":"customizer.css"}
lib/freemius/config.php CHANGED
@@ -1,391 +1,391 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.4
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- if ( ! defined( 'WP_FS__SLUG' ) ) {
14
- define( 'WP_FS__SLUG', 'freemius' );
15
- }
16
- if ( ! defined( 'WP_FS__DEV_MODE' ) ) {
17
- define( 'WP_FS__DEV_MODE', false );
18
- }
19
-
20
- #--------------------------------------------------------------------------------
21
- #region API Connectivity Issues Simulation
22
- #--------------------------------------------------------------------------------
23
-
24
- if ( ! defined( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY' ) ) {
25
- define( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY', false );
26
- }
27
- if ( ! defined( 'WP_FS__SIMULATE_NO_CURL' ) ) {
28
- define( 'WP_FS__SIMULATE_NO_CURL', false );
29
- }
30
- if ( ! defined( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE' ) ) {
31
- define( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE', false );
32
- }
33
- if ( ! defined( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL' ) ) {
34
- define( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL', false );
35
- }
36
- if ( WP_FS__SIMULATE_NO_CURL ) {
37
- define( 'FS_SDK__SIMULATE_NO_CURL', true );
38
- }
39
- if ( WP_FS__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE ) {
40
- define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE', true );
41
- }
42
- if ( WP_FS__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL ) {
43
- define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL', true );
44
- }
45
-
46
- #endregion
47
-
48
- if ( ! defined( 'WP_FS__SIMULATE_FREEMIUS_OFF' ) ) {
49
- define( 'WP_FS__SIMULATE_FREEMIUS_OFF', false );
50
- }
51
-
52
- if ( ! defined( 'WP_FS__PING_API_ON_IP_OR_HOST_CHANGES' ) ) {
53
- /**
54
- * @since 1.1.7.3
55
- * @author Vova Feldman (@svovaf)
56
- *
57
- * I'm not sure if shared servers periodically change IP, or the subdomain of the
58
- * admin dashboard. Also, I've seen sites that have strange loop of switching
59
- * between domains on a daily basis. Therefore, to eliminate the risk of
60
- * multiple unwanted connectivity test pings, temporary ignore domain or
61
- * server IP changes.
62
- */
63
- define( 'WP_FS__PING_API_ON_IP_OR_HOST_CHANGES', false );
64
- }
65
-
66
- /**
67
- * If your dev environment supports custom public network IP setup
68
- * like VVV, please update WP_FS__LOCALHOST_IP with your public IP
69
- * and uncomment it during dev.
70
- */
71
- if ( ! defined( 'WP_FS__LOCALHOST_IP' ) ) {
72
- // VVV default public network IP.
73
- define( 'WP_FS__VVV_DEFAULT_PUBLIC_IP', '192.168.50.4' );
74
-
75
- // define( 'WP_FS__LOCALHOST_IP', WP_FS__VVV_DEFAULT_PUBLIC_IP );
76
- }
77
-
78
- /**
79
- * If true and running with secret key, the opt-in process
80
- * will skip the email activation process which is invoked
81
- * when the email of the context user already exist in Freemius
82
- * database (as a security precaution, to prevent sharing user
83
- * secret with unauthorized entity).
84
- *
85
- * IMPORTANT:
86
- * AS A SECURITY PRECAUTION, WE VALIDATE THE TIMESTAMP OF THE OPT-IN REQUEST.
87
- * THEREFORE, MAKE SURE THAT WHEN USING THIS PARAMETER,YOUR TESTING ENVIRONMENT'S
88
- * CLOCK IS SYNCED.
89
- */
90
- if ( ! defined( 'WP_FS__SKIP_EMAIL_ACTIVATION' ) ) {
91
- define( 'WP_FS__SKIP_EMAIL_ACTIVATION', false );
92
- }
93
-
94
-
95
- #--------------------------------------------------------------------------------
96
- #region Directories
97
- #--------------------------------------------------------------------------------
98
-
99
- if ( ! defined( 'WP_FS__DIR' ) ) {
100
- define( 'WP_FS__DIR', dirname( __FILE__ ) );
101
- }
102
- if ( ! defined( 'WP_FS__DIR_INCLUDES' ) ) {
103
- define( 'WP_FS__DIR_INCLUDES', WP_FS__DIR . '/includes' );
104
- }
105
- if ( ! defined( 'WP_FS__DIR_TEMPLATES' ) ) {
106
- define( 'WP_FS__DIR_TEMPLATES', WP_FS__DIR . '/templates' );
107
- }
108
- if ( ! defined( 'WP_FS__DIR_ASSETS' ) ) {
109
- define( 'WP_FS__DIR_ASSETS', WP_FS__DIR . '/assets' );
110
- }
111
- if ( ! defined( 'WP_FS__DIR_CSS' ) ) {
112
- define( 'WP_FS__DIR_CSS', WP_FS__DIR_ASSETS . '/css' );
113
- }
114
- if ( ! defined( 'WP_FS__DIR_JS' ) ) {
115
- define( 'WP_FS__DIR_JS', WP_FS__DIR_ASSETS . '/js' );
116
- }
117
- if ( ! defined( 'WP_FS__DIR_IMG' ) ) {
118
- define( 'WP_FS__DIR_IMG', WP_FS__DIR_ASSETS . '/img' );
119
- }
120
- if ( ! defined( 'WP_FS__DIR_SDK' ) ) {
121
- define( 'WP_FS__DIR_SDK', WP_FS__DIR_INCLUDES . '/sdk' );
122
- }
123
-
124
- #endregion
125
-
126
- /**
127
- * Domain / URL / Address
128
- */
129
- define( 'WP_FS__ROOT_DOMAIN_PRODUCTION', 'freemius.com' );
130
- define( 'WP_FS__DOMAIN_PRODUCTION', 'wp.freemius.com' );
131
- define( 'WP_FS__ADDRESS_PRODUCTION', 'https://' . WP_FS__DOMAIN_PRODUCTION );
132
-
133
- if ( ! defined( 'WP_FS__DOMAIN_LOCALHOST' ) ) {
134
- define( 'WP_FS__DOMAIN_LOCALHOST', 'wp.freemius' );
135
- }
136
- if ( ! defined( 'WP_FS__ADDRESS_LOCALHOST' ) ) {
137
- define( 'WP_FS__ADDRESS_LOCALHOST', 'http://' . WP_FS__DOMAIN_LOCALHOST . ':8080' );
138
- }
139
-
140
- if ( ! defined( 'WP_FS__TESTING_DOMAIN' ) ) {
141
- define( 'WP_FS__TESTING_DOMAIN', 'fswp' );
142
- }
143
-
144
- #--------------------------------------------------------------------------------
145
- #region HTTP
146
- #--------------------------------------------------------------------------------
147
-
148
- if ( ! defined( 'WP_FS__IS_HTTP_REQUEST' ) ) {
149
- define( 'WP_FS__IS_HTTP_REQUEST', isset( $_SERVER['HTTP_HOST'] ) );
150
- }
151
-
152
- if ( ! defined( 'WP_FS__IS_HTTPS' ) ) {
153
- define( 'WP_FS__IS_HTTPS', ( WP_FS__IS_HTTP_REQUEST &&
154
- // Checks if CloudFlare's HTTPS (Flexible SSL support).
155
- isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
156
- 'https' === strtolower( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
157
- ) ||
158
- // Check if HTTPS request.
159
- ( isset( $_SERVER['HTTPS'] ) && 'on' == $_SERVER['HTTPS'] ) ||
160
- ( isset( $_SERVER['SERVER_PORT'] ) && 443 == $_SERVER['SERVER_PORT'] )
161
- );
162
- }
163
-
164
- if ( ! defined( 'WP_FS__IS_POST_REQUEST' ) ) {
165
- define( 'WP_FS__IS_POST_REQUEST', ( WP_FS__IS_HTTP_REQUEST &&
166
- strtoupper( $_SERVER['REQUEST_METHOD'] ) == 'POST' ) );
167
- }
168
-
169
- if ( ! defined( 'WP_FS__REMOTE_ADDR' ) ) {
170
- define( 'WP_FS__REMOTE_ADDR', fs_get_ip() );
171
- }
172
-
173
- if ( ! defined( 'WP_FS__IS_LOCALHOST' ) ) {
174
- if ( defined( 'WP_FS__LOCALHOST_IP' ) ) {
175
- define( 'WP_FS__IS_LOCALHOST', ( WP_FS__LOCALHOST_IP === WP_FS__REMOTE_ADDR ) );
176
- } else {
177
- define( 'WP_FS__IS_LOCALHOST', WP_FS__IS_HTTP_REQUEST &&
178
- is_string( WP_FS__REMOTE_ADDR ) &&
179
- ( substr( WP_FS__REMOTE_ADDR, 0, 4 ) === '127.' ||
180
- WP_FS__REMOTE_ADDR === '::1' )
181
- );
182
- }
183
- }
184
-
185
- if ( ! defined( 'WP_FS__IS_LOCALHOST_FOR_SERVER' ) ) {
186
- define( 'WP_FS__IS_LOCALHOST_FOR_SERVER', ( ! WP_FS__IS_HTTP_REQUEST ||
187
- false !== strpos( $_SERVER['HTTP_HOST'], 'localhost' ) ) );
188
- }
189
-
190
- #endregion
191
-
192
- if ( ! defined( 'WP_FS__IS_PRODUCTION_MODE' ) ) {
193
- // By default, run with Freemius production servers.
194
- define( 'WP_FS__IS_PRODUCTION_MODE', true );
195
- }
196
-
197
- if ( ! defined( 'WP_FS__ADDRESS' ) ) {
198
- define( 'WP_FS__ADDRESS', ( WP_FS__IS_PRODUCTION_MODE ? WP_FS__ADDRESS_PRODUCTION : WP_FS__ADDRESS_LOCALHOST ) );
199
- }
200
-
201
-
202
- #--------------------------------------------------------------------------------
203
- #region API
204
- #--------------------------------------------------------------------------------
205
-
206
- if ( ! defined( 'WP_FS__API_ADDRESS_LOCALHOST' ) ) {
207
- define( 'WP_FS__API_ADDRESS_LOCALHOST', 'http://api.freemius-local.com:8080' );
208
- }
209
- if ( ! defined( 'WP_FS__API_SANDBOX_ADDRESS_LOCALHOST' ) ) {
210
- define( 'WP_FS__API_SANDBOX_ADDRESS_LOCALHOST', 'http://sandbox-api.freemius:8080' );
211
- }
212
-
213
- // Set API address for local testing.
214
- if ( ! WP_FS__IS_PRODUCTION_MODE ) {
215
- if ( ! defined( 'FS_API__ADDRESS' ) ) {
216
- define( 'FS_API__ADDRESS', WP_FS__API_ADDRESS_LOCALHOST );
217
- }
218
- if ( ! defined( 'FS_API__SANDBOX_ADDRESS' ) ) {
219
- define( 'FS_API__SANDBOX_ADDRESS', WP_FS__API_SANDBOX_ADDRESS_LOCALHOST );
220
- }
221
- }
222
-
223
- #endregion
224
-
225
- #--------------------------------------------------------------------------------
226
- #region Checkout
227
- #--------------------------------------------------------------------------------
228
-
229
- if ( ! defined( 'FS_CHECKOUT__ADDRESS_PRODUCTION' ) ) {
230
- define( 'FS_CHECKOUT__ADDRESS_PRODUCTION', 'https://checkout.freemius.com' );
231
- }
232
-
233
- if ( ! defined( 'FS_CHECKOUT__ADDRESS_LOCALHOST' ) ) {
234
- define( 'FS_CHECKOUT__ADDRESS_LOCALHOST', 'http://checkout.freemius-local.com:8080' );
235
- }
236
-
237
- if ( ! defined( 'FS_CHECKOUT__ADDRESS' ) ) {
238
- define( 'FS_CHECKOUT__ADDRESS', ( WP_FS__IS_PRODUCTION_MODE ? FS_CHECKOUT__ADDRESS_PRODUCTION : FS_CHECKOUT__ADDRESS_LOCALHOST ) );
239
- }
240
-
241
- #endregion
242
-
243
- define( 'WP_FS___OPTION_PREFIX', 'fs' . ( WP_FS__IS_PRODUCTION_MODE ? '' : '_dbg' ) . '_' );
244
-
245
- if ( ! defined( 'WP_FS__ACCOUNTS_OPTION_NAME' ) ) {
246
- define( 'WP_FS__ACCOUNTS_OPTION_NAME', WP_FS___OPTION_PREFIX . 'accounts' );
247
- }
248
- if ( ! defined( 'WP_FS__API_CACHE_OPTION_NAME' ) ) {
249
- define( 'WP_FS__API_CACHE_OPTION_NAME', WP_FS___OPTION_PREFIX . 'api_cache' );
250
- }
251
- if ( ! defined( 'WP_FS__GDPR_OPTION_NAME' ) ) {
252
- define( 'WP_FS__GDPR_OPTION_NAME', WP_FS___OPTION_PREFIX . 'gdpr' );
253
- }
254
- define( 'WP_FS__OPTIONS_OPTION_NAME', WP_FS___OPTION_PREFIX . 'options' );
255
-
256
- /**
257
- * Module types
258
- *
259
- * @since 1.2.2
260
- */
261
- define( 'WP_FS__MODULE_TYPE_PLUGIN', 'plugin' );
262
- define( 'WP_FS__MODULE_TYPE_THEME', 'theme' );
263
-
264
- /**
265
- * Billing Frequencies
266
- */
267
- define( 'WP_FS__PERIOD_ANNUALLY', 'annual' );
268
- define( 'WP_FS__PERIOD_MONTHLY', 'monthly' );
269
- define( 'WP_FS__PERIOD_LIFETIME', 'lifetime' );
270
-
271
- /**
272
- * Plans
273
- */
274
- define( 'WP_FS__PLAN_DEFAULT_PAID', false );
275
- define( 'WP_FS__PLAN_FREE', 'free' );
276
- define( 'WP_FS__PLAN_TRIAL', 'trial' );
277
-
278
- /**
279
- * Times in seconds
280
- */
281
- if ( ! defined( 'WP_FS__TIME_5_MIN_IN_SEC' ) ) {
282
- define( 'WP_FS__TIME_5_MIN_IN_SEC', 300 );
283
- }
284
- if ( ! defined( 'WP_FS__TIME_10_MIN_IN_SEC' ) ) {
285
- define( 'WP_FS__TIME_10_MIN_IN_SEC', 600 );
286
- }
287
- // define( 'WP_FS__TIME_15_MIN_IN_SEC', 900 );
288
- if ( ! defined( 'WP_FS__TIME_12_HOURS_IN_SEC' ) ) {
289
- define( 'WP_FS__TIME_12_HOURS_IN_SEC', 43200 );
290
- }
291
- if ( ! defined( 'WP_FS__TIME_24_HOURS_IN_SEC' ) ) {
292
- define( 'WP_FS__TIME_24_HOURS_IN_SEC', WP_FS__TIME_12_HOURS_IN_SEC * 2 );
293
- }
294
- if ( ! defined( 'WP_FS__TIME_WEEK_IN_SEC' ) ) {
295
- define( 'WP_FS__TIME_WEEK_IN_SEC', 7 * WP_FS__TIME_24_HOURS_IN_SEC );
296
- }
297
-
298
- #--------------------------------------------------------------------------------
299
- #region Debugging
300
- #--------------------------------------------------------------------------------
301
-
302
- if ( ! defined( 'WP_FS__DEBUG_SDK' ) ) {
303
- $debug_mode = get_option( 'fs_debug_mode', null );
304
-
305
- if ( $debug_mode === null ) {
306
- $debug_mode = false;
307
- add_option( 'fs_debug_mode', $debug_mode );
308
- }
309
-
310
- define( 'WP_FS__DEBUG_SDK', is_numeric( $debug_mode ) ? ( 0 < $debug_mode ) : WP_FS__DEV_MODE );
311
- }
312
-
313
- if ( ! defined( 'WP_FS__ECHO_DEBUG_SDK' ) ) {
314
- define( 'WP_FS__ECHO_DEBUG_SDK', WP_FS__DEV_MODE && ! empty( $_GET['fs_dbg_echo'] ) );
315
- }
316
- if ( ! defined( 'WP_FS__LOG_DATETIME_FORMAT' ) ) {
317
- define( 'WP_FS__LOG_DATETIME_FORMAT', 'Y-m-d H:i:s' );
318
- }
319
- if ( ! defined( 'FS_API__LOGGER_ON' ) ) {
320
- define( 'FS_API__LOGGER_ON', WP_FS__DEBUG_SDK );
321
- }
322
-
323
- if ( WP_FS__ECHO_DEBUG_SDK ) {
324
- error_reporting( E_ALL );
325
- }
326
-
327
- #endregion
328
-
329
- if ( ! defined( 'WP_FS__SCRIPT_START_TIME' ) ) {
330
- define( 'WP_FS__SCRIPT_START_TIME', time() );
331
- }
332
- if ( ! defined( 'WP_FS__DEFAULT_PRIORITY' ) ) {
333
- define( 'WP_FS__DEFAULT_PRIORITY', 10 );
334
- }
335
- if ( ! defined( 'WP_FS__LOWEST_PRIORITY' ) ) {
336
- define( 'WP_FS__LOWEST_PRIORITY', 999999999 );
337
- }
338
-
339
- #--------------------------------------------------------------------------------
340
- #region Multisite Network
341
- #--------------------------------------------------------------------------------
342
-
343
- /**
344
- * Do not use this define directly, it will have the wrong value
345
- * during plugin uninstall/deletion when the inclusion of the plugin
346
- * is triggered due to registration with register_uninstall_hook().
347
- *
348
- * Instead, use fs_is_network_admin().
349
- *
350
- * @author Vova Feldman (@svovaf)
351
- */
352
- if ( ! defined( 'WP_FS__IS_NETWORK_ADMIN' ) ) {
353
- define( 'WP_FS__IS_NETWORK_ADMIN',
354
- is_multisite() &&
355
- ( is_network_admin() ||
356
- ( ( defined( 'DOING_AJAX' ) && DOING_AJAX &&
357
- ( isset( $_REQUEST['_fs_network_admin'] ) /*||
358
- ( ! empty( $_REQUEST['action'] ) && 'delete-plugin' === $_REQUEST['action'] )*/ )
359
- ) ||
360
- // Plugin uninstall.
361
- defined( 'WP_UNINSTALL_PLUGIN' ) )
362
- )
363
- );
364
- }
365
-
366
- /**
367
- * Do not use this define directly, it will have the wrong value
368
- * during plugin uninstall/deletion when the inclusion of the plugin
369
- * is triggered due to registration with register_uninstall_hook().
370
- *
371
- * Instead, use fs_is_blog_admin().
372
- *
373
- * @author Vova Feldman (@svovaf)
374
- */
375
- if ( ! defined( 'WP_FS__IS_BLOG_ADMIN' ) ) {
376
- define( 'WP_FS__IS_BLOG_ADMIN', is_blog_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_REQUEST['_fs_blog_admin'] ) ) );
377
- }
378
-
379
- if ( ! defined( 'WP_FS__SHOW_NETWORK_EVEN_WHEN_DELEGATED' ) ) {
380
- // Set to true to show network level settings even if delegated to site admins.
381
- define( 'WP_FS__SHOW_NETWORK_EVEN_WHEN_DELEGATED', false );
382
- }
383
-
384
- #endregion
385
-
386
- if ( ! defined( 'WP_FS__DEMO_MODE' ) ) {
387
- define( 'WP_FS__DEMO_MODE', false );
388
  }
389
  if ( ! defined( 'FS_SDK__SSLVERIFY' ) ) {
390
  define( 'FS_SDK__SSLVERIFY', false );
391
- }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.4
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ if ( ! defined( 'WP_FS__SLUG' ) ) {
14
+ define( 'WP_FS__SLUG', 'freemius' );
15
+ }
16
+ if ( ! defined( 'WP_FS__DEV_MODE' ) ) {
17
+ define( 'WP_FS__DEV_MODE', false );
18
+ }
19
+
20
+ #--------------------------------------------------------------------------------
21
+ #region API Connectivity Issues Simulation
22
+ #--------------------------------------------------------------------------------
23
+
24
+ if ( ! defined( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY' ) ) {
25
+ define( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY', false );
26
+ }
27
+ if ( ! defined( 'WP_FS__SIMULATE_NO_CURL' ) ) {
28
+ define( 'WP_FS__SIMULATE_NO_CURL', false );
29
+ }
30
+ if ( ! defined( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE' ) ) {
31
+ define( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE', false );
32
+ }
33
+ if ( ! defined( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL' ) ) {
34
+ define( 'WP_FS__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL', false );
35
+ }
36
+ if ( WP_FS__SIMULATE_NO_CURL ) {
37
+ define( 'FS_SDK__SIMULATE_NO_CURL', true );
38
+ }
39
+ if ( WP_FS__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE ) {
40
+ define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE', true );
41
+ }
42
+ if ( WP_FS__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL ) {
43
+ define( 'FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL', true );
44
+ }
45
+
46
+ #endregion
47
+
48
+ if ( ! defined( 'WP_FS__SIMULATE_FREEMIUS_OFF' ) ) {
49
+ define( 'WP_FS__SIMULATE_FREEMIUS_OFF', false );
50
+ }
51
+
52
+ if ( ! defined( 'WP_FS__PING_API_ON_IP_OR_HOST_CHANGES' ) ) {
53
+ /**
54
+ * @since 1.1.7.3
55
+ * @author Vova Feldman (@svovaf)
56
+ *
57
+ * I'm not sure if shared servers periodically change IP, or the subdomain of the
58
+ * admin dashboard. Also, I've seen sites that have strange loop of switching
59
+ * between domains on a daily basis. Therefore, to eliminate the risk of
60
+ * multiple unwanted connectivity test pings, temporary ignore domain or
61
+ * server IP changes.
62
+ */
63
+ define( 'WP_FS__PING_API_ON_IP_OR_HOST_CHANGES', false );
64
+ }
65
+
66
+ /**
67
+ * If your dev environment supports custom public network IP setup
68
+ * like VVV, please update WP_FS__LOCALHOST_IP with your public IP
69
+ * and uncomment it during dev.
70
+ */
71
+ if ( ! defined( 'WP_FS__LOCALHOST_IP' ) ) {
72
+ // VVV default public network IP.
73
+ define( 'WP_FS__VVV_DEFAULT_PUBLIC_IP', '192.168.50.4' );
74
+
75
+ // define( 'WP_FS__LOCALHOST_IP', WP_FS__VVV_DEFAULT_PUBLIC_IP );
76
+ }
77
+
78
+ /**
79
+ * If true and running with secret key, the opt-in process
80
+ * will skip the email activation process which is invoked
81
+ * when the email of the context user already exist in Freemius
82
+ * database (as a security precaution, to prevent sharing user
83
+ * secret with unauthorized entity).
84
+ *
85
+ * IMPORTANT:
86
+ * AS A SECURITY PRECAUTION, WE VALIDATE THE TIMESTAMP OF THE OPT-IN REQUEST.
87
+ * THEREFORE, MAKE SURE THAT WHEN USING THIS PARAMETER,YOUR TESTING ENVIRONMENT'S
88
+ * CLOCK IS SYNCED.
89
+ */
90
+ if ( ! defined( 'WP_FS__SKIP_EMAIL_ACTIVATION' ) ) {
91
+ define( 'WP_FS__SKIP_EMAIL_ACTIVATION', false );
92
+ }
93
+
94
+
95
+ #--------------------------------------------------------------------------------
96
+ #region Directories
97
+ #--------------------------------------------------------------------------------
98
+
99
+ if ( ! defined( 'WP_FS__DIR' ) ) {
100
+ define( 'WP_FS__DIR', dirname( __FILE__ ) );
101
+ }
102
+ if ( ! defined( 'WP_FS__DIR_INCLUDES' ) ) {
103
+ define( 'WP_FS__DIR_INCLUDES', WP_FS__DIR . '/includes' );
104
+ }
105
+ if ( ! defined( 'WP_FS__DIR_TEMPLATES' ) ) {
106
+ define( 'WP_FS__DIR_TEMPLATES', WP_FS__DIR . '/templates' );
107
+ }
108
+ if ( ! defined( 'WP_FS__DIR_ASSETS' ) ) {
109
+ define( 'WP_FS__DIR_ASSETS', WP_FS__DIR . '/assets' );
110
+ }
111
+ if ( ! defined( 'WP_FS__DIR_CSS' ) ) {
112
+ define( 'WP_FS__DIR_CSS', WP_FS__DIR_ASSETS . '/css' );
113
+ }
114
+ if ( ! defined( 'WP_FS__DIR_JS' ) ) {
115
+ define( 'WP_FS__DIR_JS', WP_FS__DIR_ASSETS . '/js' );
116
+ }
117
+ if ( ! defined( 'WP_FS__DIR_IMG' ) ) {
118
+ define( 'WP_FS__DIR_IMG', WP_FS__DIR_ASSETS . '/img' );
119
+ }
120
+ if ( ! defined( 'WP_FS__DIR_SDK' ) ) {
121
+ define( 'WP_FS__DIR_SDK', WP_FS__DIR_INCLUDES . '/sdk' );
122
+ }
123
+
124
+ #endregion
125
+
126
+ /**
127
+ * Domain / URL / Address
128
+ */
129
+ define( 'WP_FS__ROOT_DOMAIN_PRODUCTION', 'freemius.com' );
130
+ define( 'WP_FS__DOMAIN_PRODUCTION', 'wp.freemius.com' );
131
+ define( 'WP_FS__ADDRESS_PRODUCTION', 'https://' . WP_FS__DOMAIN_PRODUCTION );
132
+
133
+ if ( ! defined( 'WP_FS__DOMAIN_LOCALHOST' ) ) {
134
+ define( 'WP_FS__DOMAIN_LOCALHOST', 'wp.freemius' );
135
+ }
136
+ if ( ! defined( 'WP_FS__ADDRESS_LOCALHOST' ) ) {
137
+ define( 'WP_FS__ADDRESS_LOCALHOST', 'http://' . WP_FS__DOMAIN_LOCALHOST . ':8080' );
138
+ }
139
+
140
+ if ( ! defined( 'WP_FS__TESTING_DOMAIN' ) ) {
141
+ define( 'WP_FS__TESTING_DOMAIN', 'fswp' );
142
+ }
143
+
144
+ #--------------------------------------------------------------------------------
145
+ #region HTTP
146
+ #--------------------------------------------------------------------------------
147
+
148
+ if ( ! defined( 'WP_FS__IS_HTTP_REQUEST' ) ) {
149
+ define( 'WP_FS__IS_HTTP_REQUEST', isset( $_SERVER['HTTP_HOST'] ) );
150
+ }
151
+
152
+ if ( ! defined( 'WP_FS__IS_HTTPS' ) ) {
153
+ define( 'WP_FS__IS_HTTPS', ( WP_FS__IS_HTTP_REQUEST &&
154
+ // Checks if CloudFlare's HTTPS (Flexible SSL support).
155
+ isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
156
+ 'https' === strtolower( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
157
+ ) ||
158
+ // Check if HTTPS request.
159
+ ( isset( $_SERVER['HTTPS'] ) && 'on' == $_SERVER['HTTPS'] ) ||
160
+ ( isset( $_SERVER['SERVER_PORT'] ) && 443 == $_SERVER['SERVER_PORT'] )
161
+ );
162
+ }
163
+
164
+ if ( ! defined( 'WP_FS__IS_POST_REQUEST' ) ) {
165
+ define( 'WP_FS__IS_POST_REQUEST', ( WP_FS__IS_HTTP_REQUEST &&
166
+ strtoupper( $_SERVER['REQUEST_METHOD'] ) == 'POST' ) );
167
+ }
168
+
169
+ if ( ! defined( 'WP_FS__REMOTE_ADDR' ) ) {
170
+ define( 'WP_FS__REMOTE_ADDR', fs_get_ip() );
171
+ }
172
+
173
+ if ( ! defined( 'WP_FS__IS_LOCALHOST' ) ) {
174
+ if ( defined( 'WP_FS__LOCALHOST_IP' ) ) {
175
+ define( 'WP_FS__IS_LOCALHOST', ( WP_FS__LOCALHOST_IP === WP_FS__REMOTE_ADDR ) );
176
+ } else {
177
+ define( 'WP_FS__IS_LOCALHOST', WP_FS__IS_HTTP_REQUEST &&
178
+ is_string( WP_FS__REMOTE_ADDR ) &&
179
+ ( substr( WP_FS__REMOTE_ADDR, 0, 4 ) === '127.' ||
180
+ WP_FS__REMOTE_ADDR === '::1' )
181
+ );
182
+ }
183
+ }
184
+
185
+ if ( ! defined( 'WP_FS__IS_LOCALHOST_FOR_SERVER' ) ) {
186
+ define( 'WP_FS__IS_LOCALHOST_FOR_SERVER', ( ! WP_FS__IS_HTTP_REQUEST ||
187
+ false !== strpos( $_SERVER['HTTP_HOST'], 'localhost' ) ) );
188
+ }
189
+
190
+ #endregion
191
+
192
+ if ( ! defined( 'WP_FS__IS_PRODUCTION_MODE' ) ) {
193
+ // By default, run with Freemius production servers.
194
+ define( 'WP_FS__IS_PRODUCTION_MODE', true );
195
+ }
196
+
197
+ if ( ! defined( 'WP_FS__ADDRESS' ) ) {
198
+ define( 'WP_FS__ADDRESS', ( WP_FS__IS_PRODUCTION_MODE ? WP_FS__ADDRESS_PRODUCTION : WP_FS__ADDRESS_LOCALHOST ) );
199
+ }
200
+
201
+
202
+ #--------------------------------------------------------------------------------
203
+ #region API
204
+ #--------------------------------------------------------------------------------
205
+
206
+ if ( ! defined( 'WP_FS__API_ADDRESS_LOCALHOST' ) ) {
207
+ define( 'WP_FS__API_ADDRESS_LOCALHOST', 'http://api.freemius-local.com:8080' );
208
+ }
209
+ if ( ! defined( 'WP_FS__API_SANDBOX_ADDRESS_LOCALHOST' ) ) {
210
+ define( 'WP_FS__API_SANDBOX_ADDRESS_LOCALHOST', 'http://sandbox-api.freemius:8080' );
211
+ }
212
+
213
+ // Set API address for local testing.
214
+ if ( ! WP_FS__IS_PRODUCTION_MODE ) {
215
+ if ( ! defined( 'FS_API__ADDRESS' ) ) {
216
+ define( 'FS_API__ADDRESS', WP_FS__API_ADDRESS_LOCALHOST );
217
+ }
218
+ if ( ! defined( 'FS_API__SANDBOX_ADDRESS' ) ) {
219
+ define( 'FS_API__SANDBOX_ADDRESS', WP_FS__API_SANDBOX_ADDRESS_LOCALHOST );
220
+ }
221
+ }
222
+
223
+ #endregion
224
+
225
+ #--------------------------------------------------------------------------------
226
+ #region Checkout
227
+ #--------------------------------------------------------------------------------
228
+
229
+ if ( ! defined( 'FS_CHECKOUT__ADDRESS_PRODUCTION' ) ) {
230
+ define( 'FS_CHECKOUT__ADDRESS_PRODUCTION', 'https://checkout.freemius.com' );
231
+ }
232
+
233
+ if ( ! defined( 'FS_CHECKOUT__ADDRESS_LOCALHOST' ) ) {
234
+ define( 'FS_CHECKOUT__ADDRESS_LOCALHOST', 'http://checkout.freemius-local.com:8080' );
235
+ }
236
+
237
+ if ( ! defined( 'FS_CHECKOUT__ADDRESS' ) ) {
238
+ define( 'FS_CHECKOUT__ADDRESS', ( WP_FS__IS_PRODUCTION_MODE ? FS_CHECKOUT__ADDRESS_PRODUCTION : FS_CHECKOUT__ADDRESS_LOCALHOST ) );
239
+ }
240
+
241
+ #endregion
242
+
243
+ define( 'WP_FS___OPTION_PREFIX', 'fs' . ( WP_FS__IS_PRODUCTION_MODE ? '' : '_dbg' ) . '_' );
244
+
245
+ if ( ! defined( 'WP_FS__ACCOUNTS_OPTION_NAME' ) ) {
246
+ define( 'WP_FS__ACCOUNTS_OPTION_NAME', WP_FS___OPTION_PREFIX . 'accounts' );
247
+ }
248
+ if ( ! defined( 'WP_FS__API_CACHE_OPTION_NAME' ) ) {
249
+ define( 'WP_FS__API_CACHE_OPTION_NAME', WP_FS___OPTION_PREFIX . 'api_cache' );
250
+ }
251
+ if ( ! defined( 'WP_FS__GDPR_OPTION_NAME' ) ) {
252
+ define( 'WP_FS__GDPR_OPTION_NAME', WP_FS___OPTION_PREFIX . 'gdpr' );
253
+ }
254
+ define( 'WP_FS__OPTIONS_OPTION_NAME', WP_FS___OPTION_PREFIX . 'options' );
255
+
256
+ /**
257
+ * Module types
258
+ *
259
+ * @since 1.2.2
260
+ */
261
+ define( 'WP_FS__MODULE_TYPE_PLUGIN', 'plugin' );
262
+ define( 'WP_FS__MODULE_TYPE_THEME', 'theme' );
263
+
264
+ /**
265
+ * Billing Frequencies
266
+ */
267
+ define( 'WP_FS__PERIOD_ANNUALLY', 'annual' );
268
+ define( 'WP_FS__PERIOD_MONTHLY', 'monthly' );
269
+ define( 'WP_FS__PERIOD_LIFETIME', 'lifetime' );
270
+
271
+ /**
272
+ * Plans
273
+ */
274
+ define( 'WP_FS__PLAN_DEFAULT_PAID', false );
275
+ define( 'WP_FS__PLAN_FREE', 'free' );
276
+ define( 'WP_FS__PLAN_TRIAL', 'trial' );
277
+
278
+ /**
279
+ * Times in seconds
280
+ */
281
+ if ( ! defined( 'WP_FS__TIME_5_MIN_IN_SEC' ) ) {
282
+ define( 'WP_FS__TIME_5_MIN_IN_SEC', 300 );
283
+ }
284
+ if ( ! defined( 'WP_FS__TIME_10_MIN_IN_SEC' ) ) {
285
+ define( 'WP_FS__TIME_10_MIN_IN_SEC', 600 );
286
+ }
287
+ // define( 'WP_FS__TIME_15_MIN_IN_SEC', 900 );
288
+ if ( ! defined( 'WP_FS__TIME_12_HOURS_IN_SEC' ) ) {
289
+ define( 'WP_FS__TIME_12_HOURS_IN_SEC', 43200 );
290
+ }
291
+ if ( ! defined( 'WP_FS__TIME_24_HOURS_IN_SEC' ) ) {
292
+ define( 'WP_FS__TIME_24_HOURS_IN_SEC', WP_FS__TIME_12_HOURS_IN_SEC * 2 );
293
+ }
294
+ if ( ! defined( 'WP_FS__TIME_WEEK_IN_SEC' ) ) {
295
+ define( 'WP_FS__TIME_WEEK_IN_SEC', 7 * WP_FS__TIME_24_HOURS_IN_SEC );
296
+ }
297
+
298
+ #--------------------------------------------------------------------------------
299
+ #region Debugging
300
+ #--------------------------------------------------------------------------------
301
+
302
+ if ( ! defined( 'WP_FS__DEBUG_SDK' ) ) {
303
+ $debug_mode = get_option( 'fs_debug_mode', null );
304
+
305
+ if ( $debug_mode === null ) {
306
+ $debug_mode = false;
307
+ add_option( 'fs_debug_mode', $debug_mode );
308
+ }
309
+
310
+ define( 'WP_FS__DEBUG_SDK', is_numeric( $debug_mode ) ? ( 0 < $debug_mode ) : WP_FS__DEV_MODE );
311
+ }
312
+
313
+ if ( ! defined( 'WP_FS__ECHO_DEBUG_SDK' ) ) {
314
+ define( 'WP_FS__ECHO_DEBUG_SDK', WP_FS__DEV_MODE && ! empty( $_GET['fs_dbg_echo'] ) );
315
+ }
316
+ if ( ! defined( 'WP_FS__LOG_DATETIME_FORMAT' ) ) {
317
+ define( 'WP_FS__LOG_DATETIME_FORMAT', 'Y-m-d H:i:s' );
318
+ }
319
+ if ( ! defined( 'FS_API__LOGGER_ON' ) ) {
320
+ define( 'FS_API__LOGGER_ON', WP_FS__DEBUG_SDK );
321
+ }
322
+
323
+ if ( WP_FS__ECHO_DEBUG_SDK ) {
324
+ error_reporting( E_ALL );
325
+ }
326
+
327
+ #endregion
328
+
329
+ if ( ! defined( 'WP_FS__SCRIPT_START_TIME' ) ) {
330
+ define( 'WP_FS__SCRIPT_START_TIME', time() );
331
+ }
332
+ if ( ! defined( 'WP_FS__DEFAULT_PRIORITY' ) ) {
333
+ define( 'WP_FS__DEFAULT_PRIORITY', 10 );
334
+ }
335
+ if ( ! defined( 'WP_FS__LOWEST_PRIORITY' ) ) {
336
+ define( 'WP_FS__LOWEST_PRIORITY', 999999999 );
337
+ }
338
+
339
+ #--------------------------------------------------------------------------------
340
+ #region Multisite Network
341
+ #--------------------------------------------------------------------------------
342
+
343
+ /**
344
+ * Do not use this define directly, it will have the wrong value
345
+ * during plugin uninstall/deletion when the inclusion of the plugin
346
+ * is triggered due to registration with register_uninstall_hook().
347
+ *
348
+ * Instead, use fs_is_network_admin().
349
+ *
350
+ * @author Vova Feldman (@svovaf)
351
+ */
352
+ if ( ! defined( 'WP_FS__IS_NETWORK_ADMIN' ) ) {
353
+ define( 'WP_FS__IS_NETWORK_ADMIN',
354
+ is_multisite() &&
355
+ ( is_network_admin() ||
356
+ ( ( defined( 'DOING_AJAX' ) && DOING_AJAX &&
357
+ ( isset( $_REQUEST['_fs_network_admin'] ) && 'true' === $_REQUEST['_fs_network_admin'] /*||
358
+ ( ! empty( $_REQUEST['action'] ) && 'delete-plugin' === $_REQUEST['action'] )*/ )
359
+ ) ||
360
+ // Plugin uninstall.
361
+ defined( 'WP_UNINSTALL_PLUGIN' ) )
362
+ )
363
+ );
364
+ }
365
+
366
+ /**
367
+ * Do not use this define directly, it will have the wrong value
368
+ * during plugin uninstall/deletion when the inclusion of the plugin
369
+ * is triggered due to registration with register_uninstall_hook().
370
+ *
371
+ * Instead, use fs_is_blog_admin().
372
+ *
373
+ * @author Vova Feldman (@svovaf)
374
+ */
375
+ if ( ! defined( 'WP_FS__IS_BLOG_ADMIN' ) ) {
376
+ define( 'WP_FS__IS_BLOG_ADMIN', is_blog_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_REQUEST['_fs_blog_admin'] ) ) );
377
+ }
378
+
379
+ if ( ! defined( 'WP_FS__SHOW_NETWORK_EVEN_WHEN_DELEGATED' ) ) {
380
+ // Set to true to show network level settings even if delegated to site admins.
381
+ define( 'WP_FS__SHOW_NETWORK_EVEN_WHEN_DELEGATED', false );
382
+ }
383
+
384
+ #endregion
385
+
386
+ if ( ! defined( 'WP_FS__DEMO_MODE' ) ) {
387
+ define( 'WP_FS__DEMO_MODE', false );
388
  }
389
  if ( ! defined( 'FS_SDK__SSLVERIFY' ) ) {
390
  define( 'FS_SDK__SSLVERIFY', false );
391
+ }
lib/freemius/includes/class-freemius.php CHANGED
@@ -521,7 +521,10 @@
521
  * @author Leo Fajardo (@leorw)
522
  * @since 1.2.2
523
  */
524
- ( is_object( $this->_plugin ) ? $this->_plugin->title : $this->get_plugin_name() ),
 
 
 
525
  $this->get_unique_affix()
526
  );
527
 
@@ -1643,12 +1646,10 @@
1643
  }
1644
 
1645
  if ( $this->is_plugin() ) {
1646
- if ( $this->_is_network_active ) {
1647
- if ( version_compare( $GLOBALS['wp_version'], '5.1', '<' ) ) {
1648
- add_action( 'wpmu_new_blog', array( $this, '_after_new_blog_callback' ), 10, 6 );
1649
- } else {
1650
- add_action( 'wp_insert_site', array( $this, '_after_wp_insert_site_callback' ) );
1651
- }
1652
  }
1653
 
1654
  register_deactivation_hook( $this->_plugin_main_file_path, array( &$this, '_deactivate_plugin_hook' ) );
@@ -2107,7 +2108,7 @@
2107
  * Leverage backtrace to find caller plugin file path.
2108
  *
2109
  * @param bool $is_init Is initiation sequence.
2110
- * @param string $main_file Since 2.4.3 expects the module's main file path to potentially purge the cached path.
2111
  *
2112
  * @return string
2113
  * @since 1.0.6
@@ -2233,7 +2234,7 @@
2233
  * @param number $module_id
2234
  * @param string $slug
2235
  *
2236
- * @return string Since 2.4.3 return the module's main file path.
2237
  *
2238
  * @since 1.2.2
2239
  */
@@ -2284,7 +2285,7 @@
2284
  * If the module's main file path is identical to the main file path of another module then it means that the cached path of the current module or the other one with the same path is wrong, and therefore, we need to recalculate those paths.
2285
  *
2286
  * @author Vova Feldman (@svovaf)
2287
- * @since 2.4.3
2288
  */
2289
  if ( ! $find_caller ) {
2290
  if ( $id == $module_id ) {
@@ -2328,7 +2329,7 @@
2328
  * SDK an internal file instead of directly from functions.php.
2329
  * @since 1.2.1.7 Knows how to handle cases when an add-on includes the parent module logic.
2330
  *
2331
- * @param number $module_id @since 2.4.3
2332
  */
2333
  private function get_caller_main_file_and_type( $module_id ) {
2334
  self::require_plugin_essentials();
@@ -2491,6 +2492,13 @@
2491
  * @since 1.1.2
2492
  */
2493
  function _add_deactivation_feedback_dialog_box() {
 
 
 
 
 
 
 
2494
  $subscription_cancellation_dialog_box_template_params = $this->apply_filters( 'show_deactivation_subscription_cancellation', true ) ?
2495
  $this->_get_subscription_cancellation_dialog_box_template_params() :
2496
  array();
@@ -3060,6 +3068,13 @@
3060
  return self::instance( $addon_id );
3061
  }
3062
 
 
 
 
 
 
 
 
3063
  #endregion ------------------------------------------------------------------
3064
 
3065
  /**
@@ -3570,6 +3585,9 @@
3570
  add_action( 'plugins_loaded', array( 'Freemius', '_load_textdomain' ), 1 );
3571
  }
3572
 
 
 
 
3573
  add_action( 'admin_footer', array( 'Freemius', '_enrich_ajax_url' ) );
3574
  add_action( 'admin_footer', array( 'Freemius', '_open_support_forum_in_new_page' ) );
3575
 
@@ -3589,6 +3607,86 @@
3589
  self::$_statics_loaded = true;
3590
  }
3591
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3592
  /**
3593
  * @author Leo Fajardo (@leorw)
3594
  *
@@ -3867,11 +3965,11 @@
3867
 
3868
  /**
3869
  * @author Leo Fajardo (@leorw)
3870
- * @since 2.4.3
3871
  *
3872
  * @return array
3873
  */
3874
- private static function get_all_modules_sites() {
3875
  self::$_static_logger->entrance();
3876
 
3877
  $sites_by_type = array(
@@ -3884,6 +3982,10 @@
3884
  if ( ! is_multisite() ) {
3885
  foreach ( $module_types as $type ) {
3886
  $sites_by_type[ $type ] = self::get_all_sites( $type );
 
 
 
 
3887
  }
3888
  } else {
3889
  $sites = self::get_sites();
@@ -3956,6 +4058,10 @@
3956
  function is_on() {
3957
  self::$_static_logger->entrance();
3958
 
 
 
 
 
3959
  if ( isset( $this->_is_on ) ) {
3960
  return $this->_is_on;
3961
  }
@@ -4200,7 +4306,7 @@
4200
  if ( empty( $unique_id ) || ! is_string( $unique_id ) ) {
4201
  $key = fs_strip_url_protocol( get_site_url( $blog_id ) );
4202
 
4203
- $secure_auth = SECURE_AUTH_KEY;
4204
  if ( empty( $secure_auth ) ||
4205
  false !== strpos( $secure_auth, ' ' ) ||
4206
  'put your unique phrase here' === $secure_auth
@@ -5062,6 +5168,8 @@
5062
  }
5063
 
5064
  if ( $this->is_registered() ) {
 
 
5065
  $this->hook_callback_to_install_sync();
5066
  }
5067
 
@@ -5221,7 +5329,8 @@
5221
  */
5222
  ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $this->premium_plugin_basename() ) ) )
5223
  ) &&
5224
- $this->has_release_on_freemius()
 
5225
  ) {
5226
  FS_Plugin_Updater::instance( $this );
5227
  }
@@ -5369,7 +5478,7 @@
5369
  }
5370
 
5371
  // Send update to FS.
5372
- $result = $this->get_api_site_scope()->call( '/?fields=is_disconnected', 'put', array(
5373
  'is_disconnected' => true
5374
  ) );
5375
 
@@ -5518,7 +5627,7 @@
5518
  return true;
5519
  }
5520
 
5521
- $result = $this->get_api_site_scope()->call( '/?is_disconnected', 'put', array(
5522
  'is_disconnected' => false
5523
  ) );
5524
 
@@ -7100,7 +7209,7 @@
7100
 
7101
  /**
7102
  * @author Leo Fajardo (@leorw)
7103
- * @since 2.4.3
7104
  */
7105
  private function maybe_schedule_sync_cron() {
7106
  $next_schedule = $this->next_sync_cron();
@@ -7294,6 +7403,10 @@
7294
  * @param int|null $current_blog_id
7295
  */
7296
  function _sync_install_cron_method( array $blog_ids, $current_blog_id = null ) {
 
 
 
 
7297
  if ( $this->is_registered() ) {
7298
  if ( 1 < count( $blog_ids ) ) {
7299
  $this->sync_installs( array(), true );
@@ -7317,7 +7430,7 @@
7317
  *
7318
  * @param bool|string $email
7319
  * @param bool $is_pending_trial Since 1.2.1.5
7320
- * @param bool $is_suspicious_email Since 2.4.3 Set to true when there's an indication that email address the user opted in with is fake/dummy/placeholder.
7321
  */
7322
  function _add_pending_activation_notice(
7323
  $email = false,
@@ -7614,14 +7727,14 @@
7614
  <?php
7615
  echo $this->apply_filters( 'optin_pointer_execute', "
7616
 
7617
- optin.pointer('open');
7618
 
7619
- // Tag the opt-in pointer with custom class.
7620
- $('.wp-pointer #fs_connect')
7621
- .parents('.wp-pointer.wp-pointer-top')
7622
- .addClass('fs-opt-in-pointer');
7623
 
7624
- ", 'element', 'optin' ) ?>
7625
  }
7626
  }
7627
  });
@@ -7669,7 +7782,7 @@
7669
  }
7670
 
7671
  /* Events
7672
- ------------------------------------------------------------------------------------------------------------------*/
7673
  /**
7674
  * Delete site install from Database.
7675
  *
@@ -8151,7 +8264,7 @@
8151
  );
8152
  } else {
8153
  // Activate the license.
8154
- $install = $this->get_api_site_scope()->call(
8155
  '/',
8156
  'put',
8157
  array( 'license_key' => $this->apply_filters( 'license_key', $license->secret_key ) )
@@ -8829,6 +8942,14 @@
8829
  public function _after_new_blog_callback( $blog_id, $user_id, $domain, $path, $network_id, $meta ) {
8830
  $this->_logger->entrance();
8831
 
 
 
 
 
 
 
 
 
8832
  if ( $this->is_premium() &&
8833
  $this->is_network_connected() &&
8834
  is_object( $this->_license ) &&
@@ -8862,9 +8983,13 @@
8862
  }
8863
  }
8864
 
 
 
8865
  $this->switch_to_blog( $current_blog_id );
8866
 
8867
- if ( is_object( $this->_site ) ) {
 
 
8868
  // Already connected (with or without a license), so no need to continue.
8869
  return;
8870
  }
@@ -8897,6 +9022,8 @@
8897
  false
8898
  );
8899
 
 
 
8900
  $this->switch_to_blog( $current_blog_id );
8901
  } else {
8902
  /**
@@ -8907,8 +9034,8 @@
8907
  $has_delegated_site = false;
8908
 
8909
  $sites = self::get_sites();
8910
- foreach ( $sites as $site ) {
8911
- $blog_id = self::get_site_blog_id( $site );
8912
 
8913
  if ( $this->is_site_delegated_connection( $blog_id ) ) {
8914
  $has_delegated_site = true;
@@ -8922,15 +9049,24 @@
8922
  $this->skip_site_connection( $blog_id );
8923
  }
8924
  }
 
 
 
 
 
 
 
 
8925
  }
8926
 
8927
  /**
8928
  * @author Vova Feldman (@svovaf)
8929
- * @since 2.4.3
8930
  *
8931
  * @param \WP_Site $new_site
 
8932
  */
8933
- public function _after_wp_insert_site_callback( WP_Site $new_site ) {
8934
  $this->_logger->entrance();
8935
 
8936
  $this->_after_new_blog_callback(
@@ -9518,6 +9654,7 @@
9518
  *
9519
  * @param string[] string $override
9520
  * @param bool $only_diff
 
9521
  * @param bool $include_plugins Since 1.1.8 by default include plugin changes.
9522
  * @param bool $include_themes Since 1.1.8 by default include plugin changes.
9523
  *
@@ -9526,6 +9663,7 @@
9526
  private function get_installs_data_for_api(
9527
  array $override,
9528
  $only_diff = false,
 
9529
  $include_plugins = true,
9530
  $include_themes = true
9531
  ) {
@@ -9563,6 +9701,9 @@
9563
 
9564
  $sites = self::get_sites();
9565
 
 
 
 
9566
  foreach ( $sites as $site ) {
9567
  $blog_id = self::get_site_blog_id( $site );
9568
 
@@ -9581,10 +9722,34 @@
9581
 
9582
  $install_data = $this->get_site_info( $site );
9583
 
 
 
 
 
9584
  $uid = $install_data['uid'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9585
 
9586
  unset( $install_data['blog_id'] );
9587
  unset( $install_data['uid'] );
 
9588
 
9589
  $install_data['is_disconnected'] = $install->is_disconnected;
9590
  $install_data['is_active'] = $this->is_active_for_site( $blog_id );
@@ -9609,18 +9774,25 @@
9609
  $is_common_diff_for_any_site = $is_common_diff_for_any_site || $is_common_diff;
9610
  }
9611
 
9612
- if ( ! empty( $install_data ) || $is_common_diff ) {
9613
  // Add install ID and site unique ID.
9614
  $install_data['id'] = $install->id;
9615
  $install_data['uid'] = $uid;
 
9616
 
9617
- $installs_data[] = $install_data;
 
9618
  }
9619
  }
9620
  }
9621
 
9622
  restore_current_blog();
9623
 
 
 
 
 
 
9624
  if ( 0 < count( $installs_data ) && ( $is_common_diff_for_any_site || ! $only_diff ) ) {
9625
  if ( ! $only_diff ) {
9626
  $installs_data[] = $common;
@@ -9692,10 +9864,11 @@
9692
  *
9693
  * @param string[] string $override
9694
  * @param bool $flush
 
9695
  *
9696
  * @return false|object|string
9697
  */
9698
- private function send_install_update( $override = array(), $flush = false ) {
9699
  $this->_logger->entrance();
9700
 
9701
  $check_properties = $this->get_install_data_for_api( $override );
@@ -9721,10 +9894,9 @@
9721
  }
9722
  }
9723
 
9724
- if ( ! $keepalive_only_update ) {
9725
  /**
9726
- * Do not update the last install sync timestamp after a keepalive-only call since there were no actual
9727
- * updates sent.
9728
  *
9729
  * @author Leo Fajardo (@leorw)
9730
  * @since 2.2.3
@@ -9740,11 +9912,11 @@
9740
  $this->set_keepalive_timestamp();
9741
 
9742
  // Send updated values to FS.
9743
- $site = $this->get_api_site_scope()->call( '/', 'put', $params );
9744
 
9745
- if ( ! $keepalive_only_update && $this->is_api_result_entity( $site ) ) {
9746
  /**
9747
- * Do not clear scheduled sync after a keepalive-only call since there were no actual updates sent.
9748
  *
9749
  * @author Leo Fajardo (@leorw)
9750
  * @since 2.2.3
@@ -9766,37 +9938,29 @@
9766
  *
9767
  * @param string[] string $override
9768
  * @param bool $flush
 
9769
  *
9770
  * @return false|object|string
9771
  */
9772
- private function send_installs_update( $override = array(), $flush = false ) {
9773
  $this->_logger->entrance();
9774
 
9775
- $installs_data = $this->get_installs_data_for_api( $override, ! $flush );
 
 
 
 
 
 
9776
 
9777
- $keepalive_only_update = false;
9778
- if ( empty( $installs_data ) ) {
9779
- /**
9780
- * Pass `true` to use the network level storage since the update is for many installs.
9781
- *
9782
- * @author Leo Fajardo (@leorw)
9783
- * @since 2.2.3
9784
- */
9785
- $keepalive_only_update = $this->should_send_keepalive_update( true );
9786
 
9787
- if ( ! $keepalive_only_update ) {
9788
- /**
9789
- * There are no updates to send including keepalive.
9790
- *
9791
- * @author Leo Fajardo (@leorw)
9792
- * @since 2.2.3
9793
- */
9794
- return false;
9795
- }
9796
  }
9797
 
9798
- if ( ! $keepalive_only_update ) {
9799
- // Update last install sync timestamp if there were actual updates sent (i.e., not a keepalive-only call).
9800
  $this->set_cron_execution_timestamp( 'install_sync' );
9801
  }
9802
 
@@ -9811,8 +9975,8 @@
9811
  // Send updated values to FS.
9812
  $result = $this->get_api_user_scope()->call( "/plugins/{$this->_plugin->id}/installs.json", 'put', $installs_data );
9813
 
9814
- if ( ! $keepalive_only_update && $this->is_api_result_object( $result, 'installs' ) ) {
9815
- // I successfully sent installs update (there was an actual update sent and it's not just a keepalive-only call), clear scheduled sync if exist.
9816
  $this->clear_install_sync_cron();
9817
  }
9818
 
@@ -9862,10 +10026,10 @@
9862
  * @param string[] string $override
9863
  * @param bool $flush
9864
  */
9865
- private function sync_install( $override = array(), $flush = false ) {
9866
  $this->_logger->entrance();
9867
 
9868
- $site = $this->send_install_update( $override, $flush );
9869
 
9870
  if ( false === $site ) {
9871
  // No sync required.
@@ -9894,7 +10058,7 @@
9894
  private function sync_installs( $override = array(), $flush = false ) {
9895
  $this->_logger->entrance();
9896
 
9897
- $result = $this->send_installs_update( $override, $flush );
9898
 
9899
  if ( false === $result ) {
9900
  // No sync required.
@@ -10147,7 +10311,17 @@
10147
  return;
10148
  }
10149
 
10150
- $fs->_uninstall_plugin_event();
 
 
 
 
 
 
 
 
 
 
10151
 
10152
  $fs->do_action( 'after_uninstall' );
10153
  }
@@ -10543,7 +10717,7 @@
10543
  #endregion ------------------------------------------------------------------
10544
 
10545
  /* Account
10546
- ------------------------------------------------------------------------------------------------------------------*/
10547
 
10548
  /**
10549
  * Find plugin's slug by plugin's basename.
@@ -10607,9 +10781,14 @@
10607
  */
10608
  private static function get_all_sites(
10609
  $module_type = WP_FS__MODULE_TYPE_PLUGIN,
10610
- $blog_id = null
 
10611
  ) {
10612
- $sites = self::get_account_option( 'sites', $module_type, $blog_id );
 
 
 
 
10613
 
10614
  if ( ! is_array( $sites ) ) {
10615
  $sites = array();
@@ -11029,6 +11208,52 @@
11029
  return $this->_site;
11030
  }
11031
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11032
  /**
11033
  * Get plugin add-ons.
11034
  *
@@ -13116,7 +13341,7 @@
13116
  * Displays an email address update dialog box when the user clicks on the email address "Edit" button on the "Account" page.
13117
  *
13118
  * @author Leo Fajardo (@leorw)
13119
- * @since 2.4.3
13120
  */
13121
  function _add_email_address_update_dialog_box() {
13122
  $vars = array( 'id' => $this->_module_id );
@@ -13126,7 +13351,7 @@
13126
 
13127
  /**
13128
  * @author Leo Fajardo (@leorw)
13129
- * @since 2.4.3
13130
  */
13131
  function _add_email_address_update_option() {
13132
  if ( ! $this->should_handle_user_change() ) {
@@ -13139,7 +13364,7 @@
13139
 
13140
  /**
13141
  * @author Leo Fajardo (@leorw)
13142
- * @since 2.4.3
13143
  */
13144
  function _email_address_update_ajax_handler() {
13145
  $this->check_ajax_referer( 'update_email_address' );
@@ -13568,7 +13793,7 @@
13568
  self::shoot_ajax_failure();
13569
  }
13570
 
13571
- $site = $this->get_api_site_scope()->call(
13572
  '',
13573
  'put',
13574
  array(
@@ -15158,6 +15383,16 @@
15158
  return ( defined( 'DOING_CRON' ) && DOING_CRON );
15159
  }
15160
 
 
 
 
 
 
 
 
 
 
 
15161
  /**
15162
  * Check if a real user is visiting the admin dashboard.
15163
  *
@@ -15171,7 +15406,7 @@
15171
  is_admin() &&
15172
  ! self::is_ajax() &&
15173
  ! self::is_cron() &&
15174
- ( 'admin-post.php' !== self::get_current_page() )
15175
  );
15176
  }
15177
 
@@ -15675,11 +15910,16 @@
15675
  *
15676
  * @param int $blog_id
15677
  * @param FS_Site $install
 
15678
  *
15679
  * @return bool Since 2.3.1 returns if a switch was made.
15680
  */
15681
- function switch_to_blog( $blog_id, FS_Site $install = null ) {
15682
- if ( ! is_numeric( $blog_id ) || $blog_id == $this->_context_is_network_or_blog_id ) {
 
 
 
 
15683
  return false;
15684
  }
15685
 
@@ -15743,7 +15983,7 @@
15743
  unset( $this->_site_api );
15744
  unset( $this->_user_api );
15745
 
15746
- return false;
15747
  }
15748
 
15749
  /**
@@ -16040,6 +16280,10 @@
16040
  }
16041
  }
16042
 
 
 
 
 
16043
  if ( $this->is_sync_cron_scheduled() &&
16044
  $context_blog_id == $this->get_sync_cron_blog_id()
16045
  ) {
@@ -16073,6 +16317,10 @@
16073
 
16074
  $this->update_multisite_data_after_site_deactivation( $context_blog_id );
16075
 
 
 
 
 
16076
  $current_blog_id = get_current_blog_id();
16077
 
16078
  $this->switch_to_blog( $context_blog_id );
@@ -16106,6 +16354,10 @@
16106
 
16107
  $this->update_multisite_data_after_site_deactivation( $context_blog_id );
16108
 
 
 
 
 
16109
  $current_blog_id = get_current_blog_id();
16110
 
16111
  $this->switch_to_blog( $context_blog_id );
@@ -16127,7 +16379,7 @@
16127
  * Executed after site deletion, called from wp_delete_site
16128
  *
16129
  * @author Dario Curvino (@dudo)
16130
- * @since 2.4.3
16131
  *
16132
  * @param WP_Site $old_site
16133
  */
@@ -16383,7 +16635,7 @@
16383
  }
16384
 
16385
  /* Logger
16386
- ------------------------------------------------------------------------------------------------------------------*/
16387
  /**
16388
  * @param string $id
16389
  * @param bool $prefix_slug
@@ -16408,7 +16660,7 @@
16408
  }
16409
 
16410
  /* Security
16411
- ------------------------------------------------------------------------------------------------------------------*/
16412
  private static function _encrypt( $str ) {
16413
  if ( is_null( $str ) ) {
16414
  return null;
@@ -16628,20 +16880,6 @@
16628
  ) {
16629
  // Load site.
16630
  $this->_site = $site;
16631
-
16632
- // Load plans.
16633
- $this->_plans = $plans[ $this->_slug ];
16634
- if ( ! is_array( $this->_plans ) || empty( $this->_plans ) ) {
16635
- $this->_sync_plans();
16636
- } else {
16637
- for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) {
16638
- if ( $this->_plans[ $i ] instanceof FS_Plugin_Plan ) {
16639
- $this->_plans[ $i ] = self::decrypt_entity( $this->_plans[ $i ] );
16640
- } else {
16641
- unset( $this->_plans[ $i ] );
16642
- }
16643
- }
16644
- }
16645
  }
16646
 
16647
  $user = null;
@@ -16670,7 +16908,30 @@
16670
  /**
16671
  * This is a special fault tolerance mechanism to handle a scenario that the user data is missing.
16672
  */
16673
- $user = $this->sync_user_by_current_install();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16674
  }
16675
 
16676
  $this->_user = ( $user instanceof FS_User ) ?
@@ -16684,6 +16945,23 @@
16684
  }
16685
 
16686
  if ( is_object( $this->_site ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16687
  $this->_license = $this->_get_license_by_id( $this->_site->license_id );
16688
 
16689
  if ( $this->_site->version != $this->get_plugin_version() ) {
@@ -16702,6 +16980,13 @@
16702
  if ( $this->is_theme() ) {
16703
  $this->_register_account_hooks();
16704
  }
 
 
 
 
 
 
 
16705
  }
16706
 
16707
  /**
@@ -16938,6 +17223,7 @@
16938
  * @param bool $is_disconnected Whether or not to opt in without tracking.
16939
  * @param null|bool $is_marketing_allowed
16940
  * @param array $sites If network-level opt-in, an array of containing details of sites.
 
16941
  *
16942
  * @return string|object
16943
  * @use WP_Error
@@ -16951,7 +17237,8 @@
16951
  $trial_plan_id = false,
16952
  $is_disconnected = false,
16953
  $is_marketing_allowed = null,
16954
- $sites = array()
 
16955
  ) {
16956
  $this->_logger->entrance();
16957
 
@@ -16975,7 +17262,7 @@
16975
  $fs_user,
16976
  false,
16977
  $trial_plan_id,
16978
- true,
16979
  true,
16980
  $sites
16981
  );
@@ -17049,7 +17336,7 @@
17049
  $request = array(
17050
  'method' => 'POST',
17051
  'body' => $params,
17052
- 'timeout' => WP_FS__DEBUG_SDK ? 60 : 30,
17053
  );
17054
 
17055
  $url = $this->add_show_pending( WP_FS__ADDRESS . '/action/service/user/install/' );
@@ -17512,7 +17799,7 @@
17512
  $site->secret_key = $install_secret_key;
17513
 
17514
  $this->_site = $site;
17515
- $site_result = $this->get_api_site_scope()->get();
17516
  $site = new FS_Site( $site_result );
17517
  $this->_site = $site;
17518
 
@@ -17738,7 +18025,7 @@
17738
  *
17739
  * @return object|string If redirect is `false`, returns the next page the user should be redirected to, or the API error object if failed to install.
17740
  */
17741
- private function install_with_current_user(
17742
  $license_key = false,
17743
  $trial_plan_id = false,
17744
  $sites = array(),
@@ -18121,9 +18408,6 @@
18121
  $this->send_installs_update();
18122
  }
18123
 
18124
- // Switch install context back to the first install.
18125
- $this->_site = $first_install;
18126
-
18127
  $current_blog = get_current_blog_id();
18128
 
18129
  foreach ( $blog_2_install_map as $blog_id => $install ) {
@@ -18132,7 +18416,12 @@
18132
  $this->do_action( 'after_account_connection', $this->_user, $install );
18133
  }
18134
 
18135
- $this->switch_to_blog( $current_blog );
 
 
 
 
 
18136
 
18137
  $this->do_action( 'after_network_account_connection', $this->_user, $blog_2_install_map );
18138
  }
@@ -18236,6 +18525,10 @@
18236
  // return;
18237
  // }
18238
 
 
 
 
 
18239
  /**
18240
  * When running from a site admin with a network activated module and the connection
18241
  * was NOT delegated and the user still haven't skipped or opted-in, then hide the
@@ -19190,7 +19483,7 @@
19190
  *
19191
  * @return string
19192
  */
19193
- private static function get_ajax_action_static( $tag, $module_id = null ) {
19194
  $action = "fs_{$tag}";
19195
 
19196
  if ( ! empty( $module_id ) ) {
@@ -19433,7 +19726,7 @@
19433
  }
19434
 
19435
  /* Account Page
19436
- ------------------------------------------------------------------------------------------------------------------*/
19437
  /**
19438
  * Update site information.
19439
  *
@@ -19444,7 +19737,7 @@
19444
  * @param null|int $network_level_or_blog_id Since 2.0.0
19445
  * @param \FS_Site $site Since 2.0.0
19446
  */
19447
- private function _store_site( $store = true, $network_level_or_blog_id = null, FS_Site $site = null ) {
19448
  $this->_logger->entrance();
19449
 
19450
  if ( is_null( $site ) ) {
@@ -19459,9 +19752,12 @@
19459
 
19460
  $site_clone = clone $site;
19461
 
19462
- $sites = self::get_all_sites( $this->_module_type, $network_level_or_blog_id );
19463
 
19464
- if ( is_object( $this->_user ) && $this->_user->id != $site->user_id ) {
 
 
 
19465
  $this->sync_user_by_current_install( $site->user_id );
19466
 
19467
  $prev_stored_user_id = $this->_storage->get( 'prev_user_id', false, $network_level_or_blog_id );
@@ -19486,7 +19782,26 @@
19486
 
19487
  $sites[ $this->_slug ] = $site_clone;
19488
 
19489
- $this->set_account_option( 'sites', $sites, $store, $network_level_or_blog_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19490
  }
19491
 
19492
  /**
@@ -20727,10 +21042,10 @@
20727
  $this->switch_to_blog( $current_blog_id );
20728
  }
20729
 
20730
- $result = $this->send_install_update( array(), true );
20731
  $is_valid = $this->is_api_result_entity( $result );
20732
  } else {
20733
- $result = $this->send_installs_update( array(), true );
20734
  $is_valid = $this->is_api_result_object( $result, 'installs' );
20735
  }
20736
 
@@ -20740,7 +21055,7 @@
20740
  $this->switch_to_blog( $this->_storage->network_install_blog_id );
20741
  }
20742
 
20743
- // Show API messages only if not background sync or if paying customer.
20744
  if ( ! $background || $this->is_paying() ) {
20745
  // Try to ping API to see if not blocked.
20746
  if ( ! FS_Api::test() ) {
@@ -20750,25 +21065,49 @@
20750
  * @author Vova Feldman (@svovaf)
20751
  * @since 1.1.6 Only show message related to one of the Freemius powered plugins. Once it will be resolved it will fix the issue for all plugins anyways. There's no point to scare users with multiple error messages.
20752
  */
20753
- $api = $this->get_api_site_scope();
20754
 
20755
  if ( ! self::$_global_admin_notices->has_sticky( 'api_blocked' ) ) {
20756
- self::$_global_admin_notices->add(
20757
- sprintf(
20758
- $this->get_text_inline( 'Your server is blocking the access to Freemius\' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s', 'server-blocking-access' ),
20759
- $this->get_plugin_name(),
20760
- '<b>' . implode( ', ', $this->apply_filters( 'api_domains', array(
20761
- 'api.freemius.com',
20762
- 'wp.freemius.com'
20763
- ) ) ) . '</b>'
20764
- ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true ),
20765
- $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
20766
- 'error',
20767
- $background,
20768
- 'api_blocked'
20769
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20770
  }
20771
- } else {
20772
  // Authentication params are broken.
20773
  $this->_admin_notices->add(
20774
  $this->get_text_inline( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.', 'wrong-authentication-param-message' ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true ),
@@ -20782,6 +21121,9 @@
20782
  return;
20783
  }
20784
 
 
 
 
20785
  if ( $is_site_level_sync ) {
20786
  $site = new FS_Site( $result );
20787
  } else {
@@ -20973,7 +21315,7 @@
20973
  }
20974
 
20975
  if ( ! $this->is_addon() &&
20976
- $this->_site->is_beta() !== $site->is_beta
20977
  ) {
20978
  // Beta flag updated.
20979
  $this->_site = $site;
@@ -21707,6 +22049,10 @@
21707
  ) {
21708
  $this->_logger->entrance();
21709
 
 
 
 
 
21710
  $switch_to_blog_id = null;
21711
 
21712
  /**
@@ -22966,7 +23312,7 @@
22966
  }
22967
 
22968
  /* Pricing & Upgrade
22969
- ------------------------------------------------------------------------------------------------------------------*/
22970
  /**
22971
  * Render pricing page.
22972
  *
@@ -23125,17 +23471,17 @@
23125
  }
23126
 
23127
  /* CSS & JavaScript
23128
- ------------------------------------------------------------------------------------------------------------------*/
23129
  /* function _enqueue_script($handle, $src) {
23130
- $url = plugins_url( substr( WP_FS__DIR_JS, strlen( $this->_plugin_dir_path ) ) . '/assets/js/' . $src );
23131
 
23132
- $this->_logger->entrance( 'script = ' . $url );
23133
 
23134
- wp_enqueue_script( $handle, $url );
23135
- }*/
23136
 
23137
  /* SDK
23138
- ------------------------------------------------------------------------------------------------------------------*/
23139
  private $_user_api;
23140
 
23141
  /**
@@ -23147,7 +23493,7 @@
23147
  *
23148
  * @return FS_Api
23149
  */
23150
- private function get_api_user_scope( $flush = false ) {
23151
  if ( ! isset( $this->_user_api ) || $flush ) {
23152
  $this->_user_api = $this->get_api_user_scope_by_user( $this->_user );
23153
  }
@@ -23226,13 +23572,56 @@
23226
  $this->_site->public_key,
23227
  ! $this->is_live(),
23228
  $this->_site->secret_key,
23229
- $this->get_sdk_version()
 
23230
  );
23231
  }
23232
 
23233
  return $this->_site_api;
23234
  }
23235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23236
  private $_plugin_api;
23237
 
23238
  /**
@@ -23668,7 +24057,7 @@
23668
  }
23669
 
23670
  /* Action Links
23671
- ------------------------------------------------------------------------------------------------------------------*/
23672
  private $_action_links_hooked = false;
23673
  private $_action_links = array();
23674
 
@@ -23855,10 +24244,13 @@
23855
 
23856
  if (
23857
  $this->is_addon() &&
23858
- ! $this->is_only_premium() &&
23859
- $this->get_parent_instance()->is_anonymous()
23860
  ) {
23861
- return;
 
 
 
 
23862
  }
23863
 
23864
  if ( fs_is_network_admin() ) {
521
  * @author Leo Fajardo (@leorw)
522
  * @since 1.2.2
523
  */
524
+ ( is_object( $this->_plugin ) && isset( $this->_plugin->title ) ?
525
+ $this->_plugin->title :
526
+ $this->get_plugin_name()
527
+ ),
528
  $this->get_unique_affix()
529
  );
530
 
1646
  }
1647
 
1648
  if ( $this->is_plugin() ) {
1649
+ if ( version_compare( $GLOBALS['wp_version'], '5.1', '<' ) ) {
1650
+ add_action( 'wpmu_new_blog', array( $this, '_after_new_blog_callback' ), 10, 6 );
1651
+ } else {
1652
+ add_action( 'wp_initialize_site', array( $this, '_after_wp_initialize_site_callback' ), 11, 2 );
 
 
1653
  }
1654
 
1655
  register_deactivation_hook( $this->_plugin_main_file_path, array( &$this, '_deactivate_plugin_hook' ) );
2108
  * Leverage backtrace to find caller plugin file path.
2109
  *
2110
  * @param bool $is_init Is initiation sequence.
2111
+ * @param string $main_file Since 2.5.0 expects the module's main file path to potentially purge the cached path.
2112
  *
2113
  * @return string
2114
  * @since 1.0.6
2234
  * @param number $module_id
2235
  * @param string $slug
2236
  *
2237
+ * @return string Since 2.5.0 return the module's main file path.
2238
  *
2239
  * @since 1.2.2
2240
  */
2285
  * If the module's main file path is identical to the main file path of another module then it means that the cached path of the current module or the other one with the same path is wrong, and therefore, we need to recalculate those paths.
2286
  *
2287
  * @author Vova Feldman (@svovaf)
2288
+ * @since 2.5.0
2289
  */
2290
  if ( ! $find_caller ) {
2291
  if ( $id == $module_id ) {
2329
  * SDK an internal file instead of directly from functions.php.
2330
  * @since 1.2.1.7 Knows how to handle cases when an add-on includes the parent module logic.
2331
  *
2332
+ * @param number $module_id @since 2.5.0
2333
  */
2334
  private function get_caller_main_file_and_type( $module_id ) {
2335
  self::require_plugin_essentials();
2492
  * @since 1.1.2
2493
  */
2494
  function _add_deactivation_feedback_dialog_box() {
2495
+ if (
2496
+ $this->is_clone() ||
2497
+ ( is_object( $this->_site ) && ! $this->is_registered() )
2498
+ ) {
2499
+ return;
2500
+ }
2501
+
2502
  $subscription_cancellation_dialog_box_template_params = $this->apply_filters( 'show_deactivation_subscription_cancellation', true ) ?
2503
  $this->_get_subscription_cancellation_dialog_box_template_params() :
2504
  array();
3068
  return self::instance( $addon_id );
3069
  }
3070
 
3071
+ /**
3072
+ * @return Freemius[]
3073
+ */
3074
+ static function _get_all_instances() {
3075
+ return self::$_instances;
3076
+ }
3077
+
3078
  #endregion ------------------------------------------------------------------
3079
 
3080
  /**
3585
  add_action( 'plugins_loaded', array( 'Freemius', '_load_textdomain' ), 1 );
3586
  }
3587
 
3588
+ $clone_manager = FS_Clone_Manager::instance();
3589
+ add_action( 'init', array( $clone_manager, '_init' ) );
3590
+
3591
  add_action( 'admin_footer', array( 'Freemius', '_enrich_ajax_url' ) );
3592
  add_action( 'admin_footer', array( 'Freemius', '_open_support_forum_in_new_page' ) );
3593
 
3607
  self::$_statics_loaded = true;
3608
  }
3609
 
3610
+ #--------------------------------------------------------------------------------
3611
+ #region Clone
3612
+ #--------------------------------------------------------------------------------
3613
+
3614
+ /**
3615
+ * @author Leo Fajardo (@leorw)
3616
+ * @since 2.5.0
3617
+ *
3618
+ * @return bool
3619
+ */
3620
+ private function is_unresolved_clone() {
3621
+ if ( ! $this->is_clone() ) {
3622
+ return false;
3623
+ }
3624
+
3625
+ return FS_Clone_Manager::instance()->has_temporary_duplicate_mode_expired();
3626
+ }
3627
+
3628
+ /**
3629
+ * @author Leo Fajardo (@leorw)
3630
+ * @since 2.5.0
3631
+ */
3632
+ function is_clone() {
3633
+ if ( ! is_object( $this->_site ) ) {
3634
+ return false;
3635
+ }
3636
+
3637
+ return (
3638
+ fs_strip_url_protocol( trailingslashit( $this->_site->url ) ) !== fs_strip_url_protocol( trailingslashit( get_site_url() ) )
3639
+ );
3640
+ }
3641
+
3642
+ /**
3643
+ * @author Leo Fajardo (@leorw)
3644
+ * @since 2.5.0
3645
+ *
3646
+ * @param number $site_id
3647
+ */
3648
+ function fetch_install_by_id( $site_id ) {
3649
+ return $this->get_current_or_network_user_api_scope()->get( "/installs/{$site_id}.json" );
3650
+ }
3651
+
3652
+ /**
3653
+ * @author Leo Fajardo (@leorw)
3654
+ * @since 2.5.0
3655
+ *
3656
+ * @return string|object|bool
3657
+ */
3658
+ function _handle_long_term_duplicate() {
3659
+ $this->_logger->entrance();
3660
+
3661
+ $this->delete_current_install( false );
3662
+
3663
+ $license_key = false;
3664
+
3665
+ if (
3666
+ is_object( $this->_license ) &&
3667
+ ! $this->_license->is_utilized(
3668
+ ( WP_FS__IS_LOCALHOST_FOR_SERVER || FS_Site::is_localhost_by_address( get_site_url() ) )
3669
+ )
3670
+ ) {
3671
+ $license_key = $this->_license->secret_key;
3672
+ }
3673
+
3674
+ return $this->opt_in(
3675
+ false,
3676
+ false,
3677
+ false,
3678
+ $license_key,
3679
+ false,
3680
+ false,
3681
+ false,
3682
+ null,
3683
+ array(),
3684
+ false
3685
+ );
3686
+ }
3687
+
3688
+ #endregion
3689
+
3690
  /**
3691
  * @author Leo Fajardo (@leorw)
3692
  *
3965
 
3966
  /**
3967
  * @author Leo Fajardo (@leorw)
3968
+ * @since 2.5.0
3969
  *
3970
  * @return array
3971
  */
3972
+ static function get_all_modules_sites() {
3973
  self::$_static_logger->entrance();
3974
 
3975
  $sites_by_type = array(
3982
  if ( ! is_multisite() ) {
3983
  foreach ( $module_types as $type ) {
3984
  $sites_by_type[ $type ] = self::get_all_sites( $type );
3985
+
3986
+ foreach ( $sites_by_type[ $type ] as $slug => $install ) {
3987
+ $sites_by_type[ $type ][ $slug ] = array( $install );
3988
+ }
3989
  }
3990
  } else {
3991
  $sites = self::get_sites();
4058
  function is_on() {
4059
  self::$_static_logger->entrance();
4060
 
4061
+ if ( is_object( $this->_site ) && ! $this->is_registered() ) {
4062
+ return false;
4063
+ }
4064
+
4065
  if ( isset( $this->_is_on ) ) {
4066
  return $this->_is_on;
4067
  }
4306
  if ( empty( $unique_id ) || ! is_string( $unique_id ) ) {
4307
  $key = fs_strip_url_protocol( get_site_url( $blog_id ) );
4308
 
4309
+ $secure_auth = defined( 'SECURE_AUTH_KEY' ) ? SECURE_AUTH_KEY : '';
4310
  if ( empty( $secure_auth ) ||
4311
  false !== strpos( $secure_auth, ' ' ) ||
4312
  'put your unique phrase here' === $secure_auth
5168
  }
5169
 
5170
  if ( $this->is_registered() ) {
5171
+ FS_Clone_Manager::instance()->maybe_resolve_new_subsite_install_automatically( $this );
5172
+
5173
  $this->hook_callback_to_install_sync();
5174
  }
5175
 
5329
  */
5330
  ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $this->premium_plugin_basename() ) ) )
5331
  ) &&
5332
+ $this->has_release_on_freemius() &&
5333
+ ( ! $this->is_unresolved_clone() )
5334
  ) {
5335
  FS_Plugin_Updater::instance( $this );
5336
  }
5478
  }
5479
 
5480
  // Send update to FS.
5481
+ $result = $this->api_site_call( '/?fields=is_disconnected', 'put', array(
5482
  'is_disconnected' => true
5483
  ) );
5484
 
5627
  return true;
5628
  }
5629
 
5630
+ $result = $this->api_site_call( '/?is_disconnected', 'put', array(
5631
  'is_disconnected' => false
5632
  ) );
5633
 
7209
 
7210
  /**
7211
  * @author Leo Fajardo (@leorw)
7212
+ * @since 2.5.0
7213
  */
7214
  private function maybe_schedule_sync_cron() {
7215
  $next_schedule = $this->next_sync_cron();
7403
  * @param int|null $current_blog_id
7404
  */
7405
  function _sync_install_cron_method( array $blog_ids, $current_blog_id = null ) {
7406
+ if ( $this->is_clone() ) {
7407
+ return;
7408
+ }
7409
+
7410
  if ( $this->is_registered() ) {
7411
  if ( 1 < count( $blog_ids ) ) {
7412
  $this->sync_installs( array(), true );
7430
  *
7431
  * @param bool|string $email
7432
  * @param bool $is_pending_trial Since 1.2.1.5
7433
+ * @param bool $is_suspicious_email Since 2.5.0 Set to true when there's an indication that email address the user opted in with is fake/dummy/placeholder.
7434
  */
7435
  function _add_pending_activation_notice(
7436
  $email = false,
7727
  <?php
7728
  echo $this->apply_filters( 'optin_pointer_execute', "
7729
 
7730
+ optin.pointer('open');
7731
 
7732
+ // Tag the opt-in pointer with custom class.
7733
+ $('.wp-pointer #fs_connect')
7734
+ .parents('.wp-pointer.wp-pointer-top')
7735
+ .addClass('fs-opt-in-pointer');
7736
 
7737
+ ", 'element', 'optin' ) ?>
7738
  }
7739
  }
7740
  });
7782
  }
7783
 
7784
  /* Events
7785
+ ------------------------------------------------------------------------------------------------------------------*/
7786
  /**
7787
  * Delete site install from Database.
7788
  *
8264
  );
8265
  } else {
8266
  // Activate the license.
8267
+ $install = $this->api_site_call(
8268
  '/',
8269
  'put',
8270
  array( 'license_key' => $this->apply_filters( 'license_key', $license->secret_key ) )
8942
  public function _after_new_blog_callback( $blog_id, $user_id, $domain, $path, $network_id, $meta ) {
8943
  $this->_logger->entrance();
8944
 
8945
+ if ( ! $this->_is_network_active ) {
8946
+ FS_Clone_Manager::instance()->store_new_blog_install_info( $blog_id );
8947
+ return;
8948
+ }
8949
+
8950
+ $site = null;
8951
+ $new_blog_id = $blog_id;
8952
+
8953
  if ( $this->is_premium() &&
8954
  $this->is_network_connected() &&
8955
  is_object( $this->_license ) &&
8983
  }
8984
  }
8985
 
8986
+ $site = $this->_site;
8987
+
8988
  $this->switch_to_blog( $current_blog_id );
8989
 
8990
+ if ( is_object( $site ) ) {
8991
+ FS_Clone_Manager::instance()->store_new_blog_install_info( $blog_id, $site );
8992
+
8993
  // Already connected (with or without a license), so no need to continue.
8994
  return;
8995
  }
9022
  false
9023
  );
9024
 
9025
+ $site = $this->_site;
9026
+
9027
  $this->switch_to_blog( $current_blog_id );
9028
  } else {
9029
  /**
9034
  $has_delegated_site = false;
9035
 
9036
  $sites = self::get_sites();
9037
+ foreach ( $sites as $wp_site ) {
9038
+ $blog_id = self::get_site_blog_id( $wp_site );
9039
 
9040
  if ( $this->is_site_delegated_connection( $blog_id ) ) {
9041
  $has_delegated_site = true;
9049
  $this->skip_site_connection( $blog_id );
9050
  }
9051
  }
9052
+
9053
+ /**
9054
+ * Store the new blog's information even if there's no install so that when a clone install is stored in the new blog's storage, we can try to resolve it automatically.
9055
+ *
9056
+ * @author Leo Fajardo (@leorw)
9057
+ * @since 2.5.0
9058
+ */
9059
+ FS_Clone_Manager::instance()->store_new_blog_install_info( $new_blog_id, $site );
9060
  }
9061
 
9062
  /**
9063
  * @author Vova Feldman (@svovaf)
9064
+ * @since 2.5.0
9065
  *
9066
  * @param \WP_Site $new_site
9067
+ * @param array $args
9068
  */
9069
+ public function _after_wp_initialize_site_callback( WP_Site $new_site, $args ) {
9070
  $this->_logger->entrance();
9071
 
9072
  $this->_after_new_blog_callback(
9654
  *
9655
  * @param string[] string $override
9656
  * @param bool $only_diff
9657
+ * @param bool $is_keepalive
9658
  * @param bool $include_plugins Since 1.1.8 by default include plugin changes.
9659
  * @param bool $include_themes Since 1.1.8 by default include plugin changes.
9660
  *
9663
  private function get_installs_data_for_api(
9664
  array $override,
9665
  $only_diff = false,
9666
+ $is_keepalive = false,
9667
  $include_plugins = true,
9668
  $include_themes = true
9669
  ) {
9701
 
9702
  $sites = self::get_sites();
9703
 
9704
+ $subsite_data_by_install_id = array();
9705
+ $install_url_by_install_id = array();
9706
+
9707
  foreach ( $sites as $site ) {
9708
  $blog_id = self::get_site_blog_id( $site );
9709
 
9722
 
9723
  $install_data = $this->get_site_info( $site );
9724
 
9725
+ if ( FS_Clone_Manager::instance()->is_temporary_duplicate_by_blog_id( $install_data['blog_id'] ) ) {
9726
+ continue;
9727
+ }
9728
+
9729
  $uid = $install_data['uid'];
9730
+ $url = $install_data['url'];
9731
+
9732
+ if ( isset( $subsite_data_by_install_id[ $install->id ] ) ) {
9733
+ $clone_subsite_data = $subsite_data_by_install_id[ $install->id ];
9734
+ $clone_install_url = $install_url_by_install_id[ $install->id ];
9735
+
9736
+ if (
9737
+ /**
9738
+ * If we already have an install with the same URL as the subsite it's stored in, skip the current subsite. Otherwise, replace the existing install's data with the current subsite's install's data if the URLs match.
9739
+ *
9740
+ * @author Leo Fajardo (@leorw)
9741
+ * @since 2.5.0
9742
+ */
9743
+ fs_strip_url_protocol( untrailingslashit( $clone_install_url ) ) === fs_strip_url_protocol( untrailingslashit( $clone_subsite_data['url'] ) ) ||
9744
+ fs_strip_url_protocol( untrailingslashit( $install->url ) ) !== fs_strip_url_protocol( untrailingslashit( $url ) )
9745
+ ) {
9746
+ continue;
9747
+ }
9748
+ }
9749
 
9750
  unset( $install_data['blog_id'] );
9751
  unset( $install_data['uid'] );
9752
+ unset( $install_data['url'] );
9753
 
9754
  $install_data['is_disconnected'] = $install->is_disconnected;
9755
  $install_data['is_active'] = $this->is_active_for_site( $blog_id );
9774
  $is_common_diff_for_any_site = $is_common_diff_for_any_site || $is_common_diff;
9775
  }
9776
 
9777
+ if ( ! empty( $install_data ) || $is_common_diff || $is_keepalive ) {
9778
  // Add install ID and site unique ID.
9779
  $install_data['id'] = $install->id;
9780
  $install_data['uid'] = $uid;
9781
+ $install_data['url'] = $url;
9782
 
9783
+ $subsite_data_by_install_id[ $install->id ] = $install_data;
9784
+ $install_url_by_install_id[ $install->id ] = $install->url;
9785
  }
9786
  }
9787
  }
9788
 
9789
  restore_current_blog();
9790
 
9791
+ $installs_data = array_merge(
9792
+ $installs_data,
9793
+ array_values( $subsite_data_by_install_id )
9794
+ );
9795
+
9796
  if ( 0 < count( $installs_data ) && ( $is_common_diff_for_any_site || ! $only_diff ) ) {
9797
  if ( ! $only_diff ) {
9798
  $installs_data[] = $common;
9864
  *
9865
  * @param string[] string $override
9866
  * @param bool $flush
9867
+ * @param bool $is_two_way_sync @since 2.5.0 If true and there's a successful API request, the install sync cron will be cleared.
9868
  *
9869
  * @return false|object|string
9870
  */
9871
+ private function send_install_update( $override = array(), $flush = false, $is_two_way_sync = false ) {
9872
  $this->_logger->entrance();
9873
 
9874
  $check_properties = $this->get_install_data_for_api( $override );
9894
  }
9895
  }
9896
 
9897
+ if ( $is_two_way_sync ) {
9898
  /**
9899
+ * Update last install sync timestamp during a two-way sync call as we expect that updates are sent during this call.
 
9900
  *
9901
  * @author Leo Fajardo (@leorw)
9902
  * @since 2.2.3
9912
  $this->set_keepalive_timestamp();
9913
 
9914
  // Send updated values to FS.
9915
+ $site = $this->api_site_call( '/', 'put', $params, true );
9916
 
9917
+ if ( $is_two_way_sync && $this->is_api_result_entity( $site ) ) {
9918
  /**
9919
+ * Clear scheduled install sync after a two-way sync call.
9920
  *
9921
  * @author Leo Fajardo (@leorw)
9922
  * @since 2.2.3
9938
  *
9939
  * @param string[] string $override
9940
  * @param bool $flush
9941
+ * @param bool $is_two_way_sync @since 2.5.0 If true and there's a successful API request, the install sync cron will be cleared.
9942
  *
9943
  * @return false|object|string
9944
  */
9945
+ private function send_installs_update( $override = array(), $flush = false, $is_two_way_sync = false ) {
9946
  $this->_logger->entrance();
9947
 
9948
+ /**
9949
+ * Pass `true` to use the network level storage since the update is for many installs.
9950
+ *
9951
+ * @author Leo Fajardo (@leorw)
9952
+ * @since 2.2.3
9953
+ */
9954
+ $should_send_keepalive = $this->should_send_keepalive_update( true );
9955
 
9956
+ $installs_data = $this->get_installs_data_for_api( $override, ! $flush, $should_send_keepalive );
 
 
 
 
 
 
 
 
9957
 
9958
+ if ( empty( $installs_data ) ) {
9959
+ return false;
 
 
 
 
 
 
 
9960
  }
9961
 
9962
+ if ( $is_two_way_sync ) {
9963
+ // Update last install sync timestamp during a two-way sync call as we expect that updates are sent during this call.
9964
  $this->set_cron_execution_timestamp( 'install_sync' );
9965
  }
9966
 
9975
  // Send updated values to FS.
9976
  $result = $this->get_api_user_scope()->call( "/plugins/{$this->_plugin->id}/installs.json", 'put', $installs_data );
9977
 
9978
+ if ( $is_two_way_sync && $this->is_api_result_object( $result, 'installs' ) ) {
9979
+ // I successfully sent a two-way installs update, clear the scheduled install sync if it exists.
9980
  $this->clear_install_sync_cron();
9981
  }
9982
 
10026
  * @param string[] string $override
10027
  * @param bool $flush
10028
  */
10029
+ function sync_install( $override = array(), $flush = false ) {
10030
  $this->_logger->entrance();
10031
 
10032
+ $site = $this->send_install_update( $override, $flush, true );
10033
 
10034
  if ( false === $site ) {
10035
  // No sync required.
10058
  private function sync_installs( $override = array(), $flush = false ) {
10059
  $this->_logger->entrance();
10060
 
10061
+ $result = $this->send_installs_update( $override, $flush, true );
10062
 
10063
  if ( false === $result ) {
10064
  // No sync required.
10311
  return;
10312
  }
10313
 
10314
+ if (
10315
+ ! $fs->is_clone() &&
10316
+ /**
10317
+ * If there's a context install, run this method only when there's also a context user (e.g., when cloning a subsite of a multisite network into a single-site installation, it's possible for an install to be associated with a non-existing user entity; we want Freemius to be off in this case, while we are trying to recover the user).
10318
+ *
10319
+ * @author Leo Fajardo
10320
+ */
10321
+ ( ! is_object( $fs->_site ) || $fs->is_registered() )
10322
+ ) {
10323
+ $fs->_uninstall_plugin_event();
10324
+ }
10325
 
10326
  $fs->do_action( 'after_uninstall' );
10327
  }
10717
  #endregion ------------------------------------------------------------------
10718
 
10719
  /* Account
10720
+ ------------------------------------------------------------------------------------------------------------------*/
10721
 
10722
  /**
10723
  * Find plugin's slug by plugin's basename.
10781
  */
10782
  private static function get_all_sites(
10783
  $module_type = WP_FS__MODULE_TYPE_PLUGIN,
10784
+ $blog_id = null,
10785
+ $is_backup = false
10786
  ) {
10787
+ $sites = self::get_account_option(
10788
+ ( $is_backup ? 'prev_' : '' ) . 'sites',
10789
+ $module_type,
10790
+ $blog_id
10791
+ );
10792
 
10793
  if ( ! is_array( $sites ) ) {
10794
  $sites = array();
11208
  return $this->_site;
11209
  }
11210
 
11211
+ /**
11212
+ * @author Leo Fajardo (@leorw)
11213
+ * @since 2.5.0
11214
+ */
11215
+ function store_site( $site ) {
11216
+ $this->_site = $site;
11217
+ $this->_store_site( true );
11218
+ }
11219
+
11220
+ /**
11221
+ * Deletes the current install with an option to back it up in case restoration will be needed (e.g., if the automatic clone resolution attempt fails).
11222
+ *
11223
+ * @author Leo Fajardo (@leorw)
11224
+ * @since 2.5.0
11225
+ */
11226
+ function delete_current_install( $back_up ) {
11227
+ // Back up and delete the unique ID.
11228
+ if ( $back_up ) {
11229
+ self::$_accounts->set_option( 'prev_unique_id', $this->get_anonymous_id() );
11230
+ }
11231
+
11232
+ self::$_accounts->set_option( 'unique_id', null );
11233
+
11234
+ if ( $back_up ) {
11235
+ // Back up the install before deleting it so that it can be restored later on if necessary (e.g., if the automatic clone resolution attempt fails).
11236
+ $this->back_up_site();
11237
+ }
11238
+
11239
+ $this->_delete_site();
11240
+ $this->_site = null;
11241
+ }
11242
+
11243
+ /**
11244
+ * @author Leo Fajardo (@leorw)
11245
+ * @since 2.5.0
11246
+ */
11247
+ function restore_backup_site() {
11248
+ self::$_accounts->set_option(
11249
+ 'unique_id',
11250
+ self::$_accounts->get_option( 'prev_unique_id' )
11251
+ );
11252
+
11253
+ $sites = self::get_all_sites( $this->_module_type, null, true );
11254
+ $this->store_site( clone $sites[ $this->_slug ] );
11255
+ }
11256
+
11257
  /**
11258
  * Get plugin add-ons.
11259
  *
13341
  * Displays an email address update dialog box when the user clicks on the email address "Edit" button on the "Account" page.
13342
  *
13343
  * @author Leo Fajardo (@leorw)
13344
+ * @since 2.5.0
13345
  */
13346
  function _add_email_address_update_dialog_box() {
13347
  $vars = array( 'id' => $this->_module_id );
13351
 
13352
  /**
13353
  * @author Leo Fajardo (@leorw)
13354
+ * @since 2.5.0
13355
  */
13356
  function _add_email_address_update_option() {
13357
  if ( ! $this->should_handle_user_change() ) {
13364
 
13365
  /**
13366
  * @author Leo Fajardo (@leorw)
13367
+ * @since 2.5.0
13368
  */
13369
  function _email_address_update_ajax_handler() {
13370
  $this->check_ajax_referer( 'update_email_address' );
13793
  self::shoot_ajax_failure();
13794
  }
13795
 
13796
+ $site = $this->api_site_call(
13797
  '',
13798
  'put',
13799
  array(
15383
  return ( defined( 'DOING_CRON' ) && DOING_CRON );
15384
  }
15385
 
15386
+ /**
15387
+ * @author Leo Fajardo (@leorw)
15388
+ * @since 2.5.0
15389
+ *
15390
+ * @return bool
15391
+ */
15392
+ static function is_admin_post() {
15393
+ return ( 'admin-post.php' === self::get_current_page() );
15394
+ }
15395
+
15396
  /**
15397
  * Check if a real user is visiting the admin dashboard.
15398
  *
15406
  is_admin() &&
15407
  ! self::is_ajax() &&
15408
  ! self::is_cron() &&
15409
+ ! self::is_admin_post()
15410
  );
15411
  }
15412
 
15910
  *
15911
  * @param int $blog_id
15912
  * @param FS_Site $install
15913
+ * @param bool $flush
15914
  *
15915
  * @return bool Since 2.3.1 returns if a switch was made.
15916
  */
15917
+ function switch_to_blog( $blog_id, FS_Site $install = null, $flush = false ) {
15918
+ if ( ! is_numeric( $blog_id ) ) {
15919
+ return false;
15920
+ }
15921
+
15922
+ if ( ! $flush && $blog_id == $this->_context_is_network_or_blog_id ) {
15923
  return false;
15924
  }
15925
 
15983
  unset( $this->_site_api );
15984
  unset( $this->_user_api );
15985
 
15986
+ return true;
15987
  }
15988
 
15989
  /**
16280
  }
16281
  }
16282
 
16283
+ if ( ! $this->is_registered() ) {
16284
+ return;
16285
+ }
16286
+
16287
  if ( $this->is_sync_cron_scheduled() &&
16288
  $context_blog_id == $this->get_sync_cron_blog_id()
16289
  ) {
16317
 
16318
  $this->update_multisite_data_after_site_deactivation( $context_blog_id );
16319
 
16320
+ if ( ! $this->is_registered() ) {
16321
+ return;
16322
+ }
16323
+
16324
  $current_blog_id = get_current_blog_id();
16325
 
16326
  $this->switch_to_blog( $context_blog_id );
16354
 
16355
  $this->update_multisite_data_after_site_deactivation( $context_blog_id );
16356
 
16357
+ if ( ! $this->is_registered() ) {
16358
+ return;
16359
+ }
16360
+
16361
  $current_blog_id = get_current_blog_id();
16362
 
16363
  $this->switch_to_blog( $context_blog_id );
16379
  * Executed after site deletion, called from wp_delete_site
16380
  *
16381
  * @author Dario Curvino (@dudo)
16382
+ * @since 2.5.0
16383
  *
16384
  * @param WP_Site $old_site
16385
  */
16635
  }
16636
 
16637
  /* Logger
16638
+ ------------------------------------------------------------------------------------------------------------------*/
16639
  /**
16640
  * @param string $id
16641
  * @param bool $prefix_slug
16660
  }
16661
 
16662
  /* Security
16663
+ ------------------------------------------------------------------------------------------------------------------*/
16664
  private static function _encrypt( $str ) {
16665
  if ( is_null( $str ) ) {
16666
  return null;
16880
  ) {
16881
  // Load site.
16882
  $this->_site = $site;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16883
  }
16884
 
16885
  $user = null;
16908
  /**
16909
  * This is a special fault tolerance mechanism to handle a scenario that the user data is missing.
16910
  */
16911
+ if (
16912
+ ! isset( $this->_storage->user_recovery_from_install_last_attempt_timestamp ) ||
16913
+ time() > ( $this->_storage->user_recovery_from_install_last_attempt_timestamp + FS_Clone_Manager::CLONE_RESOLUTION_MAX_EXECUTION_TIME )
16914
+ ) {
16915
+ $user = $this->sync_user_by_current_install();
16916
+ } else {
16917
+ return;
16918
+ }
16919
+
16920
+ if ( is_object( $user ) ) {
16921
+ $this->_storage->user_was_recovered_from_install = true;
16922
+ } else {
16923
+ $this->_storage->user_recovery_from_install_attempts = isset( $this->_storage->user_recovery_from_install_attempts ) ?
16924
+ ( $this->_storage->user_recovery_from_install_attempts + 1 ) :
16925
+ 1;
16926
+
16927
+ if ( $this->_storage->user_recovery_from_install_attempts >= 3 ) {
16928
+ $this->delete_current_install( false );
16929
+ } else {
16930
+ $this->_storage->user_recovery_from_install_last_attempt_timestamp = time();
16931
+
16932
+ return;
16933
+ }
16934
+ }
16935
  }
16936
 
16937
  $this->_user = ( $user instanceof FS_User ) ?
16945
  }
16946
 
16947
  if ( is_object( $this->_site ) ) {
16948
+ // Load plans.
16949
+ $this->_plans = isset( $plans[ $this->_slug ] ) ?
16950
+ $plans[ $this->_slug ] :
16951
+ array();
16952
+
16953
+ if ( ! is_array( $this->_plans ) || empty( $this->_plans ) ) {
16954
+ $this->_sync_plans();
16955
+ } else {
16956
+ for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) {
16957
+ if ( $this->_plans[ $i ] instanceof FS_Plugin_Plan ) {
16958
+ $this->_plans[ $i ] = self::decrypt_entity( $this->_plans[ $i ] );
16959
+ } else {
16960
+ unset( $this->_plans[ $i ] );
16961
+ }
16962
+ }
16963
+ }
16964
+
16965
  $this->_license = $this->_get_license_by_id( $this->_site->license_id );
16966
 
16967
  if ( $this->_site->version != $this->get_plugin_version() ) {
16980
  if ( $this->is_theme() ) {
16981
  $this->_register_account_hooks();
16982
  }
16983
+
16984
+ if (
16985
+ $this->is_clone() &&
16986
+ empty( FS_Clone_Manager::instance()->get_clone_identification_timestamp() )
16987
+ ) {
16988
+ FS_Clone_Manager::instance()->store_clone_identification_timestamp();
16989
+ }
16990
  }
16991
 
16992
  /**
17223
  * @param bool $is_disconnected Whether or not to opt in without tracking.
17224
  * @param null|bool $is_marketing_allowed
17225
  * @param array $sites If network-level opt-in, an array of containing details of sites.
17226
+ * @param bool $redirect
17227
  *
17228
  * @return string|object
17229
  * @use WP_Error
17237
  $trial_plan_id = false,
17238
  $is_disconnected = false,
17239
  $is_marketing_allowed = null,
17240
+ $sites = array(),
17241
+ $redirect = true
17242
  ) {
17243
  $this->_logger->entrance();
17244
 
17262
  $fs_user,
17263
  false,
17264
  $trial_plan_id,
17265
+ $redirect,
17266
  true,
17267
  $sites
17268
  );
17336
  $request = array(
17337
  'method' => 'POST',
17338
  'body' => $params,
17339
+ 'timeout' => 60,
17340
  );
17341
 
17342
  $url = $this->add_show_pending( WP_FS__ADDRESS . '/action/service/user/install/' );
17799
  $site->secret_key = $install_secret_key;
17800
 
17801
  $this->_site = $site;
17802
+ $site_result = $this->get_api_site_scope( true )->get();
17803
  $site = new FS_Site( $site_result );
17804
  $this->_site = $site;
17805
 
18025
  *
18026
  * @return object|string If redirect is `false`, returns the next page the user should be redirected to, or the API error object if failed to install.
18027
  */
18028
+ function install_with_current_user(
18029
  $license_key = false,
18030
  $trial_plan_id = false,
18031
  $sites = array(),
18408
  $this->send_installs_update();
18409
  }
18410
 
 
 
 
18411
  $current_blog = get_current_blog_id();
18412
 
18413
  foreach ( $blog_2_install_map as $blog_id => $install ) {
18416
  $this->do_action( 'after_account_connection', $this->_user, $install );
18417
  }
18418
 
18419
+ // Switch install context back to the first install.
18420
+ $this->switch_to_blog(
18421
+ $current_blog,
18422
+ $first_install,
18423
+ ( $this->_site->id != $first_install->id )
18424
+ );
18425
 
18426
  $this->do_action( 'after_network_account_connection', $this->_user, $blog_2_install_map );
18427
  }
18525
  // return;
18526
  // }
18527
 
18528
+ if ( is_object( $this->_site ) && ! $this->is_registered() ) {
18529
+ return;
18530
+ }
18531
+
18532
  /**
18533
  * When running from a site admin with a network activated module and the connection
18534
  * was NOT delegated and the user still haven't skipped or opted-in, then hide the
19483
  *
19484
  * @return string
19485
  */
19486
+ static function get_ajax_action_static( $tag, $module_id = null ) {
19487
  $action = "fs_{$tag}";
19488
 
19489
  if ( ! empty( $module_id ) ) {
19726
  }
19727
 
19728
  /* Account Page
19729
+ ------------------------------------------------------------------------------------------------------------------*/
19730
  /**
19731
  * Update site information.
19732
  *
19737
  * @param null|int $network_level_or_blog_id Since 2.0.0
19738
  * @param \FS_Site $site Since 2.0.0
19739
  */
19740
+ private function _store_site( $store = true, $network_level_or_blog_id = null, FS_Site $site = null, $is_backup = false ) {
19741
  $this->_logger->entrance();
19742
 
19743
  if ( is_null( $site ) ) {
19752
 
19753
  $site_clone = clone $site;
19754
 
19755
+ $sites = self::get_all_sites( $this->_module_type, $network_level_or_blog_id, $is_backup );
19756
 
19757
+ if (
19758
+ ! $is_backup &&
19759
+ is_object( $this->_user ) && $this->_user->id != $site->user_id
19760
+ ) {
19761
  $this->sync_user_by_current_install( $site->user_id );
19762
 
19763
  $prev_stored_user_id = $this->_storage->get( 'prev_user_id', false, $network_level_or_blog_id );
19782
 
19783
  $sites[ $this->_slug ] = $site_clone;
19784
 
19785
+ $this->set_account_option(
19786
+ ( $is_backup ? 'prev_' : '' ) . 'sites',
19787
+ $sites,
19788
+ $store,
19789
+ $network_level_or_blog_id
19790
+ );
19791
+ }
19792
+
19793
+ /**
19794
+ * Stores the context site in the sites backup storage. This logic is used before deleting the site info so that it can be restored later on if necessary (e.g., if the automatic clone resolution attempt fails).
19795
+ *
19796
+ * @author Leo Fajardo (@leorw)
19797
+ * @since 2.5.0
19798
+ */
19799
+ private function back_up_site() {
19800
+ $this->_logger->entrance();
19801
+
19802
+ $site_clone = clone $this->_site;
19803
+
19804
+ $this->_store_site( true, null, $site_clone, true );
19805
  }
19806
 
19807
  /**
21042
  $this->switch_to_blog( $current_blog_id );
21043
  }
21044
 
21045
+ $result = $this->send_install_update( array(), true, true );
21046
  $is_valid = $this->is_api_result_entity( $result );
21047
  } else {
21048
+ $result = $this->send_installs_update( array(), true, true );
21049
  $is_valid = $this->is_api_result_object( $result, 'installs' );
21050
  }
21051
 
21055
  $this->switch_to_blog( $this->_storage->network_install_blog_id );
21056
  }
21057
 
21058
+ // Show API message only if not background sync or if paying customer.
21059
  if ( ! $background || $this->is_paying() ) {
21060
  // Try to ping API to see if not blocked.
21061
  if ( ! FS_Api::test() ) {
21065
  * @author Vova Feldman (@svovaf)
21066
  * @since 1.1.6 Only show message related to one of the Freemius powered plugins. Once it will be resolved it will fix the issue for all plugins anyways. There's no point to scare users with multiple error messages.
21067
  */
 
21068
 
21069
  if ( ! self::$_global_admin_notices->has_sticky( 'api_blocked' ) ) {
21070
+ // Add notice immediately if not a background sync.
21071
+ $add_notice = ( ! $background );
21072
+
21073
+ if ( ! $add_notice ) {
21074
+ $counter = (int) get_transient( '_fs_api_connection_retry_counter' );
21075
+
21076
+ // We only want to add the notice after 3 consecutive failures.
21077
+ $add_notice = ( 3 <= $counter );
21078
+
21079
+ if ( ! $add_notice ) {
21080
+ /**
21081
+ * Update counter transient only if notice shouldn't be added. If it is added the transient will be reset anyway, because the retries mechanism should only start counting if the admin isn't aware of the connectivity issue.
21082
+ *
21083
+ * Also, since the background sync happens once a day, setting the transient expiration for a week should be enough to count 3 failures, if there's an actual connectivity issue.
21084
+ */
21085
+ set_transient( '_fs_api_connection_retry_counter', $counter + 1, WP_FS__TIME_WEEK_IN_SEC );
21086
+ }
21087
+ }
21088
+
21089
+ // Add notice instantly for not-background sync and only after 3 failed attempts for background sync.
21090
+ if ( $add_notice ) {
21091
+ self::$_global_admin_notices->add(
21092
+ sprintf(
21093
+ $this->get_text_inline( 'Your server is blocking the access to Freemius\' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s', 'server-blocking-access' ),
21094
+ $this->get_plugin_name(),
21095
+ '<b>' . implode( ', ', $this->apply_filters( 'api_domains', array(
21096
+ 'api.freemius.com',
21097
+ 'wp.freemius.com'
21098
+ ) ) ) . '</b>'
21099
+ ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true ),
21100
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
21101
+ 'error',
21102
+ $background,
21103
+ 'api_blocked'
21104
+ );
21105
+
21106
+ // Notice was just shown, reset connectivity counter.
21107
+ delete_transient( '_fs_api_connection_retry_counter' );
21108
+ }
21109
  }
21110
+ } else if ( is_object( $result ) ) {
21111
  // Authentication params are broken.
21112
  $this->_admin_notices->add(
21113
  $this->get_text_inline( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.', 'wrong-authentication-param-message' ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true ),
21121
  return;
21122
  }
21123
 
21124
+ // API is working now. Delete the transient and start afresh.
21125
+ delete_transient('_fs_api_connection_retry_counter');
21126
+
21127
  if ( $is_site_level_sync ) {
21128
  $site = new FS_Site( $result );
21129
  } else {
21315
  }
21316
 
21317
  if ( ! $this->is_addon() &&
21318
+ $this->_site->is_beta() !== $site->is_beta()
21319
  ) {
21320
  // Beta flag updated.
21321
  $this->_site = $site;
22049
  ) {
22050
  $this->_logger->entrance();
22051
 
22052
+ if ( $this->is_unresolved_clone() ) {
22053
+ return false;
22054
+ }
22055
+
22056
  $switch_to_blog_id = null;
22057
 
22058
  /**
23312
  }
23313
 
23314
  /* Pricing & Upgrade
23315
+ ------------------------------------------------------------------------------------------------------------------*/
23316
  /**
23317
  * Render pricing page.
23318
  *
23471
  }
23472
 
23473
  /* CSS & JavaScript
23474
+ ------------------------------------------------------------------------------------------------------------------*/
23475
  /* function _enqueue_script($handle, $src) {
23476
+ $url = plugins_url( substr( WP_FS__DIR_JS, strlen( $this->_plugin_dir_path ) ) . '/assets/js/' . $src );
23477
 
23478
+ $this->_logger->entrance( 'script = ' . $url );
23479
 
23480
+ wp_enqueue_script( $handle, $url );
23481
+ }*/
23482
 
23483
  /* SDK
23484
+ ------------------------------------------------------------------------------------------------------------------*/
23485
  private $_user_api;
23486
 
23487
  /**
23493
  *
23494
  * @return FS_Api
23495
  */
23496
+ function get_api_user_scope( $flush = false ) {
23497
  if ( ! isset( $this->_user_api ) || $flush ) {
23498
  $this->_user_api = $this->get_api_user_scope_by_user( $this->_user );
23499
  }
23572
  $this->_site->public_key,
23573
  ! $this->is_live(),
23574
  $this->_site->secret_key,
23575
+ $this->get_sdk_version(),
23576
+ get_site_url()
23577
  );
23578
  }
23579
 
23580
  return $this->_site_api;
23581
  }
23582
 
23583
+ /**
23584
+ * @author Leo Fajardo (@leorw)
23585
+ * @since 2.5.0
23586
+ *
23587
+ * @param string $path
23588
+ * @param string $method
23589
+ * @param array $params
23590
+ * @param bool $flush_instance
23591
+ *
23592
+ * @return array|mixed|string|void
23593
+ * @throws Freemius_Exception
23594
+ */
23595
+ private function api_site_call( $path, $method = 'GET', $params = array(), $flush_instance = false ) {
23596
+ $result = $this->get_api_site_scope( $flush_instance )->call( $path, $method, $params );
23597
+
23598
+ /**
23599
+ * Checks if the local install's URL is different from the remote install's URL, update the local install if necessary, and then run the clone handler if the install's URL is different from the URL of the site.
23600
+ *
23601
+ * @author Leo Fajardo (@leorw)
23602
+ * @since 2.5.0
23603
+ */
23604
+ if (
23605
+ $this->is_registered() &&
23606
+ FS_Api::is_api_result_entity( $result ) &&
23607
+ isset( $result->url )
23608
+ ) {
23609
+ $stored_local_url = trailingslashit( $this->_site->url );
23610
+ $stored_remote_url = trailingslashit( $result->url );
23611
+
23612
+ if ( $stored_local_url !== $stored_remote_url ) {
23613
+ $this->_site->url = $result->url;
23614
+ $this->_store_site();
23615
+ }
23616
+
23617
+ if ( fs_strip_url_protocol( $stored_remote_url ) !== fs_strip_url_protocol( trailingslashit( get_site_url() ) ) ) {
23618
+ FS_Clone_Manager::instance()->maybe_run_clone_resolution();
23619
+ }
23620
+ }
23621
+
23622
+ return $result;
23623
+ }
23624
+
23625
  private $_plugin_api;
23626
 
23627
  /**
24057
  }
24058
 
24059
  /* Action Links
24060
+ ------------------------------------------------------------------------------------------------------------------*/
24061
  private $_action_links_hooked = false;
24062
  private $_action_links = array();
24063
 
24244
 
24245
  if (
24246
  $this->is_addon() &&
24247
+ ! $this->is_only_premium()
 
24248
  ) {
24249
+ $parent = $this->get_parent_instance();
24250
+
24251
+ if ( is_object( $parent ) && $parent->is_anonymous() ) {
24252
+ return;
24253
+ }
24254
  }
24255
 
24256
  if ( fs_is_network_admin() ) {
lib/freemius/includes/class-fs-admin-notices.php CHANGED
@@ -125,13 +125,10 @@
125
  $is_sticky = false,
126
  $id = '',
127
  $store_if_sticky = true,
128
- $network_level_or_blog_id = null
 
129
  ) {
130
- if ( $this->should_use_network_notices( $id, $network_level_or_blog_id ) ) {
131
- $notices = $this->_network_notices;
132
- } else {
133
- $notices = $this->get_site_notices( $network_level_or_blog_id );
134
- }
135
 
136
  $notices->add(
137
  $message,
@@ -139,7 +136,11 @@
139
  $type,
140
  $is_sticky,
141
  $id,
142
- $store_if_sticky
 
 
 
 
143
  );
144
  }
145
 
@@ -149,8 +150,9 @@
149
  *
150
  * @param string|string[] $ids
151
  * @param int|null $network_level_or_blog_id
 
152
  */
153
- function remove_sticky( $ids, $network_level_or_blog_id = null ) {
154
  if ( ! is_array( $ids ) ) {
155
  $ids = array( $ids );
156
  }
@@ -161,7 +163,7 @@
161
  $notices = $this->get_site_notices( $network_level_or_blog_id );
162
  }
163
 
164
- return $notices->remove_sticky( $ids );
165
  }
166
 
167
  /**
@@ -176,11 +178,7 @@
176
  * @return bool
177
  */
178
  function has_sticky( $id, $network_level_or_blog_id = null ) {
179
- if ( $this->should_use_network_notices( $id, $network_level_or_blog_id ) ) {
180
- $notices = $this->_network_notices;
181
- } else {
182
- $notices = $this->get_site_notices( $network_level_or_blog_id );
183
- }
184
 
185
  return $notices->has_sticky( $id );
186
  }
@@ -200,6 +198,7 @@
200
  * @param string|null $plugin_title
201
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network and
202
  * blog admin pages.
 
203
  */
204
  function add_sticky(
205
  $message,
@@ -209,15 +208,30 @@
209
  $network_level_or_blog_id = null,
210
  $wp_user_id = null,
211
  $plugin_title = null,
212
- $is_network_and_blog_admins = false
 
 
213
  ) {
214
- if ( $this->should_use_network_notices( $id, $network_level_or_blog_id ) ) {
215
- $notices = $this->_network_notices;
216
- } else {
217
- $notices = $this->get_site_notices( $network_level_or_blog_id );
218
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
- $notices->add_sticky( $message, $id, $title, $type, $wp_user_id, $plugin_title, $is_network_and_blog_admins );
221
  }
222
 
223
  /**
@@ -317,5 +331,22 @@
317
  return fs_is_network_admin();
318
  }
319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  #endregion
321
  }
125
  $is_sticky = false,
126
  $id = '',
127
  $store_if_sticky = true,
128
+ $network_level_or_blog_id = null,
129
+ $is_dimissible = null
130
  ) {
131
+ $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );
 
 
 
 
132
 
133
  $notices->add(
134
  $message,
136
  $type,
137
  $is_sticky,
138
  $id,
139
+ $store_if_sticky,
140
+ null,
141
+ null,
142
+ false,
143
+ $is_dimissible
144
  );
145
  }
146
 
150
  *
151
  * @param string|string[] $ids
152
  * @param int|null $network_level_or_blog_id
153
+ * @param bool $store
154
  */
155
+ function remove_sticky( $ids, $network_level_or_blog_id = null, $store = true ) {
156
  if ( ! is_array( $ids ) ) {
157
  $ids = array( $ids );
158
  }
163
  $notices = $this->get_site_notices( $network_level_or_blog_id );
164
  }
165
 
166
+ return $notices->remove_sticky( $ids, $store );
167
  }
168
 
169
  /**
178
  * @return bool
179
  */
180
  function has_sticky( $id, $network_level_or_blog_id = null ) {
181
+ $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );
 
 
 
 
182
 
183
  return $notices->has_sticky( $id );
184
  }
198
  * @param string|null $plugin_title
199
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network and
200
  * blog admin pages.
201
+ * @param bool $is_dismissible
202
  */
203
  function add_sticky(
204
  $message,
208
  $network_level_or_blog_id = null,
209
  $wp_user_id = null,
210
  $plugin_title = null,
211
+ $is_network_and_blog_admins = false,
212
+ $is_dismissible = true,
213
+ $data = array()
214
  ) {
215
+ $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );
216
+
217
+ $notices->add_sticky( $message, $id, $title, $type, $wp_user_id, $plugin_title, $is_network_and_blog_admins, $is_dismissible, $data );
218
+ }
219
+
220
+ /**
221
+ * Retrieves the data of a sticky notice.
222
+ *
223
+ * @author Leo Fajardo (@leorw)
224
+ * @since 2.4.3
225
+ *
226
+ * @param string $id
227
+ * @param int|null $network_level_or_blog_id
228
+ *
229
+ * @return array|null
230
+ */
231
+ function get_sticky( $id, $network_level_or_blog_id ) {
232
+ $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );
233
 
234
+ return $notices->get_sticky( $id );
235
  }
236
 
237
  /**
331
  return fs_is_network_admin();
332
  }
333
 
334
+ /**
335
+ * Retrieves an instance of FS_Admin_Notice_Manager.
336
+ *
337
+ * @author Leo Fajardo (@leorw)
338
+ * @since 2.5.0
339
+ *
340
+ * @param string $id
341
+ * @param int|null $network_level_or_blog_id
342
+ *
343
+ * @return FS_Admin_Notice_Manager
344
+ */
345
+ private function get_site_or_network_notices( $id, $network_level_or_blog_id ) {
346
+ return $this->should_use_network_notices( $id, $network_level_or_blog_id ) ?
347
+ $this->_network_notices :
348
+ $this->get_site_notices( $network_level_or_blog_id );
349
+ }
350
+
351
  #endregion
352
  }
lib/freemius/includes/class-fs-api.php CHANGED
@@ -64,6 +64,14 @@
64
  */
65
  private $_sdk_version;
66
 
 
 
 
 
 
 
 
 
67
  /**
68
  * @param string $slug
69
  * @param string $scope 'app', 'developer', 'user' or 'install'.
@@ -72,6 +80,7 @@
72
  * @param bool $is_sandbox
73
  * @param bool|string $secret_key Element's secret key.
74
  * @param null|string $sdk_version
 
75
  *
76
  * @return FS_Api
77
  */
@@ -82,14 +91,15 @@
82
  $public_key,
83
  $is_sandbox,
84
  $secret_key = false,
85
- $sdk_version = null
 
86
  ) {
87
  $identifier = md5( $slug . $scope . $id . $public_key . ( is_string( $secret_key ) ? $secret_key : '' ) . json_encode( $is_sandbox ) );
88
 
89
  if ( ! isset( self::$_instances[ $identifier ] ) ) {
90
  self::_init();
91
 
92
- self::$_instances[ $identifier ] = new FS_Api( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox, $sdk_version );
93
  }
94
 
95
  return self::$_instances[ $identifier ];
@@ -123,6 +133,7 @@
123
  * @param bool|string $secret_key Element's secret key.
124
  * @param bool $is_sandbox
125
  * @param null|string $sdk_version
 
126
  */
127
  private function __construct(
128
  $slug,
@@ -131,12 +142,14 @@
131
  $public_key,
132
  $secret_key,
133
  $is_sandbox,
134
- $sdk_version
 
135
  ) {
136
  $this->_api = new Freemius_Api_WordPress( $scope, $id, $public_key, $secret_key, $is_sandbox );
137
 
138
  $this->_slug = $slug;
139
  $this->_sdk_version = $sdk_version;
 
140
  $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug . '_api', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
141
  }
142
 
@@ -198,6 +211,17 @@
198
  }
199
  }
200
 
 
 
 
 
 
 
 
 
 
 
 
201
  $result = $this->_api->Api( $path, $method, $params );
202
 
203
  if ( null !== $result &&
64
  */
65
  private $_sdk_version;
66
 
67
+ /**
68
+ * @author Leo Fajardo (@leorw)
69
+ * @since 2.5.0
70
+ *
71
+ * @var string
72
+ */
73
+ private $_url;
74
+
75
  /**
76
  * @param string $slug
77
  * @param string $scope 'app', 'developer', 'user' or 'install'.
80
  * @param bool $is_sandbox
81
  * @param bool|string $secret_key Element's secret key.
82
  * @param null|string $sdk_version
83
+ * @param null|string $url
84
  *
85
  * @return FS_Api
86
  */
91
  $public_key,
92
  $is_sandbox,
93
  $secret_key = false,
94
+ $sdk_version = null,
95
+ $url = null
96
  ) {
97
  $identifier = md5( $slug . $scope . $id . $public_key . ( is_string( $secret_key ) ? $secret_key : '' ) . json_encode( $is_sandbox ) );
98
 
99
  if ( ! isset( self::$_instances[ $identifier ] ) ) {
100
  self::_init();
101
 
102
+ self::$_instances[ $identifier ] = new FS_Api( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox, $sdk_version, $url );
103
  }
104
 
105
  return self::$_instances[ $identifier ];
133
  * @param bool|string $secret_key Element's secret key.
134
  * @param bool $is_sandbox
135
  * @param null|string $sdk_version
136
+ * @param null|string $url
137
  */
138
  private function __construct(
139
  $slug,
142
  $public_key,
143
  $secret_key,
144
  $is_sandbox,
145
+ $sdk_version,
146
+ $url
147
  ) {
148
  $this->_api = new Freemius_Api_WordPress( $scope, $id, $public_key, $secret_key, $is_sandbox );
149
 
150
  $this->_slug = $slug;
151
  $this->_sdk_version = $sdk_version;
152
+ $this->_url = $url;
153
  $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug . '_api', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
154
  }
155
 
211
  }
212
  }
213
 
214
+ /**
215
+ * @since 2.5.0 Include the site's URL, if available, in all API requests that are going through the API manager.
216
+ */
217
+ if ( ! empty( $this->_url ) ) {
218
+ if ( false === strpos( $path, 'url=' ) &&
219
+ ! isset( $params['url'] )
220
+ ) {
221
+ $path = add_query_arg( 'url', $this->_url, $path );
222
+ }
223
+ }
224
+
225
  $result = $this->_api->Api( $path, $method, $params );
226
 
227
  if ( null !== $result &&
lib/freemius/includes/i18n.php CHANGED
@@ -278,7 +278,7 @@
278
  'permissions-profile' => _fs_text( 'Your Profile Overview' ),
279
  'permissions-profile_desc' => _fs_text( 'Name and email address' ),
280
  'permissions-site' => _fs_text( 'Your Site Overview' ),
281
- 'permissions-site_desc' => _fs_text( 'Site URL, WP version, PHP info, plugins & themes' ),
282
  'permissions-events' => _fs_text( 'Current %s Events' ),
283
  'permissions-events_desc' => _fs_text( 'Activation, deactivation and uninstall' ),
284
  'permissions-plugins_themes' => _fs_text( 'Plugins & Themes' ),
278
  'permissions-profile' => _fs_text( 'Your Profile Overview' ),
279
  'permissions-profile_desc' => _fs_text( 'Name and email address' ),
280
  'permissions-site' => _fs_text( 'Your Site Overview' ),
281
+ 'permissions-site_desc' => _fs_text( 'Site URL, WP version, PHP version, plugins & themes' ),
282
  'permissions-events' => _fs_text( 'Current %s Events' ),
283
  'permissions-events_desc' => _fs_text( 'Activation, deactivation and uninstall' ),
284
  'permissions-plugins_themes' => _fs_text( 'Plugins & Themes' ),
lib/freemius/includes/managers/class-fs-admin-notice-manager.php CHANGED
@@ -160,7 +160,10 @@
160
  false,
161
  isset( $msg['wp_user_id'] ) ? $msg['wp_user_id'] : null,
162
  ! empty( $msg['plugin'] ) ? $msg['plugin'] : null,
163
- $is_network_and_blog_admins
 
 
 
164
  );
165
  }
166
  }
@@ -355,6 +358,8 @@
355
  * @param string|null $plugin_title
356
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
357
  * and blog admin pages.
 
 
358
  *
359
  * @uses add_action()
360
  */
@@ -367,7 +372,9 @@
367
  $store_if_sticky = true,
368
  $wp_user_id = null,
369
  $plugin_title = null,
370
- $is_network_and_blog_admins = false
 
 
371
  ) {
372
  $notices_type = $this->get_notices_type();
373
 
@@ -387,14 +394,16 @@
387
  }
388
 
389
  $message_object = array(
390
- 'message' => $message,
391
- 'title' => $title,
392
- 'type' => $type,
393
- 'sticky' => $is_sticky,
394
- 'id' => $id,
395
- 'manager_id' => $this->_id,
396
- 'plugin' => ( ! is_null( $plugin_title ) ? $plugin_title : $this->_title ),
397
- 'wp_user_id' => $wp_user_id,
 
 
398
  );
399
 
400
  if ( $is_sticky && $store_if_sticky ) {
@@ -409,15 +418,16 @@
409
  * @since 1.0.7
410
  *
411
  * @param string|string[] $ids
 
412
  */
413
- function remove_sticky( $ids ) {
414
  if ( ! is_array( $ids ) ) {
415
  $ids = array( $ids );
416
  }
417
 
418
  foreach ( $ids as $id ) {
419
  // Remove from sticky storage.
420
- $this->_sticky_storage->remove( $id );
421
 
422
  if ( isset( $this->_notices[ $id ] ) ) {
423
  unset( $this->_notices[ $id ] );
@@ -453,14 +463,32 @@
453
  * @param string|null $plugin_title
454
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
455
  * and blog admin pages.
 
 
456
  */
457
- function add_sticky( $message, $id, $title = '', $type = 'success', $wp_user_id = null, $plugin_title = null, $is_network_and_blog_admins = false ) {
458
  if ( ! empty( $this->_module_unique_affix ) ) {
459
  $message = fs_apply_filter( $this->_module_unique_affix, "sticky_message_{$id}", $message );
460
  $title = fs_apply_filter( $this->_module_unique_affix, "sticky_title_{$id}", $title );
461
  }
462
 
463
- $this->add( $message, $title, $type, true, $id, true, $wp_user_id, $plugin_title, $is_network_and_blog_admins );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  }
465
 
466
  /**
160
  false,
161
  isset( $msg['wp_user_id'] ) ? $msg['wp_user_id'] : null,
162
  ! empty( $msg['plugin'] ) ? $msg['plugin'] : null,
163
+ $is_network_and_blog_admins,
164
+ isset( $msg['dismissible'] ) ?
165
+ $msg['dismissible'] :
166
+ null
167
  );
168
  }
169
  }
358
  * @param string|null $plugin_title
359
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
360
  * and blog admin pages.
361
+ * @param bool|null $is_dismissible
362
+ * @param array $data
363
  *
364
  * @uses add_action()
365
  */
372
  $store_if_sticky = true,
373
  $wp_user_id = null,
374
  $plugin_title = null,
375
+ $is_network_and_blog_admins = false,
376
+ $is_dismissible = null,
377
+ $data = array()
378
  ) {
379
  $notices_type = $this->get_notices_type();
380
 
394
  }
395
 
396
  $message_object = array(
397
+ 'message' => $message,
398
+ 'title' => $title,
399
+ 'type' => $type,
400
+ 'sticky' => $is_sticky,
401
+ 'id' => $id,
402
+ 'manager_id' => $this->_id,
403
+ 'plugin' => ( ! is_null( $plugin_title ) ? $plugin_title : $this->_title ),
404
+ 'wp_user_id' => $wp_user_id,
405
+ 'dismissible' => $is_dismissible,
406
+ 'data' => $data
407
  );
408
 
409
  if ( $is_sticky && $store_if_sticky ) {
418
  * @since 1.0.7
419
  *
420
  * @param string|string[] $ids
421
+ * @param bool $store
422
  */
423
+ function remove_sticky( $ids, $store = true ) {
424
  if ( ! is_array( $ids ) ) {
425
  $ids = array( $ids );
426
  }
427
 
428
  foreach ( $ids as $id ) {
429
  // Remove from sticky storage.
430
+ $this->_sticky_storage->remove( $id, $store );
431
 
432
  if ( isset( $this->_notices[ $id ] ) ) {
433
  unset( $this->_notices[ $id ] );
463
  * @param string|null $plugin_title
464
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
465
  * and blog admin pages.
466
+ * @param bool $is_dimissible
467
+ * @param array $data
468
  */
469
+ function add_sticky( $message, $id, $title = '', $type = 'success', $wp_user_id = null, $plugin_title = null, $is_network_and_blog_admins = false, $is_dimissible = true, $data = array() ) {
470
  if ( ! empty( $this->_module_unique_affix ) ) {
471
  $message = fs_apply_filter( $this->_module_unique_affix, "sticky_message_{$id}", $message );
472
  $title = fs_apply_filter( $this->_module_unique_affix, "sticky_title_{$id}", $title );
473
  }
474
 
475
+ $this->add( $message, $title, $type, true, $id, true, $wp_user_id, $plugin_title, $is_network_and_blog_admins, $is_dimissible, $data );
476
+ }
477
+
478
+ /**
479
+ * Retrieves the data of an sticky notice.
480
+ *
481
+ * @author Leo Fajardo (@leorw)
482
+ * @since 2.4.3
483
+ *
484
+ * @param string $id Message ID.
485
+ *
486
+ * @return array|null
487
+ */
488
+ function get_sticky( $id ) {
489
+ return isset( $this->_sticky_storage->{$id} ) ?
490
+ $this->_sticky_storage->{$id} :
491
+ null;
492
  }
493
 
494
  /**
lib/freemius/includes/managers/class-fs-clone-manager.php ADDED
@@ -0,0 +1,1480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @author Leo Fajardo (@leorw)
7
+ * @since 2.5.0
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit;
12
+ }
13
+
14
+ /**
15
+ * Manages the detection of clones and provides the logged-in WordPress user with options for manually resolving them.
16
+ *
17
+ * @since 2.5.0
18
+ *
19
+ * @property int $clone_identification_timestamp
20
+ * @property int $temporary_duplicate_mode_selection_timestamp
21
+ * @property int $temporary_duplicate_notice_shown_timestamp
22
+ * @property string $request_handler_id
23
+ * @property int $request_handler_timestamp
24
+ * @property int $request_handler_retries_count
25
+ */
26
+ class FS_Clone_Manager {
27
+ /**
28
+ * @var FS_Option_Manager
29
+ */
30
+ private $_storage;
31
+ /**
32
+ * @var FS_Option_Manager
33
+ */
34
+ private $_network_storage;
35
+ /**
36
+ * @var array {
37
+ * @type int $clone_identification_timestamp
38
+ * @type int $temporary_duplicate_mode_selection_timestamp
39
+ * @type int $temporary_duplicate_notice_shown_timestamp
40
+ * @type string $request_handler_id
41
+ * @type int $request_handler_timestamp
42
+ * @type int $request_handler_retries_count
43
+ * }
44
+ */
45
+ private $_data;
46
+ /**
47
+ * @var array {
48
+ * @type array $new_blog_install_map
49
+ * }
50
+ */
51
+ private $_network_data;
52
+ /**
53
+ * @var FS_Admin_Notices
54
+ */
55
+ private $_notices;
56
+ /**
57
+ * @var FS_Logger
58
+ */
59
+ protected $_logger;
60
+
61
+ /**
62
+ * @var int 3 minutes
63
+ */
64
+ const CLONE_RESOLUTION_MAX_EXECUTION_TIME = 180;
65
+ /**
66
+ * @var int
67
+ */
68
+ const CLONE_RESOLUTION_MAX_RETRIES = 3;
69
+ /**
70
+ * @var int
71
+ */
72
+ const TEMPORARY_DUPLICATE_PERIOD = WP_FS__TIME_WEEK_IN_SEC * 2;
73
+ /**
74
+ * @var string
75
+ */
76
+ const OPTION_NAME = 'clone_resolution';
77
+ /**
78
+ * @var string
79
+ */
80
+ const OPTION_MANAGER_NAME = 'clone_management';
81
+ /**
82
+ * @var string
83
+ */
84
+ const OPTION_TEMPORARY_DUPLICATE = 'temporary_duplicate';
85
+ /**
86
+ * @var string
87
+ */
88
+ const OPTION_NEW_HOME = 'new_home';
89
+
90
+ #--------------------------------------------------------------------------------
91
+ #region Singleton
92
+ #--------------------------------------------------------------------------------
93
+
94
+ /**
95
+ * @var FS_Clone_Manager
96
+ */
97
+ private static $_instance;
98
+
99
+ /**
100
+ * @return FS_Clone_Manager
101
+ */
102
+ static function instance() {
103
+ if ( ! isset( self::$_instance ) ) {
104
+ self::$_instance = new self();
105
+ }
106
+
107
+ return self::$_instance;
108
+ }
109
+
110
+ #endregion
111
+
112
+ private function __construct() {
113
+ $this->_storage = FS_Option_Manager::get_manager( WP_FS___OPTION_PREFIX . self::OPTION_MANAGER_NAME, true );
114
+ $this->_network_storage = FS_Option_Manager::get_manager( WP_FS___OPTION_PREFIX . self::OPTION_MANAGER_NAME, true, true );
115
+ $this->_data = $this->_storage->get_option( self::OPTION_NAME, array() );
116
+ $this->_network_data = $this->_network_storage->get_option( self::OPTION_NAME, array() );
117
+
118
+ $this->_notices = FS_Admin_Notices::instance( 'global_clone_resolution_notices', '', '', true );
119
+ $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . '_clone_manager', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
120
+
121
+ $defaults = array(
122
+ 'clone_identification_timestamp' => null,
123
+ 'temporary_duplicate_mode_selection_timestamp' => null,
124
+ 'temporary_duplicate_notice_shown_timestamp' => null,
125
+ 'request_handler_id' => null,
126
+ 'request_handler_timestamp' => null,
127
+ 'request_handler_retries_count' => null,
128
+ );
129
+
130
+ if ( ! is_array( $this->_data ) ) {
131
+ $this->_data = $defaults;
132
+ } else {
133
+ foreach ( $defaults as $name => $value ) {
134
+ $this->_data[ $name ] = isset( $this->_data[ $name ] ) ?
135
+ $this->_data[ $name ] :
136
+ $value;
137
+ }
138
+ }
139
+
140
+ if (
141
+ ! is_array( $this->_network_data ) ||
142
+ ! isset( $this->_network_data['new_blog_install_map'] )
143
+ ) {
144
+ $this->_network_data = array( 'new_blog_install_map' => null );
145
+ }
146
+ }
147
+
148
+ /**
149
+ * @author Leo Fajardo (@leorw)
150
+ * @since 2.5.0
151
+ */
152
+ function _init() {
153
+ if ( is_admin() ) {
154
+ if ( Freemius::is_admin_post() ) {
155
+ add_action( 'admin_post_fs_clone_resolution', array( $this, '_handle_clone_resolution' ) );
156
+ }
157
+
158
+ if (
159
+ empty( $this->get_clone_identification_timestamp() ) &&
160
+ (
161
+ ! fs_is_network_admin() ||
162
+ ! ( $this->is_clone_resolution_options_notice_shown() || $this->is_temporary_duplicate_notice_shown() )
163
+ )
164
+ ) {
165
+ $this->hide_clone_admin_notices();
166
+ } else {
167
+ if ( Freemius::is_ajax() ) {
168
+ Freemius::add_ajax_action_static( 'handle_clone_resolution', array( $this, '_clone_resolution_action_ajax_handler' ) );
169
+ } else if ( ! Freemius::is_cron() && ! Freemius::is_admin_post() ) {
170
+ $this->maybe_show_clone_admin_notice();
171
+
172
+ add_action( 'admin_footer', array( $this, '_add_clone_resolution_javascript' ) );
173
+ }
174
+ }
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Retrieves the timestamp that was stored when a clone was identified.
180
+ *
181
+ * @return int|null
182
+ */
183
+ function get_clone_identification_timestamp() {
184
+ return $this->clone_identification_timestamp;
185
+ }
186
+
187
+ /**
188
+ * Stores the time when a clone was identified.
189
+ */
190
+ function store_clone_identification_timestamp() {
191
+ $this->clone_identification_timestamp = time();
192
+ }
193
+
194
+ /**
195
+ * Retrieves the timestamp for the temporary duplicate mode's expiration.
196
+ *
197
+ * @return int
198
+ */
199
+ function get_temporary_duplicate_expiration_timestamp() {
200
+ $temporary_duplicate_mode_start_timestamp = $this->was_temporary_duplicate_mode_selected() ?
201
+ $this->temporary_duplicate_mode_selection_timestamp :
202
+ $this->get_clone_identification_timestamp();
203
+
204
+ return ( $temporary_duplicate_mode_start_timestamp + self::TEMPORARY_DUPLICATE_PERIOD );
205
+ }
206
+
207
+ /**
208
+ * Determines if the SDK should handle clones. The SDK handles clones only up to 3 times with 3 min interval.
209
+ *
210
+ * @return bool
211
+ */
212
+ private function should_handle_clones() {
213
+ if ( ! isset( $this->request_handler_timestamp ) ) {
214
+ return true;
215
+ }
216
+
217
+ if ( $this->request_handler_retries_count >= self::CLONE_RESOLUTION_MAX_RETRIES ) {
218
+ return false;
219
+ }
220
+
221
+ // Give the logic that handles clones enough time to finish (it is given 3 minutes for now).
222
+ return ( time() > ( $this->request_handler_timestamp + self::CLONE_RESOLUTION_MAX_EXECUTION_TIME ) );
223
+ }
224
+
225
+ /**
226
+ * Executes the clones handler logic if it should be executed, i.e., based on the return value of the should_handle_clones() method.
227
+ *
228
+ * @author Leo Fajardo (@leorw)
229
+ * @since 2.5.0
230
+ */
231
+ function maybe_run_clone_resolution() {
232
+ if ( ! $this->should_handle_clones() ) {
233
+ return;
234
+ }
235
+
236
+ $this->request_handler_retries_count = isset( $this->request_handler_retries_count ) ?
237
+ ( $this->request_handler_retries_count + 1 ) :
238
+ 1;
239
+
240
+ $this->request_handler_timestamp = time();
241
+
242
+ $handler_id = ( rand() . microtime() );
243
+ $this->request_handler_id = $handler_id;
244
+
245
+ // Add cookies to trigger request with the same user access permissions.
246
+ $cookies = array();
247
+ foreach ( $_COOKIE as $name => $value ) {
248
+ $cookies[] = new WP_Http_Cookie( array(
249
+ 'name' => $name,
250
+ 'value' => $value,
251
+ ) );
252
+ }
253
+
254
+ wp_remote_post(
255
+ admin_url( 'admin-post.php' ),
256
+ array(
257
+ 'method' => 'POST',
258
+ 'body' => array(
259
+ 'action' => 'fs_clone_resolution',
260
+ 'handler_id' => $handler_id,
261
+ ),
262
+ 'timeout' => 0.01,
263
+ 'blocking' => false,
264
+ 'sslverify' => false,
265
+ 'cookies' => $cookies,
266
+ )
267
+ );
268
+ }
269
+
270
+ /**
271
+ * Executes the clones handler logic.
272
+ *
273
+ * @author Leo Fajardo (@leorw)
274
+ * @since 2.5.0
275
+ */
276
+ function _handle_clone_resolution() {
277
+ $handler_id = fs_request_get( 'handler_id' );
278
+
279
+ if ( empty( $handler_id ) ) {
280
+ return;
281
+ }
282
+
283
+ if (
284
+ ! isset( $this->request_handler_id ) ||
285
+ $this->request_handler_id !== $handler_id
286
+ ) {
287
+ return;
288
+ }
289
+
290
+ if ( ! $this->try_automatic_resolution() ) {
291
+ $this->store_clone_identification_timestamp();
292
+ $this->clear_temporary_duplicate_notice_shown_timestamp();
293
+ }
294
+ }
295
+
296
+ #--------------------------------------------------------------------------------
297
+ #region Automatic Clone Resolution
298
+ #--------------------------------------------------------------------------------
299
+
300
+ /**
301
+ * @var array All installs cache.
302
+ */
303
+ private $all_installs;
304
+
305
+ /**
306
+ * Checks if a given instance's install is a clone of another subsite in the network.
307
+ *
308
+ * @author Vova Feldman (@svovaf)
309
+ *
310
+ * @return FS_Site
311
+ */
312
+ private function find_network_subsite_clone_install( Freemius $instance ) {
313
+ if ( ! is_multisite() ) {
314
+ // Not a multi-site network.
315
+ return null;
316
+ }
317
+
318
+ if ( ! isset( $this->all_installs ) ) {
319
+ $this->all_installs = Freemius::get_all_modules_sites();
320
+ }
321
+
322
+ // Check if there's another blog that has the same site.
323
+ $module_type = $instance->get_module_type();
324
+ $sites_by_module_type = ! empty( $this->all_installs[ $module_type ] ) ?
325
+ $this->all_installs[ $module_type ] :
326
+ array();
327
+
328
+ $slug = $instance->get_slug();
329
+ $sites_by_slug = ! empty( $sites_by_module_type[ $slug ] ) ?
330
+ $sites_by_module_type[ $slug ] :
331
+ array();
332
+
333
+ $current_blog_id = get_current_blog_id();
334
+
335
+ $current_install = $instance->get_site();
336
+
337
+ foreach ( $sites_by_slug as $site ) {
338
+ if (
339
+ $current_install->id == $site->id &&
340
+ $current_blog_id != $site->blog_id
341
+ ) {
342
+ // Clone is identical to an install on another subsite in the network.
343
+ return $site;
344
+ }
345
+ }
346
+
347
+ return null;
348
+ }
349
+
350
+ /**
351
+ * Tries to find a different install of the context product that is associated with the current URL and loads it.
352
+ *
353
+ * @author Leo Fajardo (@leorw)
354
+ * @since 2.5.0
355
+ *
356
+ * @param Freemius $instance
357
+ * @param string $url
358
+ *
359
+ * @return object
360
+ */
361
+ private function find_other_install_by_url( Freemius $instance, $url ) {
362
+ $result = $instance->get_api_user_scope()->get( "/plugins/{$instance->get_id()}/installs.json?search=" . urlencode( $url ) . "&all=true", true );
363
+
364
+ $current_install = $instance->get_site();
365
+
366
+ if ( $instance->is_api_result_object( $result, 'installs' ) ) {
367
+ foreach ( $result->installs as $install ) {
368
+ if ( $install->id == $current_install->id ) {
369
+ continue;
370
+ }
371
+
372
+ if (
373
+ $instance->is_only_premium() &&
374
+ ! FS_Plugin_License::is_valid_id( $install->license_id )
375
+ ) {
376
+ continue;
377
+ }
378
+
379
+ // When searching for installs by a URL, the API will first strip any paths and search for any matching installs by the subdomain. Therefore, we need to test if there's a match between the current URL and the install's URL before continuing.
380
+ if ( $url !== fs_strip_url_protocol( untrailingslashit( $install->url ) ) ) {
381
+ continue;
382
+ }
383
+
384
+ // Found a different install that is associated with the current URL, load it and replace the current install with it if no updated install is found.
385
+ return $install;
386
+ }
387
+ }
388
+
389
+ return null;
390
+ }
391
+
392
+ /**
393
+ * Delete the current install associated with a given instance and opt-in/activate-license to create a fresh install.
394
+ *
395
+ * @author Vova Feldman (@svovaf)
396
+ * @since 2.5.0
397
+ *
398
+ * @param Freemius $instance
399
+ * @param string|false $license_key
400
+ *
401
+ * @return bool TRUE if successfully connected. FALSE if failed and had to restore install from backup.
402
+ */
403
+ private function delete_install_and_connect( Freemius $instance, $license_key = false ) {
404
+ $user = Freemius::_get_user_by_id( $instance->get_site()->user_id );
405
+
406
+ $instance->delete_current_install( true );
407
+
408
+ if ( ! is_object( $user ) ) {
409
+ // Get logged-in WordPress user.
410
+ $current_user = Freemius::_get_current_wp_user();
411
+
412
+ // Find the relevant FS user by email address.
413
+ $user = Freemius::_get_user_by_email( $current_user->user_email );
414
+ }
415
+
416
+ if ( is_object( $user ) ) {
417
+ // When a clone is found, we prefer to use the same user of the original install for the opt-in.
418
+ $instance->install_with_user( $user, $license_key, false, false );
419
+ } else {
420
+ // If no user is found, activate with the license.
421
+ $instance->opt_in(
422
+ false,
423
+ false,
424
+ false,
425
+ $license_key
426
+ );
427
+ }
428
+
429
+ if ( is_object( $instance->get_site() ) ) {
430
+ // Install successfully created.
431
+ return true;
432
+ }
433
+
434
+ // Restore from backup.
435
+ $instance->restore_backup_site();
436
+
437
+ return false;
438
+ }
439
+
440
+ /**
441
+ * Try to resolve the clone situation automatically.
442
+ *
443
+ * @param Freemius $instance
444
+ * @param string $current_url
445
+ * @param bool $is_localhost
446
+ * @param bool|null $is_clone_of_network_subsite
447
+ *
448
+ * @return bool If managed to automatically resolve the clone.
449
+ */
450
+ private function try_resolve_clone_automatically( Freemius $instance, $current_url, $is_localhost, $is_clone_of_network_subsite = null ) {
451
+ // Try to find a different install of the context product that is associated with the current URL.
452
+ $associated_install = $this->find_other_install_by_url( $instance, $current_url );
453
+
454
+ if ( is_object( $associated_install ) ) {
455
+ // Replace the current install with a different install that is associated with the current URL.
456
+ $instance->store_site( new FS_Site( clone $associated_install ) );
457
+ $instance->sync_install( array( 'is_new_site' => true ), true );
458
+
459
+ return true;
460
+ }
461
+
462
+ if ( ! $instance->is_premium() ) {
463
+ // For free products, opt-in with the context user to create new install.
464
+ return $this->delete_install_and_connect( $instance );
465
+ }
466
+
467
+ $license = $instance->_get_license();
468
+ $can_activate_license = ( is_object( $license ) && ! $license->is_utilized( $is_localhost ) );
469
+
470
+ if ( ! $can_activate_license ) {
471
+ // License can't be activated, therefore, can't be automatically resolved.
472
+ return false;
473
+ }
474
+
475
+ $is_clone_of_network_subsite = ( ! is_null( $is_clone_of_network_subsite ) ) ?
476
+ $is_clone_of_network_subsite :
477
+ is_object( $this->find_network_subsite_clone_install( $instance ) );
478
+
479
+ if (
480
+ $is_clone_of_network_subsite ||
481
+ WP_FS__IS_LOCALHOST_FOR_SERVER ||
482
+ $is_localhost
483
+ ) {
484
+ // If the site is a clone of another subsite in the network, or a localhost one, try to auto activate the license.
485
+ return $this->delete_install_and_connect( $instance, $license->secret_key );
486
+ }
487
+
488
+ return false;
489
+ }
490
+
491
+ /**
492
+ * Tries to recover the install of a newly created subsite or resolve it if it's a clone.
493
+ *
494
+ * @author Leo Fajardo (@leorw)
495
+ * @since 2.5.0
496
+ *
497
+ * @param Freemius $instance
498
+ */
499
+ function maybe_resolve_new_subsite_install_automatically( Freemius $instance ) {
500
+ if ( ! $instance->is_user_in_admin() ) {
501
+ // Try to recover an install or resolve a clone only when there's a user in admin to prevent doing it prematurely (e.g., the install can get replaced with clone data again).
502
+ return;
503
+ }
504
+
505
+ if ( ! is_multisite() ) {
506
+ return;
507
+ }
508
+
509
+ $new_blog_install_map = $this->new_blog_install_map;
510
+
511
+ if ( empty( $new_blog_install_map ) || ! is_array( $new_blog_install_map ) ) {
512
+ return;
513
+ }
514
+
515
+ $is_network_admin = fs_is_network_admin();
516
+
517
+ if ( ! $is_network_admin ) {
518
+ // If not in network admin, handle the current site.
519
+ $blog_id = get_current_blog_id();
520
+ } else {
521
+ // If in network admin, handle only the first site.
522
+ $blog_ids = array_keys( $new_blog_install_map );
523
+ $blog_id = $blog_ids[0];
524
+ }
525
+
526
+ if ( ! isset( $new_blog_install_map[ $blog_id ] ) ) {
527
+ // There's no site to handle.
528
+ return;
529
+ }
530
+
531
+ $expected_install_id = $new_blog_install_map[ $blog_id ]['install_id'];
532
+
533
+ $current_install = $instance->get_install_by_blog_id( $blog_id );
534
+ $current_install_id = is_object( $current_install ) ?
535
+ $current_install->id :
536
+ null;
537
+
538
+ if ( $expected_install_id == $current_install_id ) {
539
+ // Remove the current site's information from the map to prevent handling it again.
540
+ $this->remove_new_blog_install_info_from_storage( $blog_id );
541
+
542
+ return;
543
+ }
544
+
545
+ $instance->switch_to_blog( $blog_id );
546
+
547
+ $current_url = fs_strip_url_protocol( untrailingslashit( get_site_url() ) );
548
+ $current_install_url = is_object( $current_install ) ?
549
+ fs_strip_url_protocol( untrailingslashit( $current_install->url ) ) :
550
+ null;
551
+
552
+ // This can be `false` even if the install is a clone as the URL can be updated as part of the cloning process.
553
+ $is_clone = ( ! is_null( $current_install_url ) && $current_url !== $current_install_url );
554
+
555
+ if ( ! FS_Site::is_valid_id( $expected_install_id ) ) {
556
+ $expected_install = null;
557
+ } else {
558
+ $expected_install = $instance->fetch_install_by_id( $expected_install_id );
559
+ }
560
+
561
+ if ( FS_Api::is_api_result_entity( $expected_install ) ) {
562
+ // Replace the current install with the expected install.
563
+ $instance->store_site( new FS_Site( clone $expected_install ) );
564
+ $instance->sync_install( array( 'is_new_site' => true ), true );
565
+ } else {
566
+ $network_subsite_clone_install = null;
567
+
568
+ if ( ! $is_clone ) {
569
+ // It is possible that `$is_clone` is `false` but the install is actually a clone as the following call checks the install ID and not the URL.
570
+ $network_subsite_clone_install = $this->find_network_subsite_clone_install( $instance );
571
+ }
572
+
573
+ if ( $is_clone || is_object( $network_subsite_clone_install ) ) {
574
+ // If there's no expected install (or it couldn't be fetched) and the current install is a clone, try to resolve the clone automatically.
575
+ $is_localhost = FS_Site::is_localhost_by_address( $current_url );
576
+
577
+ $resolved = $this->try_resolve_clone_automatically( $instance, $current_url, $is_localhost, is_object( $network_subsite_clone_install ) );
578
+
579
+ if ( ! $resolved && is_object( $network_subsite_clone_install ) ) {
580
+ if ( empty( $this->get_clone_identification_timestamp() ) ) {
581
+ $this->store_clone_identification_timestamp();
582
+ }
583
+
584
+ // Since the clone couldn't be identified based on the URL, replace the stored install with the cloned install so that the manual clone resolution notice will appear.
585
+ $instance->store_site( clone $network_subsite_clone_install );
586
+ }
587
+ }
588
+ }
589
+
590
+ $instance->restore_current_blog();
591
+
592
+ // Remove the current site's information from the map to prevent handling it again.
593
+ $this->remove_new_blog_install_info_from_storage( $blog_id );
594
+ }
595
+
596
+ /**
597
+ * If a new install was created after creating a new subsite, its ID is stored in the blog-install map so that it can be recovered in case it's replaced with a clone install (e.g., when the newly created subsite is a clone).
598
+ *
599
+ * @author Leo Fajardo (@leorw)
600
+ * @since 2.5.0
601
+ *
602
+ * @param int $blog_id
603
+ * @param FS_Site $site
604
+ */
605
+ function store_new_blog_install_info( $blog_id, $site = null ) {
606
+ $new_blog_install_map = $this->new_blog_install_map;
607
+
608
+ if (
609
+ empty( $new_blog_install_map ) ||
610
+ ! is_array( $new_blog_install_map )
611
+ ) {
612
+ $new_blog_install_map = array();
613
+ }
614
+
615
+ $install_id = null;
616
+
617
+ if ( is_object( $site ) ) {
618
+ $install_id = $site->id;
619
+ }
620
+
621
+ $new_blog_install_map[ $blog_id ] = array( 'install_id' => $install_id );
622
+
623
+ $this->new_blog_install_map = $new_blog_install_map;
624
+ }
625
+
626
+ /**
627
+ * @author Leo Fajardo (@leorw)
628
+ * @since 2.5.0
629
+ *
630
+ * @param int $blog_id
631
+ */
632
+ private function remove_new_blog_install_info_from_storage( $blog_id ) {
633
+ $new_blog_install_map = $this->new_blog_install_map;
634
+
635
+ unset( $new_blog_install_map[ $blog_id ] );
636
+ $this->new_blog_install_map = $new_blog_install_map;
637
+ }
638
+
639
+ /**
640
+ * Tries to resolve all clones automatically.
641
+ *
642
+ * @author Leo Fajardo (@leorw)
643
+ * @since 2.5.0
644
+ *
645
+ * @return bool If managed to automatically resolve all clones.
646
+ */
647
+ private function try_automatic_resolution() {
648
+ $this->_logger->entrance();
649
+
650
+ $current_url = fs_strip_url_protocol( untrailingslashit( get_site_url() ) );
651
+ $is_localhost = FS_Site::is_localhost_by_address( $current_url );
652
+
653
+ $require_manual_resolution = false;
654
+
655
+ $instances = Freemius::_get_all_instances();
656
+
657
+ foreach ( $instances as $instance ) {
658
+ if ( ! $instance->is_registered() ) {
659
+ continue;
660
+ }
661
+
662
+ if ( ! $instance->is_clone() ) {
663
+ continue;
664
+ }
665
+
666
+ if ( ! $this->try_resolve_clone_automatically( $instance, $current_url, $is_localhost ) ) {
667
+ $require_manual_resolution = true;
668
+ }
669
+ }
670
+
671
+ return ( ! $require_manual_resolution );
672
+ }
673
+
674
+ #endregion
675
+
676
+ #--------------------------------------------------------------------------------
677
+ #region Manual Clone Resolution
678
+ #--------------------------------------------------------------------------------
679
+
680
+ /**
681
+ * @author Leo Fajardo (@leorw)
682
+ * @since 2.5.0
683
+ */
684
+ function _add_clone_resolution_javascript() {
685
+ $vars = array( 'ajax_action' => Freemius::get_ajax_action_static( 'handle_clone_resolution' ) );
686
+
687
+ fs_require_once_template( 'clone-resolution-js.php', $vars );
688
+ }
689
+
690
+ /**
691
+ * @author Leo Fajardo (@leorw)
692
+ * @since 2.5.0
693
+ */
694
+ function _clone_resolution_action_ajax_handler() {
695
+ $this->_logger->entrance();
696
+
697
+ check_ajax_referer( Freemius::get_ajax_action_static( 'handle_clone_resolution' ), 'security' );
698
+
699
+ $clone_action = fs_request_get( 'clone_action' );
700
+
701
+ if ( empty( $clone_action ) ) {
702
+ Freemius::shoot_ajax_failure( array(
703
+ 'message' => fs_text_inline( 'Invalid clone resolution action.', 'invalid-clone-resolution-action-error' ),
704
+ 'redirect_url' => '',
705
+ ) );
706
+ }
707
+
708
+ $result = array();
709
+
710
+ if ( self::OPTION_TEMPORARY_DUPLICATE === $clone_action ) {
711
+ $this->store_temporary_duplicate_timestamp();
712
+ } else {
713
+ $result = $this->resolve_cloned_sites( $clone_action );
714
+ }
715
+
716
+ if ( 'temporary_duplicate_license_activation' !== $clone_action ) {
717
+ $this->remove_clone_resolution_options_notice();
718
+ } else {
719
+ $this->remove_temporary_duplicate_notice();
720
+ }
721
+
722
+ Freemius::shoot_ajax_success( $result );
723
+ }
724
+
725
+ /**
726
+ * @author Leo Fajardo (@leorw)
727
+ * @since 2.5.0
728
+ *
729
+ * @param string $clone_action
730
+ */
731
+ private function resolve_cloned_sites( $clone_action ) {
732
+ $this->_logger->entrance();
733
+
734
+ $instances_with_clone_count = 0;
735
+ $instance_with_error = null;
736
+ $has_error = false;
737
+
738
+ $instances = Freemius::_get_all_instances();
739
+
740
+ foreach ( $instances as $instance ) {
741
+ if ( ! $instance->is_registered() ) {
742
+ continue;
743
+ }
744
+
745
+ if ( ! $instance->is_clone() ) {
746
+ continue;
747
+ }
748
+
749
+ $instances_with_clone_count ++;
750
+
751
+ if ( FS_Clone_Manager::OPTION_NEW_HOME === $clone_action ) {
752
+ $instance->sync_install( array( 'is_new_site' => true ), true );
753
+ } else {
754
+ $instance->_handle_long_term_duplicate();
755
+
756
+ if ( ! is_object( $instance->get_site() ) ) {
757
+ $has_error = true;
758
+
759
+ if ( ! is_object( $instance_with_error ) ) {
760
+ $instance_with_error = $instance;
761
+ }
762
+ }
763
+ }
764
+ }
765
+
766
+ $redirect_url = '';
767
+
768
+ if (
769
+ 1 === $instances_with_clone_count &&
770
+ $has_error
771
+ ) {
772
+ $redirect_url = $instance_with_error->get_activation_url();
773
+ }
774
+
775
+ return ( array( 'redirect_url' => $redirect_url ) );
776
+ }
777
+
778
+ /**
779
+ * @author Leo Fajardo (@leorw)
780
+ * @since 2.5.0
781
+ */
782
+ private function hide_clone_admin_notices() {
783
+ $this->remove_clone_resolution_options_notice( false );
784
+ $this->remove_temporary_duplicate_notice( false );
785
+ }
786
+
787
+ /**
788
+ * @author Leo Fajardo (@leorw)
789
+ * @since 2.5.0
790
+ */
791
+ private function maybe_show_clone_admin_notice() {
792
+ $this->_logger->entrance();
793
+
794
+ if ( fs_is_network_admin() ) {
795
+ $existing_notice_ids = $this->maybe_remove_notices();
796
+
797
+ if ( ! empty( $existing_notice_ids ) ) {
798
+ fs_enqueue_local_style( 'fs_clone_resolution_notice', '/admin/clone-resolution.css' );
799
+ }
800
+
801
+ return;
802
+ }
803
+
804
+ $first_instance_with_clone = null;
805
+
806
+ $site_urls = array();
807
+ $sites_with_license_urls = array();
808
+ $sites_with_premium_version_count = 0;
809
+ $product_ids = array();
810
+ $product_titles = array();
811
+
812
+ $instances = Freemius::_get_all_instances();
813
+
814
+ foreach ( $instances as $instance ) {
815
+ if ( ! $instance->is_registered() ) {
816
+ continue;
817
+ }
818
+
819
+ if ( ! $instance->is_clone() ) {
820
+ continue;
821
+ }
822
+
823
+ $install = $instance->get_site();
824
+
825
+ $site_urls[] = $install->url;
826
+ $product_ids[] = $instance->get_id();
827
+ $product_titles[] = $instance->get_plugin_title();
828
+
829
+ if ( is_null( $first_instance_with_clone ) ) {
830
+ $first_instance_with_clone = $instance;
831
+ }
832
+
833
+ if ( is_object( $instance->_get_license() ) ) {
834
+ $sites_with_license_urls[] = $install->url;
835
+ }
836
+
837
+ if ( $instance->is_premium() ) {
838
+ $sites_with_premium_version_count ++;
839
+ }
840
+ }
841
+
842
+ if ( empty( $site_urls ) && empty( $sites_with_license_urls ) ) {
843
+ $this->hide_clone_admin_notices();
844
+
845
+ return;
846
+ }
847
+
848
+ $site_urls = array_unique( $site_urls );
849
+ $sites_with_license_urls = array_unique( $sites_with_license_urls );
850
+
851
+ $module_label = fs_text_inline( 'products', 'products' );
852
+ $admin_notice_module_title = null;
853
+
854
+ $has_temporary_duplicate_mode_expired = $this->has_temporary_duplicate_mode_expired();
855
+
856
+ if (
857
+ ! $this->was_temporary_duplicate_mode_selected() ||
858
+ $has_temporary_duplicate_mode_expired
859
+ ) {
860
+ if ( ! empty( $site_urls ) ) {
861
+ fs_enqueue_local_style( 'fs_clone_resolution_notice', '/admin/clone-resolution.css' );
862
+
863
+ $doc_url = 'https://freemius.com/help/documentation/wordpress-sdk/safe-mode-clone-resolution-duplicate-website/';
864
+
865
+ if ( 1 === count( $instances ) ) {
866
+ $doc_url = fs_apply_filter(
867
+ $first_instance_with_clone->get_unique_affix(),
868
+ 'clone_resolution_documentation_url',
869
+ $doc_url
870
+ );
871
+ }
872
+
873
+ $this->add_manual_clone_resolution_admin_notice(
874
+ $product_ids,
875
+ $product_titles,
876
+ $site_urls,
877
+ get_site_url(),
878
+ ( count( $site_urls ) === count( $sites_with_license_urls ) ),
879
+ ( count( $site_urls ) === $sites_with_premium_version_count ),
880
+ $doc_url
881
+ );
882
+ }
883
+
884
+ return;
885
+ }
886
+
887
+ if ( empty( $sites_with_license_urls ) ) {
888
+ return;
889
+ }
890
+
891
+ if ( ! $this->is_temporary_duplicate_notice_shown() ) {
892
+ $last_time_temporary_duplicate_notice_shown = $this->last_time_temporary_duplicate_notice_was_shown();
893
+ $was_temporary_duplicate_notice_shown_before = is_numeric( $last_time_temporary_duplicate_notice_shown );
894
+
895
+ if ( $was_temporary_duplicate_notice_shown_before ) {
896
+ $temporary_duplicate_mode_expiration_timestamp = $this->get_temporary_duplicate_expiration_timestamp();
897
+ $current_time = time();
898
+
899
+ if (
900
+ $current_time > $temporary_duplicate_mode_expiration_timestamp ||
901
+ $current_time < ( $temporary_duplicate_mode_expiration_timestamp - ( 2 * WP_FS__TIME_24_HOURS_IN_SEC ) )
902
+ ) {
903
+ // Do not show the notice if the temporary duplicate mode has already expired or it will expire more than 2 days from now.
904
+ return;
905
+ }
906
+ }
907
+ }
908
+
909
+ if ( 1 === count( $sites_with_license_urls ) ) {
910
+ $module_label = $first_instance_with_clone->get_module_label( true );
911
+ $admin_notice_module_title = $first_instance_with_clone->get_plugin_title();
912
+ }
913
+
914
+ fs_enqueue_local_style( 'fs_clone_resolution_notice', '/admin/clone-resolution.css' );
915
+
916
+ $this->add_temporary_duplicate_sticky_notice(
917
+ $product_ids,
918
+ $this->get_temporary_duplicate_admin_notice_string( $sites_with_license_urls, $product_titles, $module_label ),
919
+ $admin_notice_module_title
920
+ );
921
+ }
922
+
923
+ /**
924
+ * Removes the notices from the storage if the context product is either no longer active on the context subsite or it's active but there's no longer any clone. This prevents the notices from being shown on the network-level admin page when they are no longer relevant.
925
+ *
926
+ * @author Leo Fajardo (@leorw)
927
+ * @since 2.5.1
928
+ *
929
+ * @return string[]
930
+ */
931
+ private function maybe_remove_notices() {
932
+ $notices = array(
933
+ 'clone_resolution_options_notice' => $this->_notices->get_sticky( 'clone_resolution_options_notice', true ),
934
+ 'temporary_duplicate_notice' => $this->_notices->get_sticky( 'temporary_duplicate_notice', true ),
935
+ );
936
+
937
+ $instances = Freemius::_get_all_instances();
938
+
939
+ foreach ( $notices as $id => $notice ) {
940
+ if ( ! is_array( $notice ) ) {
941
+ unset( $notices[ $id ] );
942
+ continue;
943
+ }
944
+
945
+ if ( empty( $notice['data'] ) || ! is_array( $notice['data'] ) ) {
946
+ continue;
947
+ }
948
+
949
+ if ( empty( $notice['data']['product_ids'] ) || empty( $notice['data']['blog_id'] ) ) {
950
+ continue;
951
+ }
952
+
953
+ $product_ids = $notice['data']['product_ids'];
954
+ $blog_id = $notice['data']['blog_id'];
955
+ $has_clone = false;
956
+
957
+ if ( ! is_null( get_site( $blog_id ) ) ) {
958
+ foreach ( $product_ids as $product_id ) {
959
+ if ( ! isset( $instances[ 'm_' . $product_id ] ) ) {
960
+ continue;
961
+ }
962
+
963
+ $instance = $instances[ 'm_' . $product_id ];
964
+
965
+ $plugin_basename = $instance->get_plugin_basename();
966
+
967
+ $is_plugin_active = is_plugin_active_for_network( $plugin_basename );
968
+
969
+ if ( ! $is_plugin_active ) {
970
+ switch_to_blog( $blog_id );
971
+
972
+ $is_plugin_active = is_plugin_active( $plugin_basename );
973
+
974
+ restore_current_blog();
975
+ }
976
+
977
+ if ( ! $is_plugin_active ) {
978
+ continue;
979
+ }
980
+
981
+ $install = $instance->get_install_by_blog_id( $blog_id );
982
+
983
+ if ( ! is_object( $install ) ) {
984
+ continue;
985
+ }
986
+
987
+ $subsite_url = trailingslashit( get_site_url( $blog_id ) );
988
+ $install_url = trailingslashit( $install->url );
989
+
990
+ $has_clone = ( fs_strip_url_protocol( $install_url ) !== fs_strip_url_protocol( $subsite_url ) );
991
+ }
992
+ }
993
+
994
+ if ( ! $has_clone ) {
995
+ $this->_notices->remove_sticky( $id, true, false );
996
+ unset( $notices[ $id ] );
997
+ }
998
+ }
999
+
1000
+ return array_keys( $notices );
1001
+ }
1002
+
1003
+ /**
1004
+ * Adds a notice that provides the logged-in WordPress user with manual clone resolution options.
1005
+ *
1006
+ * @param number[] $product_ids
1007
+ * @param string[] $site_urls
1008
+ * @param string $current_url
1009
+ * @param bool $has_license
1010
+ * @param bool $is_premium
1011
+ * @param string $doc_url
1012
+ */
1013
+ private function add_manual_clone_resolution_admin_notice(
1014
+ $product_ids,
1015
+ $product_titles,
1016
+ $site_urls,
1017
+ $current_url,
1018
+ $has_license,
1019
+ $is_premium,
1020
+ $doc_url
1021
+ ) {
1022
+ $this->_logger->entrance();
1023
+
1024
+ $total_sites = count( $site_urls );
1025
+ $sites_list = '';
1026
+
1027
+ $total_products = count( $product_titles );
1028
+ $products_list = '';
1029
+
1030
+ if ( 1 === $total_products ) {
1031
+ $notice_header = sprintf(
1032
+ '<div class="fs-notice-header"><p>%s</p></div>',
1033
+ fs_esc_html_inline( '%1$s has been placed into safe mode because we noticed that %2$s is an exact copy of %3$s.', 'single-cloned-site-safe-mode-message' )
1034
+ );
1035
+ } else {
1036
+ $notice_header = sprintf(
1037
+ '<div class="fs-notice-header"><p>%s</p></div>',
1038
+ ( 1 === $total_sites ) ?
1039
+ fs_esc_html_inline( 'The products below have been placed into safe mode because we noticed that %2$s is an exact copy of %3$s:%1$s', 'multiple-products-cloned-site-safe-mode-message' ) :
1040
+ fs_esc_html_inline( 'The products below have been placed into safe mode because we noticed that %2$s is an exact copy of these sites:%3$s%1$s', 'multiple-products-multiple-cloned-sites-safe-mode-message' )
1041
+ );
1042
+
1043
+ foreach ( $product_titles as $product_title ) {
1044
+ $products_list .= sprintf( '<li>%s</li>', $product_title );
1045
+ }
1046
+
1047
+ $products_list = '<ol>' . $products_list . '</ol>';
1048
+
1049
+ foreach ( $site_urls as $site_url ) {
1050
+ $sites_list .= sprintf(
1051
+ '<li><a href="%s" target="_blank">%s</a></li>',
1052
+ $site_url,
1053
+ fs_strip_url_protocol( $site_url )
1054
+ );
1055
+ }
1056
+
1057
+ $sites_list = '<ol>' . $sites_list . '</ol>';
1058
+ }
1059
+
1060
+ $remote_site_link = '<b>' . (1 === $total_sites ?
1061
+ sprintf(
1062
+ '<a href="%s" target="_blank">%s</a>',
1063
+ $site_urls[0],
1064
+ fs_strip_url_protocol( $site_urls[0] )
1065
+ ) :
1066
+ fs_text_inline( 'the above-mentioned sites', 'above-mentioned-sites' )) . '</b>';
1067
+
1068
+ $current_site_link = sprintf(
1069
+ '<b><a href="%s" target="_blank">%s</a></b>',
1070
+ $current_url,
1071
+ fs_strip_url_protocol( $current_url )
1072
+ );
1073
+
1074
+ $button_template = '<button class="button" data-clone-action="%s">%s</button>';
1075
+ $option_template = '<div class="fs-clone-resolution-option"><strong>%s</strong><p>%s</p><div>%s</div></div>';
1076
+
1077
+ $duplicate_option = sprintf(
1078
+ $option_template,
1079
+ fs_esc_html_inline( 'Is %2$s a duplicate of %4$s?', 'duplicate-site-confirmation-message' ),
1080
+ fs_esc_html_inline( 'Yes, %2$s is a duplicate of %4$s for the purpose of testing, staging, or development.', 'duplicate-site-message' ),
1081
+ ($this->has_temporary_duplicate_mode_expired() ?
1082
+ sprintf(
1083
+ $button_template,
1084
+ 'long_term_duplicate',
1085
+ fs_text_inline( 'Long-Term Duplicate', 'long-term-duplicate' )
1086
+ ) :
1087
+ sprintf(
1088
+ $button_template,
1089
+ 'temporary_duplicate',
1090
+ fs_text_inline( 'Duplicate Website', 'duplicate-site' )
1091
+ ))
1092
+ );
1093
+
1094
+ $migration_option = sprintf(
1095
+ $option_template,
1096
+ fs_esc_html_inline( 'Is %2$s the new home of %4$s?', 'migrate-site-confirmation-message' ),
1097
+ sprintf(
1098
+ fs_esc_html_inline( 'Yes, %%2$s is replacing %%4$s. I would like to migrate my %s from %%4$s to %%2$s.', 'migrate-site-message' ),
1099
+ ( $has_license ? fs_text_inline( 'license', 'license' ) : fs_text_inline( 'data', 'data' ) )
1100
+ ),
1101
+ sprintf(
1102
+ $button_template,
1103
+ 'new_home',
1104
+ $has_license ?
1105
+ fs_text_inline( 'Migrate License', 'migrate-product-license' ) :
1106
+ fs_text_inline( 'Migrate', 'migrate-product-data' )
1107
+ )
1108
+ );
1109
+
1110
+ $new_website = sprintf(
1111
+ $option_template,
1112
+ fs_esc_html_inline( 'Is %2$s a new website?', 'new-site-confirmation-message' ),
1113
+ fs_esc_html_inline( 'Yes, %2$s is a new and different website that is separate from %4$s.', 'new-site-message' ) .
1114
+ ($is_premium ?
1115
+ ' ' . fs_text_inline( 'It requires license activation.', 'new-site-requires-license-activation-message' ) :
1116
+ ''
1117
+ ),
1118
+ sprintf(
1119
+ $button_template,
1120
+ 'new_website',
1121
+ ( ! $is_premium || ! $has_license ) ?
1122
+ fs_text_inline( 'New Website', 'new-website' ) :
1123
+ fs_text_inline( 'Activate License', 'activate-license' )
1124
+ )
1125
+ );
1126
+
1127
+ /**
1128
+ * %1$s - single product's title or product titles list.
1129
+ * %2$s - site's URL.
1130
+ * %3$s - single install's URL or install URLs list.
1131
+ * %4$s - Clone site's link or "the above-mentioned sites" if there are multiple clone sites.
1132
+ */
1133
+ $message = sprintf(
1134
+ $notice_header .
1135
+ '<div class="fs-clone-resolution-options-container" data-ajax-url="' . esc_attr( admin_url( 'admin-ajax.php?_fs_network_admin=false', 'relative' ) ) . '">' .
1136
+ $duplicate_option .
1137
+ $migration_option .
1138
+ $new_website . '</div>' .
1139
+ sprintf( '<div class="fs-clone-documentation-container">Unsure what to do? <a href="%s" target="_blank">Read more here</a>.</div>', $doc_url ),
1140
+ // %1$s
1141
+ ( 1 === $total_products ?
1142
+ sprintf( '<b>%s</b>', $product_titles[0] ) :
1143
+ ( 1 === $total_sites ?
1144
+ sprintf( '<div>%s</div>', $products_list ) :
1145
+ sprintf( '<div><p><strong>%s</strong>:</p>%s</div>', fs_esc_html_x_inline( 'Products', 'Clone resolution admin notice products list label', 'products' ), $products_list ) )
1146
+ ),
1147
+ // %2$s
1148
+ $current_site_link,
1149
+ // %3$s
1150
+ ( 1 === $total_sites ?
1151
+ $remote_site_link :
1152
+ $sites_list ),
1153
+ // %4$s
1154
+ $remote_site_link
1155
+ );
1156
+
1157
+ $this->_notices->add_sticky(
1158
+ $message,
1159
+ 'clone_resolution_options_notice',
1160
+ '',
1161
+ 'warn',
1162
+ true,
1163
+ null,
1164
+ null,
1165
+ true,
1166
+ // Intentionally not dismissible.
1167
+ false,
1168
+ array(
1169
+ 'product_ids' => $product_ids,
1170
+ 'blog_id' => get_current_blog_id()
1171
+ )
1172
+ );
1173
+ }
1174
+
1175
+ #endregion
1176
+
1177
+ #--------------------------------------------------------------------------------
1178
+ #region Temporary Duplicate (Short Term)
1179
+ #--------------------------------------------------------------------------------
1180
+
1181
+ /**
1182
+ * @author Leo Fajardo (@leorw)
1183
+ * @since 2.5.0
1184
+ *
1185
+ * @return string
1186
+ */
1187
+ private function get_temporary_duplicate_admin_notice_string( $site_urls, $product_titles, $module_label ) {
1188
+ $this->_logger->entrance();
1189
+
1190
+ $temporary_duplicate_end_date = $this->get_temporary_duplicate_expiration_timestamp();
1191
+ $temporary_duplicate_end_date = date( 'M j, Y', $temporary_duplicate_end_date );
1192
+
1193
+ $current_url = get_site_url();
1194
+ $current_site_link = sprintf(
1195
+ '<b><a href="%s" target="_blank">%s</a></b>',
1196
+ $current_url,
1197
+ fs_strip_url_protocol( $current_url )
1198
+ );
1199
+
1200
+ $total_sites = count( $site_urls );
1201
+ $sites_list = '';
1202
+
1203
+ $total_products = count( $product_titles );
1204
+ $products_list = '';
1205
+
1206
+ if ( $total_sites > 1 ) {
1207
+ foreach ( $site_urls as $site_url ) {
1208
+ $sites_list .= sprintf(
1209
+ '<li><a href="%s" target="_blank">%s</a></li>',
1210
+ $site_url,
1211
+ fs_strip_url_protocol( $site_url )
1212
+ );
1213
+ }
1214
+
1215
+ $sites_list = '<ol class="fs-sites-list">' . $sites_list . '</ol>';
1216
+ }
1217
+
1218
+ if ( $total_products > 1 ) {
1219
+ foreach ( $product_titles as $product_title ) {
1220
+ $products_list .= sprintf( '<li>%s</li>', $product_title );
1221
+ }
1222
+
1223
+ $products_list = '<ol>' . $products_list . '</ol>';
1224
+ }
1225
+
1226
+ return sprintf(
1227
+ sprintf(
1228
+ '<div>%s</div>',
1229
+ ( 1 === $total_sites ?
1230
+ sprintf( '<p>%s</p>', fs_esc_html_inline( 'You marked this website, %s, as a temporary duplicate of %s.', 'temporary-duplicate-message' ) ) :
1231
+ sprintf( '<p>%s:</p>', fs_esc_html_inline( 'You marked this website, %s, as a temporary duplicate of these sites', 'temporary-duplicate-of-sites-message' ) ) . '%s' )
1232
+ ) . '%s',
1233
+ $current_site_link,
1234
+ ( 1 === $total_sites ?
1235
+ sprintf(
1236
+ '<b><a href="%s" target="_blank">%s</a></b>',
1237
+ $site_urls[0],
1238
+ fs_strip_url_protocol( $site_urls[0] )
1239
+ ) :
1240
+ $sites_list ),
1241
+ sprintf(
1242
+ '<div class="fs-clone-resolution-options-container fs-duplicate-site-options" data-ajax-url="%s" data-blog-id="' . get_current_blog_id() . '"><p>%s</p>%s<p>%s</p></div>',
1243
+ esc_attr( admin_url( 'admin-ajax.php?_fs_network_admin=false', 'relative' ) ),
1244
+ sprintf(
1245
+ fs_esc_html_inline( "%s automatic security & feature updates and paid functionality will keep working without interruptions until %s (or when your license expires, whatever comes first).", 'duplicate-site-confirmation-message' ),
1246
+ ( 1 === $total_products ?
1247
+ sprintf(
1248
+ fs_esc_html_x_inline( "The %s's", '"The <product_label>", e.g.: "The plugin"', 'the-product-x'),
1249
+ "<strong>{$module_label}</strong>"
1250
+ ) :
1251
+ fs_esc_html_inline( "The following products'", 'the-following-products' ) ),
1252
+ sprintf( '<strong>%s</strong>', $temporary_duplicate_end_date )
1253
+ ),
1254
+ ( 1 === $total_products ?
1255
+ '' :
1256
+ sprintf( '<div>%s</div>', $products_list )
1257
+ ),
1258
+ sprintf(
1259
+ fs_esc_html_inline( 'If this is a long term duplicate, to keep automatic updates and paid functionality after %s, please %s.', 'duplicate-site-message' ),
1260
+ sprintf( '<strong>%s</strong>', $temporary_duplicate_end_date),
1261
+ sprintf( '<a href="#" id="fs_temporary_duplicate_license_activation_link" data-clone-action="temporary_duplicate_license_activation">%s</a>', fs_esc_html_inline( 'activate a license here', 'activate-license-here' ) )
1262
+ )
1263
+ )
1264
+ );
1265
+ }
1266
+
1267
+ /**
1268
+ * Determines if the temporary duplicate mode has already expired.
1269
+ *
1270
+ * @return bool
1271
+ */
1272
+ function has_temporary_duplicate_mode_expired() {
1273
+ $temporary_duplicate_mode_start_timestamp = $this->was_temporary_duplicate_mode_selected() ?
1274
+ $this->temporary_duplicate_mode_selection_timestamp :
1275
+ $this->get_clone_identification_timestamp();
1276
+
1277
+ if ( ! is_numeric( $temporary_duplicate_mode_start_timestamp ) ) {
1278
+ return false;
1279
+ }
1280
+
1281
+ return ( time() > ( $temporary_duplicate_mode_start_timestamp + self::TEMPORARY_DUPLICATE_PERIOD ) );
1282
+ }
1283
+
1284
+ /**
1285
+ * Determines if the logged-in WordPress user manually selected the temporary duplicate mode for the site.
1286
+ *
1287
+ * @return bool
1288
+ */
1289
+ function was_temporary_duplicate_mode_selected() {
1290
+ return (
1291
+ isset( $this->temporary_duplicate_mode_selection_timestamp ) &&
1292
+ is_numeric( $this->temporary_duplicate_mode_selection_timestamp )
1293
+ );
1294
+ }
1295
+
1296
+ /**
1297
+ * Stores the time when the logged-in WordPress user selected the temporary duplicate mode for the site.
1298
+ */
1299
+ private function store_temporary_duplicate_timestamp() {
1300
+ $this->temporary_duplicate_mode_selection_timestamp = time();
1301
+ }
1302
+
1303
+ /**
1304
+ * Removes the notice that is shown when the logged-in WordPress user has selected the temporary duplicate mode for the site.
1305
+ *
1306
+ * @param bool $store
1307
+ */
1308
+ function remove_clone_resolution_options_notice( $store = true ) {
1309
+ $this->_notices->remove_sticky( 'clone_resolution_options_notice', true, $store );
1310
+ }
1311
+
1312
+ /**
1313
+ * Removes the notice that is shown when the logged-in WordPress user has selected the temporary duplicate mode for the site.
1314
+ *
1315
+ * @param bool $store
1316
+ */
1317
+ function remove_temporary_duplicate_notice( $store = true ) {
1318
+ $this->_notices->remove_sticky( 'temporary_duplicate_notice', true, $store );
1319
+ }
1320
+
1321
+ /**
1322
+ * Determines if the manual clone resolution options notice is currently being shown.
1323
+ *
1324
+ * @return bool
1325
+ */
1326
+ function is_clone_resolution_options_notice_shown() {
1327
+ return $this->_notices->has_sticky( 'clone_resolution_options_notice', true );
1328
+ }
1329
+
1330
+ /**
1331
+ * Determines if the temporary duplicate notice is currently being shown.
1332
+ *
1333
+ * @return bool
1334
+ */
1335
+ function is_temporary_duplicate_notice_shown() {
1336
+ return $this->_notices->has_sticky( 'temporary_duplicate_notice', true );
1337
+ }
1338
+
1339
+ /**
1340
+ * Determines if a site was marked as a temporary duplicate and if it's still a temporary duplicate.
1341
+ *
1342
+ * @return bool
1343
+ */
1344
+ function is_temporary_duplicate_by_blog_id( $blog_id ) {
1345
+ $storage = FS_Option_Manager::get_manager( WP_FS___OPTION_PREFIX . self::OPTION_MANAGER_NAME, true, $blog_id );
1346
+ $data = $storage->get_option( self::OPTION_NAME, array() );
1347
+
1348
+ if ( ! is_array( $data ) ) {
1349
+ return false;
1350
+ }
1351
+
1352
+ $was_temporary_duplicate_mode_selected = (
1353
+ isset( $data['temporary_duplicate_mode_selection_timestamp'] ) &&
1354
+ is_numeric( $data['temporary_duplicate_mode_selection_timestamp'] )
1355
+ );
1356
+
1357
+ if ( ! $was_temporary_duplicate_mode_selected ) {
1358
+ return false;
1359
+ }
1360
+
1361
+ return ( time() < ( $data['temporary_duplicate_mode_selection_timestamp'] + self::TEMPORARY_DUPLICATE_PERIOD ) );
1362
+ }
1363
+
1364
+ /**
1365
+ * Determines the last time the temporary duplicate notice was shown.
1366
+ *
1367
+ * @return int|null
1368
+ */
1369
+ function last_time_temporary_duplicate_notice_was_shown() {
1370
+ return ( ! isset( $this->temporary_duplicate_notice_shown_timestamp ) ) ?
1371
+ null :
1372
+ $this->temporary_duplicate_notice_shown_timestamp;
1373
+ }
1374
+
1375
+ /**
1376
+ * Clears the time that has been stored when the temporary duplicate notice was shown.
1377
+ */
1378
+ function clear_temporary_duplicate_notice_shown_timestamp() {
1379
+ $this->temporary_duplicate_notice_shown_timestamp = null;
1380
+ }
1381
+
1382
+ /**
1383
+ * Adds a temporary duplicate notice that provides the logged-in WordPress user with an option to activate a license for the site.
1384
+ *
1385
+ * @param number[] $product_ids
1386
+ * @param string $message
1387
+ * @param string $message
1388
+ * @param string|null $plugin_title
1389
+ */
1390
+ function add_temporary_duplicate_sticky_notice( $product_ids, $message, $plugin_title = null ) {
1391
+ $this->_logger->entrance();
1392
+
1393
+ $this->_notices->add_sticky(
1394
+ $message,
1395
+ 'temporary_duplicate_notice',
1396
+ '',
1397
+ 'promotion',
1398
+ true,
1399
+ null,
1400
+ $plugin_title,
1401
+ true,
1402
+ true,
1403
+ array(
1404
+ 'product_ids' => $product_ids,
1405
+ 'blog_id' => get_current_blog_id()
1406
+ )
1407
+ );
1408
+
1409
+ $this->temporary_duplicate_notice_shown_timestamp = time();
1410
+ }
1411
+
1412
+ #endregion
1413
+
1414
+ /**
1415
+ * @author Leo Fajardo
1416
+ * @since 2.5.0
1417
+ *
1418
+ * @param string $key
1419
+ *
1420
+ * @return bool
1421
+ */
1422
+ private function should_use_network_storage( $key ) {
1423
+ return ( 'new_blog_install_map' === $key );
1424
+ }
1425
+
1426
+ #--------------------------------------------------------------------------------
1427
+ #region Magic methods
1428
+ #--------------------------------------------------------------------------------
1429
+
1430
+ /**
1431
+ * @param string $name
1432
+ * @param int|string $value
1433
+ */
1434
+ function __set( $name, $value ) {
1435
+ if ( ! $this->should_use_network_storage( $name ) ) {
1436
+ $storage = $this->_storage;
1437
+ $data = $this->_data;
1438
+ } else {
1439
+ $storage = $this->_network_storage;
1440
+ $data = $this->_network_data;
1441
+ }
1442
+
1443
+ if ( ! array_key_exists( $name, $data ) ) {
1444
+ return;
1445
+ }
1446
+
1447
+ $data[ $name ] = $value;
1448
+
1449
+ $storage->set_option( self::OPTION_NAME, $data, true );
1450
+ }
1451
+
1452
+ /**
1453
+ * @param string $name
1454
+ *
1455
+ * @return bool
1456
+ */
1457
+ function __isset( $name ) {
1458
+ return (
1459
+ isset( $this->_data[ $name ] ) ||
1460
+ isset( $this->_network_data[ $name ] )
1461
+ );
1462
+ }
1463
+
1464
+ /**
1465
+ * @param string $name
1466
+ *
1467
+ * @return null|int|string
1468
+ */
1469
+ function __get( $name ) {
1470
+ $data = ( ! $this->should_use_network_storage( $name ) ) ?
1471
+ $this->_data :
1472
+ $this->_network_data;
1473
+
1474
+ return array_key_exists( $name, $data ) ?
1475
+ $data[ $name ] :
1476
+ null;
1477
+ }
1478
+
1479
+ #endregion
1480
+ }
lib/freemius/includes/managers/class-fs-key-value-storage.php CHANGED
@@ -298,6 +298,7 @@
298
  return $this->get( $k, null );
299
  }
300
 
 
301
  function offsetSet( $k, $v ) {
302
  if ( is_null( $k ) ) {
303
  throw new Exception( 'Can\'t append value to request params.' );
@@ -306,14 +307,17 @@
306
  }
307
  }
308
 
 
309
  function offsetExists( $k ) {
310
  return array_key_exists( $k, $this->_data );
311
  }
312
 
 
313
  function offsetUnset( $k ) {
314
  unset( $this->$k );
315
  }
316
 
 
317
  function offsetGet( $k ) {
318
  return $this->get( $k, null );
319
  }
@@ -325,6 +329,7 @@
325
  * @link http://php.net/manual/en/iterator.current.php
326
  * @return mixed Can return any type.
327
  */
 
328
  public function current() {
329
  return current( $this->_data );
330
  }
@@ -336,6 +341,7 @@
336
  * @link http://php.net/manual/en/iterator.next.php
337
  * @return void Any returned value is ignored.
338
  */
 
339
  public function next() {
340
  next( $this->_data );
341
  }
@@ -347,6 +353,7 @@
347
  * @link http://php.net/manual/en/iterator.key.php
348
  * @return mixed scalar on success, or null on failure.
349
  */
 
350
  public function key() {
351
  return key( $this->_data );
352
  }
@@ -359,6 +366,7 @@
359
  * @return boolean The return value will be casted to boolean and then evaluated.
360
  * Returns true on success or false on failure.
361
  */
 
362
  public function valid() {
363
  $key = key( $this->_data );
364
 
@@ -372,6 +380,7 @@
372
  * @link http://php.net/manual/en/iterator.rewind.php
373
  * @return void Any returned value is ignored.
374
  */
 
375
  public function rewind() {
376
  reset( $this->_data );
377
  }
@@ -386,6 +395,7 @@
386
  * <p>
387
  * The return value is cast to an integer.
388
  */
 
389
  public function count() {
390
  return count( $this->_data );
391
  }
298
  return $this->get( $k, null );
299
  }
300
 
301
+ #[ReturnTypeWillChange]
302
  function offsetSet( $k, $v ) {
303
  if ( is_null( $k ) ) {
304
  throw new Exception( 'Can\'t append value to request params.' );
307
  }
308
  }
309
 
310
+ #[ReturnTypeWillChange]
311
  function offsetExists( $k ) {
312
  return array_key_exists( $k, $this->_data );
313
  }
314
 
315
+ #[ReturnTypeWillChange]
316
  function offsetUnset( $k ) {
317
  unset( $this->$k );
318
  }
319
 
320
+ #[ReturnTypeWillChange]
321
  function offsetGet( $k ) {
322
  return $this->get( $k, null );
323
  }
329
  * @link http://php.net/manual/en/iterator.current.php
330
  * @return mixed Can return any type.
331
  */
332
+ #[ReturnTypeWillChange]
333
  public function current() {
334
  return current( $this->_data );
335
  }
341
  * @link http://php.net/manual/en/iterator.next.php
342
  * @return void Any returned value is ignored.
343
  */
344
+ #[ReturnTypeWillChange]
345
  public function next() {
346
  next( $this->_data );
347
  }
353
  * @link http://php.net/manual/en/iterator.key.php
354
  * @return mixed scalar on success, or null on failure.
355
  */
356
+ #[ReturnTypeWillChange]
357
  public function key() {
358
  return key( $this->_data );
359
  }
366
  * @return boolean The return value will be casted to boolean and then evaluated.
367
  * Returns true on success or false on failure.
368
  */
369
+ #[ReturnTypeWillChange]
370
  public function valid() {
371
  $key = key( $this->_data );
372
 
380
  * @link http://php.net/manual/en/iterator.rewind.php
381
  * @return void Any returned value is ignored.
382
  */
383
+ #[ReturnTypeWillChange]
384
  public function rewind() {
385
  reset( $this->_data );
386
  }
395
  * <p>
396
  * The return value is cast to an integer.
397
  */
398
+ #[ReturnTypeWillChange]
399
  public function count() {
400
  return count( $this->_data );
401
  }
lib/freemius/includes/managers/class-fs-plugin-manager.php CHANGED
@@ -211,10 +211,23 @@
211
  * @return bool|\FS_Plugin
212
  */
213
  function get() {
214
- return isset( $this->_module ) ?
215
- $this->_module :
216
- false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
218
-
219
-
220
  }
211
  * @return bool|\FS_Plugin
212
  */
213
  function get() {
214
+ if ( isset( $this->_module ) ) {
215
+ return $this->_module;
216
+ }
217
+
218
+ if ( empty( $this->_module_id ) ) {
219
+ return false;
220
+ }
221
+
222
+ /**
223
+ * Return an FS_Plugin entity that has its `id` and `is_live` properties set (`is_live` is initialized in the FS_Plugin constructor) to avoid triggering an error that is relevant to these properties when the FS_Plugin entity is used before the `parse_settings()` method is called. This can happen when creating a regular WordPress site by cloning a subsite of a multisite network and the data that is stored in the network-level storage is not cloned.
224
+ *
225
+ * @author Leo Fajardo (@leorw)
226
+ * @since 2.5.0
227
+ */
228
+ $plugin = new FS_Plugin();
229
+ $plugin->id = $this->_module_id;
230
+
231
+ return $plugin;
232
  }
 
 
233
  }
lib/freemius/languages/freemius-cs_CZ.mo CHANGED
Binary file
lib/freemius/languages/freemius-da_DK.mo CHANGED
Binary file
lib/freemius/languages/freemius-de_DE.mo ADDED
Binary file
lib/freemius/languages/freemius-en.mo CHANGED
Binary file
lib/freemius/languages/freemius-es_ES.mo CHANGED
Binary file
lib/freemius/languages/freemius-fr_FR.mo CHANGED
Binary file
lib/freemius/languages/freemius-he_IL.mo CHANGED
Binary file
lib/freemius/languages/freemius-hu_HU.mo CHANGED
Binary file
lib/freemius/languages/freemius-it_IT.mo CHANGED
Binary file
lib/freemius/languages/freemius-ja.mo CHANGED
Binary file
lib/freemius/languages/freemius-nl_NL.mo CHANGED
Binary file
lib/freemius/languages/freemius-ru_RU.mo CHANGED
Binary file
lib/freemius/languages/freemius-ta.mo CHANGED
Binary file
lib/freemius/languages/freemius-zh_CN.mo CHANGED
Binary file
lib/freemius/require.php CHANGED
@@ -17,6 +17,7 @@
17
  // require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-abstract-manager.php';
18
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-option-manager.php';
19
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-gdpr-manager.php';
 
20
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-cache-manager.php';
21
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-notice-manager.php';
22
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-menu-manager.php';
17
  // require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-abstract-manager.php';
18
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-option-manager.php';
19
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-gdpr-manager.php';
20
+ require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-clone-manager.php';
21
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-cache-manager.php';
22
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-notice-manager.php';
23
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-menu-manager.php';
lib/freemius/start.php CHANGED
@@ -15,7 +15,7 @@
15
  *
16
  * @var string
17
  */
18
- $this_sdk_version = '2.4.2.16';
19
 
20
  #region SDK Selection Logic --------------------------------------------------------------------
21
 
15
  *
16
  * @var string
17
  */
18
+ $this_sdk_version = '2.5.0';
19
 
20
  #region SDK Selection Logic --------------------------------------------------------------------
21
 
lib/freemius/templates/account.php CHANGED
@@ -46,7 +46,7 @@
46
  $site = $fs->get_site();
47
  $name = $user->get_name();
48
  $license = $fs->_get_license();
49
- $is_license_foreign = ( $user->id != $license->user_id );
50
  $is_data_debug_mode = $fs->is_data_debug_mode();
51
  $is_whitelabeled = $fs->is_whitelabeled();
52
  $subscription = ( is_object( $license ) ?
@@ -91,12 +91,15 @@
91
  ) );
92
  }
93
 
94
- $payments = $fs->_fetch_payments();
 
 
95
 
96
- $show_billing = ( ! $is_whitelabeled && is_array( $payments ) && 0 < count( $payments ) );
 
97
 
98
 
99
- $has_tabs = $fs->_add_tabs_before_content();
100
 
101
  if ( $has_tabs ) {
102
  $query_params['tabs'] = 'true';
@@ -145,6 +148,7 @@
145
  $install = $fs->get_install_by_blog_id( $site_info['blog_id'] );
146
  $view_params = array(
147
  'freemius' => $fs,
 
148
  'license' => $license,
149
  'site' => $site_info,
150
  'install' => $install,
@@ -371,7 +375,7 @@
371
 
372
  $profile = array();
373
 
374
- if ( ! $is_whitelabeled ) {
375
  $profile[] = array(
376
  'id' => 'user_name',
377
  'title' => fs_text_inline( 'Name', 'name', $slug ),
@@ -737,12 +741,30 @@
737
  <div class="fs-table-body">
738
  <table class="widefat">
739
  <?php
 
 
740
  foreach ( $site_view_params as $view_params ) {
741
  fs_require_template(
742
  'account/partials/site.php',
743
  $view_params
744
  );
745
- } ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
746
  </table>
747
  </div>
748
  </div>
@@ -846,7 +868,7 @@
846
 
847
  <?php
848
  if ( $show_billing ) {
849
- $view_params = array( 'id' => $VARS['id'] );
850
  fs_require_once_template( 'account/billing.php', $view_params );
851
  fs_require_once_template( 'account/payments.php', $view_params );
852
  }
@@ -1102,4 +1124,4 @@
1102
  'module_slug' => $slug,
1103
  'module_version' => $fs->get_plugin_version(),
1104
  );
1105
- fs_require_template( 'powered-by.php', $params );
46
  $site = $fs->get_site();
47
  $name = $user->get_name();
48
  $license = $fs->_get_license();
49
+ $is_license_foreign = ( is_object( $license ) && $user->id != $license->user_id );
50
  $is_data_debug_mode = $fs->is_data_debug_mode();
51
  $is_whitelabeled = $fs->is_whitelabeled();
52
  $subscription = ( is_object( $license ) ?
91
  ) );
92
  }
93
 
94
+ $show_billing = ( ! $is_whitelabeled && ! $fs->apply_filters( 'hide_billing_and_payments_info', false ) );
95
+ if ( $show_billing ) {
96
+ $payments = $fs->_fetch_payments();
97
 
98
+ $show_billing = ( is_array( $payments ) && 0 < count( $payments ) );
99
+ }
100
 
101
 
102
+ $has_tabs = $fs->_add_tabs_before_content();
103
 
104
  if ( $has_tabs ) {
105
  $query_params['tabs'] = 'true';
148
  $install = $fs->get_install_by_blog_id( $site_info['blog_id'] );
149
  $view_params = array(
150
  'freemius' => $fs,
151
+ 'user' => $fs->get_user(),
152
  'license' => $license,
153
  'site' => $site_info,
154
  'install' => $install,
375
 
376
  $profile = array();
377
 
378
+ if ( ! $is_whitelabeled ) {
379
  $profile[] = array(
380
  'id' => 'user_name',
381
  'title' => fs_text_inline( 'Name', 'name', $slug ),
741
  <div class="fs-table-body">
742
  <table class="widefat">
743
  <?php
744
+ $current_blog_id = get_current_blog_id();
745
+
746
  foreach ( $site_view_params as $view_params ) {
747
  fs_require_template(
748
  'account/partials/site.php',
749
  $view_params
750
  );
751
+ }
752
+
753
+ /**
754
+ * It's possible for the `Freemius::switch_to_blog()` method to be called within the `site.php` template and this changes the Freemius instance's context, so this check is for restoring the previous context based on the previously retrieved site.
755
+ *
756
+ * @author Leo Fajardo (@leorw)
757
+ * @since 2.5.0
758
+ */
759
+ $current_install = $fs->get_site();
760
+
761
+ if (
762
+ is_object( $site ) &&
763
+ ( ! is_object( $current_install ) || $current_install->id != $site->id )
764
+ ) {
765
+ $fs->switch_to_blog( $current_blog_id, $site, true );
766
+ }
767
+ ?>
768
  </table>
769
  </div>
770
  </div>
868
 
869
  <?php
870
  if ( $show_billing ) {
871
+ $view_params = array( 'id' => $VARS['id'], 'payments' => $payments );
872
  fs_require_once_template( 'account/billing.php', $view_params );
873
  fs_require_once_template( 'account/payments.php', $view_params );
874
  }
1124
  'module_slug' => $slug,
1125
  'module_version' => $fs->get_plugin_version(),
1126
  );
1127
+ fs_require_template( 'powered-by.php', $params );
lib/freemius/templates/account/partials/site.php CHANGED
@@ -23,7 +23,7 @@
23
  $is_whitelabeled = $fs->is_whitelabeled();
24
  $has_paid_plan = $fs->has_paid_plan();
25
  $is_premium = $fs->is_premium();
26
- $main_user = $fs->get_user();
27
  $blog_id = $site['blog_id'];
28
 
29
  $install = $VARS['install'];
@@ -32,7 +32,7 @@
32
  $trial_plan = $fs->get_trial_plan();
33
  $free_text = fs_text_inline( 'Free', 'free', $slug );
34
 
35
- if ( $is_whitelabeled && $fs->is_delegated_connection( $blog_id ) ) {
36
  $is_whitelabeled = $fs->is_whitelabeled( true, $blog_id );
37
  }
38
  ?>
@@ -174,7 +174,7 @@
174
  <?php $row_index ++ ?>
175
  <!--/ Blog ID -->
176
 
177
- <?php if ( $is_registered && $install->user_id != $main_user->id ) : ?>
178
  <?php
179
  /**
180
  * @var FS_User $user
23
  $is_whitelabeled = $fs->is_whitelabeled();
24
  $has_paid_plan = $fs->has_paid_plan();
25
  $is_premium = $fs->is_premium();
26
+ $main_user = $VARS['user'];
27
  $blog_id = $site['blog_id'];
28
 
29
  $install = $VARS['install'];
32
  $trial_plan = $fs->get_trial_plan();
33
  $free_text = fs_text_inline( 'Free', 'free', $slug );
34
 
35
+ if ( $is_whitelabeled && is_object( $install ) && $fs->is_delegated_connection( $blog_id ) ) {
36
  $is_whitelabeled = $fs->is_whitelabeled( true, $blog_id );
37
  }
38
  ?>
174
  <?php $row_index ++ ?>
175
  <!--/ Blog ID -->
176
 
177
+ <?php if ( $install->user_id != $main_user->id ) : ?>
178
  <?php
179
  /**
180
  * @var FS_User $user
lib/freemius/templates/account/payments.php CHANGED
@@ -10,19 +10,19 @@
10
  exit;
11
  }
12
 
13
- /**
14
- * @var array $VARS
15
- * @var Freemius $fs
16
- */
17
- $fs = freemius( $VARS['id'] );
18
 
19
- $slug = $fs->get_slug();
 
 
 
20
 
21
- $payments = $fs->_fetch_payments();
22
-
23
- $show_payments = ( is_array( $payments ) && 0 < count( $payments ) );
24
 
25
- if ( $show_payments ) :
26
  ?>
27
  <div class="postbox">
28
  <div id="fs_payments">
@@ -56,4 +56,3 @@
56
  </div>
57
  </div>
58
  <?php
59
- endif;
10
  exit;
11
  }
12
 
13
+ /**
14
+ * @var array $VARS
15
+ * @var Freemius $fs
16
+ */
17
+ $fs = freemius( $VARS['id'] );
18
 
19
+ /**
20
+ * @var FS_Payment[] $payments
21
+ */
22
+ $payments = $VARS['payments'];
23
 
24
+ $slug = $fs->get_slug();
 
 
25
 
 
26
  ?>
27
  <div class="postbox">
28
  <div id="fs_payments">
56
  </div>
57
  </div>
58
  <?php
 
lib/freemius/templates/admin-notice.php CHANGED
@@ -45,6 +45,9 @@
45
  case 'promotion':
46
  echo 'updated promotion';
47
  break;
 
 
 
48
  case 'update':
49
  // echo 'update-nag update';
50
  // break;
@@ -64,7 +67,7 @@
64
  } ?>"><?php if ( ! empty( $VARS['plugin'] ) ) : ?>
65
  <label class="fs-plugin-title"><?php echo $VARS['plugin'] ?></label>
66
  <?php endif ?>
67
- <?php if ( ! empty( $VARS['sticky'] ) ) : ?>
68
  <div class="fs-close"><i class="dashicons dashicons-no"
69
  title="<?php echo esc_attr( $dismiss_text ) ?>"></i> <span><?php echo esc_html( $dismiss_text ) ?></span>
70
  </div>
45
  case 'promotion':
46
  echo 'updated promotion';
47
  break;
48
+ case 'warn':
49
+ echo 'notice notice-warning';
50
+ break;
51
  case 'update':
52
  // echo 'update-nag update';
53
  // break;
67
  } ?>"><?php if ( ! empty( $VARS['plugin'] ) ) : ?>
68
  <label class="fs-plugin-title"><?php echo $VARS['plugin'] ?></label>
69
  <?php endif ?>
70
+ <?php if ( ! empty( $VARS['sticky'] ) && ( ! isset( $VARS['dismissible'] ) || false !== $VARS['dismissible'] ) ) : ?>
71
  <div class="fs-close"><i class="dashicons dashicons-no"
72
  title="<?php echo esc_attr( $dismiss_text ) ?>"></i> <span><?php echo esc_html( $dismiss_text ) ?></span>
73
  </div>
lib/freemius/templates/clone-resolution-js.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 2.5.0
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+ ?>
13
+ <script type="text/javascript">
14
+ ( function( $ ) {
15
+ var $errorMessage = null;
16
+
17
+ $( document ).ready( function() {
18
+ var $cloneResolutionNotice = $( 'div[data-id="clone_resolution_options_notice"], div[data-id="temporary_duplicate_notice"]' );
19
+
20
+ if ( 0 === $cloneResolutionNotice.length ) {
21
+ return;
22
+ }
23
+
24
+ $errorMessage = $cloneResolutionNotice.find( '#fs_clone_resolution_error_message' );
25
+
26
+ /**
27
+ * Triggers an AJAX request when the license activation link or any of the buttons on the clone resolution options notice is clicked. The AJAX request will then handle the action the user has chosen.
28
+ */
29
+ $cloneResolutionNotice.on( 'click', '.button, #fs_temporary_duplicate_license_activation_link', function( evt ) {
30
+ evt.preventDefault();
31
+
32
+ var $this = $( this ),
33
+ $body = $( 'body' ),
34
+ cursor = $body.css( 'cursor' );
35
+
36
+ if ( $this.hasClass( 'disabled' ) ) {
37
+ return;
38
+ }
39
+
40
+ var beforeUnload = function() {
41
+ return '<?php fs_esc_js_echo_inline( 'Please wait', 'please-wait' ) ?>';
42
+ };
43
+
44
+ $.ajax( {
45
+ // Get the parent options container from the child as `$cloneResolutionNotice` can have different AJAX URLs if both the manual clone resolution options and temporary duplicate notices are shown (for different subsites in a multisite network).
46
+ url : $this.parents( '.fs-clone-resolution-options-container' ).data( 'ajax-url' ),
47
+ method : 'POST',
48
+ data : {
49
+ action : '<?php echo $VARS['ajax_action'] ?>',
50
+ security : '<?php echo wp_create_nonce( $VARS['ajax_action'] ) ?>',
51
+ clone_action: $this.data( 'clone-action' )
52
+ },
53
+ beforeSend: function() {
54
+ $body.css( { cursor: 'wait' } );
55
+
56
+ $cloneResolutionNotice.find( '.button' ).addClass( 'disabled' );
57
+
58
+ $( window ).on( 'beforeunload', beforeUnload );
59
+ },
60
+ success : function( resultObj ) {
61
+ $( window ).off( 'beforeunload', beforeUnload );
62
+
63
+ if ( resultObj.data.redirect_url && '' !== resultObj.data.redirect_url ) {
64
+ window.location = resultObj.data.redirect_url;
65
+ } else {
66
+ window.location.reload();
67
+ }
68
+ },
69
+ error : function() {
70
+ $body.css( { cursor: cursor } );
71
+ $cloneResolutionNotice.find( '.button' ).removeClass( 'disabled' );
72
+ }
73
+ } );
74
+ } );
75
+ } );
76
+ } )( jQuery );
77
+ </script>
lib/freemius/templates/connect.php CHANGED
@@ -373,7 +373,7 @@
373
  'icon-class' => 'dashicons dashicons-admin-settings',
374
  'tooltip' => ( $require_license_key ? sprintf( $fs->get_text_inline( 'So you can manage and control your license remotely from the User Dashboard.', 'permissions-site_tooltip' ), $fs->get_module_type() ) : '' ),
375
  'label' => $fs->get_text_inline( 'Your Site Overview', 'permissions-site' ),
376
- 'desc' => $fs->get_text_inline( 'Site URL, WP version, PHP info', 'permissions-site_desc' ),
377
  'priority' => 10,
378
  );
379
 
@@ -435,11 +435,12 @@
435
  <?php endif ?>
436
  <ul><?php
437
  foreach ( $permissions as $id => $permission ) : ?>
 
438
  <li id="fs-permission-<?php echo esc_attr( $id ); ?>"
439
- class="fs-permission fs-<?php echo esc_attr( $id ); ?>">
440
  <i class="<?php echo esc_attr( $permission['icon-class'] ); ?>"></i>
441
  <?php if ( isset( $permission['optional'] ) && true === $permission['optional'] ) : ?>
442
- <div class="fs-switch fs-small fs-round fs-<?php echo (! isset( $permission['default'] ) || true === $permission['default'] ) ? 'on' : 'off' ?>">
443
  <div class="fs-toggle"></div>
444
  </div>
445
  <?php endif ?>
@@ -605,6 +606,11 @@
605
  updatePrimaryCtaText( actionType );
606
  });
607
 
 
 
 
 
 
608
  $sitesListContainer.delegate( '.action', 'click', function( evt ) {
609
  var $this = $( evt.target );
610
  if ( $this.hasClass( 'selected' ) ) {
@@ -882,6 +888,8 @@
882
  $(this)
883
  .toggleClass( 'fs-on' )
884
  .toggleClass( 'fs-off' );
 
 
885
  });
886
 
887
  if (requireLicenseKey) {
373
  'icon-class' => 'dashicons dashicons-admin-settings',
374
  'tooltip' => ( $require_license_key ? sprintf( $fs->get_text_inline( 'So you can manage and control your license remotely from the User Dashboard.', 'permissions-site_tooltip' ), $fs->get_module_type() ) : '' ),
375
  'label' => $fs->get_text_inline( 'Your Site Overview', 'permissions-site' ),
376
+ 'desc' => $fs->get_text_inline( 'Site URL, WP version, PHP version', 'permissions-site_desc' ),
377
  'priority' => 10,
378
  );
379
 
435
  <?php endif ?>
436
  <ul><?php
437
  foreach ( $permissions as $id => $permission ) : ?>
438
+ <?php $is_permission_on = ( ! isset( $permission['default'] ) || true === $permission['default'] ); ?>
439
  <li id="fs-permission-<?php echo esc_attr( $id ); ?>"
440
+ class="fs-permission fs-<?php echo esc_attr( $id ); ?><?php echo ( ! $is_permission_on ) ? ' fs-disabled' : ''; ?>">
441
  <i class="<?php echo esc_attr( $permission['icon-class'] ); ?>"></i>
442
  <?php if ( isset( $permission['optional'] ) && true === $permission['optional'] ) : ?>
443
+ <div class="fs-switch fs-small fs-round fs-<?php echo $is_permission_on ? 'on' : 'off' ?>">
444
  <div class="fs-toggle"></div>
445
  </div>
446
  <?php endif ?>
606
  updatePrimaryCtaText( actionType );
607
  });
608
 
609
+ $sitesListContainer.delegate( 'td:not(:first-child)', 'click', function() {
610
+ // If a site row is clicked, trigger a click on the checkbox.
611
+ $( this ).parent().find( 'td:first-child input' ).click();
612
+ } );
613
+
614
  $sitesListContainer.delegate( '.action', 'click', function( evt ) {
615
  var $this = $( evt.target );
616
  if ( $this.hasClass( 'selected' ) ) {
888
  $(this)
889
  .toggleClass( 'fs-on' )
890
  .toggleClass( 'fs-off' );
891
+
892
+ $( this ).parent().toggleClass( 'fs-disabled' );
893
  });
894
 
895
  if (requireLicenseKey) {
lib/freemius/templates/debug.php CHANGED
@@ -386,9 +386,6 @@
386
  </thead>
387
  <tbody>
388
  <?php foreach ( $sites_map as $slug => $sites ) : ?>
389
- <?php if ( ! is_array( $sites ) ) {
390
- $sites = array( $sites );
391
- } ?>
392
  <?php foreach ( $sites as $site ) : ?>
393
  <tr>
394
  <td><?php echo $site->id ?></td>
386
  </thead>
387
  <tbody>
388
  <?php foreach ( $sites_map as $slug => $sites ) : ?>
 
 
 
389
  <?php foreach ( $sites as $site ) : ?>
390
  <tr>
391
  <td><?php echo $site->id ?></td>
lib/freemius/templates/forms/email-address-update.php CHANGED
@@ -5,7 +5,7 @@
5
  * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
  *
7
  * @author Leo Fajardo (@leorw)
8
- * @since 2.4.3
9
  */
10
 
11
  if ( ! defined( 'ABSPATH' ) ) {
5
  * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
  *
7
  * @author Leo Fajardo (@leorw)
8
+ * @since 2.5.0
9
  */
10
 
11
  if ( ! defined( 'ABSPATH' ) ) {
lib/freemius/templates/forms/license-activation.php CHANGED
@@ -59,23 +59,51 @@
59
  if ( $is_network_activation ) {
60
  $all_sites = Freemius::get_sites();
61
 
 
 
 
62
  foreach ( $all_sites as $site ) {
63
  $site_details = $fs->get_site_info( $site );
64
 
 
 
 
 
65
  $blog_id = Freemius::get_site_blog_id( $site );
66
  $install = $fs->get_install_by_blog_id($blog_id);
67
 
68
- if ( is_object( $install ) && FS_Plugin_License::is_valid_id( $install->license_id ) ) {
69
- $site_details['license_id'] = $install->license_id;
70
- }
 
71
 
72
- $sites_details[] = $site_details;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  }
74
 
75
  if ( $is_network_activation ) {
76
  $vars = array(
77
  'id' => $fs->get_id(),
78
- 'sites' => $sites_details,
79
  'require_license_key' => true
80
  );
81
 
59
  if ( $is_network_activation ) {
60
  $all_sites = Freemius::get_sites();
61
 
62
+ $subsite_data_by_install_id = array();
63
+ $install_url_by_install_id = array();
64
+
65
  foreach ( $all_sites as $site ) {
66
  $site_details = $fs->get_site_info( $site );
67
 
68
+ if ( FS_Clone_Manager::instance()->is_temporary_duplicate_by_blog_id( $site_details['blog_id'] ) ) {
69
+ continue;
70
+ }
71
+
72
  $blog_id = Freemius::get_site_blog_id( $site );
73
  $install = $fs->get_install_by_blog_id($blog_id);
74
 
75
+ if ( is_object( $install ) ) {
76
+ if ( isset( $subsite_data_by_install_id[ $install->id ] ) ) {
77
+ $clone_subsite_data = $subsite_data_by_install_id[ $install->id ];
78
+ $clone_install_url = $install_url_by_install_id[ $install->id ];
79
 
80
+ if (
81
+ /**
82
+ * If we already have an install with the same URL as the subsite it's stored in, skip the current subsite. Otherwise, replace the existing install's data with the current subsite's install's data if the URLs match.
83
+ *
84
+ * @author Leo Fajardo (@leorw)
85
+ * @since 2.5.0
86
+ */
87
+ fs_strip_url_protocol( untrailingslashit( $clone_install_url ) ) === fs_strip_url_protocol( untrailingslashit( $clone_subsite_data['url'] ) ) ||
88
+ fs_strip_url_protocol( untrailingslashit( $install->url ) ) !== fs_strip_url_protocol( untrailingslashit( $site_details['url'] ) )
89
+ ) {
90
+ continue;
91
+ }
92
+ }
93
+
94
+ if ( FS_Plugin_License::is_valid_id( $install->license_id ) ) {
95
+ $site_details['license_id'] = $install->license_id;
96
+ }
97
+
98
+ $subsite_data_by_install_id[ $install->id ] = $site_details;
99
+ $install_url_by_install_id[ $install->id ] = $install->url;
100
+ }
101
  }
102
 
103
  if ( $is_network_activation ) {
104
  $vars = array(
105
  'id' => $fs->get_id(),
106
+ 'sites' => array_values( $subsite_data_by_install_id ),
107
  'require_license_key' => true
108
  );
109
 
lib/freemius/templates/powered-by.php CHANGED
@@ -33,12 +33,12 @@
33
 
34
  $fs = freemius( $VARS['module_id'] );
35
 
36
- wp_enqueue_script( 'jquery' );
37
- wp_enqueue_script( 'json2' );
38
- fs_enqueue_local_script( 'postmessage', 'nojquery.ba-postmessage.min.js' );
39
- fs_enqueue_local_script( 'fs-postmessage', 'postmessage.js' );
40
- ?>
41
- <?php if ( ! $fs->is_whitelabeled() ) : ?>
42
  <div id="pframe"></div>
43
  <script type="text/javascript">
44
  (function ($) {
@@ -58,4 +58,4 @@
58
  });
59
  })(jQuery);
60
  </script>
61
- <?php endif ?>
33
 
34
  $fs = freemius( $VARS['module_id'] );
35
 
36
+ if ( ! $fs->is_whitelabeled() && ! $fs->apply_filters( 'hide_freemius_powered_by', false ) ) {
37
+ wp_enqueue_script( 'jquery' );
38
+ wp_enqueue_script( 'json2' );
39
+ fs_enqueue_local_script( 'postmessage', 'nojquery.ba-postmessage.min.js' );
40
+ fs_enqueue_local_script( 'fs-postmessage', 'postmessage.js' );
41
+ ?>
42
  <div id="pframe"></div>
43
  <script type="text/javascript">
44
  (function ($) {
58
  });
59
  })(jQuery);
60
  </script>
61
+ <?php } ?>
lib/freemius/templates/pricing.php CHANGED
@@ -137,7 +137,7 @@
137
  ),
138
  'selector' => '#fs_pricing_wrapper',
139
  'unique_affix' => $fs->get_unique_affix(),
140
- 'show_annual_in_monthly' => $fs->apply_filter( 'pricing/show_annual_in_monthly', true ),
141
  ), $query_params );
142
 
143
  wp_add_inline_script( 'freemius-pricing', 'Freemius.pricing.new( ' . json_encode( $pricing_config ) . ' )' );
137
  ),
138
  'selector' => '#fs_pricing_wrapper',
139
  'unique_affix' => $fs->get_unique_affix(),
140
+ 'show_annual_in_monthly' => $fs->apply_filters( 'pricing/show_annual_in_monthly', true ),
141
  ), $query_params );
142
 
143
  wp_add_inline_script( 'freemius-pricing', 'Freemius.pricing.new( ' . json_encode( $pricing_config ) . ' )' );
lib/wp-content-aware-engine/assets/css/condition_groups.css CHANGED
@@ -1,6 +1,6 @@
1
  /*!
2
- * @package WP Content Aware Engine
3
  * @author Joachim Jensen <joachim@dev.institute>
4
  * @license GPLv3
5
- * @copyright 2020 by Joachim Jensen
6
- */.wpca-pull-left{float:left}.rtl .wpca-pull-left,.wpca-pull-right{float:right}.rtl .wpca-pull-right{float:left}.wpca-alert{position:fixed;top:0;font-weight:700;color:#fff;font-size:1.3em;width:100%}.wpca-alert .wpca-error,.wpca-alert .wpca-success{box-sizing:border-box;text-align:center;box-shadow:0 1px 4px rgba(0,0,0,.3),inset 0 0 0 3px rgba(0,0,0,.1);padding:18px 30px}.wpca-alert .wpca-success{background-color:#46b450}.wpca-alert .wpca-error{background-color:#ff7058}@media (min-width:601px){.wpca-alert{width:auto;top:auto;bottom:30px;right:30px}.wpca-error,.wpca-success{min-width:320px;position:relative;border-radius:40px}.rtl .wpca-alert{left:30px;right:auto}}#cas-groups{padding:6px 0 0}#cas-groups .button-small .dashicons{vertical-align:text-top}#cas-groups>ul{position:relative;margin:0;transition:all 1s ease;max-height:9999px;height:auto}.cas-group-body,.cas-group-new{box-sizing:border-box;border-width:1px;border-style:solid;border-color:#ccd0d4;background-color:#f1f1f1;overflow:hidden;position:relative;margin-bottom:0;border-radius:4px;box-shadow:0 1px 1px 1px rgba(0,0,0,.04)}.cas-group-body .cas-group-cell{padding:10px;background-color:#fff;border-top:1px solid #ccd0d4}.cas-group-body .cas-group-actions{padding:5px;overflow:hidden;vertical-align:middle;color:#888}.cas-group-body .cas-group-actions .dashicons{vertical-align:middle}.cas-group-body .cas-group-actions .spinner{float:none}.cas-group-body .cas-group-options{overflow:hidden;margin:0}.cas-group-body .cas-group-options li{overflow:hidden;margin:0;padding:8px 5px;border-top:#ddd 1px solid}.cas-group-actions .wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection{height:26px;font-size:11px}.cas-group-actions .wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection .select2-selection__arrow{height:26px}.cas-group-actions .wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection .select2-selection__rendered{line-height:26px}.wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection{background-color:#f3f5f6;border-color:#0071a1;padding:0 6px 1px;border-radius:3px;vertical-align:top}.wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection:hover{border-color:#016087;background-color:#f1f1f1}.wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection .select2-selection__placeholder{color:#0071a1}.cas-group-single{margin:0}.cas-condition:first-of-type>.cas-group-sep,.cas-group-single:first-of-type>.cas-group-sep{display:none}.cas-group-single:first-of-type>.cas-group-sep.wpca-group-negate{display:table}.cas-group-single:first-of-type>.cas-group-sep.wpca-group-negate:before{display:none}.cas-group-sep{display:table;margin:0 auto;color:#fff;text-transform:uppercase;font-size:.8em;font-weight:700;line-height:1;clear:both;direction:ltr}.cas-group-sep:after,.cas-group-sep:before{position:relative;display:block;content:'';width:2px;height:8px;margin:0 auto;background-color:#ccd0d4}.cas-group-sep span{background:#777;display:inline-block;padding:3px 5px 4px;border-radius:4px;vertical-align:middle}.cas-group-body .cas-group-sep{display:block;overflow:hidden;text-align:center;margin:5px -10px}.cas-group-body .cas-group-sep:after,.cas-group-body .cas-group-sep:before{background-color:transparent;border-top:1px solid #eee;content:"";display:inline-block;height:1px;position:relative;vertical-align:middle;width:50%}.cas-group-body .cas-group-sep:before{right:0;margin-left:-50%}.cas-group-body .cas-group-sep:after{left:0;margin-right:-50%}.cas-group-label{font-weight:700;margin:0 0 6px}.wpca-condition-remove{cursor:pointer;margin:0 8px 0 0;color:#aaa;transition:color .3s ease}.wpca-condition-remove:hover{color:#444}.cas-group-new{background-color:#fff}.cas-group-new>div:first-of-type{background-color:#f1f1f1;border-right:#ccd0d4 1px solid}.cas-group-new>div{padding:5px;display:inline-block;vertical-align:middle}.cas-group-new a{padding:0 10px}label.cae-toggle{vertical-align:top}label.cae-toggle input{display:none}label.cae-toggle .cae-toggle-bar{overflow:hidden;cursor:pointer;position:relative;padding-right:34px;height:22px;line-height:22px;display:inline-block;vertical-align:middle}label.cae-toggle .cae-toggle-bar:before{top:0;right:0;position:absolute;display:inline-block;width:34px;content:"";padding:0;height:inherit;line-height:inherit;border-radius:22px;background-color:#bbb;box-shadow:0 0 2px rgba(0,0,0,.2) inset;transition:background-color .2s ease-in}label.cae-toggle .cae-toggle-bar:after{content:"";display:block;width:18px;height:18px;margin:0;background:#FFF;position:absolute;top:2px;right:14px;border-radius:22px;transition:right .2s ease-in 0s;box-shadow:0 1px 2px rgba(0,0,0,.2)}label.cae-toggle input:checked+.cae-toggle-bar:before{background-color:#7ad03a}label.cae-toggle input:checked+.cae-toggle-bar:after{right:2px}label.cae-toggle input:disabled+.cae-toggle-bar:after{background:rgba(255,255,255,.5)}@media (min-width:783px){#cas-groups .button-small .dashicons{font-size:14px;height:14px;width:14px}.cas-group-body .cas-group-options input[type=number],.cas-group-body .cas-group-options input[type=text],.cas-group-body .cas-group-options select{font-size:.9em;min-height:24px}}@media (min-width:1041px){.cas-group-label{float:left;width:220px;margin:5px 0 0}.wpca-group-description{float:left}.cas-group-input{margin-left:221px;white-space:nowrap;overflow:hidden}.cas-group-body .cas-group-options li{padding:8px 10%}.rtl .cas-group-label{float:right}.rtl .cas-group-input{margin-right:221px;margin-left:auto}}.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;-ms-user-select:none;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir=rtl] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;-ms-user-select:none;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:#fff;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;-ms-user-select:none;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0!important;clip:rect(0 0 0 0)!important;-webkit-clip-path:inset(50%)!important;clip-path:inset(50%)!important;height:1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;white-space:nowrap!important}.select2-container--wpca.select2-container .select2-selection--single{background-color:#fff;border:1px solid #ddd;border-radius:0}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:700}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent;border-style:solid;border-width:5px 4px 0;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--single .select2-selection__clear{float:left}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--wpca.select2-container.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--wpca.select2-container.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--wpca.select2-container.select2-container--open .select2-selection--single{border:1px solid #5b9dd9}.select2-container--wpca.select2-container.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888;border-width:0 4px 5px}.select2-container--wpca.select2-container .select2-selection--multiple{background-color:#fff;color:#444;border:1px solid #ddd;border-radius:0;cursor:text;min-height:32px;line-height:1}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px 3px;width:100%}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:700;margin-top:5px;margin-right:10px}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__choice{background-color:#bfe7f1;color:#23282d;border:1px solid #439eb4;border-radius:2px;cursor:default;float:left;margin:4px 4px 0 0;padding:2px 6px 4px;max-width:100%;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;box-shadow:0 1px 0 0 rgba(255,255,255,.9) inset,0 1px 1px rgba(0,0,0,.1)}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__choice__remove{color:#439eb4;cursor:pointer;font-size:16px;display:inline-block;font-weight:700;margin-right:6px}.select2-container--wpca .select2-results__option[aria-selected=true]:not([data-selected=false]),.select2-container--wpca .select2-results__option[data-selected=true],.select2-container--wpca.select2-container.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__choice__remove:hover{color:#444}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-search--inline,.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-selection__choice,.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-selection__placeholder{float:right}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-selection__choice{margin:4px 0 0 4px}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--wpca.select2-container.select2-container--focus .select2-selection--multiple{border:1px solid #5b9dd9;background-color:#fafafa;outline:0}.select2-container--wpca.select2-container.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--wpca.select2-container .select2-dropdown{overflow:hidden;border-color:#5b9dd9}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-1{padding-left:10px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-2{padding-left:20px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-3{padding-left:30px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-4{padding-left:40px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-5{padding-left:50px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-6{padding-left:60px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-7{padding-left:70px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-8{padding-left:80px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-9{padding-left:90px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-10{padding-left:100px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-1{padding-right:10px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-2{padding-right:20px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-3{padding-right:30px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-4{padding-right:40px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-5{padding-right:50px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-6{padding-right:60px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-7{padding-right:70px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-8{padding-right:80px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-9{padding-right:90px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-10{padding-right:100px}.select2-container--wpca.select2-container .select2-dropdown.select2-dropdown--below{box-shadow:0 2px 5px rgba(0,0,0,.15)}.select2-container--wpca.select2-container .select2-dropdown.select2-dropdown--above{box-shadow:0 -2px 5px rgba(0,0,0,.15)}.select2-container--wpca.select2-container--open.select2-container--above .select2-selection--multiple,.select2-container--wpca.select2-container--open.select2-container--above .select2-selection--single{border-top-left-radius:0;border-top-right-radius:0;border-top:1px solid #ddd}.select2-container--wpca.select2-container--open.select2-container--below .select2-selection--multiple,.select2-container--wpca.select2-container--open.select2-container--below .select2-selection--single{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:1px solid #ddd}.select2-container--wpca .select2-search--dropdown .select2-search__field{border:1px solid #5b9dd9;line-height:normal}.select2-container--wpca .select2-search--inline{width:auto;margin:0;z-index:1030}.select2-container--wpca .select2-search--inline .select2-search__field{font-family:inherit;padding:0;background:0 0!important;min-height:auto;border:none;outline:0;box-shadow:none!important;-webkit-appearance:textfield;margin-top:6px!important;line-height:normal}.select2-container--wpca .select2-search--inline .select2-search__field:not([placeholder='']){width:100%!important;margin-right:9999px}.select2-container--wpca[dir=rtl] .select2-search--inline .select2-search__field:not([placeholder='']){margin-left:9999px;margin-right:auto}.select2-container--wpca .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--wpca .select2-results__option[role=group]{padding:0}.select2-container--wpca .select2-results__option[aria-disabled=true]{color:#999}.select2-container--wpca .select2-results__option .select2-results__option{padding-left:20px;margin:0}.select2-container--wpca .select2-results__option--highlighted[aria-selected]{background-color:#f2f2f2}.select2-container--wpca .select2-results__group{cursor:default;display:block;padding:6px}
1
  /*!
2
+ * @package wp-content-aware-engine
3
  * @author Joachim Jensen <joachim@dev.institute>
4
  * @license GPLv3
5
+ * @copyright 2022 by Joachim Jensen
6
+ */.cas-group-sep,.wpca-alert{color:#fff;font-weight:700}.wpca-pull-left{float:left}.rtl .wpca-pull-left,.wpca-pull-right{float:right}.rtl .wpca-pull-right{float:left}.wpca-alert{position:fixed;top:0;font-size:1.3em;width:100%}.wpca-alert .wpca-error,.wpca-alert .wpca-success{box-sizing:border-box;text-align:center;box-shadow:0 1px 4px rgba(0,0,0,.3),inset 0 0 0 3px rgba(0,0,0,.1);padding:18px 30px}.cas-group-body,.cas-group-icon,.cas-group-new{box-shadow:0 1px 1px 1px rgba(0,0,0,.04);box-sizing:border-box}.wpca-alert .wpca-success{background-color:#46b450}.wpca-alert .wpca-error{background-color:#ff7058}@media (min-width:601px){.wpca-alert{width:auto;top:auto;bottom:30px;right:30px}.wpca-error,.wpca-success{min-width:320px;position:relative;border-radius:40px}.rtl .wpca-alert{left:30px;right:auto}}#cas-groups{padding:6px 0 0}#cas-groups .button-small .dashicons{vertical-align:text-top}#cas-groups>ul{position:relative;margin:0;transition:all 1s ease;max-height:9999px;height:auto}.cas-group-body,.cas-group-new{border-width:1px;border-style:solid;border-color:#c3c4c7;background-color:#f1f1f1;overflow:hidden;position:relative;margin-bottom:0;border-radius:4px}.cas-group-body .cas-group-cell{padding:10px;background-color:#fff;border-top:1px solid #c3c4c7}.cas-group-body .cas-group-actions{padding:5px;overflow:hidden;vertical-align:middle;color:#888}.cas-group-body .cas-group-actions .dashicons{vertical-align:middle}.cas-group-body .cas-group-actions .spinner{float:none}.cas-group-body .cas-group-options{overflow:hidden;margin:0}.cas-group-body .cas-group-options li{overflow:hidden;margin:0;padding:8px 5px;border-top:#ddd 1px solid}.cas-group-actions .wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection{height:26px;font-size:11px}.cas-group-actions .wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection .select2-selection__arrow{height:26px}.cas-group-actions .wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection .select2-selection__rendered{line-height:26px}.wpca-conditions-add+.select2-container.select2-container--wpca.select2-container--open .select2-selection{border-color:#0a4b78;background-color:#dcdcde!important;box-shadow:inset 0 2px 5px -3px #0a4b78}.wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection{background-color:#f6f7f7;border-color:#2271b1;padding:0 6px 1px;border-radius:3px;vertical-align:top}.wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection:hover{color:#0a4b78;border-color:#0a4b78;background-color:#f0f0f1}.wpca-conditions-add+.select2-container.select2-container--wpca .select2-selection .select2-selection__placeholder{color:#2271b1}.cas-group-single{margin:0}.cas-condition:first-of-type>.cas-group-sep,.cas-group-single:first-of-type>.cas-group-sep{display:none}.cas-group-single:first-of-type>.cas-group-sep.wpca-group-negate{display:table}.cas-group-single:first-of-type>.cas-group-sep.wpca-group-negate:before{display:none}.cas-group-sep{display:table;margin:0 auto;text-transform:uppercase;font-size:.8em;line-height:1;clear:both;direction:ltr}.cas-group-sep:after,.cas-group-sep:before{position:relative;display:block;content:'';width:1px;height:12px;margin:0 auto;border-left:1px dashed #777}.cas-group-sep span{background-color:#777;display:inline-block;padding:5px 7px;border-radius:15px;vertical-align:middle}.cas-group-body .cas-group-sep{display:block;overflow:hidden;text-align:center;margin:10px -10px 0}.cas-group-body .cas-group-sep:after,.cas-group-body .cas-group-sep:before{background-color:transparent;border-top:1px solid #e3e3e3;content:"";display:inline-block;height:1px;position:relative;vertical-align:middle;width:50%}.cas-group-body .cas-group-sep:before{right:0;margin-left:-50%}.cas-group-body .cas-group-sep:after{left:0;margin-right:-50%}.cas-group-label{font-weight:700;margin:0 0 10px}.wpca-condition-remove{cursor:pointer;color:#aaa;transition:color .3s ease;float:right}.wpca-condition-remove:hover{color:#b32d2e}.cas-group-new{background-color:#fff}.cas-group-new>div:first-of-type{background-color:#f1f1f1;border-right:#c3c4c7 1px solid}.cas-group-new>div{padding:5px;display:inline-block;vertical-align:middle}.cas-group-new a{padding:0 10px}.cas-group-icon{float:left;border:1px solid #e3e3e3;width:40px;height:40px;border-radius:6px;text-align:center;padding:3px;display:none}.cas-group-icon .dashicons{color:#2271b1;font-size:28px;width:100%;height:100%;line-height:34px}label.cae-toggle{vertical-align:top}label.cae-toggle input{display:none}label.cae-toggle .cae-toggle-bar{overflow:hidden;cursor:pointer;position:relative;padding-right:34px;height:22px;line-height:22px;display:inline-block;vertical-align:middle}label.cae-toggle .cae-toggle-bar:before{top:0;right:0;position:absolute;display:inline-block;width:34px;content:"";padding:0;height:inherit;line-height:inherit;border-radius:22px;background-color:#bbb;box-shadow:0 0 2px rgba(0,0,0,.2) inset;transition:background-color .2s ease-in}label.cae-toggle .cae-toggle-bar:after{content:"";display:block;width:18px;height:18px;margin:0;background:#FFF;position:absolute;top:2px;right:14px;border-radius:22px;transition:right .2s ease-in 0s;box-shadow:0 1px 2px rgba(0,0,0,.2)}label.cae-toggle input:checked+.cae-toggle-bar:before{background-color:#7ad03a}label.cae-toggle input:checked+.cae-toggle-bar:after{right:2px}label.cae-toggle input:disabled+.cae-toggle-bar:after{background:rgba(255,255,255,.5)}@media (min-width:783px){.cas-group-input,.cas-group-label{margin-left:50px}#cas-groups .button-small .dashicons{font-size:14px;height:14px;width:14px}.cas-group-icon{display:block}.cas-group-input{white-space:nowrap;overflow:hidden}.rtl .cas-group-input{margin-right:50px;margin-left:auto}.cas-group-body .cas-group-options input[type=number],.cas-group-body .cas-group-options input[type=text],.cas-group-body .cas-group-options select{font-size:.9em;min-height:24px}}@media (min-width:1041px){.cas-group-input,.cas-group-label{margin-left:50px}.wpca-group-description{float:left}.cas-group-input{white-space:nowrap;overflow:hidden}.cas-group-body .cas-group-options li{padding:8px 10%}}.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:30px;-ms-user-select:none;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir=rtl] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;-ms-user-select:none;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:#fff;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;-ms-user-select:none;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0!important;clip:rect(0 0 0 0)!important;-webkit-clip-path:inset(50%)!important;clip-path:inset(50%)!important;height:1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;white-space:nowrap!important}.select2-container--wpca.select2-container .select2-selection--single{background-color:#fff;border:1px solid #8c8f94;border-radius:4px}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:700}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--wpca.select2-container .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent;border-style:solid;border-width:5px 4px 0;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--single .select2-selection__clear{float:left}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--wpca.select2-container.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--wpca.select2-container.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--wpca.select2-container.select2-container--open .select2-selection--single{border:1px solid #5b9dd9}.select2-container--wpca.select2-container.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888;border-width:0 4px 5px}.select2-container--wpca.select2-container .select2-selection--multiple{background-color:#fff;color:#444;border:1px solid #8c8f94;border-radius:4px;cursor:text;min-height:39px;line-height:1}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 6px 4px;width:100%}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:700;margin-top:5px;margin-right:10px}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__choice{background-color:#bfe7f1;color:#23282d;border:1px solid #5b9dd9;border-radius:4px;cursor:default;float:left;margin:5px 5px 0 0;padding:4px 8px;max-width:100%;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;box-shadow:0 1px 0 0 rgba(255,255,255,.9) inset,0 1px 1px rgba(0,0,0,.1)}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__choice__remove{color:#5b9dd9;cursor:pointer;font-size:16px;display:inline-block;font-weight:700;margin:0 6px 2px 0}.select2-container--wpca .select2-results__option[aria-selected=true]:not([data-selected=false]),.select2-container--wpca .select2-results__option[data-selected=true],.select2-container--wpca.select2-container.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--wpca.select2-container .select2-selection--multiple .select2-selection__choice__remove:hover{color:#444}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-search--inline,.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-selection__choice,.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-selection__placeholder{float:right}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-selection__choice{margin:5px 0 0 5px}.select2-container--wpca.select2-container[dir=rtl] .select2-selection--multiple .select2-selection__choice__remove{margin-left:6px;margin-right:auto}.select2-container--wpca.select2-container.select2-container--focus .select2-selection--multiple{border:1px solid #5b9dd9;background-color:#fafafa;outline:0}.select2-container--wpca.select2-container.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--wpca.select2-container .select2-dropdown{overflow:hidden;border-color:#5b9dd9}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-1{padding-left:10px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-2{padding-left:20px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-3{padding-left:30px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-4{padding-left:40px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-5{padding-left:50px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-6{padding-left:60px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-7{padding-left:70px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-8{padding-left:80px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-9{padding-left:90px}.select2-container--wpca.select2-container .select2-dropdown .wpca-level-10{padding-left:100px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-1{padding-right:10px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-2{padding-right:20px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-3{padding-right:30px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-4{padding-right:40px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-5{padding-right:50px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-6{padding-right:60px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-7{padding-right:70px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-8{padding-right:80px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-9{padding-right:90px}.select2-container--wpca.select2-container .select2-dropdown[dir=rtl] .wpca-level-10{padding-right:100px}.select2-container--wpca.select2-container .select2-dropdown.select2-dropdown--below{box-shadow:0 2px 5px rgba(0,0,0,.15)}.select2-container--wpca.select2-container .select2-dropdown.select2-dropdown--above{box-shadow:0 -2px 5px rgba(0,0,0,.15)}.select2-container--wpca.select2-container--open.select2-container--above .select2-selection--multiple,.select2-container--wpca.select2-container--open.select2-container--above .select2-selection--single{border-top-left-radius:0;border-top-right-radius:0;border-top:1px solid #8c8f94}.select2-container--wpca.select2-container--open.select2-container--below .select2-selection--multiple,.select2-container--wpca.select2-container--open.select2-container--below .select2-selection--single{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:1px solid #8c8f94}.select2-container--wpca .select2-search--dropdown .select2-search__field{border:1px solid #5b9dd9;line-height:normal}.select2-container--wpca .select2-search--inline{max-width:100%;width:auto;margin:0;z-index:1030}.select2-container--wpca .select2-search--inline .select2-search__field{font-family:inherit;padding:0;background:0 0!important;min-height:auto;max-width:100%;border:none;outline:0;box-shadow:none!important;-webkit-appearance:textfield;margin-top:10px!important;line-height:normal}.select2-container--wpca .select2-search--inline .select2-search__field:not([placeholder='']){width:100%!important;margin-right:9999px}.select2-container--wpca[dir=rtl] .select2-search--inline .select2-search__field:not([placeholder='']){margin-left:9999px;margin-right:auto}.select2-container--wpca .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--wpca .select2-results__option[role=group]{padding:0}.select2-container--wpca .select2-results__option[aria-disabled=true]{color:#999}.select2-container--wpca .select2-results__option .select2-results__option{padding-left:20px;margin:0}.select2-container--wpca .select2-results__option--highlighted[aria-selected]{background-color:#f2f2f2}.select2-container--wpca .select2-results__group{cursor:default;display:block;padding:6px}
lib/wp-content-aware-engine/assets/js/condition_groups.min.js CHANGED
@@ -2,6 +2,6 @@
2
  * @package wp-content-aware-engine
3
  * @author Joachim Jensen <joachim@dev.institute>
4
  * @license GPLv3
5
- * @copyright 2021 by Joachim Jensen
6
  */
7
- var CAE=CAE||{};!function($,CAE){"use strict";CAE.settings={views:{}},CAE.Models={},CAE.Models.Alert=Backbone.Model.extend({defaults:{text:"",success:!0},sync:function(){return!1},url:"",reset:function(){this.set(this.defaults)}}),CAE.Models.Condition=Backbone.Model.extend({unsaved:{prompt:WPCA.unsaved,unloadWindowPrompt:!0},defaults:{module:null,label:"",placeholder:"",values:[],default_value:null},initialize:function(){this.startTracking(),this.on("destroy",this.stopTracking,this)},sync:function(){return!1},url:""}),CAE.Models.Group=Backbone.Model.extend({unsaved:{prompt:WPCA.unsaved,unloadWindowPrompt:!0},defaults:function(){var e=WPCA.meta_default;return e.id=null,e.status="wpca_or",e.exposure=1,e},initialize:function(){this.startTracking(),this.on("destroy",this.stopTracking,this),this.conditions||(this.conditions=new CAE.Models.ConditionCollection)},parse:function(e){var t=[];if(_.has(e,"conditions")){for(var n in e.conditions)if(e.conditions.hasOwnProperty(n)){var i,o=[],s=e.conditions[n];for(i in s.data)s.data.hasOwnProperty(i)&&o.push({text:"object"==typeof s.data[i]?s.data[i].text:s.data[i],id:i});delete s.data,s.module=n,s.values=o,t.push(s)}delete e.conditions}return this.conditions=new CAE.Models.ConditionCollection(t),e},sync:function(){return!1},url:""}),CAE.Models.GroupCollection=Backbone.Collection.extend({model:CAE.Models.Group,parse:function(e){return e}}),CAE.Models.ConditionCollection=Backbone.Collection.extend({model:CAE.Models.Condition}),CAE.Views={},CAE.Views.Alert=Backbone.Epoxy.View.extend({bindings:"data-vm",tagName:"div",className:"wpca-alert",template:"<div data-vm=\"classes:{'wpca-success':success,'wpca-error':not(success)},text:text\"></div>",timer:4e3,success:function(e){this.model.set({text:e,success:!0})},failure:function(e){this.model.set({text:e,success:!1})},dismiss:function(){var e=this;this.$el.fadeOut("slow",function(){e.model.reset()})},initialize:function(){this.listenTo(this.model,"change:text",this.show),this.$el.appendTo("body").hide().html(this.template)},show:function(){var e;""!==this.model.get("text")&&(this.$el.fadeIn("slow"),e=this,setTimeout(function(){e.dismiss()},this.timer))}}),CAE.Views.Condition=Backbone.Epoxy.View.extend({bindings:"data-vm",model:CAE.Models.Condition,tagName:"div",className:"cas-condition",templateName:"#wpca-template-condition",events:{"click .js-wpca-condition-remove":"removeModel"},initialize:function(){this.listenTo(this.model,"destroy",this.remove);var e=$(this.templateName);e.length?(this.template=e.html(),this.$el.append(this.template),this.createSuggestInput()):this.model.destroy()},removeModel:function(e){var t=this;this.$el.slideUp(300,function(){t.model.destroy()})},createSuggestInput:function(){var n,e,t,i=this.$el.find(".js-wpca-suggest");i.length&&(n=this.model,e=this.model.get("values"),t=$("<div></div>").html(n.get("placeholder")).text(),i.select2({cachedResults:{},quietMillis:400,searchTimer:null,type:n.get("module"),theme:"wpca",dir:WPCA.text_direction,placeholder:t,minimumInputLength:0,closeOnSelect:!0,width:"100%",language:{noResults:function(){return WPCA.noResults},searching:function(){return WPCA.searching+"..."},loadingMore:function(){return WPCA.loadingMore+"..."}},nextSearchTerm:function(e,t){return t},templateResult:function(e){return e.level?$('<span class="wpca-level-'+e.level+'">'+e.text+"</span>"):e.text},data:e,dataAdapter:l.wpcaDataAdapter,ajax:{}}).on("select2:selecting",function(e){i.data("forceOpen",!0)}).on("select2:closing",function(e){i.data("forceOpen")&&(e.preventDefault(),i.data("forceOpen",!1))}),e.length&&i.val(_.map(e,function(e){return e.id})).trigger("change"),i.on("change",function(e){var t=i.select2("data");n.set("values",t)}))}}),CAE.Views.Group=Backbone.Epoxy.View.extend({bindings:"data-vm",model:CAE.Models.Group,tagName:"li",className:"cas-group-single",template:$("#wpca-template-group").html(),itemView:function(e){return new(CAE.Views[e.model.get("module")]?CAE.Views[e.model.get("module")]:CAE.Views.Condition)(e)},events:{"click .js-wpca-save-group":"saveGroup","click .js-wpca-options":"showOptions"},computeds:{statusNegated:{deps:["status"],get:function(e){return"negated"==e},set:function(e){this.setBinding("status",e?"negated":"wpca_or")}},statusExcept:{deps:["status"],get:function(e){return"wpca_except"==e},set:function(e){this.setBinding("status",e?"wpca_except":"wpca_or")}},statusLabel:function(){switch(this.getBinding("status")){case"wpca_except":return WPCA.condition_except;case"negated":return WPCA.condition_not;default:return WPCA.condition_or}}},bindingFilters:{int:{get:function(e){return e?1:0},set:function(e){return e?1:0}},binary:{get:function(e){return e?1:0},set:function(e){return e?1:0}},hasModule:function(e){for(var t={},n=1;n<arguments.length;n++)t[arguments[n]]=!0;return e.filter(function(e){return t.hasOwnProperty(e.get("module"))}).length==arguments.length-1},hasAnyModule:function(e){for(var t={},n=1;n<arguments.length;n++)t[arguments[n]]=!0;return!!e.find(function(e){return t.hasOwnProperty(e.get("module"))})}},initialize:function(){this.collection=this.model.conditions,this.$el.hide().html(this.template).fadeIn(300),this.listenTo(this.model,"destroy",this.remove),this.listenTo(this.model,"unsavedChanges",this.saveChanges),this.listenTo(this.model.conditions,"unsavedChanges",this.saveChanges),this.listenTo(this.model.conditions,"add remove",this.saveAddRemove);var t=this,n=$(".js-wpca-add-and",this.$el);n.select2({theme:"wpca",placeholder:"+ "+WPCA.newCondition,minimumInputLength:0,closeOnSelect:!0,allowClear:!1,width:"resolve",matcher:l.wpcaModuleMatcher,nextSearchTerm:function(e,t){return t},data:WPCA.conditions}).on("select2:select",function(e){e=e.params.data;t.model.conditions.findWhere({module:e.id})||(e=new CAE.Models.Condition({module:e.id,label:e.text,placeholder:e.placeholder,default_value:e.default_value}),t.model.conditions.add(e)),n.val(null).trigger("change")})},showOptions:function(e){$(e.delegateTarget).find(".cas-group-options").slideToggle(200),$(e.currentTarget).toggleClass("active")},saveChanges:function(e,t){e&&i.start(this)},saveAddRemove:function(e,t,n){t.length?n.add?""!==e.get("default_value")&&i.start(this):this.model.get("id")&&i.start(this):(i.clear(this),this.model.get("id")?this.saveGroup():this.removeModel())},removeModel:function(){var e=this;this.$el.slideUp(400,function(){e.model.destroy()})},saveGroup:function(e){var i=this.$el.find(".spinner"),o=this.$el.find(".js-wpca-save-group"),t=this,n=_.clone(this.model.attributes);n.action="wpca/add-rule",n.token=l.nonce,n.current_id=l.sidebarID,n.post_type=WPCA.post_type,n.conditions={},this.model.conditions.each(function(e){e.get("values").length?n.conditions[e.get("module")]=e.get("values").map(function(e){return e.id}):""!==e.get("default_value")&&(n.conditions[e.get("module")]=[e.get("default_value")])}),o.attr("disabled",!0),i.addClass("is-active"),$.ajax({url:ajaxurl,data:n,dataType:"JSON",type:"POST",success:function(e){l.alert.success(e.message),e.removed?t.removeModel():e.new_post_id&&t.model.set("id",e.new_post_id,{silent:!0}),e.removed||(o.hide(),i.removeClass("is-active"),t.model.restartTracking(),t.model.conditions.each(function(e){e.restartTracking()}))},error:function(e,t,n){o.attr("disabled",!1).show(),i.removeClass("is-active"),l.alert.failure(e.responseJSON.data)}})},slideRemove:function(){this.$el.slideUp(400,function(){this.remove()})}}),CAE.Views.GroupCollection=Backbone.Epoxy.View.extend({bindings:"data-vm",el:"#cas-groups",collection:CAE.Models.GroupCollection,events:{"click .js-wpca-add-quick":"addGroupQuick","click .js-wpca-save":"saveAll"},conditionsById:{},initialize:function(){var n=this;this.conditionsById=_.chain(WPCA.conditions).pluck(["children"]).flatten().indexBy("id").value();var i=$(".js-wpca-add-or",this.$el);i.select2({theme:"wpca",placeholder:"+ "+WPCA.newGroup,minimumInputLength:0,closeOnSelect:!0,allowClear:!1,width:"auto",matcher:l.wpcaModuleMatcher,nextSearchTerm:function(e,t){return t},data:WPCA.conditions}).on("select2:select",function(e){var t=e.params.data,e=new CAE.Models.Group,t=new CAE.Models.Condition({module:t.id,label:t.text,placeholder:t.placeholder,default_value:t.default_value});n.collection.add(e),e.conditions.add(t),i.val(null).trigger("change")})},itemView:function(e){return new CAE.Views.Group(e)},addGroupQuick:function(e){e.preventDefault();var t,n,i=$(e.currentTarget).data("config"),o=new CAE.Models.Group;for(t in o.set(i.options),this.collection.add(o),i.modules)this.conditionsById.hasOwnProperty(i.modules[t])&&(n=this.conditionsById[i.modules[t]],n=new CAE.Models.Condition({module:n.id,label:n.text,placeholder:n.placeholder,default_value:n.default_value}),o.conditions.add(n))}}),$.fn.select2.amd.require(["select2/selection/search"],function(e){e.prototype.searchRemoveChoice=function(e,t){this.trigger("unselect",{data:t}),this.$search.val(""),this.handleSearch()}},null,!0),$.fn.select2.amd.require(["select2/results"],function(e){e.prototype.ensureHighlightVisible=function(){this.$results.resize()}},null,!0),$.fn.select2.amd.define("select2/wpca/conditionData",["select2/data/array","select2/utils"],function(e,t){function n(e,t){n.__super__.constructor.call(this,e,t)}return t.Extend(n,e),n.prototype.query=function(n,i){n.term=n.term||"";var o=this.options.options,s=o.cachedResults[n.term],a=n.page||1;if(s&&s.page>=a){if(!(1<a))return void i({results:s.items,pagination:{more:s.more}});a=s.page}clearTimeout(o.searchTimer),o.searchTimer=setTimeout(function(){$.ajax({url:ajaxurl,data:{search:n.term,paged:a,limit:20,action:"wpca/module/"+o.type,sidebar_id:l.sidebarID,nonce:l.nonce},dataType:"JSON",type:"POST",success:function(e){var t=20<=e.length;o.cachedResults[n.term]={page:a,more:t,items:s?o.cachedResults[n.term].items.concat(e):e},i({results:e,pagination:{more:t}})}})},o.quietMillis)},n}),$.fn.select2.amd.define("select2/wpca/moduleMatcher",["select2/diacritics"],function(t){function l(e){return e.replace(/[^\u0000-\u007E]/g,function(e){return t[e]||e})}return function e(t,n){if(null==t.term||""===t.term.trim())return n;var i=l(n.text).toUpperCase(),o=l(t.term).toUpperCase();if(-1<i.indexOf(o))return n;if(n.children&&0<n.children.length){for(var s=$.extend(!0,{},n),a=n.children.length-1;0<=a;a--)null==e(t,n.children[a])&&s.children.splice(a,1);return 0<s.children.length?s:e(t,s)}return null}});var i={treshold:2e3,timerQueue:{},start:function(e){this.clear(e);var t=this;this.timerQueue[e.cid]=window.setTimeout(function(){t.set(e)},this.treshold)},set:function(e){e.saveGroup()},clear:function(e){e&&this.timerQueue[e.cid]&&window.clearInterval(this.timerQueue[e.cid])}},l={nonce:$("#_ca_nonce").val(),sidebarID:$("#post_ID").val(),alert:null,wpcaDataAdapter:$.fn.select2.amd.require("select2/wpca/conditionData"),wpcaModuleMatcher:$.fn.select2.amd.require("select2/wpca/moduleMatcher"),init:function(){this.alert=new CAE.Views.Alert({model:new CAE.Models.Alert}),CAE.conditionGroups=new CAE.Views.GroupCollection({collection:new CAE.Models.GroupCollection(WPCA.groups,{parse:!0})})}};$(function(){l.init()})}(jQuery,CAE);
2
  * @package wp-content-aware-engine
3
  * @author Joachim Jensen <joachim@dev.institute>
4
  * @license GPLv3
5
+ * @copyright 2022 by Joachim Jensen
6
  */
7
+ var CAE=CAE||{};!function($,CAE,WPCA){"use strict";CAE.settings={views:{}},CAE.Models={},CAE.Models.Alert=Backbone.Model.extend({defaults:{text:"",success:!0},sync:function(){return!1},url:"",reset:function(){this.set(this.defaults)}}),CAE.Models.Condition=Backbone.Model.extend({unsaved:{prompt:WPCA.unsaved,unloadWindowPrompt:!0},defaults:{module:null,label:"",icon:null,placeholder:"",values:[],default_value:null},initialize:function(){this.startTracking(),this.on("destroy",this.stopTracking,this)},sync:function(){return!1},url:""}),CAE.Models.Group=Backbone.Model.extend({unsaved:{prompt:WPCA.unsaved,unloadWindowPrompt:!0},defaults:function(){var e=WPCA.meta_default;return e.id=null,e.status="wpca_or",e.exposure=1,e},initialize:function(){this.startTracking(),this.on("destroy",this.stopTracking,this),this.conditions||(this.conditions=new CAE.Models.ConditionCollection)},parse:function(e){var t=[];if(_.has(e,"conditions")){for(var n in e.conditions)if(e.conditions.hasOwnProperty(n)){var i,o=[],s=e.conditions[n];for(i in s.data)s.data.hasOwnProperty(i)&&o.push({text:"object"==typeof s.data[i]?s.data[i].text:s.data[i],id:i});delete s.data,s.module=n,s.values=o,t.push(s)}delete e.conditions}return this.conditions=new CAE.Models.ConditionCollection(t),e},sync:function(){return!1},url:""}),CAE.Models.GroupCollection=Backbone.Collection.extend({model:CAE.Models.Group,parse:function(e){return e}}),CAE.Models.ConditionCollection=Backbone.Collection.extend({model:CAE.Models.Condition}),CAE.Views={},CAE.Views.Alert=Backbone.Epoxy.View.extend({bindings:"data-vm",tagName:"div",className:"wpca-alert",template:"<div data-vm=\"classes:{'wpca-success':success,'wpca-error':not(success)},text:text\"></div>",timer:4e3,success:function(e){this.model.set({text:e,success:!0})},failure:function(e){this.model.set({text:e,success:!1})},dismiss:function(){var e=this;this.$el.fadeOut("slow",function(){e.model.reset()})},initialize:function(){this.listenTo(this.model,"change:text",this.show),this.$el.appendTo("body").hide().html(this.template)},show:function(){var e;""!==this.model.get("text")&&(this.$el.fadeIn("slow"),e=this,setTimeout(function(){e.dismiss()},this.timer))}}),CAE.Views.Condition=Backbone.Epoxy.View.extend({bindings:"data-vm",model:CAE.Models.Condition,tagName:"div",className:"cas-condition",templateName:"#wpca-template-condition",events:{"click .js-wpca-condition-remove":"removeModel"},computeds:{getIcon:function(){var e=this.getBinding("icon");return"string"!=typeof e?"":e.startsWith("dashi")?'<span class="dashicons '+e+'"></span>':'<img src="'+e+'" />'}},initialize:function(){this.listenTo(this.model,"destroy",this.remove);var e=$(this.templateName);e.length?(this.template=e.html(),this.$el.append(this.template),this.createSuggestInput()):this.model.destroy()},removeModel:function(e){var t=this;this.$el.slideUp(300,function(){t.model.destroy()})},createSuggestInput:function(){var n,e,t,i=this.$el.find(".js-wpca-suggest");i.length&&(n=this.model,e=this.model.get("values"),t=$("<div></div>").html(n.get("placeholder")).text(),i.select2({cachedResults:{},quietMillis:400,searchTimer:null,type:n.get("module"),theme:"wpca",dir:WPCA.text_direction,placeholder:t,minimumInputLength:0,closeOnSelect:!0,width:"100%",language:{noResults:function(){return WPCA.noResults},searching:function(){return WPCA.searching+"..."},loadingMore:function(){return WPCA.loadingMore+"..."}},nextSearchTerm:function(e,t){return t},templateResult:function(e){return e.level?$('<span class="wpca-level-'+e.level+'">'+e.text+"</span>"):e.text},data:e,dataAdapter:l.wpcaDataAdapter,ajax:{}}).on("select2:selecting",function(e){i.data("forceOpen",!0)}).on("select2:closing",function(e){i.data("forceOpen")&&(e.preventDefault(),i.data("forceOpen",!1))}),e.length&&i.val(_.map(e,function(e){return e.id})).trigger("change"),i.on("change",function(e){var t=i.select2("data");n.set("values",t)}))}}),CAE.Views.Group=Backbone.Epoxy.View.extend({bindings:"data-vm",model:CAE.Models.Group,tagName:"li",className:"cas-group-single",template:$("#wpca-template-group").html(),itemView:function(e){return new(CAE.Views[e.model.get("module")]?CAE.Views[e.model.get("module")]:CAE.Views.Condition)(e)},events:{"click .js-wpca-save-group":"saveGroup","click .js-wpca-options":"showOptions"},computeds:{statusNegated:{deps:["status"],get:function(e){return"negated"==e},set:function(e){this.setBinding("status",e?"negated":"wpca_or")}},statusExcept:{deps:["status"],get:function(e){return"wpca_except"==e},set:function(e){this.setBinding("status",e?"wpca_except":"wpca_or")}},statusLabel:function(){switch(this.getBinding("status")){case"wpca_except":return WPCA.condition_except;case"negated":return WPCA.condition_not;default:return WPCA.condition_or}}},bindingFilters:{int:{get:function(e){return e?1:0},set:function(e){return e?1:0}},binary:{get:function(e){return e?1:0},set:function(e){return e?1:0}},hasModule:function(e){for(var t={},n=1;n<arguments.length;n++)t[arguments[n]]=!0;return e.filter(function(e){return t.hasOwnProperty(e.get("module"))}).length==arguments.length-1},hasAnyModule:function(e){for(var t={},n=1;n<arguments.length;n++)t[arguments[n]]=!0;return!!e.find(function(e){return t.hasOwnProperty(e.get("module"))})}},initialize:function(){this.collection=this.model.conditions,this.$el.hide().html(this.template).fadeIn(300),this.listenTo(this.model,"destroy",this.remove),this.listenTo(this.model,"unsavedChanges",this.saveChanges),this.listenTo(this.model.conditions,"unsavedChanges",this.saveChanges),this.listenTo(this.model.conditions,"add remove",this.saveAddRemove);var t=this,n=$(".js-wpca-add-and",this.$el);n.select2({theme:"wpca",placeholder:"+ "+WPCA.newCondition,minimumInputLength:0,closeOnSelect:!0,allowClear:!1,width:"resolve",matcher:l.wpcaModuleMatcher,nextSearchTerm:function(e,t){return t},data:WPCA.conditions}).on("select2:select",function(e){e=e.params.data;t.model.conditions.findWhere({module:e.id})||(e=new CAE.Models.Condition({module:e.id,label:e.text,icon:e.icon,placeholder:e.placeholder,default_value:e.default_value}),t.model.conditions.add(e)),n.val(null).trigger("change")})},showOptions:function(e){$(e.delegateTarget).find(".cas-group-options").slideToggle(200),$(e.currentTarget).toggleClass("active")},saveChanges:function(e,t){e&&i.start(this)},saveAddRemove:function(e,t,n){t.length?n.add?""!==e.get("default_value")&&i.start(this):this.model.get("id")&&i.start(this):(i.clear(this),this.model.get("id")?this.saveGroup():this.removeModel())},removeModel:function(){var e=this;this.$el.slideUp(400,function(){e.model.destroy()})},saveGroup:function(e){var i=this.$el.find(".spinner"),o=this.$el.find(".js-wpca-save-group"),t=this,n=_.clone(this.model.attributes);n.action="wpca/add-rule",n.token=l.nonce,n.current_id=l.sidebarID,n.post_type=WPCA.post_type,n.conditions={},this.model.conditions.each(function(e){e.get("values").length?n.conditions[e.get("module")]=e.get("values").map(function(e){return e.id}):""!==e.get("default_value")&&(n.conditions[e.get("module")]=[e.get("default_value")])}),o.attr("disabled",!0),i.addClass("is-active"),$.ajax({url:ajaxurl,data:n,dataType:"JSON",type:"POST",success:function(e){l.alert.success(e.message),e.removed?t.removeModel():e.new_post_id&&t.model.set("id",e.new_post_id,{silent:!0}),e.removed||(o.hide(),i.removeClass("is-active"),t.model.restartTracking(),t.model.conditions.each(function(e){e.restartTracking()}))},error:function(e,t,n){o.attr("disabled",!1).show(),i.removeClass("is-active"),l.alert.failure(e.responseJSON.data)}})},slideRemove:function(){this.$el.slideUp(400,function(){this.remove()})}}),CAE.Views.GroupCollection=Backbone.Epoxy.View.extend({bindings:"data-vm",el:"#cas-groups",collection:CAE.Models.GroupCollection,events:{"click .js-wpca-add-quick":"addGroupQuick","click .js-wpca-save":"saveAll"},conditionsById:{},initialize:function(){var n=this;this.conditionsById=_.chain(WPCA.conditions).pluck(["children"]).flatten().indexBy("id").value();var i=$(".js-wpca-add-or",this.$el);i.select2({theme:"wpca",placeholder:"+ "+WPCA.newGroup,minimumInputLength:0,closeOnSelect:!0,allowClear:!1,width:"auto",matcher:l.wpcaModuleMatcher,nextSearchTerm:function(e,t){return t},data:WPCA.conditions}).on("select2:select",function(e){var t=e.params.data,e=new CAE.Models.Group,t=new CAE.Models.Condition({module:t.id,label:t.text,icon:t.icon,placeholder:t.placeholder,default_value:t.default_value});n.collection.add(e),e.conditions.add(t),i.val(null).trigger("change")})},itemView:function(e){return new CAE.Views.Group(e)},addGroupQuick:function(e){e.preventDefault();var t,n,i=$(e.currentTarget).data("config"),o=new CAE.Models.Group;for(t in o.set(i.options),this.collection.add(o),i.modules)this.conditionsById.hasOwnProperty(i.modules[t])&&(n=this.conditionsById[i.modules[t]],n=new CAE.Models.Condition({module:n.id,label:n.text,icon:n.icon,placeholder:n.placeholder,default_value:n.default_value}),o.conditions.add(n))}}),$.fn.select2.amd.require(["select2/selection/search"],function(e){e.prototype.searchRemoveChoice=function(e,t){this.trigger("unselect",{data:t}),this.$search.val(""),this.handleSearch()}},null,!0),$.fn.select2.amd.require(["select2/results"],function(e){e.prototype.ensureHighlightVisible=function(){this.$results.resize()}},null,!0),$.fn.select2.amd.define("select2/wpca/conditionData",["select2/data/array","select2/utils"],function(e,t){function n(e,t){n.__super__.constructor.call(this,e,t)}return t.Extend(n,e),n.prototype.query=function(n,i){n.term=n.term||"";var o=this.options.options,s=o.cachedResults[n.term],a=n.page||1;if(s&&s.page>=a){if(!(1<a))return void i({results:s.items,pagination:{more:s.more}});a=s.page}clearTimeout(o.searchTimer),o.searchTimer=setTimeout(function(){$.ajax({url:ajaxurl,data:{search:n.term,paged:a,limit:20,action:"wpca/module/"+o.type,sidebar_id:l.sidebarID,nonce:l.nonce},dataType:"JSON",type:"POST",success:function(e){var t=20<=e.length;o.cachedResults[n.term]={page:a,more:t,items:s?o.cachedResults[n.term].items.concat(e):e},i({results:e,pagination:{more:t}})}})},o.quietMillis)},n}),$.fn.select2.amd.define("select2/wpca/moduleMatcher",["select2/diacritics"],function(t){function l(e){return e.replace(/[^\u0000-\u007E]/g,function(e){return t[e]||e})}return function e(t,n){if(null==t.term||""===t.term.trim())return n;var i=l(n.text).toUpperCase(),o=l(t.term).toUpperCase();if(-1<i.indexOf(o))return n;if(n.children&&0<n.children.length){for(var s=$.extend(!0,{},n),a=n.children.length-1;0<=a;a--)null==e(t,n.children[a])&&s.children.splice(a,1);return 0<s.children.length?s:e(t,s)}return null}});var i={treshold:2e3,timerQueue:{},start:function(e){this.clear(e);var t=this;this.timerQueue[e.cid]=window.setTimeout(function(){t.set(e)},this.treshold)},set:function(e){e.saveGroup()},clear:function(e){e&&this.timerQueue[e.cid]&&window.clearInterval(this.timerQueue[e.cid])}},l={nonce:$("#_ca_nonce").val(),sidebarID:$("#post_ID").val(),alert:null,wpcaDataAdapter:$.fn.select2.amd.require("select2/wpca/conditionData"),wpcaModuleMatcher:$.fn.select2.amd.require("select2/wpca/moduleMatcher"),init:function(){this.alert=new CAE.Views.Alert({model:new CAE.Models.Alert}),CAE.conditionGroups=new CAE.Views.GroupCollection({collection:new CAE.Models.GroupCollection(WPCA.groups,{parse:!0})})}};$(function(){l.init()})}(jQuery,CAE,WPCA);
lib/wp-content-aware-engine/bootstrap.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
@@ -12,7 +12,7 @@ defined('ABSPATH') || exit;
12
  * Version of this WPCA
13
  * @var string
14
  */
15
- $this_wpca_version = '9.6a';
16
 
17
  /**
18
  * Class to make sure the latest
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
12
  * Version of this WPCA
13
  * @var string
14
  */
15
+ $this_wpca_version = '9.7a';
16
 
17
  /**
18
  * Class to make sure the latest
lib/wp-content-aware-engine/collection.php ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ class WPCACollection implements IteratorAggregate, Countable
10
+ {
11
+ /** @var array */
12
+ private $items;
13
+
14
+ /**
15
+ * @param array $items
16
+ */
17
+ public function __construct($items = [])
18
+ {
19
+ $this->items = $items;
20
+ }
21
+
22
+ /**
23
+ * @param mixed $value
24
+ * @return $this
25
+ */
26
+ public function add($value)
27
+ {
28
+ //backwards compat with $value,$key signature
29
+ $args = func_get_args();
30
+ if (count($args) === 2) {
31
+ list($value2, $key) = $args;
32
+ if (!$this->has($key)) {
33
+ $this->put($key, $value2);
34
+ }
35
+ return $this;
36
+ }
37
+
38
+ $this->items[] = $value;
39
+ return $this;
40
+ }
41
+
42
+ /**
43
+ * @param string $key
44
+ * @param mixed $value
45
+ * @return $this
46
+ */
47
+ public function put($key, $value)
48
+ {
49
+ $this->items[$key] = $value;
50
+ return $this;
51
+ }
52
+
53
+ public function set($value, $key)
54
+ {
55
+ _deprecated_function(__METHOD__, '2.0');
56
+ $this->put($key, $value);
57
+ }
58
+
59
+ /**
60
+ * @param string $key
61
+ * @return $this
62
+ */
63
+ public function remove($key)
64
+ {
65
+ unset($this->items[$key]);
66
+ return $this;
67
+ }
68
+
69
+ /**
70
+ * @return mixed|null
71
+ */
72
+ public function pop()
73
+ {
74
+ return array_pop($this->items);
75
+ }
76
+
77
+ /**
78
+ * @param string $key
79
+ *
80
+ * @return bool
81
+ */
82
+ public function has($key)
83
+ {
84
+ return isset($this->items[$key]);
85
+ }
86
+
87
+ /**
88
+ * @param string $key
89
+ * @param mixed|null $default_value
90
+ *
91
+ * @return mixed|null
92
+ */
93
+ public function get($key, $default_value = null)
94
+ {
95
+ return $this->has($key) ? $this->items[$key] : $default_value;
96
+ }
97
+
98
+ /**
99
+ * @return array
100
+ */
101
+ public function all()
102
+ {
103
+ return $this->items;
104
+ }
105
+
106
+ public function get_all()
107
+ {
108
+ _deprecated_function(__METHOD__, '2.0');
109
+ return $this->all();
110
+ }
111
+
112
+ public function set_all($items)
113
+ {
114
+ _deprecated_function(__METHOD__, '2.0');
115
+ foreach ($items as $item) {
116
+ $this->add($item);
117
+ }
118
+ }
119
+
120
+ /**
121
+ * @param callable $callback
122
+ * @return static
123
+ */
124
+ public function filter($callback)
125
+ {
126
+ if (!is_callable($callback)) {
127
+ return $this;
128
+ }
129
+
130
+ return new static(array_filter($this->items, $callback, ARRAY_FILTER_USE_BOTH));
131
+ }
132
+
133
+ /**
134
+ * @inheritDoc
135
+ */
136
+ #[ReturnTypeWillChange]
137
+ public function count()
138
+ {
139
+ return count($this->items);
140
+ }
141
+
142
+ /**
143
+ * @return bool
144
+ */
145
+ public function is_empty()
146
+ {
147
+ return empty($this->items);
148
+ }
149
+
150
+ /**
151
+ * @inheritDoc
152
+ */
153
+ #[ReturnTypeWillChange]
154
+ public function getIterator()
155
+ {
156
+ return new ArrayIterator($this->items);
157
+ }
158
+ }
lib/wp-content-aware-engine/core.php CHANGED
@@ -1,1241 +1,1227 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- if (!class_exists('WPCACore')) {
12
- $domain = explode('/', plugin_basename(__FILE__));
13
- define('WPCA_DOMAIN', $domain[0]);
14
- define('WPCA_PATH', plugin_dir_path(__FILE__));
15
-
16
- /**
17
- * Core for WordPress Content Aware Engine
18
- */
19
- final class WPCACore
20
- {
21
-
22
- /**
23
- * Using class prefix instead of namespace
24
- * for PHP5.2 compatibility
25
- */
26
- const CLASS_PREFIX = 'WPCA';
27
-
28
- /**
29
- * Prefix for data (keys) stored in database
30
- */
31
- const PREFIX = '_ca_';
32
-
33
- /**
34
- * Post Type for condition groups
35
- */
36
- const TYPE_CONDITION_GROUP = 'condition_group';
37
-
38
- /**
39
- * Post Statuses for condition groups
40
- */
41
- /**
42
- * @deprecated
43
- */
44
- const STATUS_NEGATED = 'negated';
45
- /**
46
- * @deprecated
47
- */
48
- const STATUS_PUBLISHED = 'publish';
49
- const STATUS_OR = 'wpca_or';
50
- const STATUS_EXCEPT = 'wpca_except';
51
-
52
- /**
53
- * Exposures for condition groups
54
- */
55
- const EXP_SINGULAR = 0;
56
- const EXP_SINGULAR_ARCHIVE = 1;
57
- const EXP_ARCHIVE = 2;
58
-
59
- /**
60
- * @deprecated 7.0
61
- */
62
- const CAPABILITY = 'edit_theme_options';
63
-
64
- /**
65
- * Name for generated nonces
66
- */
67
- const NONCE = '_ca_nonce';
68
-
69
- const OPTION_CONDITION_TYPE_CACHE = '_ca_condition_type_cache';
70
- const OPTION_POST_TYPE_OPTIONS = '_ca_post_type_options';
71
-
72
- /**
73
- * Post Types that use the engine
74
- * @var WPCAPostTypeManager
75
- */
76
- private static $type_manager;
77
-
78
- /**
79
- * Conditions retrieved from database
80
- * @var array
81
- */
82
- private static $condition_cache = [];
83
-
84
- /**
85
- * Objects retrieved from database
86
- * @var array
87
- */
88
- private static $post_cache = [];
89
-
90
- private static $wp_query_original = [];
91
-
92
- private static $filtered_modules = [];
93
-
94
- /**
95
- * Constructor
96
- */
97
- public static function init()
98
- {
99
- spl_autoload_register([__CLASS__,'_autoload_class_files']);
100
-
101
- if (is_admin()) {
102
- add_action(
103
- 'admin_enqueue_scripts',
104
- [__CLASS__,'add_group_script_styles'],
105
- 9
106
- );
107
- add_action(
108
- 'delete_post',
109
- [__CLASS__,'sync_group_deletion']
110
- );
111
- add_action(
112
- 'trashed_post',
113
- [__CLASS__,'sync_group_trashed']
114
- );
115
- add_action(
116
- 'untrashed_post',
117
- [__CLASS__,'sync_group_untrashed']
118
- );
119
- add_action(
120
- 'wpca/modules/save-data',
121
- [__CLASS__,'save_condition_options'],
122
- 10,
123
- 3
124
- );
125
-
126
- add_action(
127
- 'wp_ajax_wpca/add-rule',
128
- [__CLASS__,'ajax_update_group']
129
- );
130
- }
131
-
132
- add_action(
133
- 'init',
134
- [__CLASS__,'add_group_post_type'],
135
- 99
136
- );
137
-
138
- add_action(
139
- 'init',
140
- [__CLASS__,'schedule_cache_condition_types'],
141
- 99
142
- );
143
-
144
- add_action(
145
- 'wpca/cache_condition_types',
146
- [__CLASS__,'cache_condition_types'],
147
- 999
148
- );
149
- }
150
-
151
- /**
152
- * Get post type manager
153
- *
154
- * @deprecated 4.0
155
- * @since 1.0
156
- * @return WPCAPostTypeManager
157
- */
158
- public static function post_types()
159
- {
160
- return self::types();
161
- }
162
-
163
- /**
164
- * Get type manager
165
- *
166
- * @since 4.0
167
- * @return WPCAPostTypeManager
168
- */
169
- public static function types()
170
- {
171
- if (!isset(self::$type_manager)) {
172
- self::$type_manager = new WPCATypeManager();
173
- }
174
- return self::$type_manager;
175
- }
176
-
177
- /**
178
- * @since 8.0
179
- *
180
- * @return void
181
- */
182
- public static function schedule_cache_condition_types()
183
- {
184
- if (wp_next_scheduled('wpca/cache_condition_types') !== false) {
185
- return;
186
- }
187
-
188
- wp_schedule_event(get_gmt_from_date('today 02:00:00', 'U'), 'daily', 'wpca/cache_condition_types');
189
- }
190
-
191
- /**
192
- * Cache condition types currently in use
193
- *
194
- * @since 8.0
195
- *
196
- * @return void
197
- */
198
- public static function cache_condition_types()
199
- {
200
- $all_modules = [];
201
- $modules_by_type = [];
202
- $cache = [];
203
-
204
- $types = self::types();
205
- foreach ($types as $type => $modules) {
206
- $modules_by_type[$type] = [];
207
- $cache[$type] = [];
208
- foreach ($modules as $module) {
209
- $modules_by_type[$type][$module->get_data_key()] = $module->get_id();
210
- $all_modules[$module->get_data_key()] = $module->get_data_key();
211
- }
212
- }
213
-
214
- if (!$all_modules) {
215
- update_option(self::OPTION_CONDITION_TYPE_CACHE, []);
216
- return;
217
- }
218
-
219
- global $wpdb;
220
-
221
- $query = '
222
- SELECT p.post_type, m.meta_key
223
- FROM '.$wpdb->posts.' p
224
- INNER JOIN '.$wpdb->posts.' c ON c.post_parent = p.ID
225
- INNER JOIN '.$wpdb->postmeta.' m ON m.post_id = c.ID
226
- WHERE p.post_type IN ('.self::sql_prepare_in(array_keys($modules_by_type)).')
227
- AND m.meta_key IN ('.self::sql_prepare_in($all_modules).')
228
- GROUP BY p.post_type, m.meta_key
229
- ';
230
-
231
- $results = (array) $wpdb->get_results($query);
232
-
233
- foreach ($results as $result) {
234
- if (isset($modules_by_type[$result->post_type][$result->meta_key])) {
235
- $cache[$result->post_type][] = $modules_by_type[$result->post_type][$result->meta_key];
236
- }
237
- }
238
-
239
- update_option(self::OPTION_CONDITION_TYPE_CACHE, $cache);
240
- }
241
-
242
- /**
243
- * Register group post type
244
- *
245
- * @since 1.0
246
- * @return void
247
- */
248
- public static function add_group_post_type()
249
- {
250
- //This is just a safety placeholder,
251
- //authorization will be done with parent object's cap
252
- $capability = 'edit_theme_options';
253
- $capabilities = [
254
- 'edit_post' => $capability,
255
- 'read_post' => $capability,
256
- 'delete_post' => $capability,
257
- 'edit_posts' => $capability,
258
- 'delete_posts' => $capability,
259
- 'edit_others_posts' => $capability,
260
- 'publish_posts' => $capability,
261
- 'read_private_posts' => $capability
262
- ];
263
-
264
- register_post_type(self::TYPE_CONDITION_GROUP, [
265
- 'labels' => [
266
- 'name' => __('Condition Groups', WPCA_DOMAIN),
267
- 'singular_name' => __('Condition Group', WPCA_DOMAIN),
268
- ],
269
- 'capabilities' => $capabilities,
270
- 'public' => false,
271
- 'hierarchical' => false,
272
- 'exclude_from_search' => true,
273
- 'publicly_queryable' => false,
274
- 'show_ui' => false,
275
- 'show_in_menu' => false,
276
- 'show_in_nav_menus' => false,
277
- 'show_in_admin_bar' => false,
278
- 'show_in_rest' => false,
279
- 'has_archive' => false,
280
- 'rewrite' => false,
281
- 'query_var' => false,
282
- 'supports' => ['title'],
283
- 'can_export' => false,
284
- 'delete_with_user' => false
285
- ]);
286
-
287
- register_post_status(self::STATUS_NEGATED, [
288
- 'label' => _x('Negated', 'condition status', WPCA_DOMAIN),
289
- 'public' => false,
290
- 'exclude_from_search' => true,
291
- 'show_in_admin_all_list' => false,
292
- 'show_in_admin_status_list' => false,
293
- ]);
294
- register_post_status(self::STATUS_EXCEPT, [
295
- 'label' => _x('Exception', 'condition status', WPCA_DOMAIN),
296
- 'public' => false,
297
- 'exclude_from_search' => true,
298
- 'show_in_admin_all_list' => false,
299
- 'show_in_admin_status_list' => false,
300
- ]);
301
- register_post_status(self::STATUS_OR, [
302
- 'label' => _x('Or', 'condition status', WPCA_DOMAIN),
303
- 'public' => false,
304
- 'exclude_from_search' => true,
305
- 'show_in_admin_all_list' => false,
306
- 'show_in_admin_status_list' => false,
307
- ]);
308
- }
309
-
310
- /**
311
- * Get group IDs by their parent ID
312
- *
313
- * @since 1.0
314
- * @param int $parent_id
315
- * @return array
316
- */
317
- private static function get_group_ids_by_parent($parent_id)
318
- {
319
- if (!self::types()->has(get_post_type($parent_id))) {
320
- return [];
321
- }
322
-
323
- global $wpdb;
324
- return (array)$wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_parent = '%d'", $parent_id));
325
- }
326
-
327
- /**
328
- * Delete groups from database when their parent is deleted
329
- *
330
- * @since 1.0
331
- * @param int $post_id
332
- * @return void
333
- */
334
- public static function sync_group_deletion($post_id)
335
- {
336
- $groups = self::get_group_ids_by_parent($post_id);
337
- if ($groups) {
338
- foreach ($groups as $group_id) {
339
- //Takes care of metadata and terms too
340
- wp_delete_post($group_id, true);
341
- }
342
- }
343
- }
344
-
345
- /**
346
- * Trash groups when their parent is trashed
347
- *
348
- * @since 1.0
349
- * @param int $post_id
350
- * @return void
351
- */
352
- public static function sync_group_trashed($post_id)
353
- {
354
- $groups = self::get_group_ids_by_parent($post_id);
355
- if ($groups) {
356
- foreach ($groups as $group_id) {
357
- wp_trash_post($group_id);
358
- }
359
- }
360
- }
361
-
362
- /**
363
- * Untrash groups when their parent is untrashed
364
- *
365
- * @since 1.0
366
- * @param int $post_id
367
- * @return void
368
- */
369
- public static function sync_group_untrashed($post_id)
370
- {
371
- $groups = self::get_group_ids_by_parent($post_id);
372
- if ($groups) {
373
- foreach ($groups as $group_id) {
374
- wp_untrash_post($group_id);
375
- }
376
- }
377
- }
378
-
379
- /**
380
- * @param string $post_type
381
- * @return array
382
- */
383
- public static function get_conditional_modules($post_type)
384
- {
385
- if (!isset(self::$filtered_modules[$post_type])) {
386
- return [];
387
- }
388
- return self::$filtered_modules[$post_type];
389
- }
390
-
391
- /**
392
- * Get filtered condition groups
393
- *
394
- * @since 2.0
395
- * @return array
396
- */
397
- public static function get_conditions($post_type)
398
- {
399
- global $wpdb, $wp_query, $post;
400
-
401
- if (!self::types()->has($post_type) || (!$wp_query->query && !$post) || is_admin()) {
402
- return [];
403
- }
404
-
405
- // Return cache if present
406
- if (isset(self::$condition_cache[$post_type])) {
407
- return self::$condition_cache[$post_type];
408
- }
409
-
410
- $where = [];
411
- $join = [];
412
-
413
- $cache = [
414
- $post_type
415
- ];
416
-
417
- $modules = self::types()->get($post_type)->get_all();
418
- $modules = self::filter_condition_type_cache($post_type, $modules);
419
-
420
- //avoid combining as long as negated conditions are being deprecated
421
- // foreach (self::types() as $other_type => $other_modules) {
422
- // if ($other_type == $post_type) {
423
- // continue;
424
- // }
425
- // if (self::filter_condition_type_cache($other_type, $other_modules->get_all()) === $modules) {
426
- // $cache[] = $other_type;
427
- // }
428
- // }
429
-
430
- self::fix_wp_query();
431
-
432
- $in_context_by_module_id = [];
433
- foreach ($modules as $module) {
434
- $id = $module->get_id();
435
- $in_context = apply_filters("wpca/module/$id/in-context", $module->in_context());
436
- $in_context_by_module_id[$id] = $in_context;
437
-
438
- if(!$in_context) {
439
- continue;
440
- }
441
-
442
- $data = $module->get_context_data();
443
-
444
- if(empty($data)) {
445
- $in_context_by_module_id[$id] = false;
446
- continue;
447
- }
448
-
449
- if (is_array($data)) {
450
- $name = $module->get_query_name();
451
- $data = "($name.meta_value IS NULL OR $name.meta_value IN (".self::sql_prepare_in($data).'))';
452
- }
453
- $join[$id] = $module->db_join();
454
- $where[$id] = apply_filters("wpca/module/$id/db-where", $data);
455
- self::$filtered_modules[$post_type][] = $module;
456
- }
457
-
458
- $use_negated_conditions = self::get_option($post_type, 'legacy.negated_conditions', false);
459
-
460
- // Check if there are any conditions for current content
461
- $groups_in_context = [];
462
- if (!empty($where)) {
463
- $post_status = [
464
- self::STATUS_PUBLISHED,
465
- self::STATUS_OR,
466
- self::STATUS_EXCEPT
467
- ];
468
-
469
- if ($use_negated_conditions) {
470
- $post_status[] = self::STATUS_NEGATED;
471
- }
472
-
473
- $chunk_size = count($join);
474
- if (defined('WPCA_SQL_JOIN_SIZE') && is_integer(WPCA_SQL_JOIN_SIZE) && WPCA_SQL_JOIN_SIZE > 0) {
475
- $chunk_size = WPCA_SQL_JOIN_SIZE;
476
- } elseif (defined('WPCA_SQL_COMPATIBILITY_MODE') && WPCA_SQL_COMPATIBILITY_MODE === true) {
477
- //Syntax changed in MySQL 5.5 and MariaDB 10.0 (reports as version 5.5)
478
- $wpdb->query('SET'.(version_compare($wpdb->db_version(), '5.5', '>=') ? ' SESSION' : ' OPTION').' SQL_BIG_SELECTS = 1');
479
- }
480
-
481
- $joins = array_chunk($join, $chunk_size);
482
- $joins_max = count($joins) - 1;
483
- $wheres = array_chunk($where, $chunk_size);
484
- $group_ids = [];
485
- $groups_in_context = [];
486
-
487
- $where2 = [];
488
- $where2[] = "p.post_type = '".self::TYPE_CONDITION_GROUP."'";
489
- $where2[] = "p.post_status IN ('".implode("','", $post_status)."')";
490
- $where2[] = 'p.menu_order '.(is_archive() || is_home() ? '>=' : '<=').' 1';
491
-
492
- foreach ($joins as $i => $join) {
493
- if ($i == $joins_max) {
494
- $groups_in_context = $wpdb->get_results(
495
- 'SELECT p.ID, p.post_parent, p.post_status '.
496
- "FROM $wpdb->posts p ".
497
- implode(' ', $join).'
498
- WHERE
499
- '.implode(' AND ', $wheres[$i]).'
500
- AND '.implode(' AND ', $where2).
501
- (!empty($group_ids) ? ' AND p.id IN ('.implode(',', $group_ids).')' : ''),
502
- OBJECT_K
503
- );
504
- break;
505
- }
506
-
507
- $group_ids = array_merge($group_ids, $wpdb->get_col(
508
- 'SELECT p.ID '.
509
- "FROM $wpdb->posts p ".
510
- implode(' ', $join).'
511
- WHERE
512
- '.implode(' AND ', $wheres[$i]).'
513
- AND '.implode(' AND ', $where2)
514
- ));
515
- }
516
- }
517
-
518
- $groups_negated = [];
519
- if ($use_negated_conditions) {
520
- $groups_negated = $wpdb->get_results($wpdb->prepare(
521
- 'SELECT p.ID, p.post_parent '.
522
- "FROM $wpdb->posts p ".
523
- "WHERE p.post_type = '%s' ".
524
- "AND p.post_status = '%s' ",
525
- self::TYPE_CONDITION_GROUP,
526
- self::STATUS_NEGATED
527
- ), OBJECT_K);
528
- }
529
-
530
- if (!empty($groups_in_context) || !empty($groups_negated)) {
531
- //Force update of meta cache to prevent lazy loading
532
- update_meta_cache('post', array_keys($groups_in_context + $groups_negated));
533
- }
534
-
535
- //Exclude types that have unrelated content in same group
536
- foreach ($modules as $module) {
537
- $groups_in_context = $module->filter_excluded_context(
538
- $groups_in_context,
539
- $in_context_by_module_id[$module->get_id()]
540
- );
541
- }
542
-
543
- //exclude exceptions
544
- $excepted = [];
545
- foreach ($groups_in_context as $group_id => $group) {
546
- if ($group->post_status == self::STATUS_EXCEPT) {
547
- $excepted[$group->post_parent] = 1;
548
- }
549
- }
550
-
551
- //condition group => type
552
- $valid = [];
553
- foreach ($groups_in_context as $group) {
554
- $valid[$group->ID] = $group->post_parent;
555
- }
556
-
557
- foreach ($valid as $group_id => $parent_id) {
558
- if (isset($excepted[$parent_id])) {
559
- unset($valid[$group_id]);
560
- }
561
- }
562
-
563
- if ($use_negated_conditions) {
564
- //Filter negated groups
565
- //type => group
566
- $handled_already = array_flip($valid);
567
- foreach ($groups_negated as $group) {
568
- if (isset($valid[$group->ID])) {
569
- unset($valid[$group->ID]);
570
- } else {
571
- $valid[$group->ID] = $group->post_parent;
572
- }
573
- if (isset($handled_already[$group->post_parent])) {
574
- unset($valid[$group->ID]);
575
- }
576
- $handled_already[$group->post_parent] = 1;
577
- }
578
- }
579
-
580
- self::restore_wp_query();
581
-
582
- foreach ($cache as $cache_type) {
583
- self::$condition_cache[$cache_type] = $valid;
584
- }
585
-
586
- return self::$condition_cache[$post_type];
587
- }
588
-
589
- /**
590
- * Get filtered posts from a post type
591
- *
592
- * @since 1.0
593
- * @return array
594
- */
595
- public static function get_posts($post_type)
596
- {
597
- if (isset(self::$post_cache[$post_type])) {
598
- return self::$post_cache[$post_type];
599
- }
600
-
601
- $valid = self::get_conditions($post_type);
602
-
603
- //if cache hasn't been set, method was called too early
604
- if (!isset(self::$condition_cache[$post_type])) {
605
- return false;
606
- }
607
-
608
- self::$post_cache[$post_type] = [];
609
-
610
- $results = [];
611
-
612
- if (!empty($valid)) {
613
- $data = new WP_Query([
614
- 'post__in' => array_values($valid),
615
- 'post_type' => $post_type,
616
- 'post_status' => 'publish',
617
- 'posts_per_page' => -1,
618
- 'ignore_sticky_posts' => true,
619
- 'update_post_term_cache' => false,
620
- 'update_post_meta_cache' => true,
621
- 'suppress_filters' => true,
622
- 'no_found_rows' => true,
623
- 'orderby' => 'none'
624
- ]);
625
-
626
- $results = array_merge($results, $data->posts);
627
- }
628
-
629
- //legacy sorting
630
- uasort($results, function (WP_Post $post_a, WP_Post $post_b) {
631
- //asc
632
- if ($post_a->menu_order != $post_b->menu_order) {
633
- return $post_a->menu_order < $post_b->menu_order ? -1 : 1;
634
- }
635
-
636
- $post_a_handle = get_post_meta($post_a->ID, '_ca_handle', true);
637
- $post_b_handle = get_post_meta($post_b->ID, '_ca_handle', true);
638
-
639
- //desc
640
- if ($post_a_handle != $post_b_handle) {
641
- return $post_a_handle > $post_b_handle ? -1 : 1;
642
- }
643
-
644
- //desc
645
- if ($post_a->post_date != $post_b->post_date) {
646
- return $post_a->post_date > $post_b->post_date ? -1 : 1;
647
- }
648
-
649
- return 0;
650
- });
651
-
652
- $results = array_reduce($results, function ($carry, $post) {
653
- $carry[$post->ID] = (object)[
654
- 'ID' => $post->ID,
655
- 'post_type' => $post->post_type,
656
- 'handle' => get_post_meta($post->ID, '_ca_handle', true),
657
- 'menu_order' => $post->menu_order,
658
- 'post_date' => $post->post_date
659
- ];
660
- return $carry;
661
- }, []);
662
-
663
- self::$post_cache[$post_type] = apply_filters("wpca/posts/{$post_type}", $results);
664
-
665
- return self::$post_cache[$post_type];
666
- }
667
-
668
- public static function render_group_meta_box($post, $screen, $context = 'normal', $priority = 'default')
669
- {
670
- if (!($post instanceof WP_Post) || !self::types()->has($post->post_type)) {
671
- return;
672
- }
673
-
674
- $post_type_obj = get_post_type_object($post->post_type);
675
-
676
- if (!current_user_can($post_type_obj->cap->edit_post, $post->ID)) {
677
- return;
678
- }
679
-
680
- $template = WPCAView::make('condition_options', [
681
- 'post_type' => $post->post_type
682
- ]);
683
- add_action('wpca/group/settings', [$template,'render'], -1, 2);
684
-
685
- $template = WPCAView::make('group_template', [
686
- 'post_type' => $post->post_type
687
- ]);
688
- add_action('admin_footer', [$template,'render']);
689
-
690
- $template = WPCAView::make('condition_template');
691
- add_action('admin_footer', [$template,'render']);
692
-
693
- $view = WPCAView::make('meta_box', [
694
- 'post_type' => $post->post_type,
695
- 'nonce' => wp_nonce_field(self::PREFIX.$post->ID, self::NONCE, true, false),
696
- ]);
697
-
698
- $title = isset($post_type_obj->labels->ca_title) ? $post_type_obj->labels->ca_title : __('Conditional Logic', WPCA_DOMAIN);
699
-
700
- add_meta_box(
701
- 'cas-rules',
702
- $title,
703
- [$view,'render'],
704
- $screen,
705
- $context,
706
- $priority
707
- );
708
- }
709
-
710
- /**
711
- * Insert new condition group for a post type
712
- * Uses current post per default
713
- *
714
- * @since 1.0
715
- * @param WP_Post|int $post
716
- * @return int
717
- */
718
- public static function add_condition_group($post_id = null)
719
- {
720
- $post = get_post($post_id);
721
-
722
- //Make sure to go from auto-draft to draft
723
- if ($post->post_status == 'auto-draft') {
724
- wp_update_post([
725
- 'ID' => $post->ID,
726
- 'post_title' => '',
727
- 'post_status' => 'draft'
728
- ]);
729
- }
730
-
731
- return wp_insert_post([
732
- 'post_status' => self::STATUS_OR,
733
- 'menu_order' => self::EXP_SINGULAR_ARCHIVE,
734
- 'post_type' => self::TYPE_CONDITION_GROUP,
735
- 'post_author' => $post->post_author,
736
- 'post_parent' => $post->ID,
737
- ]);
738
- }
739
-
740
- /**
741
- * Get condition groups for a post type
742
- * Uses current post per default
743
- *
744
- * @since 1.0
745
- * @param WP_Post|int $post_id
746
- * @return array
747
- */
748
- private static function get_condition_groups($post_id = null)
749
- {
750
- $post = get_post($post_id);
751
- $groups = [];
752
-
753
- if ($post) {
754
- $groups = get_posts([
755
- 'posts_per_page' => -1,
756
- 'post_type' => self::TYPE_CONDITION_GROUP,
757
- 'post_parent' => $post->ID,
758
- 'post_status' => [self::STATUS_PUBLISHED,self::STATUS_NEGATED,self::STATUS_EXCEPT, self::STATUS_OR],
759
- 'order' => 'ASC',
760
- 'orderby' => 'post_status'
761
- ]);
762
- }
763
- return $groups;
764
- }
765
-
766
- /**
767
- * AJAX callback to update a condition group
768
- *
769
- * @since 1.0
770
- * @return void
771
- */
772
- public static function ajax_update_group()
773
- {
774
- if (!isset($_POST['current_id']) ||
775
- !check_ajax_referer(self::PREFIX.$_POST['current_id'], 'token', false)) {
776
- wp_send_json_error(__('Unauthorized request', WPCA_DOMAIN), 403);
777
- }
778
-
779
- $parent_id = (int)$_POST['current_id'];
780
- $parent_type = get_post_type_object($_POST['post_type']);
781
-
782
- if (! current_user_can($parent_type->cap->edit_post, $parent_id)) {
783
- wp_send_json_error(__('Unauthorized request', WPCA_DOMAIN), 403);
784
- }
785
-
786
- $response = [
787
- 'message' => __('Conditions updated', WPCA_DOMAIN)
788
- ];
789
-
790
- //Make sure some rules are sent
791
- if (!isset($_POST['conditions'])) {
792
- //Otherwise we delete group
793
- if ($_POST['id'] && wp_delete_post(intval($_POST['id']), true) === false) {
794
- wp_send_json_error(__('Could not delete conditions', WPCA_DOMAIN), 500);
795
- }
796
-
797
- $response['removed'] = true;
798
- wp_send_json($response);
799
- }
800
-
801
- //If ID was not sent at this point, it is a new group
802
- if (!$_POST['id']) {
803
- $post_id = self::add_condition_group($parent_id);
804
- $response['new_post_id'] = $post_id;
805
- } else {
806
- $post_id = (int)$_POST['id'];
807
- }
808
-
809
- wp_update_post([
810
- 'ID' => $post_id,
811
- 'post_status' => self::sanitize_status($_POST['status']),
812
- 'menu_order' => (int)$_POST['exposure']
813
- ]);
814
-
815
- //Prune condition type cache, will rebuild within 24h
816
- update_option(self::OPTION_CONDITION_TYPE_CACHE, []);
817
-
818
- foreach (self::types()->get($parent_type->name)->get_all() as $module) {
819
- //send $_POST here
820
- $module->save_data($post_id);
821
- }
822
-
823
- do_action('wpca/modules/save-data', $post_id, $parent_type->name);
824
-
825
- wp_send_json($response);
826
- }
827
-
828
- /**
829
- * @param string $status
830
- *
831
- * @return string
832
- */
833
- private static function sanitize_status($status)
834
- {
835
- switch ($status) {
836
- case self::STATUS_NEGATED:
837
- return self::STATUS_NEGATED;
838
- case self::STATUS_EXCEPT:
839
- return self::STATUS_EXCEPT;
840
- case self::STATUS_OR:
841
- case self::STATUS_PUBLISHED:
842
- default:
843
- return self::STATUS_OR;
844
- }
845
- }
846
-
847
- /**
848
- * Save registered meta for condition group
849
- *
850
- * @since 3.2
851
- * @param int $group_id
852
- * @return void
853
- */
854
- public static function save_condition_options($group_id, $post_type)
855
- {
856
- $meta_keys = self::get_condition_meta_keys($post_type);
857
- foreach ($meta_keys as $key => $default_value) {
858
- $value = isset($_POST[$key]) ? $_POST[$key] : false;
859
- if ($value) {
860
- update_post_meta($group_id, $key, $value);
861
- } elseif (get_post_meta($group_id, $key, true)) {
862
- delete_post_meta($group_id, $key);
863
- }
864
- }
865
- }
866
-
867
- public static function add_group_script_styles($hook)
868
- {
869
- $current_screen = get_current_screen();
870
-
871
- wp_register_style(
872
- self::PREFIX.'condition-groups',
873
- plugins_url('/assets/css/condition_groups.css', __FILE__),
874
- [],
875
- WPCA_VERSION
876
- );
877
-
878
- if (self::types()->has($current_screen->post_type) && $current_screen->base == 'post') {
879
- self::enqueue_scripts_styles($current_screen->post_type);
880
- }
881
- }
882
-
883
- /**
884
- * Get condition option defaults
885
- *
886
- * @since 3.2
887
- * @param string $post_type
888
- * @return array
889
- */
890
- public static function get_condition_meta_keys($post_type)
891
- {
892
- $group_meta = [
893
- '_ca_autoselect' => 0
894
- ];
895
- return apply_filters('wpca/condition/meta', $group_meta, $post_type);
896
- }
897
-
898
- /**
899
- * Register and enqueue scripts and styles
900
- * for post edit screen
901
- *
902
- * @since 1.0
903
- * @param string $hook
904
- * @return void
905
- */
906
- public static function enqueue_scripts_styles($post_type = '')
907
- {
908
- $post_type = empty($post_type) ? get_post_type() : $post_type;
909
-
910
- $group_meta = self::get_condition_meta_keys($post_type);
911
-
912
- $groups = self::get_condition_groups();
913
- $data = [];
914
- $i = 0;
915
- foreach ($groups as $group) {
916
- $data[$i] = [
917
- 'id' => $group->ID,
918
- 'status' => $group->post_status,
919
- 'exposure' => $group->menu_order,
920
- 'conditions' => []
921
- ];
922
-
923
- foreach (self::types()->get($post_type)->get_all() as $module) {
924
- $data[$i]['conditions'] = $module->get_group_data($data[$i]['conditions'], $group->ID);
925
- }
926
-
927
- foreach ($group_meta as $meta_key => $default_value) {
928
- $value = get_post_meta($group->ID, $meta_key, true);
929
- if ($value === false) {
930
- $value = $default_value;
931
- }
932
- $data[$i][$meta_key] = $value;
933
- }
934
- $i++;
935
- }
936
-
937
- $conditions = [
938
- 'general' => [
939
- 'text' => __('General'),
940
- 'children' => []
941
- ],
942
- 'post_type' => [
943
- 'text' => __('Post Types'),
944
- 'children' => []
945
- ],
946
- 'taxonomy' => [
947
- 'text' => __('Taxonomies'),
948
- 'children' => []
949
- ],
950
- 'plugins' => [
951
- 'text' => __('Plugins'),
952
- 'children' => []
953
- ]
954
- ];
955
-
956
- foreach (self::types()->get($post_type)->get_all() as $module) {
957
- $category = $module->get_category();
958
- if (!isset($conditions[$category])) {
959
- $category = 'general';
960
- }
961
-
962
- //array_values used for backwards compatibility
963
- $conditions[$category]['children'] = array_values($module->list_module($conditions[$category]['children']));
964
- }
965
-
966
- foreach ($conditions as $key => $condition) {
967
- if (empty($condition['children'])) {
968
- unset($conditions[$key]);
969
- }
970
- }
971
-
972
- //Make sure to use packaged version
973
- if (wp_script_is('select2', 'registered')) {
974
- wp_deregister_script('select2');
975
- wp_deregister_style('select2');
976
- }
977
-
978
- $plugins_url = plugins_url('', __FILE__);
979
-
980
- //Add to head to take priority
981
- //if being added under other name
982
- wp_register_script(
983
- 'select2',
984
- $plugins_url . '/assets/js/select2.min.js',
985
- ['jquery'],
986
- '4.0.3',
987
- false
988
- );
989
-
990
- wp_register_script(
991
- 'backbone.trackit',
992
- $plugins_url . '/assets/js/backbone.trackit.min.js',
993
- ['backbone'],
994
- '0.1.0',
995
- true
996
- );
997
-
998
- wp_register_script(
999
- 'backbone.epoxy',
1000
- $plugins_url . '/assets/js/backbone.epoxy.min.js',
1001
- ['backbone'],
1002
- '1.3.3',
1003
- true
1004
- );
1005
-
1006
- wp_register_script(
1007
- self::PREFIX.'condition-groups',
1008
- $plugins_url . '/assets/js/condition_groups.min.js',
1009
- ['jquery','select2','backbone.trackit','backbone.epoxy'],
1010
- WPCA_VERSION,
1011
- true
1012
- );
1013
-
1014
- wp_enqueue_script(self::PREFIX.'condition-groups');
1015
- wp_localize_script(self::PREFIX.'condition-groups', 'WPCA', [
1016
- 'searching' => __('Searching', WPCA_DOMAIN),
1017
- 'noResults' => __('No results found.', WPCA_DOMAIN),
1018
- 'loadingMore' => __('Loading more results', WPCA_DOMAIN),
1019
- 'unsaved' => __('Conditions have unsaved changes. Do you want to continue and discard these changes?', WPCA_DOMAIN),
1020
- 'newGroup' => __('New condition group', WPCA_DOMAIN),
1021
- 'newCondition' => __('Meet ALL of these conditions', WPCA_DOMAIN),
1022
- 'conditions' => array_values($conditions),
1023
- 'groups' => $data,
1024
- 'meta_default' => $group_meta,
1025
- 'post_type' => $post_type,
1026
- 'text_direction' => is_rtl() ? 'rtl' : 'ltr',
1027
- 'condition_not' => __('Not', WPCA_DOMAIN),
1028
- 'condition_or' => __('Or', WPCA_DOMAIN),
1029
- 'condition_except' => __('Except', WPCA_DOMAIN)
1030
- ]);
1031
- wp_enqueue_style(self::PREFIX.'condition-groups');
1032
-
1033
- //@todo remove when ultimate member includes fix
1034
- wp_dequeue_style('um_styles');
1035
-
1036
- //@todo remove when events calendar pro plugin includes fix
1037
- wp_dequeue_script('tribe-select2');
1038
- }
1039
-
1040
- /**
1041
- * Modify wp_query for plugin compatibility
1042
- *
1043
- * @since 5.0
1044
- * @return void
1045
- */
1046
- private static function fix_wp_query()
1047
- {
1048
- $query = [];
1049
-
1050
- //When themes don't declare WooCommerce support,
1051
- //conditionals are not working properly for Shop
1052
- if (defined('WOOCOMMERCE_VERSION') && function_exists('is_shop') && is_shop() && !is_post_type_archive('product')) {
1053
- $query = [
1054
- 'is_archive' => true,
1055
- 'is_post_type_archive' => true,
1056
- 'is_page' => false,
1057
- 'is_singular' => false,
1058
- 'query_vars' => [
1059
- 'post_type' => 'product'
1060
- ]
1061
- ];
1062
- }
1063
-
1064
- self::set_wp_query($query);
1065
- }
1066
-
1067
- /**
1068
- * Restore original wp_query
1069
- *
1070
- * @since 5.0
1071
- * @return void
1072
- */
1073
- private static function restore_wp_query()
1074
- {
1075
- self::set_wp_query(self::$wp_query_original);
1076
- self::$wp_query_original = [];
1077
- }
1078
-
1079
- /**
1080
- * Set properties in wp_query and save original value
1081
- *
1082
- * @since 5.0
1083
- * @param array $query
1084
- */
1085
- private static function set_wp_query($query)
1086
- {
1087
- global $wp_query;
1088
- foreach ($query as $key => $val) {
1089
- $is_array = is_array($val);
1090
-
1091
- if (!isset(self::$wp_query_original[$key])) {
1092
- self::$wp_query_original[$key] = $is_array ? [] : $wp_query->$key;
1093
- }
1094
-
1095
- if ($is_array) {
1096
- foreach ($val as $k1 => $v1) {
1097
- if (!isset(self::$wp_query_original[$key][$k1])) {
1098
- self::$wp_query_original[$key][$k1] = $wp_query->{$key}[$k1];
1099
- }
1100
- $wp_query->{$key}[$k1] = $v1;
1101
- }
1102
- } else {
1103
- $wp_query->$key = $val;
1104
- }
1105
- }
1106
- }
1107
-
1108
- /**
1109
- * Autoload class files
1110
- *
1111
- * @since 1.0
1112
- * @param string $class
1113
- * @return void
1114
- */
1115
- private static function _autoload_class_files($class)
1116
- {
1117
- if (strpos($class, self::CLASS_PREFIX) === 0) {
1118
- $class = str_replace(self::CLASS_PREFIX, '', $class);
1119
- $class = self::str_replace_first('_', '/', $class);
1120
- $class = strtolower($class);
1121
- $file = WPCA_PATH . $class . '.php';
1122
- if (file_exists($file)) {
1123
- include($file);
1124
- }
1125
- }
1126
- }
1127
-
1128
- /**
1129
- * Helper function to replace first
1130
- * occurence of substring
1131
- *
1132
- * @since 1.0
1133
- * @param string $search
1134
- * @param string $replace
1135
- * @param string $subject
1136
- * @return string
1137
- */
1138
- private static function str_replace_first($search, $replace, $subject)
1139
- {
1140
- $pos = strpos($subject, $search);
1141
- if ($pos !== false) {
1142
- $subject = substr_replace($subject, $replace, $pos, strlen($search));
1143
- }
1144
- return $subject;
1145
- }
1146
-
1147
- /**
1148
- * @since 8.0
1149
- * @param array $input
1150
- *
1151
- * @return string
1152
- */
1153
- private static function sql_prepare_in($input)
1154
- {
1155
- $output = array_map(function ($value) {
1156
- return "'" . esc_sql($value) . "'";
1157
- }, $input);
1158
- return implode(',', $output);
1159
- }
1160
-
1161
- /**
1162
- * @since 8.0
1163
- * @param string $type
1164
- * @param array $modules
1165
- *
1166
- * @return array
1167
- */
1168
- private static function filter_condition_type_cache($type, $modules)
1169
- {
1170
- $included_conditions = get_option(self::OPTION_CONDITION_TYPE_CACHE, []);
1171
-
1172
- if (!$included_conditions || !isset($included_conditions[$type])) {
1173
- return $modules;
1174
- }
1175
-
1176
- $included_conditions_lookup = array_flip($included_conditions[$type]);
1177
- $filtered_modules = [];
1178
-
1179
- foreach ($modules as $module) {
1180
- if (isset($included_conditions_lookup[$module->get_id()])) {
1181
- $filtered_modules[] = $module;
1182
- }
1183
- }
1184
-
1185
- return $filtered_modules;
1186
- }
1187
-
1188
- /**
1189
- * @param string $post_type
1190
- * @param string $name
1191
- * @param mixed|null $default_value
1192
- *
1193
- * @return mixed|null
1194
- */
1195
- public static function get_option($post_type, $name, $default_value = null)
1196
- {
1197
- if (!self::types()->has($post_type)) {
1198
- return $default_value;
1199
- }
1200
-
1201
- $value = get_option(self::OPTION_POST_TYPE_OPTIONS, []);
1202
- $levels = explode('.', $post_type.'.'.$name);
1203
-
1204
- foreach ($levels as $option_level) {
1205
- if (!is_array($value) || !isset($value[$option_level])) {
1206
- return $default_value;
1207
- }
1208
- $value = $value[$option_level];
1209
- }
1210
- return $value;
1211
- }
1212
-
1213
- /**
1214
- * @param string $post_type
1215
- * @param string $name
1216
- * @param mixed $value
1217
- *
1218
- * @return bool
1219
- */
1220
- public static function save_option($post_type, $name, $value)
1221
- {
1222
- if (!self::types()->has($post_type)) {
1223
- return false;
1224
- }
1225
-
1226
- $options = get_option(self::OPTION_POST_TYPE_OPTIONS, []);
1227
- $keys = explode('.', $post_type.'.'.$name);
1228
- $array = &$options;
1229
-
1230
- foreach ($keys as $key) {
1231
- if (!isset($array[$key]) || !is_array($array[$key])) {
1232
- $array[$key] = [];
1233
- }
1234
- $array = &$array[$key];
1235
- }
1236
- $array = $value;
1237
-
1238
- return update_option(self::OPTION_POST_TYPE_OPTIONS, $options);
1239
- }
1240
- }
1241
- }
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ if (!class_exists('WPCACore')) {
12
+ $domain = explode('/', plugin_basename(__FILE__));
13
+ define('WPCA_DOMAIN', $domain[0]);
14
+ define('WPCA_PATH', plugin_dir_path(__FILE__));
15
+
16
+ /**
17
+ * Core for WordPress Content Aware Engine
18
+ */
19
+ final class WPCACore
20
+ {
21
+ /**
22
+ * Using class prefix instead of namespace
23
+ * for PHP5.2 compatibility
24
+ */
25
+ const CLASS_PREFIX = 'WPCA';
26
+
27
+ /**
28
+ * Prefix for data (keys) stored in database
29
+ */
30
+ const PREFIX = '_ca_';
31
+
32
+ /**
33
+ * Post Type for condition groups
34
+ */
35
+ const TYPE_CONDITION_GROUP = 'condition_group';
36
+
37
+ /**
38
+ * Post Statuses for condition groups
39
+ */
40
+ /**
41
+ * @deprecated
42
+ */
43
+ const STATUS_NEGATED = 'negated';
44
+ /**
45
+ * @deprecated
46
+ */
47
+ const STATUS_PUBLISHED = 'publish';
48
+ const STATUS_OR = 'wpca_or';
49
+ const STATUS_EXCEPT = 'wpca_except';
50
+
51
+ /**
52
+ * Exposures for condition groups
53
+ */
54
+ const EXP_SINGULAR = 0;
55
+ const EXP_SINGULAR_ARCHIVE = 1;
56
+ const EXP_ARCHIVE = 2;
57
+
58
+ /**
59
+ * @deprecated 7.0
60
+ */
61
+ const CAPABILITY = 'edit_theme_options';
62
+
63
+ /**
64
+ * Name for generated nonces
65
+ */
66
+ const NONCE = '_ca_nonce';
67
+
68
+ const OPTION_CONDITION_TYPE_CACHE = '_ca_condition_type_cache';
69
+ const OPTION_POST_TYPE_OPTIONS = '_ca_post_type_options';
70
+
71
+ /**
72
+ * Post Types that use the engine
73
+ * @var WPCATypeManager
74
+ */
75
+ private static $type_manager;
76
+
77
+ /**
78
+ * Conditions retrieved from database
79
+ * @var array
80
+ */
81
+ private static $condition_cache = [];
82
+
83
+ /**
84
+ * Objects retrieved from database
85
+ * @var array
86
+ */
87
+ private static $post_cache = [];
88
+
89
+ private static $wp_query_original = [];
90
+
91
+ private static $filtered_modules = [];
92
+
93
+ /**
94
+ * Constructor
95
+ */
96
+ public static function init()
97
+ {
98
+ spl_autoload_register([__CLASS__,'_autoload_class_files']);
99
+
100
+ if (is_admin()) {
101
+ add_action(
102
+ 'admin_enqueue_scripts',
103
+ [__CLASS__,'add_group_script_styles'],
104
+ 9
105
+ );
106
+ add_action(
107
+ 'delete_post',
108
+ [__CLASS__,'sync_group_deletion']
109
+ );
110
+ add_action(
111
+ 'trashed_post',
112
+ [__CLASS__,'sync_group_trashed']
113
+ );
114
+ add_action(
115
+ 'untrashed_post',
116
+ [__CLASS__,'sync_group_untrashed']
117
+ );
118
+ add_action(
119
+ 'wpca/modules/save-data',
120
+ [__CLASS__,'save_condition_options'],
121
+ 10,
122
+ 3
123
+ );
124
+
125
+ add_action(
126
+ 'wp_ajax_wpca/add-rule',
127
+ [__CLASS__,'ajax_update_group']
128
+ );
129
+ }
130
+
131
+ add_action(
132
+ 'init',
133
+ [__CLASS__,'add_group_post_type'],
134
+ 99
135
+ );
136
+
137
+ add_action(
138
+ 'init',
139
+ [__CLASS__,'schedule_cache_condition_types'],
140
+ 99
141
+ );
142
+
143
+ add_action(
144
+ 'wpca/cache_condition_types',
145
+ [__CLASS__,'cache_condition_types'],
146
+ 999
147
+ );
148
+ }
149
+
150
+ /**
151
+ * Get type manager
152
+ *
153
+ * @since 4.0
154
+ * @return WPCATypeManager
155
+ */
156
+ public static function types()
157
+ {
158
+ if (!isset(self::$type_manager)) {
159
+ self::$type_manager = new WPCATypeManager();
160
+ }
161
+ return self::$type_manager;
162
+ }
163
+
164
+ /**
165
+ * @since 8.0
166
+ *
167
+ * @return void
168
+ */
169
+ public static function schedule_cache_condition_types()
170
+ {
171
+ if (wp_next_scheduled('wpca/cache_condition_types') !== false) {
172
+ return;
173
+ }
174
+
175
+ wp_schedule_event(get_gmt_from_date('today 02:00:00', 'U'), 'daily', 'wpca/cache_condition_types');
176
+ }
177
+
178
+ /**
179
+ * Cache condition types currently in use
180
+ *
181
+ * @since 8.0
182
+ *
183
+ * @return void
184
+ */
185
+ public static function cache_condition_types()
186
+ {
187
+ $all_modules = [];
188
+ $modules_by_type = [];
189
+ $cache = [];
190
+
191
+ $types = self::types();
192
+ foreach ($types as $type => $modules) {
193
+ $modules_by_type[$type] = [];
194
+ $cache[$type] = [];
195
+ foreach ($modules as $module) {
196
+ $modules_by_type[$type][$module->get_data_key()] = $module->get_id();
197
+ $all_modules[$module->get_data_key()] = $module->get_data_key();
198
+ }
199
+ }
200
+
201
+ if (!$all_modules) {
202
+ update_option(self::OPTION_CONDITION_TYPE_CACHE, []);
203
+ return;
204
+ }
205
+
206
+ global $wpdb;
207
+
208
+ $query = '
209
+ SELECT p.post_type, m.meta_key
210
+ FROM ' . $wpdb->posts . ' p
211
+ INNER JOIN ' . $wpdb->posts . ' c ON c.post_parent = p.ID
212
+ INNER JOIN ' . $wpdb->postmeta . ' m ON m.post_id = c.ID
213
+ WHERE p.post_type IN (' . self::sql_prepare_in(array_keys($modules_by_type)) . ')
214
+ AND m.meta_key IN (' . self::sql_prepare_in($all_modules) . ')
215
+ GROUP BY p.post_type, m.meta_key
216
+ ';
217
+
218
+ $results = (array) $wpdb->get_results($query);
219
+
220
+ foreach ($results as $result) {
221
+ if (isset($modules_by_type[$result->post_type][$result->meta_key])) {
222
+ $cache[$result->post_type][] = $modules_by_type[$result->post_type][$result->meta_key];
223
+ }
224
+ }
225
+
226
+ update_option(self::OPTION_CONDITION_TYPE_CACHE, $cache);
227
+ }
228
+
229
+ /**
230
+ * Register group post type
231
+ *
232
+ * @since 1.0
233
+ * @return void
234
+ */
235
+ public static function add_group_post_type()
236
+ {
237
+ //This is just a safety placeholder,
238
+ //authorization will be done with parent object's cap
239
+ $capability = 'edit_theme_options';
240
+ $capabilities = [
241
+ 'edit_post' => $capability,
242
+ 'read_post' => $capability,
243
+ 'delete_post' => $capability,
244
+ 'edit_posts' => $capability,
245
+ 'delete_posts' => $capability,
246
+ 'edit_others_posts' => $capability,
247
+ 'publish_posts' => $capability,
248
+ 'read_private_posts' => $capability
249
+ ];
250
+
251
+ register_post_type(self::TYPE_CONDITION_GROUP, [
252
+ 'labels' => [
253
+ 'name' => __('Condition Groups', WPCA_DOMAIN),
254
+ 'singular_name' => __('Condition Group', WPCA_DOMAIN),
255
+ ],
256
+ 'capabilities' => $capabilities,
257
+ 'public' => false,
258
+ 'hierarchical' => false,
259
+ 'exclude_from_search' => true,
260
+ 'publicly_queryable' => false,
261
+ 'show_ui' => false,
262
+ 'show_in_menu' => false,
263
+ 'show_in_nav_menus' => false,
264
+ 'show_in_admin_bar' => false,
265
+ 'show_in_rest' => false,
266
+ 'has_archive' => false,
267
+ 'rewrite' => false,
268
+ 'query_var' => false,
269
+ 'supports' => ['title'],
270
+ 'can_export' => false,
271
+ 'delete_with_user' => false
272
+ ]);
273
+
274
+ register_post_status(self::STATUS_NEGATED, [
275
+ 'label' => _x('Negated', 'condition status', WPCA_DOMAIN),
276
+ 'public' => false,
277
+ 'exclude_from_search' => true,
278
+ 'show_in_admin_all_list' => false,
279
+ 'show_in_admin_status_list' => false,
280
+ ]);
281
+ register_post_status(self::STATUS_EXCEPT, [
282
+ 'label' => _x('Exception', 'condition status', WPCA_DOMAIN),
283
+ 'public' => false,
284
+ 'exclude_from_search' => true,
285
+ 'show_in_admin_all_list' => false,
286
+ 'show_in_admin_status_list' => false,
287
+ ]);
288
+ register_post_status(self::STATUS_OR, [
289
+ 'label' => _x('Or', 'condition status', WPCA_DOMAIN),
290
+ 'public' => false,
291
+ 'exclude_from_search' => true,
292
+ 'show_in_admin_all_list' => false,
293
+ 'show_in_admin_status_list' => false,
294
+ ]);
295
+ }
296
+
297
+ /**
298
+ * Get group IDs by their parent ID
299
+ *
300
+ * @since 1.0
301
+ * @param int $parent_id
302
+ * @return array
303
+ */
304
+ private static function get_group_ids_by_parent($parent_id)
305
+ {
306
+ if (!self::types()->has(get_post_type($parent_id))) {
307
+ return [];
308
+ }
309
+
310
+ global $wpdb;
311
+ return (array)$wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_parent = '%d'", $parent_id));
312
+ }
313
+
314
+ /**
315
+ * Delete groups from database when their parent is deleted
316
+ *
317
+ * @since 1.0
318
+ * @param int $post_id
319
+ * @return void
320
+ */
321
+ public static function sync_group_deletion($post_id)
322
+ {
323
+ $groups = self::get_group_ids_by_parent($post_id);
324
+ if ($groups) {
325
+ foreach ($groups as $group_id) {
326
+ //Takes care of metadata and terms too
327
+ wp_delete_post($group_id, true);
328
+ }
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Trash groups when their parent is trashed
334
+ *
335
+ * @since 1.0
336
+ * @param int $post_id
337
+ * @return void
338
+ */
339
+ public static function sync_group_trashed($post_id)
340
+ {
341
+ $groups = self::get_group_ids_by_parent($post_id);
342
+ if ($groups) {
343
+ foreach ($groups as $group_id) {
344
+ wp_trash_post($group_id);
345
+ }
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Untrash groups when their parent is untrashed
351
+ *
352
+ * @since 1.0
353
+ * @param int $post_id
354
+ * @return void
355
+ */
356
+ public static function sync_group_untrashed($post_id)
357
+ {
358
+ $groups = self::get_group_ids_by_parent($post_id);
359
+ if ($groups) {
360
+ foreach ($groups as $group_id) {
361
+ wp_untrash_post($group_id);
362
+ }
363
+ }
364
+ }
365
+
366
+ /**
367
+ * @param string $post_type
368
+ * @return array
369
+ */
370
+ public static function get_conditional_modules($post_type)
371
+ {
372
+ if (!isset(self::$filtered_modules[$post_type])) {
373
+ return [];
374
+ }
375
+ return self::$filtered_modules[$post_type];
376
+ }
377
+
378
+ /**
379
+ * Get filtered condition groups
380
+ *
381
+ * @since 2.0
382
+ * @return array
383
+ */
384
+ public static function get_conditions($post_type)
385
+ {
386
+ global $wpdb, $wp_query, $post;
387
+
388
+ if (!self::types()->has($post_type) || (!$wp_query->query && !$post) || is_admin()) {
389
+ return [];
390
+ }
391
+
392
+ // Return cache if present
393
+ if (isset(self::$condition_cache[$post_type])) {
394
+ return self::$condition_cache[$post_type];
395
+ }
396
+
397
+ $where = [];
398
+ $join = [];
399
+
400
+ $cache = [
401
+ $post_type
402
+ ];
403
+
404
+ $modules = self::types()->get($post_type)->all();
405
+ $modules = self::filter_condition_type_cache($post_type, $modules);
406
+
407
+ //avoid combining as long as negated conditions are being deprecated
408
+ // foreach (self::types() as $other_type => $other_modules) {
409
+ // if ($other_type == $post_type) {
410
+ // continue;
411
+ // }
412
+ // if (self::filter_condition_type_cache($other_type, $other_modules->all()) === $modules) {
413
+ // $cache[] = $other_type;
414
+ // }
415
+ // }
416
+
417
+ self::fix_wp_query();
418
+
419
+ $in_context_by_module_id = [];
420
+ foreach ($modules as $module) {
421
+ $id = $module->get_id();
422
+ $in_context = apply_filters("wpca/module/$id/in-context", $module->in_context());
423
+ $in_context_by_module_id[$id] = $in_context;
424
+
425
+ if (!$in_context) {
426
+ continue;
427
+ }
428
+
429
+ $data = $module->get_context_data();
430
+
431
+ if (empty($data)) {
432
+ $in_context_by_module_id[$id] = false;
433
+ continue;
434
+ }
435
+
436
+ if (is_array($data)) {
437
+ $name = $module->get_query_name();
438
+ $data = "($name.meta_value IS NULL OR $name.meta_value IN (" . self::sql_prepare_in($data) . '))';
439
+ }
440
+ $join[$id] = $module->db_join();
441
+ $where[$id] = apply_filters("wpca/module/$id/db-where", $data);
442
+ self::$filtered_modules[$post_type][] = $module;
443
+ }
444
+
445
+ $use_negated_conditions = self::get_option($post_type, 'legacy.negated_conditions', false);
446
+
447
+ // Check if there are any conditions for current content
448
+ $groups_in_context = [];
449
+ if (!empty($where)) {
450
+ $post_status = [
451
+ self::STATUS_PUBLISHED,
452
+ self::STATUS_OR,
453
+ self::STATUS_EXCEPT
454
+ ];
455
+
456
+ if ($use_negated_conditions) {
457
+ $post_status[] = self::STATUS_NEGATED;
458
+ }
459
+
460
+ $chunk_size = count($join);
461
+ if (defined('WPCA_SQL_JOIN_SIZE') && is_integer(WPCA_SQL_JOIN_SIZE) && WPCA_SQL_JOIN_SIZE > 0) {
462
+ $chunk_size = WPCA_SQL_JOIN_SIZE;
463
+ } elseif (defined('WPCA_SQL_COMPATIBILITY_MODE') && WPCA_SQL_COMPATIBILITY_MODE === true) {
464
+ //Syntax changed in MySQL 5.5 and MariaDB 10.0 (reports as version 5.5)
465
+ $wpdb->query('SET' . (version_compare($wpdb->db_version(), '5.5', '>=') ? ' SESSION' : ' OPTION') . ' SQL_BIG_SELECTS = 1');
466
+ }
467
+
468
+ $joins = array_chunk($join, $chunk_size);
469
+ $joins_max = count($joins) - 1;
470
+ $wheres = array_chunk($where, $chunk_size);
471
+ $group_ids = [];
472
+ $groups_in_context = [];
473
+
474
+ $where2 = [];
475
+ $where2[] = "p.post_type = '" . self::TYPE_CONDITION_GROUP . "'";
476
+ $where2[] = "p.post_status IN ('" . implode("','", $post_status) . "')";
477
+ $where2[] = 'p.menu_order ' . (is_archive() || is_home() ? '>=' : '<=') . ' 1';
478
+
479
+ foreach ($joins as $i => $join) {
480
+ if ($i == $joins_max) {
481
+ $groups_in_context = $wpdb->get_results(
482
+ 'SELECT p.ID, p.post_parent, p.post_status ' .
483
+ "FROM $wpdb->posts p " .
484
+ implode(' ', $join) . '
485
+ WHERE
486
+ ' . implode(' AND ', $wheres[$i]) . '
487
+ AND ' . implode(' AND ', $where2) .
488
+ (!empty($group_ids) ? ' AND p.id IN (' . implode(',', $group_ids) . ')' : ''),
489
+ OBJECT_K
490
+ );
491
+ break;
492
+ }
493
+
494
+ $group_ids = array_merge($group_ids, $wpdb->get_col(
495
+ 'SELECT p.ID ' .
496
+ "FROM $wpdb->posts p " .
497
+ implode(' ', $join) . '
498
+ WHERE
499
+ ' . implode(' AND ', $wheres[$i]) . '
500
+ AND ' . implode(' AND ', $where2)
501
+ ));
502
+ }
503
+ }
504
+
505
+ $groups_negated = [];
506
+ if ($use_negated_conditions) {
507
+ $groups_negated = $wpdb->get_results($wpdb->prepare(
508
+ 'SELECT p.ID, p.post_parent ' .
509
+ "FROM $wpdb->posts p " .
510
+ "WHERE p.post_type = '%s' " .
511
+ "AND p.post_status = '%s' ",
512
+ self::TYPE_CONDITION_GROUP,
513
+ self::STATUS_NEGATED
514
+ ), OBJECT_K);
515
+ }
516
+
517
+ if (!empty($groups_in_context) || !empty($groups_negated)) {
518
+ //Force update of meta cache to prevent lazy loading
519
+ update_meta_cache('post', array_keys($groups_in_context + $groups_negated));
520
+ }
521
+
522
+ //Exclude types that have unrelated content in same group
523
+ foreach ($modules as $module) {
524
+ $groups_in_context = $module->filter_excluded_context(
525
+ $groups_in_context,
526
+ $in_context_by_module_id[$module->get_id()]
527
+ );
528
+ }
529
+
530
+ //exclude exceptions
531
+ $excepted = [];
532
+ foreach ($groups_in_context as $group_id => $group) {
533
+ if ($group->post_status == self::STATUS_EXCEPT) {
534
+ $excepted[$group->post_parent] = 1;
535
+ }
536
+ }
537
+
538
+ //condition group => type
539
+ $valid = [];
540
+ foreach ($groups_in_context as $group) {
541
+ $valid[$group->ID] = $group->post_parent;
542
+ }
543
+
544
+ foreach ($valid as $group_id => $parent_id) {
545
+ if (isset($excepted[$parent_id])) {
546
+ unset($valid[$group_id]);
547
+ }
548
+ }
549
+
550
+ if ($use_negated_conditions) {
551
+ //Filter negated groups
552
+ //type => group
553
+ $handled_already = array_flip($valid);
554
+ foreach ($groups_negated as $group) {
555
+ if (isset($valid[$group->ID])) {
556
+ unset($valid[$group->ID]);
557
+ } else {
558
+ $valid[$group->ID] = $group->post_parent;
559
+ }
560
+ if (isset($handled_already[$group->post_parent])) {
561
+ unset($valid[$group->ID]);
562
+ }
563
+ $handled_already[$group->post_parent] = 1;
564
+ }
565
+ }
566
+
567
+ self::restore_wp_query();
568
+
569
+ foreach ($cache as $cache_type) {
570
+ self::$condition_cache[$cache_type] = $valid;
571
+ }
572
+
573
+ return self::$condition_cache[$post_type];
574
+ }
575
+
576
+ /**
577
+ * Get filtered posts from a post type
578
+ *
579
+ * @since 1.0
580
+ * @return array|bool
581
+ */
582
+ public static function get_posts($post_type)
583
+ {
584
+ if (isset(self::$post_cache[$post_type])) {
585
+ return self::$post_cache[$post_type];
586
+ }
587
+
588
+ $valid = self::get_conditions($post_type);
589
+
590
+ //if cache hasn't been set, method was called too early
591
+ if (!isset(self::$condition_cache[$post_type])) {
592
+ return false;
593
+ }
594
+
595
+ self::$post_cache[$post_type] = [];
596
+
597
+ $results = [];
598
+
599
+ if (!empty($valid)) {
600
+ $data = new WP_Query([
601
+ 'post__in' => array_values($valid),
602
+ 'post_type' => $post_type,
603
+ 'post_status' => 'publish',
604
+ 'posts_per_page' => -1,
605
+ 'ignore_sticky_posts' => true,
606
+ 'update_post_term_cache' => false,
607
+ 'update_post_meta_cache' => true,
608
+ 'suppress_filters' => true,
609
+ 'no_found_rows' => true,
610
+ 'orderby' => 'none'
611
+ ]);
612
+
613
+ $results = array_merge($results, $data->posts);
614
+ }
615
+
616
+ //legacy sorting
617
+ uasort($results, function (WP_Post $post_a, WP_Post $post_b) {
618
+ //asc
619
+ if ($post_a->menu_order != $post_b->menu_order) {
620
+ return $post_a->menu_order < $post_b->menu_order ? -1 : 1;
621
+ }
622
+
623
+ $post_a_handle = get_post_meta($post_a->ID, '_ca_handle', true);
624
+ $post_b_handle = get_post_meta($post_b->ID, '_ca_handle', true);
625
+
626
+ //desc
627
+ if ($post_a_handle != $post_b_handle) {
628
+ return $post_a_handle > $post_b_handle ? -1 : 1;
629
+ }
630
+
631
+ //desc
632
+ if ($post_a->post_date != $post_b->post_date) {
633
+ return $post_a->post_date > $post_b->post_date ? -1 : 1;
634
+ }
635
+
636
+ return 0;
637
+ });
638
+
639
+ $results = array_reduce($results, function ($carry, $post) {
640
+ $carry[$post->ID] = (object)[
641
+ 'ID' => $post->ID,
642
+ 'post_type' => $post->post_type,
643
+ 'handle' => get_post_meta($post->ID, '_ca_handle', true),
644
+ 'menu_order' => $post->menu_order,
645
+ 'post_date' => $post->post_date
646
+ ];
647
+ return $carry;
648
+ }, []);
649
+
650
+ self::$post_cache[$post_type] = apply_filters("wpca/posts/$post_type", $results);
651
+
652
+ return self::$post_cache[$post_type];
653
+ }
654
+
655
+ public static function render_group_meta_box($post, $screen, $context = 'normal', $priority = 'default')
656
+ {
657
+ if (!($post instanceof WP_Post) || !self::types()->has($post->post_type)) {
658
+ return;
659
+ }
660
+
661
+ $post_type_obj = get_post_type_object($post->post_type);
662
+
663
+ if (!current_user_can($post_type_obj->cap->edit_post, $post->ID)) {
664
+ return;
665
+ }
666
+
667
+ $template = WPCAView::make('condition_options', [
668
+ 'post_type' => $post->post_type
669
+ ]);
670
+ add_action('wpca/group/settings', [$template,'render'], -1, 2);
671
+
672
+ $template = WPCAView::make('group_template', [
673
+ 'post_type' => $post->post_type
674
+ ]);
675
+ add_action('admin_footer', [$template,'render']);
676
+
677
+ $template = WPCAView::make('condition_template');
678
+ add_action('admin_footer', [$template,'render']);
679
+
680
+ $view = WPCAView::make('meta_box', [
681
+ 'post_type' => $post->post_type,
682
+ 'nonce' => wp_nonce_field(self::PREFIX . $post->ID, self::NONCE, true, false),
683
+ ]);
684
+
685
+ $title = isset($post_type_obj->labels->ca_title) ? $post_type_obj->labels->ca_title : __('Conditional Logic', WPCA_DOMAIN);
686
+
687
+ add_meta_box(
688
+ 'cas-rules',
689
+ $title,
690
+ [$view,'render'],
691
+ $screen,
692
+ $context,
693
+ $priority
694
+ );
695
+ }
696
+
697
+ /**
698
+ * Insert new condition group for a post type
699
+ * Uses current post per default
700
+ *
701
+ * @param int|null $post_id
702
+ * @return int|WP_Error
703
+ */
704
+ public static function add_condition_group($post_id = null)
705
+ {
706
+ $post = get_post($post_id);
707
+
708
+ //Make sure to go from auto-draft to draft
709
+ if ($post->post_status == 'auto-draft') {
710
+ wp_update_post([
711
+ 'ID' => $post->ID,
712
+ 'post_title' => '',
713
+ 'post_status' => 'draft'
714
+ ]);
715
+ }
716
+
717
+ return wp_insert_post([
718
+ 'post_status' => self::STATUS_OR,
719
+ 'menu_order' => self::EXP_SINGULAR_ARCHIVE,
720
+ 'post_type' => self::TYPE_CONDITION_GROUP,
721
+ 'post_author' => $post->post_author,
722
+ 'post_parent' => $post->ID,
723
+ ]);
724
+ }
725
+
726
+ /**
727
+ * Get condition groups for a post type
728
+ * Uses current post per default
729
+ *
730
+ * @since 1.0
731
+ * @param WP_Post|int $post_id
732
+ * @return array
733
+ */
734
+ private static function get_condition_groups($post_id = null)
735
+ {
736
+ $post = get_post($post_id);
737
+ $groups = [];
738
+
739
+ if ($post) {
740
+ $groups = get_posts([
741
+ 'posts_per_page' => -1,
742
+ 'post_type' => self::TYPE_CONDITION_GROUP,
743
+ 'post_parent' => $post->ID,
744
+ 'post_status' => [self::STATUS_PUBLISHED,self::STATUS_NEGATED,self::STATUS_EXCEPT, self::STATUS_OR],
745
+ 'order' => 'DESC',
746
+ 'orderby' => 'post_status'
747
+ ]);
748
+ }
749
+ return $groups;
750
+ }
751
+
752
+ /**
753
+ * AJAX callback to update a condition group
754
+ *
755
+ * @since 1.0
756
+ * @return void
757
+ */
758
+ public static function ajax_update_group()
759
+ {
760
+ if (!isset($_POST['current_id']) ||
761
+ !check_ajax_referer(self::PREFIX . $_POST['current_id'], 'token', false)) {
762
+ wp_send_json_error(__('Unauthorized request', WPCA_DOMAIN), 403);
763
+ }
764
+
765
+ $parent_id = (int)$_POST['current_id'];
766
+ $parent_type = get_post_type_object($_POST['post_type']);
767
+
768
+ if (!current_user_can($parent_type->cap->edit_post, $parent_id)) {
769
+ wp_send_json_error(__('Unauthorized request', WPCA_DOMAIN), 403);
770
+ }
771
+
772
+ $response = [
773
+ 'message' => __('Conditions updated', WPCA_DOMAIN)
774
+ ];
775
+
776
+ //Make sure some rules are sent
777
+ if (!isset($_POST['conditions'])) {
778
+ //Otherwise we delete group
779
+ if ($_POST['id'] && wp_delete_post(intval($_POST['id']), true) === false) {
780
+ wp_send_json_error(__('Could not delete conditions', WPCA_DOMAIN), 500);
781
+ }
782
+
783
+ $response['removed'] = true;
784
+ wp_send_json($response);
785
+ }
786
+
787
+ //If ID was not sent at this point, it is a new group
788
+ if (!$_POST['id']) {
789
+ $post_id = self::add_condition_group($parent_id);
790
+ $response['new_post_id'] = $post_id;
791
+ } else {
792
+ $post_id = (int)$_POST['id'];
793
+ }
794
+
795
+ wp_update_post([
796
+ 'ID' => $post_id,
797
+ 'post_status' => self::sanitize_status($_POST['status']),
798
+ 'menu_order' => (int)$_POST['exposure']
799
+ ]);
800
+
801
+ //Prune condition type cache, will rebuild within 24h
802
+ update_option(self::OPTION_CONDITION_TYPE_CACHE, []);
803
+
804
+ foreach (self::types()->get($parent_type->name)->all() as $module) {
805
+ //send $_POST here
806
+ $module->save_data($post_id);
807
+ }
808
+
809
+ do_action('wpca/modules/save-data', $post_id, $parent_type->name);
810
+
811
+ wp_send_json($response);
812
+ }
813
+
814
+ /**
815
+ * @param string $status
816
+ *
817
+ * @return string
818
+ */
819
+ private static function sanitize_status($status)
820
+ {
821
+ switch ($status) {
822
+ case self::STATUS_NEGATED:
823
+ return self::STATUS_NEGATED;
824
+ case self::STATUS_EXCEPT:
825
+ return self::STATUS_EXCEPT;
826
+ case self::STATUS_OR:
827
+ case self::STATUS_PUBLISHED:
828
+ default:
829
+ return self::STATUS_OR;
830
+ }
831
+ }
832
+
833
+ /**
834
+ * Save registered meta for condition group
835
+ *
836
+ * @since 3.2
837
+ * @param int $group_id
838
+ * @return void
839
+ */
840
+ public static function save_condition_options($group_id, $post_type)
841
+ {
842
+ $meta_keys = self::get_condition_meta_keys($post_type);
843
+ foreach ($meta_keys as $key => $default_value) {
844
+ $value = isset($_POST[$key]) ? $_POST[$key] : false;
845
+ if ($value) {
846
+ update_post_meta($group_id, $key, $value);
847
+ } elseif (get_post_meta($group_id, $key, true)) {
848
+ delete_post_meta($group_id, $key);
849
+ }
850
+ }
851
+ }
852
+
853
+ public static function add_group_script_styles($hook)
854
+ {
855
+ $current_screen = get_current_screen();
856
+
857
+ wp_register_style(
858
+ self::PREFIX . 'condition-groups',
859
+ plugins_url('/assets/css/condition_groups.css', __FILE__),
860
+ [],
861
+ WPCA_VERSION
862
+ );
863
+
864
+ if (self::types()->has($current_screen->post_type) && $current_screen->base == 'post') {
865
+ self::enqueue_scripts_styles($current_screen->post_type);
866
+ }
867
+ }
868
+
869
+ /**
870
+ * Get condition option defaults
871
+ *
872
+ * @since 3.2
873
+ * @param string $post_type
874
+ * @return array
875
+ */
876
+ public static function get_condition_meta_keys($post_type)
877
+ {
878
+ $group_meta = [
879
+ '_ca_autoselect' => 0
880
+ ];
881
+ return apply_filters('wpca/condition/meta', $group_meta, $post_type);
882
+ }
883
+
884
+ /**
885
+ * Register and enqueue scripts and styles
886
+ * for post edit screen
887
+ *
888
+ * @since 1.0
889
+ *
890
+ * @param string $post_type
891
+ */
892
+ public static function enqueue_scripts_styles($post_type = '')
893
+ {
894
+ $post_type = empty($post_type) ? get_post_type() : $post_type;
895
+
896
+ $group_meta = self::get_condition_meta_keys($post_type);
897
+
898
+ $groups = self::get_condition_groups();
899
+ $data = [];
900
+ $i = 0;
901
+ foreach ($groups as $group) {
902
+ $data[$i] = [
903
+ 'id' => $group->ID,
904
+ 'status' => $group->post_status,
905
+ 'exposure' => $group->menu_order,
906
+ 'conditions' => []
907
+ ];
908
+
909
+ foreach (self::types()->get($post_type)->all() as $module) {
910
+ $data[$i]['conditions'] = $module->get_group_data($data[$i]['conditions'], $group->ID);
911
+ }
912
+
913
+ foreach ($group_meta as $meta_key => $default_value) {
914
+ $value = get_post_meta($group->ID, $meta_key, true);
915
+ if ($value === false) {
916
+ $value = $default_value;
917
+ }
918
+ $data[$i][$meta_key] = $value;
919
+ }
920
+ $i++;
921
+ }
922
+
923
+ $conditions = [
924
+ 'general' => [
925
+ 'text' => __('General'),
926
+ 'children' => []
927
+ ],
928
+ 'post_type' => [
929
+ 'text' => __('Post Types'),
930
+ 'children' => []
931
+ ],
932
+ 'taxonomy' => [
933
+ 'text' => __('Taxonomies'),
934
+ 'children' => []
935
+ ],
936
+ 'plugins' => [
937
+ 'text' => __('Plugins'),
938
+ 'children' => []
939
+ ]
940
+ ];
941
+
942
+ foreach (self::types()->get($post_type)->all() as $module) {
943
+ $category = $module->get_category();
944
+ if (!isset($conditions[$category])) {
945
+ $category = 'general';
946
+ }
947
+
948
+ //array_values used for backwards compatibility
949
+ $conditions[$category]['children'] = array_values($module->list_module($conditions[$category]['children']));
950
+ }
951
+
952
+ foreach ($conditions as $key => $condition) {
953
+ if (empty($condition['children'])) {
954
+ unset($conditions[$key]);
955
+ }
956
+ }
957
+
958
+ //Make sure to use packaged version
959
+ if (wp_script_is('select2', 'registered')) {
960
+ wp_deregister_script('select2');
961
+ wp_deregister_style('select2');
962
+ }
963
+
964
+ $plugins_url = plugins_url('', __FILE__);
965
+
966
+ //Add to head to take priority
967
+ //if being added under other name
968
+ wp_register_script(
969
+ 'select2',
970
+ $plugins_url . '/assets/js/select2.min.js',
971
+ ['jquery'],
972
+ '4.0.3',
973
+ false
974
+ );
975
+
976
+ wp_register_script(
977
+ 'backbone.trackit',
978
+ $plugins_url . '/assets/js/backbone.trackit.min.js',
979
+ ['backbone'],
980
+ '0.1.0',
981
+ true
982
+ );
983
+
984
+ wp_register_script(
985
+ 'backbone.epoxy',
986
+ $plugins_url . '/assets/js/backbone.epoxy.min.js',
987
+ ['backbone'],
988
+ '1.3.3',
989
+ true
990
+ );
991
+
992
+ wp_register_script(
993
+ self::PREFIX . 'condition-groups',
994
+ $plugins_url . '/assets/js/condition_groups.min.js',
995
+ ['jquery','select2','backbone.trackit','backbone.epoxy'],
996
+ WPCA_VERSION,
997
+ true
998
+ );
999
+
1000
+ wp_enqueue_script(self::PREFIX . 'condition-groups');
1001
+ wp_localize_script(self::PREFIX . 'condition-groups', 'WPCA', [
1002
+ 'searching' => __('Searching', WPCA_DOMAIN),
1003
+ 'noResults' => __('No results found.', WPCA_DOMAIN),
1004
+ 'loadingMore' => __('Loading more results', WPCA_DOMAIN),
1005
+ 'unsaved' => __('Conditions have unsaved changes. Do you want to continue and discard these changes?', WPCA_DOMAIN),
1006
+ 'newGroup' => __('New condition group', WPCA_DOMAIN),
1007
+ 'newCondition' => __('Meet ALL of these conditions', WPCA_DOMAIN),
1008
+ 'conditions' => array_values($conditions),
1009
+ 'groups' => $data,
1010
+ 'meta_default' => $group_meta,
1011
+ 'post_type' => $post_type,
1012
+ 'text_direction' => is_rtl() ? 'rtl' : 'ltr',
1013
+ 'condition_not' => __('Not', WPCA_DOMAIN),
1014
+ 'condition_or' => __('Or', WPCA_DOMAIN),
1015
+ 'condition_except' => __('Except', WPCA_DOMAIN)
1016
+ ]);
1017
+ wp_enqueue_style(self::PREFIX . 'condition-groups');
1018
+
1019
+ //@todo remove when ultimate member includes fix
1020
+ wp_dequeue_style('um_styles');
1021
+
1022
+ //@todo remove when events calendar pro plugin includes fix
1023
+ wp_dequeue_script('tribe-select2');
1024
+ }
1025
+
1026
+ /**
1027
+ * Modify wp_query for plugin compatibility
1028
+ *
1029
+ * @since 5.0
1030
+ * @return void
1031
+ */
1032
+ private static function fix_wp_query()
1033
+ {
1034
+ $query = [];
1035
+
1036
+ //When themes don't declare WooCommerce support,
1037
+ //conditionals are not working properly for Shop
1038
+ if (defined('WOOCOMMERCE_VERSION') && function_exists('is_shop') && is_shop() && !is_post_type_archive('product')) {
1039
+ $query = [
1040
+ 'is_archive' => true,
1041
+ 'is_post_type_archive' => true,
1042
+ 'is_page' => false,
1043
+ 'is_singular' => false,
1044
+ 'query_vars' => [
1045
+ 'post_type' => 'product'
1046
+ ]
1047
+ ];
1048
+ }
1049
+
1050
+ self::set_wp_query($query);
1051
+ }
1052
+
1053
+ /**
1054
+ * Restore original wp_query
1055
+ *
1056
+ * @since 5.0
1057
+ * @return void
1058
+ */
1059
+ private static function restore_wp_query()
1060
+ {
1061
+ self::set_wp_query(self::$wp_query_original);
1062
+ self::$wp_query_original = [];
1063
+ }
1064
+
1065
+ /**
1066
+ * Set properties in wp_query and save original value
1067
+ *
1068
+ * @since 5.0
1069
+ * @param array $query
1070
+ */
1071
+ private static function set_wp_query($query)
1072
+ {
1073
+ global $wp_query;
1074
+ foreach ($query as $key => $val) {
1075
+ $is_array = is_array($val);
1076
+
1077
+ if (!isset(self::$wp_query_original[$key])) {
1078
+ self::$wp_query_original[$key] = $is_array ? [] : $wp_query->$key;
1079
+ }
1080
+
1081
+ if ($is_array) {
1082
+ foreach ($val as $k1 => $v1) {
1083
+ if (!isset(self::$wp_query_original[$key][$k1])) {
1084
+ self::$wp_query_original[$key][$k1] = $wp_query->{$key}[$k1];
1085
+ }
1086
+ $wp_query->{$key}[$k1] = $v1;
1087
+ }
1088
+ } else {
1089
+ $wp_query->$key = $val;
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ /**
1095
+ * Autoload class files
1096
+ *
1097
+ * @since 1.0
1098
+ * @param string $class
1099
+ * @return void
1100
+ */
1101
+ private static function _autoload_class_files($class)
1102
+ {
1103
+ if (strpos($class, self::CLASS_PREFIX) === 0) {
1104
+ $class = str_replace(self::CLASS_PREFIX, '', $class);
1105
+ $class = self::str_replace_first('_', '/', $class);
1106
+ $class = strtolower($class);
1107
+ $file = WPCA_PATH . $class . '.php';
1108
+ if (file_exists($file)) {
1109
+ include $file;
1110
+ }
1111
+ }
1112
+ }
1113
+
1114
+ /**
1115
+ * Helper function to replace first
1116
+ * occurence of substring
1117
+ *
1118
+ * @since 1.0
1119
+ * @param string $search
1120
+ * @param string $replace
1121
+ * @param string $subject
1122
+ * @return string
1123
+ */
1124
+ private static function str_replace_first($search, $replace, $subject)
1125
+ {
1126
+ $pos = strpos($subject, $search);
1127
+ if ($pos !== false) {
1128
+ $subject = substr_replace($subject, $replace, $pos, strlen($search));
1129
+ }
1130
+ return $subject;
1131
+ }
1132
+
1133
+ /**
1134
+ * @since 8.0
1135
+ * @param array $input
1136
+ *
1137
+ * @return string
1138
+ */
1139
+ public static function sql_prepare_in($input)
1140
+ {
1141
+ $output = array_map(function ($value) {
1142
+ return "'" . esc_sql($value) . "'";
1143
+ }, $input);
1144
+ return implode(',', $output);
1145
+ }
1146
+
1147
+ /**
1148
+ * @since 8.0
1149
+ * @param string $type
1150
+ * @param array $modules
1151
+ *
1152
+ * @return array
1153
+ */
1154
+ private static function filter_condition_type_cache($type, $modules)
1155
+ {
1156
+ $included_conditions = get_option(self::OPTION_CONDITION_TYPE_CACHE, []);
1157
+
1158
+ if (!$included_conditions || !isset($included_conditions[$type])) {
1159
+ return $modules;
1160
+ }
1161
+
1162
+ $included_conditions_lookup = array_flip($included_conditions[$type]);
1163
+ $filtered_modules = [];
1164
+
1165
+ foreach ($modules as $module) {
1166
+ if (isset($included_conditions_lookup[$module->get_id()])) {
1167
+ $filtered_modules[] = $module;
1168
+ }
1169
+ }
1170
+
1171
+ return $filtered_modules;
1172
+ }
1173
+
1174
+ /**
1175
+ * @param string $post_type
1176
+ * @param string $name
1177
+ * @param mixed|null $default_value
1178
+ *
1179
+ * @return mixed|null
1180
+ */
1181
+ public static function get_option($post_type, $name, $default_value = null)
1182
+ {
1183
+ if (!self::types()->has($post_type)) {
1184
+ return $default_value;
1185
+ }
1186
+
1187
+ $value = get_option(self::OPTION_POST_TYPE_OPTIONS, []);
1188
+ $levels = explode('.', $post_type . '.' . $name);
1189
+
1190
+ foreach ($levels as $option_level) {
1191
+ if (!is_array($value) || !isset($value[$option_level])) {
1192
+ return $default_value;
1193
+ }
1194
+ $value = $value[$option_level];
1195
+ }
1196
+ return $value;
1197
+ }
1198
+
1199
+ /**
1200
+ * @param string $post_type
1201
+ * @param string $name
1202
+ * @param mixed $value
1203
+ *
1204
+ * @return bool
1205
+ */
1206
+ public static function save_option($post_type, $name, $value)
1207
+ {
1208
+ if (!self::types()->has($post_type)) {
1209
+ return false;
1210
+ }
1211
+
1212
+ $options = get_option(self::OPTION_POST_TYPE_OPTIONS, []);
1213
+ $keys = explode('.', $post_type . '.' . $name);
1214
+ $array = &$options;
1215
+
1216
+ foreach ($keys as $key) {
1217
+ if (!isset($array[$key]) || !is_array($array[$key])) {
1218
+ $array[$key] = [];
1219
+ }
1220
+ $array = &$array[$key];
1221
+ }
1222
+ $array = $value;
1223
+
1224
+ return update_option(self::OPTION_POST_TYPE_OPTIONS, $options);
1225
+ }
1226
+ }
1227
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/wp-content-aware-engine/meta.php CHANGED
@@ -1,238 +1,236 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- if (!class_exists('WPCAMeta')) {
12
- /**
13
- * Post Meta
14
- */
15
- class WPCAMeta
16
- {
17
-
18
- /**
19
- * Id
20
- * @var string
21
- */
22
- private $id;
23
-
24
- /**
25
- * Title
26
- * @var string
27
- */
28
- private $title;
29
-
30
- /**
31
- * Description
32
- * @var string
33
- */
34
- private $description;
35
-
36
- /**
37
- * Default value
38
- * @var mixed
39
- */
40
- private $default_value;
41
-
42
- /**
43
- * Input type
44
- * @var string
45
- */
46
- private $input_type;
47
-
48
- /**
49
- * Input list
50
- * @var string
51
- */
52
- private $input_list;
53
-
54
- /**
55
- * Callback to sanitize data before save
56
- * @var func
57
- */
58
- private $sanitizer;
59
-
60
- /**
61
- * Constructor
62
- *
63
- * @since 1.0
64
- */
65
- public function __construct(
66
- $id,
67
- $title,
68
- $default_value = '',
69
- $input_type = 'text',
70
- $input_list = [],
71
- $description = '',
72
- $sanitizer = ''
73
- ) {
74
- $this->id = $id;
75
- $this->title = $title;
76
- $this->default_value = $default_value;
77
- $this->input_type = $input_type;
78
- $this->input_list = $input_list;
79
- $this->description = $description;
80
- $this->sanitizer = $sanitizer;
81
- }
82
-
83
- /**
84
- * Get meta id
85
- *
86
- * @since 1.0
87
- * @return string
88
- */
89
- public function get_id()
90
- {
91
- return $this->id;
92
- }
93
- /**
94
- * Get meta title
95
- *
96
- * @since 1.0
97
- * @return string
98
- */
99
- public function get_title()
100
- {
101
- return $this->title;
102
- }
103
-
104
- /**
105
- * Get meta input type
106
- *
107
- * @since 1.0
108
- * @return string
109
- */
110
- public function get_input_type()
111
- {
112
- return $this->input_type;
113
- }
114
-
115
- /**
116
- * Get meta input list
117
- *
118
- * @since 1.0
119
- * @return array
120
- */
121
- public function get_input_list()
122
- {
123
- return $this->input_list;
124
- }
125
-
126
- /**
127
- * Set meta input list
128
- *
129
- * @since 1.0
130
- * @param array $input_list
131
- */
132
- public function set_input_list($input_list)
133
- {
134
- $this->input_list = $input_list;
135
- }
136
-
137
- /**
138
- * Get this meta data for a post
139
- *
140
- * @since 1.0
141
- * @param int $post_id
142
- * @param boolean $default_fallback
143
- * @param boolean $single
144
- * @return mixed
145
- */
146
- public function get_data($post_id, $default_fallback = false, $single = true)
147
- {
148
- $data = get_post_meta($post_id, WPCACore::PREFIX . $this->id, $single);
149
- if ($data == '' && $default_fallback) {
150
- $data = $this->default_value;
151
- }
152
- return $data;
153
- }
154
-
155
- /**
156
- * Update this meta data for a post
157
- *
158
- * @since 1.0
159
- * @param int $post_id
160
- * @param string $value
161
- * @return void
162
- */
163
- public function update($post_id, $value)
164
- {
165
- if ($this->input_type != 'multi') {
166
- update_post_meta($post_id, WPCACore::PREFIX . $this->id, $value);
167
- } else {
168
- add_post_meta($post_id, WPCACore::PREFIX . $this->id, $value);
169
- }
170
- }
171
-
172
- /**
173
- * Delete this meta data for a post
174
- *
175
- * @since 1.0
176
- * @param int $post_id
177
- * @param string $value
178
- * @return void
179
- */
180
- public function delete($post_id, $value = '')
181
- {
182
- delete_post_meta($post_id, WPCACore::PREFIX . $this->id, $value);
183
- }
184
-
185
- /**
186
- * Save data based on POST
187
- *
188
- * @since 4.2
189
- * @param int $post_id
190
- * @param string $value
191
- * @return void
192
- */
193
- public function save($post_id)
194
- {
195
- $value = isset($_POST[$this->id]) ? $_POST[$this->id] : '';
196
- if ($this->sanitizer && is_callable($this->sanitizer)) {
197
- $value = call_user_func($this->sanitizer, $value);
198
- }
199
- if ($this->input_type != 'multi') {
200
- //value can be 0 and valid
201
- if ($value != '') {
202
- $this->update($post_id, $value);
203
- } elseif ($this->get_data($post_id, false, true) != '') {
204
- $this->delete($post_id);
205
- }
206
- } else {
207
- $old = array_flip($this->get_data($post_id, false, false));
208
- if (is_array($value)) {
209
- foreach ($value as $meta) {
210
- if (isset($old[$meta])) {
211
- unset($old[$meta]);
212
- } else {
213
- $this->update($post_id, $meta);
214
- }
215
- }
216
- }
217
-
218
- foreach ($old as $meta => $v) {
219
- $this->delete($post_id, $meta);
220
- }
221
- }
222
- }
223
-
224
- /**
225
- * Get this meta data for a post
226
- * represented by entry in input list
227
- *
228
- * @since 1.0
229
- * @param int $post_id
230
- * @return mixed
231
- */
232
- public function get_list_data($post_id, $default_fallback = true)
233
- {
234
- $data = $this->get_data($post_id, $default_fallback);
235
- return isset($this->input_list[$data]) ? $this->input_list[$data] : null;
236
- }
237
- }
238
- }
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ if (!class_exists('WPCAMeta')) {
12
+ /**
13
+ * Post Meta
14
+ */
15
+ class WPCAMeta
16
+ {
17
+ /**
18
+ * Id
19
+ * @var string
20
+ */
21
+ private $id;
22
+
23
+ /**
24
+ * Title
25
+ * @var string
26
+ */
27
+ private $title;
28
+
29
+ /**
30
+ * Description
31
+ * @var string
32
+ */
33
+ private $description;
34
+
35
+ /**
36
+ * Default value
37
+ * @var mixed
38
+ */
39
+ private $default_value;
40
+
41
+ /**
42
+ * Input type
43
+ * @var string
44
+ */
45
+ private $input_type;
46
+
47
+ /**
48
+ * Input list
49
+ * @var string
50
+ */
51
+ private $input_list;
52
+
53
+ /**
54
+ * Callback to sanitize data before save
55
+ * @var func
56
+ */
57
+ private $sanitizer;
58
+
59
+ /**
60
+ * Constructor
61
+ *
62
+ * @since 1.0
63
+ */
64
+ public function __construct(
65
+ $id,
66
+ $title,
67
+ $default_value = '',
68
+ $input_type = 'text',
69
+ $input_list = [],
70
+ $description = '',
71
+ $sanitizer = ''
72
+ ) {
73
+ $this->id = $id;
74
+ $this->title = $title;
75
+ $this->default_value = $default_value;
76
+ $this->input_type = $input_type;
77
+ $this->input_list = $input_list;
78
+ $this->description = $description;
79
+ $this->sanitizer = $sanitizer;
80
+ }
81
+
82
+ /**
83
+ * Get meta id
84
+ *
85
+ * @since 1.0
86
+ * @return string
87
+ */
88
+ public function get_id()
89
+ {
90
+ return $this->id;
91
+ }
92
+
93
+ /**
94
+ * Get meta title
95
+ *
96
+ * @since 1.0
97
+ * @return string
98
+ */
99
+ public function get_title()
100
+ {
101
+ return $this->title;
102
+ }
103
+
104
+ /**
105
+ * Get meta input type
106
+ *
107
+ * @since 1.0
108
+ * @return string
109
+ */
110
+ public function get_input_type()
111
+ {
112
+ return $this->input_type;
113
+ }
114
+
115
+ /**
116
+ * Get meta input list
117
+ *
118
+ * @since 1.0
119
+ * @return array
120
+ */
121
+ public function get_input_list()
122
+ {
123
+ return $this->input_list;
124
+ }
125
+
126
+ /**
127
+ * Set meta input list
128
+ *
129
+ * @since 1.0
130
+ * @param array $input_list
131
+ */
132
+ public function set_input_list($input_list)
133
+ {
134
+ $this->input_list = $input_list;
135
+ }
136
+
137
+ /**
138
+ * Get this meta data for a post
139
+ *
140
+ * @since 1.0
141
+ * @param int $post_id
142
+ * @param boolean $default_fallback
143
+ * @param boolean $single
144
+ * @return mixed
145
+ */
146
+ public function get_data($post_id, $default_fallback = false, $single = true)
147
+ {
148
+ $data = get_post_meta($post_id, WPCACore::PREFIX . $this->id, $single);
149
+ if ($data == '' && $default_fallback) {
150
+ $data = $this->default_value;
151
+ }
152
+ return $data;
153
+ }
154
+
155
+ /**
156
+ * Update this meta data for a post
157
+ *
158
+ * @since 1.0
159
+ * @param int $post_id
160
+ * @param string $value
161
+ * @return void
162
+ */
163
+ public function update($post_id, $value)
164
+ {
165
+ if ($this->input_type != 'multi') {
166
+ update_post_meta($post_id, WPCACore::PREFIX . $this->id, $value);
167
+ } else {
168
+ add_post_meta($post_id, WPCACore::PREFIX . $this->id, $value);
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Delete this meta data for a post
174
+ *
175
+ * @since 1.0
176
+ * @param int $post_id
177
+ * @param string $value
178
+ * @return void
179
+ */
180
+ public function delete($post_id, $value = '')
181
+ {
182
+ delete_post_meta($post_id, WPCACore::PREFIX . $this->id, $value);
183
+ }
184
+
185
+ /**
186
+ * Save data based on POST
187
+ *
188
+ * @since 4.2
189
+ * @param int $post_id
190
+ */
191
+ public function save($post_id)
192
+ {
193
+ $value = isset($_POST[$this->id]) ? $_POST[$this->id] : '';
194
+ if ($this->sanitizer && is_callable($this->sanitizer)) {
195
+ $value = call_user_func($this->sanitizer, $value);
196
+ }
197
+ if ($this->input_type != 'multi') {
198
+ //value can be 0 and valid
199
+ if ($value != '') {
200
+ $this->update($post_id, $value);
201
+ } elseif ($this->get_data($post_id, false, true) != '') {
202
+ $this->delete($post_id);
203
+ }
204
+ } else {
205
+ $old = array_flip($this->get_data($post_id, false, false));
206
+ if (is_array($value)) {
207
+ foreach ($value as $meta) {
208
+ if (isset($old[$meta])) {
209
+ unset($old[$meta]);
210
+ } else {
211
+ $this->update($post_id, $meta);
212
+ }
213
+ }
214
+ }
215
+
216
+ foreach ($old as $meta => $v) {
217
+ $this->delete($post_id, $meta);
218
+ }
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Get this meta data for a post
224
+ * represented by entry in input list
225
+ *
226
+ * @since 1.0
227
+ * @param int $post_id
228
+ * @return mixed
229
+ */
230
+ public function get_list_data($post_id, $default_fallback = true)
231
+ {
232
+ $data = $this->get_data($post_id, $default_fallback);
233
+ return isset($this->input_list[$data]) ? $this->input_list[$data] : null;
234
+ }
235
+ }
236
+ }
 
 
lib/wp-content-aware-engine/module/author.php CHANGED
@@ -1,96 +1,97 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- /**
12
- *
13
- * Author Module
14
- *
15
- * Detects if current content is:
16
- * a) post type written by any or specific author
17
- * b) any or specific author archive
18
- *
19
- */
20
- class WPCAModule_author extends WPCAModule_Base
21
- {
22
- public function __construct()
23
- {
24
- parent::__construct('author', __('Authors', WPCA_DOMAIN));
25
- $this->placeholder = __('All Authors', WPCA_DOMAIN);
26
- $this->default_value = $this->id;
27
- $this->query_name = 'ca';
28
- }
29
-
30
- /**
31
- * @inheritDoc
32
- */
33
- public function in_context()
34
- {
35
- return (is_singular() && !is_front_page()) || is_author();
36
- }
37
-
38
- /**
39
- * @inheritDoc
40
- */
41
- public function get_context_data()
42
- {
43
- global $post;
44
- return [
45
- $this->id,
46
- (string)(is_singular() ? $post->post_author : get_query_var('author'))
47
- ];
48
- }
49
-
50
- /**
51
- * @inheritDoc
52
- */
53
- protected function parse_query_args($args)
54
- {
55
- $new_args = [
56
- 'number' => $args['limit'],
57
- 'offset' => ($args['paged'] - 1) * $args['limit'],
58
- 'search' => $args['search'],
59
- 'fields' => ['ID','display_name'],
60
- 'orderby' => 'display_name',
61
- 'order' => 'ASC',
62
- 'include' => $args['include'],
63
- 'count_total' => false,
64
- ];
65
- if ($new_args['search']) {
66
- if (false !== strpos($new_args['search'], '@')) {
67
- $new_args['search_columns'] = [ 'user_email' ];
68
- } elseif (is_numeric($new_args['search'])) {
69
- $new_args['search_columns'] = [ 'user_login', 'ID' ];
70
- } else {
71
- $new_args['search_columns'] = [ 'user_nicename', 'user_login', 'display_name' ];
72
- }
73
- $new_args['search'] = '*'.$new_args['search'].'*';
74
- }
75
- return $new_args;
76
- }
77
-
78
- /**
79
- * @inheritDoc
80
- */
81
- protected function _get_content($args = [])
82
- {
83
- $user_query = new WP_User_Query($args);
84
- $author_list = [];
85
-
86
- if ($user_query->results) {
87
- foreach ($user_query->get_results() as $user) {
88
- $author_list[] = [
89
- 'id' => $user->ID,
90
- 'text' => $user->display_name
91
- ];
92
- }
93
- }
94
- return $author_list;
95
- }
96
- }
 
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ /**
12
+ *
13
+ * Author Module
14
+ *
15
+ * Detects if current content is:
16
+ * a) post type written by any or specific author
17
+ * b) any or specific author archive
18
+ *
19
+ */
20
+ class WPCAModule_author extends WPCAModule_Base
21
+ {
22
+ public function __construct()
23
+ {
24
+ parent::__construct('author', __('Authors', WPCA_DOMAIN));
25
+ $this->placeholder = __('All Authors', WPCA_DOMAIN);
26
+ $this->icon = 'dashicons-admin-users';
27
+ $this->default_value = $this->id;
28
+ $this->query_name = 'ca';
29
+ }
30
+
31
+ /**
32
+ * @inheritDoc
33
+ */
34
+ public function in_context()
35
+ {
36
+ return (is_singular() && !is_front_page()) || is_author();
37
+ }
38
+
39
+ /**
40
+ * @inheritDoc
41
+ */
42
+ public function get_context_data()
43
+ {
44
+ global $post;
45
+ return [
46
+ $this->id,
47
+ (string)(is_singular() ? $post->post_author : get_query_var('author'))
48
+ ];
49
+ }
50
+
51
+ /**
52
+ * @inheritDoc
53
+ */
54
+ protected function parse_query_args($args)
55
+ {
56
+ $new_args = [
57
+ 'number' => $args['limit'],
58
+ 'offset' => ($args['paged'] - 1) * $args['limit'],
59
+ 'search' => $args['search'],
60
+ 'fields' => ['ID','display_name'],
61
+ 'orderby' => 'display_name',
62
+ 'order' => 'ASC',
63
+ 'include' => $args['include'],
64
+ 'count_total' => false,
65
+ ];
66
+ if ($new_args['search']) {
67
+ if (false !== strpos($new_args['search'], '@')) {
68
+ $new_args['search_columns'] = ['user_email'];
69
+ } elseif (is_numeric($new_args['search'])) {
70
+ $new_args['search_columns'] = ['user_login', 'ID'];
71
+ } else {
72
+ $new_args['search_columns'] = ['user_nicename', 'user_login', 'display_name'];
73
+ }
74
+ $new_args['search'] = '*' . $new_args['search'] . '*';
75
+ }
76
+ return $new_args;
77
+ }
78
+
79
+ /**
80
+ * @inheritDoc
81
+ */
82
+ protected function _get_content($args = [])
83
+ {
84
+ $user_query = new WP_User_Query($args);
85
+ $author_list = [];
86
+
87
+ if ($user_query->results) {
88
+ foreach ($user_query->get_results() as $user) {
89
+ $author_list[] = [
90
+ 'id' => $user->ID,
91
+ 'text' => $user->display_name
92
+ ];
93
+ }
94
+ }
95
+ return $author_list;
96
+ }
97
+ }
lib/wp-content-aware-engine/module/base.php CHANGED
@@ -1,363 +1,376 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- /**
12
- *
13
- * All modules should extend this one.
14
- *
15
- */
16
- abstract class WPCAModule_Base
17
- {
18
- /**
19
- * @var string
20
- */
21
- protected $query_name;
22
-
23
- /**
24
- * @var string
25
- */
26
- protected $id;
27
-
28
- /**
29
- * @var string
30
- */
31
- protected $name;
32
-
33
- /**
34
- * @var string
35
- */
36
- protected $description;
37
-
38
- /**
39
- * Placeholder label
40
- *
41
- * @var string
42
- */
43
- protected $placeholder;
44
-
45
- /**
46
- * Default condition value
47
- * Use to target any condition content
48
- *
49
- * @var string
50
- */
51
- protected $default_value = '';
52
-
53
- /**
54
- * @var string
55
- */
56
- protected $category = 'general';
57
-
58
- /**
59
- * @param string $id
60
- * @param string $title
61
- * @param string $description
62
- * @param string $placeholder
63
- */
64
- public function __construct($id, $title, $description = '', $placeholder = '')
65
- {
66
- $this->id = $id;
67
- $this->name = $title;
68
- $this->description = $description;
69
- $this->placeholder = $placeholder;
70
- }
71
-
72
- /**
73
- * Initiate module
74
- *
75
- * @since 2.0
76
- * @return void
77
- */
78
- public function initiate()
79
- {
80
- if (is_admin()) {
81
- add_action(
82
- 'wp_ajax_wpca/module/'.$this->id,
83
- [$this,'ajax_print_content']
84
- );
85
- }
86
- }
87
-
88
- /**
89
- * @since 9.0
90
- * @return bool
91
- */
92
- public function can_enable()
93
- {
94
- return true;
95
- }
96
-
97
- /**
98
- * Set module info in list
99
- * @since 2.0
100
- * @param array $list
101
- *
102
- * @return array
103
- */
104
- public function list_module($list)
105
- {
106
- $list[] = [
107
- 'id' => $this->id,
108
- 'text' => $this->name,
109
- 'placeholder' => $this->placeholder,
110
- 'default_value' => $this->default_value,
111
- ];
112
- return $list;
113
- }
114
-
115
- /**
116
- * Default query join
117
- *
118
- * @global wpdb $wpdb
119
- * @since 1.0
120
- * @return string
121
- */
122
- public function db_join()
123
- {
124
- global $wpdb;
125
-
126
- $name = $this->get_query_name();
127
- $key = $this->get_data_key();
128
-
129
- return "LEFT JOIN $wpdb->postmeta $name ON $name.post_id = p.ID AND $name.meta_key = '$key' ";
130
- }
131
-
132
- /**
133
- * @since 1.0
134
- *
135
- * @return string
136
- */
137
- final public function get_id()
138
- {
139
- return $this->id;
140
- }
141
-
142
- /**
143
- * @since 7.0
144
- *
145
- * @return string
146
- */
147
- final public function get_name()
148
- {
149
- return $this->name;
150
- }
151
-
152
- /**
153
- * @since 7.0
154
- *
155
- * @return string
156
- */
157
- final public function get_category()
158
- {
159
- return $this->category;
160
- }
161
-
162
- /**
163
- * @since 6.0
164
- * @return string
165
- */
166
- public function get_query_name()
167
- {
168
- return $this->query_name ? $this->query_name : $this->id;
169
- }
170
-
171
- /**
172
- * @since 6.0
173
- * @return string
174
- */
175
- public function get_data_key()
176
- {
177
- return WPCACore::PREFIX . $this->id;
178
- }
179
-
180
- /**
181
- * Save data on POST
182
- *
183
- * @since 1.0
184
- * @param int $post_id
185
- * @return void
186
- */
187
- public function save_data($post_id)
188
- {
189
- $meta_key = $this->get_data_key();
190
- $old = array_flip(get_post_meta($post_id, $meta_key, false));
191
- $new = isset($_POST['conditions'][$this->id]) ? $_POST['conditions'][$this->id] : '';
192
-
193
- if (is_array($new)) {
194
- //$new = array_unique($new);
195
- // Skip existing data or insert new data
196
- foreach ($new as $new_single) {
197
- if (isset($old[$new_single])) {
198
- unset($old[$new_single]);
199
- } else {
200
- add_post_meta($post_id, $meta_key, $new_single);
201
- }
202
- }
203
- // Remove existing data that have not been skipped
204
- foreach ($old as $old_key => $old_value) {
205
- delete_post_meta($post_id, $meta_key, $old_key);
206
- }
207
- } elseif (!empty($old)) {
208
- // Remove any old values when $new is empty
209
- delete_post_meta($post_id, $meta_key);
210
- }
211
- }
212
-
213
- /**
214
- * Get data for condition group
215
- *
216
- * @since 2.0
217
- * @param array $group_data
218
- * @param int $post_id
219
- * @return array
220
- */
221
- public function get_group_data($group_data, $post_id)
222
- {
223
- $data = get_post_custom_values($this->get_data_key(), $post_id);
224
- if ($data) {
225
- $group_data[$this->id] = [
226
- 'label' => $this->name,
227
- 'placeholder' => $this->placeholder,
228
- 'data' => $this->get_content(['include' => $data]),
229
- 'default_value' => $this->default_value
230
- ];
231
- }
232
- return $group_data;
233
- }
234
-
235
- /**
236
- * Get content for sidebar edit screen
237
- *
238
- * @since 1.0
239
- * @param array $args
240
- * @return array
241
- */
242
- abstract protected function _get_content($args = []);
243
-
244
- /**
245
- * Determine if current content is relevant
246
- *
247
- * @since 1.0
248
- * @return boolean
249
- */
250
- abstract public function in_context();
251
-
252
- /**
253
- * Get data from current content
254
- *
255
- * @since 1.0
256
- * @return array|string
257
- */
258
- abstract public function get_context_data();
259
-
260
- /**
261
- * Remove posts if they have data from
262
- * other contexts (meaning conditions arent met)
263
- *
264
- * @since 3.2
265
- * @param array $posts
266
- * @param boolean $in_context
267
- * @return array
268
- */
269
- public function filter_excluded_context($posts, $in_context = false)
270
- {
271
- if (!$in_context) {
272
- foreach ($posts as $id => $group) {
273
- if (get_post_custom_values($this->get_data_key(), $id) !== null) {
274
- unset($posts[$id]);
275
- }
276
- }
277
- }
278
- return $posts;
279
- }
280
-
281
- /**
282
- * @param array $args
283
- *
284
- * @return array
285
- */
286
- protected function parse_query_args($args)
287
- {
288
- return $args;
289
- }
290
-
291
- /**
292
- * @param array $args
293
- *
294
- * @return array
295
- */
296
- protected function get_content($args)
297
- {
298
- $args = array_merge([
299
- 'include' => [],
300
- 'paged' => 1,
301
- 'search' => false,
302
- 'limit' => -1,
303
- ], $args);
304
- return $this->_get_content($this->parse_query_args($args));
305
- }
306
-
307
- /**
308
- * Print JSON for AJAX request
309
- *
310
- * @since 1.0
311
- * @return void
312
- */
313
- final public function ajax_print_content()
314
- {
315
- if (!isset($_POST['sidebar_id']) ||
316
- !check_ajax_referer(WPCACore::PREFIX.$_POST['sidebar_id'], 'nonce', false)) {
317
- wp_die();
318
- }
319
-
320
- if (!isset($_POST['action'], $_POST['paged'])) {
321
- wp_die();
322
- }
323
-
324
- $response = $this->get_content([
325
- 'paged' => $_POST['paged'],
326
- 'search' => isset($_POST['search']) ? $_POST['search'] : false,
327
- 'limit' => isset($_POST['limit']) ? $_POST['limit'] : 20,
328
- 'item_object' => $_POST['action']
329
- ]);
330
-
331
- //ECMAScript has no standard to guarantee
332
- //prop order in an object, send array instead
333
- //todo: fix in each module
334
- $fix_response = [];
335
- foreach ($response as $id => $title) {
336
- if (!is_array($title)) {
337
- $fix_response[] = [
338
- 'id' => $id,
339
- 'text' => $title
340
- ];
341
- } else {
342
- $fix_response[] = $title;
343
- }
344
- }
345
-
346
- wp_send_json($fix_response);
347
- }
348
-
349
- /**
350
- * Destructor
351
- *
352
- * @since 4.0
353
- */
354
- public function __destruct()
355
- {
356
- if (is_admin()) {
357
- remove_action(
358
- 'wp_ajax_wpca/module/'.$this->id,
359
- [$this,'ajax_print_content']
360
- );
361
- }
362
- }
363
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ /**
12
+ *
13
+ * All modules should extend this one.
14
+ *
15
+ */
16
+ abstract class WPCAModule_Base
17
+ {
18
+ /**
19
+ * @var string
20
+ */
21
+ protected $query_name;
22
+
23
+ /**
24
+ * @var string
25
+ */
26
+ protected $id;
27
+
28
+ /**
29
+ * @var string
30
+ */
31
+ protected $name;
32
+
33
+ /**
34
+ * @var string
35
+ */
36
+ protected $description;
37
+
38
+ /**
39
+ * @var string
40
+ */
41
+ protected $placeholder;
42
+
43
+ /**
44
+ * @var string
45
+ */
46
+ protected $icon;
47
+
48
+ /**
49
+ * Default condition value
50
+ * Use to target any condition content
51
+ *
52
+ * @var string
53
+ */
54
+ protected $default_value = '';
55
+
56
+ /**
57
+ * @var string
58
+ */
59
+ protected $category = 'general';
60
+
61
+ /**
62
+ * @param string $id
63
+ * @param string $title
64
+ * @param string $description
65
+ * @param string $placeholder
66
+ */
67
+ public function __construct($id, $title, $description = '', $placeholder = '')
68
+ {
69
+ $this->id = $id;
70
+ $this->name = $title;
71
+ $this->description = $description;
72
+ $this->placeholder = $placeholder;
73
+ }
74
+
75
+ /**
76
+ * Initiate module
77
+ *
78
+ * @since 2.0
79
+ * @return void
80
+ */
81
+ public function initiate()
82
+ {
83
+ if (is_admin()) {
84
+ add_action(
85
+ 'wp_ajax_wpca/module/' . $this->id,
86
+ [$this,'ajax_print_content']
87
+ );
88
+ }
89
+ }
90
+
91
+ /**
92
+ * @since 9.0
93
+ * @return bool
94
+ */
95
+ public function can_enable()
96
+ {
97
+ return true;
98
+ }
99
+
100
+ /**
101
+ * Set module info in list
102
+ * @since 2.0
103
+ * @param array $list
104
+ *
105
+ * @return array
106
+ */
107
+ public function list_module($list)
108
+ {
109
+ $list[] = [
110
+ 'id' => $this->id,
111
+ 'text' => $this->name,
112
+ 'icon' => $this->get_icon(),
113
+ 'placeholder' => $this->placeholder,
114
+ 'default_value' => $this->default_value,
115
+ ];
116
+ return $list;
117
+ }
118
+
119
+ /**
120
+ * Default query join
121
+ *
122
+ * @global wpdb $wpdb
123
+ * @since 1.0
124
+ * @return string
125
+ */
126
+ public function db_join()
127
+ {
128
+ global $wpdb;
129
+
130
+ $name = $this->get_query_name();
131
+ $key = $this->get_data_key();
132
+
133
+ return "LEFT JOIN $wpdb->postmeta $name ON $name.post_id = p.ID AND $name.meta_key = '$key' ";
134
+ }
135
+
136
+ /**
137
+ * @since 1.0
138
+ *
139
+ * @return string
140
+ */
141
+ final public function get_id()
142
+ {
143
+ return $this->id;
144
+ }
145
+
146
+ /**
147
+ * @since 7.0
148
+ *
149
+ * @return string
150
+ */
151
+ final public function get_name()
152
+ {
153
+ return $this->name;
154
+ }
155
+
156
+ /**
157
+ * @return string
158
+ */
159
+ final public function get_icon()
160
+ {
161
+ return $this->icon;
162
+ }
163
+
164
+ /**
165
+ * @since 7.0
166
+ *
167
+ * @return string
168
+ */
169
+ final public function get_category()
170
+ {
171
+ return $this->category;
172
+ }
173
+
174
+ /**
175
+ * @since 6.0
176
+ * @return string
177
+ */
178
+ public function get_query_name()
179
+ {
180
+ return $this->query_name ? $this->query_name : $this->id;
181
+ }
182
+
183
+ /**
184
+ * @since 6.0
185
+ * @return string
186
+ */
187
+ public function get_data_key()
188
+ {
189
+ return WPCACore::PREFIX . $this->id;
190
+ }
191
+
192
+ /**
193
+ * Save data on POST
194
+ *
195
+ * @since 1.0
196
+ * @param int $post_id
197
+ * @return void
198
+ */
199
+ public function save_data($post_id)
200
+ {
201
+ $meta_key = $this->get_data_key();
202
+ $old = array_flip(get_post_meta($post_id, $meta_key, false));
203
+ $new = isset($_POST['conditions'][$this->id]) ? $_POST['conditions'][$this->id] : '';
204
+
205
+ if (is_array($new)) {
206
+ //$new = array_unique($new);
207
+ // Skip existing data or insert new data
208
+ foreach ($new as $new_single) {
209
+ if (isset($old[$new_single])) {
210
+ unset($old[$new_single]);
211
+ } else {
212
+ add_post_meta($post_id, $meta_key, $new_single);
213
+ }
214
+ }
215
+ // Remove existing data that have not been skipped
216
+ foreach ($old as $old_key => $old_value) {
217
+ delete_post_meta($post_id, $meta_key, $old_key);
218
+ }
219
+ } elseif (!empty($old)) {
220
+ // Remove any old values when $new is empty
221
+ delete_post_meta($post_id, $meta_key);
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Get data for condition group
227
+ *
228
+ * @since 2.0
229
+ * @param array $group_data
230
+ * @param int $post_id
231
+ * @return array
232
+ */
233
+ public function get_group_data($group_data, $post_id)
234
+ {
235
+ $data = get_post_custom_values($this->get_data_key(), $post_id);
236
+ if ($data) {
237
+ $group_data[$this->id] = [
238
+ 'label' => $this->name,
239
+ 'icon' => $this->get_icon(),
240
+ 'placeholder' => $this->placeholder,
241
+ 'data' => $this->get_content(['include' => $data]),
242
+ 'default_value' => $this->default_value
243
+ ];
244
+ }
245
+ return $group_data;
246
+ }
247
+
248
+ /**
249
+ * Get content for sidebar edit screen
250
+ *
251
+ * @since 1.0
252
+ * @param array $args
253
+ * @return array
254
+ */
255
+ abstract protected function _get_content($args = []);
256
+
257
+ /**
258
+ * Determine if current content is relevant
259
+ *
260
+ * @since 1.0
261
+ * @return boolean
262
+ */
263
+ abstract public function in_context();
264
+
265
+ /**
266
+ * Get data from current content
267
+ *
268
+ * @since 1.0
269
+ * @return array|string
270
+ */
271
+ abstract public function get_context_data();
272
+
273
+ /**
274
+ * Remove posts if they have data from
275
+ * other contexts (meaning conditions arent met)
276
+ *
277
+ * @since 3.2
278
+ * @param array $posts
279
+ * @param boolean $in_context
280
+ * @return array
281
+ */
282
+ public function filter_excluded_context($posts, $in_context = false)
283
+ {
284
+ if (!$in_context) {
285
+ foreach ($posts as $id => $group) {
286
+ if (get_post_custom_values($this->get_data_key(), $id) !== null) {
287
+ unset($posts[$id]);
288
+ }
289
+ }
290
+ }
291
+ return $posts;
292
+ }
293
+
294
+ /**
295
+ * @param array $args
296
+ *
297
+ * @return array
298
+ */
299
+ protected function parse_query_args($args)
300
+ {
301
+ return $args;
302
+ }
303
+
304
+ /**
305
+ * @param array $args
306
+ *
307
+ * @return array
308
+ */
309
+ protected function get_content($args)
310
+ {
311
+ $args = array_merge([
312
+ 'include' => [],
313
+ 'paged' => 1,
314
+ 'search' => false,
315
+ 'limit' => -1,
316
+ ], $args);
317
+ return $this->_get_content($this->parse_query_args($args));
318
+ }
319
+
320
+ /**
321
+ * Print JSON for AJAX request
322
+ *
323
+ * @since 1.0
324
+ * @return void
325
+ */
326
+ final public function ajax_print_content()
327
+ {
328
+ if (!isset($_POST['sidebar_id']) ||
329
+ !check_ajax_referer(WPCACore::PREFIX . $_POST['sidebar_id'], 'nonce', false)) {
330
+ wp_die();
331
+ }
332
+
333
+ if (!isset($_POST['action'], $_POST['paged'])) {
334
+ wp_die();
335
+ }
336
+
337
+ $response = $this->get_content([
338
+ 'paged' => $_POST['paged'],
339
+ 'search' => isset($_POST['search']) ? $_POST['search'] : false,
340
+ 'limit' => isset($_POST['limit']) ? $_POST['limit'] : 20,
341
+ 'item_object' => $_POST['action']
342
+ ]);
343
+
344
+ //ECMAScript has no standard to guarantee
345
+ //prop order in an object, send array instead
346
+ //todo: fix in each module
347
+ $fix_response = [];
348
+ foreach ($response as $id => $title) {
349
+ if (!is_array($title)) {
350
+ $fix_response[] = [
351
+ 'id' => $id,
352
+ 'text' => $title
353
+ ];
354
+ } else {
355
+ $fix_response[] = $title;
356
+ }
357
+ }
358
+
359
+ wp_send_json($fix_response);
360
+ }
361
+
362
+ /**
363
+ * Destructor
364
+ *
365
+ * @since 4.0
366
+ */
367
+ public function __destruct()
368
+ {
369
+ if (is_admin()) {
370
+ remove_action(
371
+ 'wp_ajax_wpca/module/' . $this->id,
372
+ [$this,'ajax_print_content']
373
+ );
374
+ }
375
+ }
376
+ }
lib/wp-content-aware-engine/module/bbpress.php CHANGED
@@ -1,99 +1,100 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- /**
12
- *
13
- * bbPress Module
14
- * Requires bbPress 2.5+
15
- *
16
- * Detects if current content is:
17
- * a) any or specific bbpress user profile
18
- *
19
- */
20
- class WPCAModule_bbpress extends WPCAModule_author
21
- {
22
- /**
23
- * @var string
24
- */
25
- protected $category = 'plugins';
26
-
27
- public function __construct()
28
- {
29
- parent::__construct();
30
- $this->id = 'bb_profile';
31
- $this->name = __('bbPress User Profiles', WPCA_DOMAIN);
32
- $this->placeholder = __('All Profiles', WPCA_DOMAIN);
33
- $this->default_value = $this->id;
34
- $this->query_name = 'cbb';
35
- }
36
-
37
- /**
38
- * @inheritDoc
39
- */
40
- public function initiate()
41
- {
42
- parent::initiate();
43
- add_filter(
44
- 'wpca/module/post_type/db-where',
45
- [$this,'add_forum_dependency']
46
- );
47
- }
48
-
49
- /**
50
- * @inheritDoc
51
- */
52
- public function can_enable()
53
- {
54
- return function_exists('bbp_get_version')
55
- && function_exists('bbp_is_single_user')
56
- && function_exists('bbp_get_displayed_user_id')
57
- && function_exists('bbp_get_forum_id');
58
- }
59
-
60
- /**
61
- * @inheritDoc
62
- */
63
- public function in_context()
64
- {
65
- return bbp_is_single_user();
66
- }
67
-
68
- /**
69
- * @inheritDoc
70
- */
71
- public function get_context_data()
72
- {
73
- $data = [$this->id];
74
- $data[] = bbp_get_displayed_user_id();
75
- return $data;
76
- }
77
-
78
- /**
79
- * Sidebars to be displayed with forums will also
80
- * be dislpayed with respective topics and replies
81
- *
82
- * @since 1.0
83
- * @param string $where
84
- * @return string
85
- */
86
- public function add_forum_dependency($where)
87
- {
88
- if (is_singular(['topic','reply'])) {
89
- $data = [
90
- get_post_type(),
91
- get_the_ID(),
92
- 'forum'
93
- ];
94
- $data[] = bbp_get_forum_id();
95
- $where = "(cp.meta_value IS NULL OR cp.meta_value IN('".implode("','", $data)."'))";
96
- }
97
- return $where;
98
- }
99
- }
 
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ /**
12
+ *
13
+ * bbPress Module
14
+ * Requires bbPress 2.5+
15
+ *
16
+ * Detects if current content is:
17
+ * a) any or specific bbpress user profile
18
+ *
19
+ */
20
+ class WPCAModule_bbpress extends WPCAModule_author
21
+ {
22
+ /**
23
+ * @var string
24
+ */
25
+ protected $category = 'plugins';
26
+
27
+ public function __construct()
28
+ {
29
+ parent::__construct();
30
+ $this->id = 'bb_profile';
31
+ $this->name = __('bbPress User Profiles', WPCA_DOMAIN);
32
+ $this->icon = 'dashicons-buddicons-bbpress-logo';
33
+ $this->placeholder = __('All Profiles', WPCA_DOMAIN);
34
+ $this->default_value = $this->id;
35
+ $this->query_name = 'cbb';
36
+ }
37
+
38
+ /**
39
+ * @inheritDoc
40
+ */
41
+ public function initiate()
42
+ {
43
+ parent::initiate();
44
+ add_filter(
45
+ 'wpca/module/post_type/db-where',
46
+ [$this,'add_forum_dependency']
47
+ );
48
+ }
49
+
50
+ /**
51
+ * @inheritDoc
52
+ */
53
+ public function can_enable()
54
+ {
55
+ return function_exists('bbp_get_version')
56
+ && function_exists('bbp_is_single_user')
57
+ && function_exists('bbp_get_displayed_user_id')
58
+ && function_exists('bbp_get_forum_id');
59
+ }
60
+
61
+ /**
62
+ * @inheritDoc
63
+ */
64
+ public function in_context()
65
+ {
66
+ return bbp_is_single_user();
67
+ }
68
+
69
+ /**
70
+ * @inheritDoc
71
+ */
72
+ public function get_context_data()
73
+ {
74
+ $data = [$this->id];
75
+ $data[] = bbp_get_displayed_user_id();
76
+ return $data;
77
+ }
78
+
79
+ /**
80
+ * Sidebars to be displayed with forums will also
81
+ * be dislpayed with respective topics and replies
82
+ *
83
+ * @since 1.0
84
+ * @param string $where
85
+ * @return string
86
+ */
87
+ public function add_forum_dependency($where)
88
+ {
89
+ if (is_singular(['topic','reply'])) {
90
+ $data = [
91
+ get_post_type(),
92
+ get_the_ID(),
93
+ 'forum'
94
+ ];
95
+ $data[] = bbp_get_forum_id();
96
+ $where = '(cp.meta_value IS NULL OR cp.meta_value IN(' . WPCACore::sql_prepare_in($data) . '))';
97
+ }
98
+ return $where;
99
+ }
100
+ }
lib/wp-content-aware-engine/module/bp_member.php CHANGED
@@ -1,154 +1,138 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- /**
12
- *
13
- * BuddyPress Member Page Module
14
- * Requires BuddyPress 2.6+
15
- *
16
- * Detects if current content is:
17
- * a) a specific buddypress member page
18
- *
19
- */
20
- class WPCAModule_bp_member extends WPCAModule_Base
21
- {
22
- /**
23
- * @var string
24
- */
25
- protected $category = 'plugins';
26
-
27
- /**
28
- * Cached search string
29
- * @var string
30
- */
31
- protected $search_string;
32
-
33
- public function __construct()
34
- {
35
- parent::__construct('bp_member', __('BuddyPress Profiles', WPCA_DOMAIN));
36
- $this->default_value = 0;
37
- $this->placeholder = __('All Sections', WPCA_DOMAIN);
38
- $this->query_name = 'cbp';
39
- }
40
-
41
- /**
42
- * @inheritDoc
43
- */
44
- public function can_enable()
45
- {
46
- return defined('BP_VERSION');
47
- }
48
-
49
- /**
50
- * @inheritDoc
51
- */
52
- public function initiate()
53
- {
54
- parent::initiate();
55
- add_filter(
56
- 'wpca/module/static/in-context',
57
- [$this,'static_is_content']
58
- );
59
- }
60
-
61
- /**
62
- * @inheritDoc
63
- */
64
- protected function _get_content($args = [])
65
- {
66
- global $bp;
67
-
68
- if (isset($args['paged']) && $args['paged'] > 1) {
69
- return [];
70
- }
71
-
72
- $content = [];
73
- $is_search = isset($args['search']) && $args['search'];
74
-
75
- if (isset($bp->members->nav)) {
76
- foreach ($bp->members->nav->get_item_nav() as $item) {
77
- $content[$item->slug] = [
78
- 'id' => $item->slug,
79
- 'text' => strip_tags($item->name)
80
- ];
81
- if ($item->children) {
82
- $level = $is_search ? 0 : 1;
83
- foreach ($item->children as $child_item) {
84
- $content[$item->slug.'-'.$child_item->slug] = [
85
- 'text' => strip_tags($child_item->name),
86
- 'id' => $item->slug.'-'.$child_item->slug,
87
- 'level' => $level
88
- ];
89
- }
90
- }
91
- }
92
- }
93
-
94
- if (!empty($args['include'])) {
95
- $content = array_intersect_key($content, array_flip($args['include']));
96
- } elseif ($is_search) {
97
- $this->search_string = $args['search'];
98
- $content = array_filter($content, [$this,'_filter_search']);
99
- }
100
-
101
- return $content;
102
- }
103
-
104
- /**
105
- * Filter content based on search
106
- *
107
- * @since 2.0
108
- * @param string $value
109
- * @return boolean
110
- */
111
- protected function _filter_search($value)
112
- {
113
- return mb_stripos($value['text'], $this->search_string) !== false;
114
- }
115
-
116
- /**
117
- * @inheritDoc
118
- */
119
- public function in_context()
120
- {
121
- global $bp;
122
- return isset($bp->displayed_user->domain) && $bp->displayed_user->domain;
123
- }
124
-
125
- /**
126
- * @inheritDoc
127
- */
128
- public function get_context_data()
129
- {
130
- global $bp;
131
- $data = [$this->default_value];
132
- if (isset($bp->current_component)) {
133
- $data[] = $bp->current_component;
134
- if (isset($bp->current_action)) {
135
- $data[] = $bp->current_component.'-'.$bp->current_action;
136
- }
137
- }
138
- return $data;
139
- }
140
-
141
- /**
142
- * Avoid collision with content of static module
143
- * Somehow buddypress pages pass is_404()
144
- *
145
- * @since 1.0
146
- * @param boolean $content
147
- * @return boolean
148
- */
149
- public function static_is_content($content)
150
- {
151
- //TODO: test if deprecated
152
- return $content && !$this->in_context();
153
- }
154
- }
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ /**
12
+ *
13
+ * BuddyPress Member Page Module
14
+ * Requires BuddyPress 2.6+
15
+ *
16
+ * Detects if current content is:
17
+ * a) a specific buddypress member page
18
+ *
19
+ */
20
+ class WPCAModule_bp_member extends WPCAModule_Base
21
+ {
22
+ /**
23
+ * @var string
24
+ */
25
+ protected $category = 'plugins';
26
+
27
+ public function __construct()
28
+ {
29
+ parent::__construct('bp_member', __('BuddyPress Profiles', WPCA_DOMAIN));
30
+ $this->default_value = 0;
31
+ $this->placeholder = __('All Sections', WPCA_DOMAIN);
32
+ $this->icon = 'dashicons-buddicons-buddypress-logo';
33
+ $this->query_name = 'cbp';
34
+ }
35
+
36
+ /**
37
+ * @inheritDoc
38
+ */
39
+ public function can_enable()
40
+ {
41
+ return defined('BP_VERSION');
42
+ }
43
+
44
+ /**
45
+ * @inheritDoc
46
+ */
47
+ public function initiate()
48
+ {
49
+ parent::initiate();
50
+ add_filter(
51
+ 'wpca/module/static/in-context',
52
+ [$this,'static_is_content']
53
+ );
54
+ }
55
+
56
+ /**
57
+ * @inheritDoc
58
+ */
59
+ protected function _get_content($args = [])
60
+ {
61
+ global $bp;
62
+
63
+ if (isset($args['paged']) && $args['paged'] > 1) {
64
+ return [];
65
+ }
66
+
67
+ $content = [];
68
+ $is_search = isset($args['search']) && $args['search'];
69
+
70
+ if (isset($bp->members->nav)) {
71
+ foreach ($bp->members->nav->get_item_nav() as $item) {
72
+ $content[$item->slug] = [
73
+ 'id' => $item->slug,
74
+ 'text' => strip_tags($item->name)
75
+ ];
76
+ if ($item->children) {
77
+ $level = $is_search ? 0 : 1;
78
+ foreach ($item->children as $child_item) {
79
+ $content[$item->slug . '-' . $child_item->slug] = [
80
+ 'text' => strip_tags($child_item->name),
81
+ 'id' => $item->slug . '-' . $child_item->slug,
82
+ 'level' => $level
83
+ ];
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ if (!empty($args['include'])) {
90
+ $content = array_intersect_key($content, array_flip($args['include']));
91
+ } elseif ($is_search) {
92
+ $content = array_filter($content, function ($value) use ($args) {
93
+ return mb_stripos($value['text'], $args['search']) !== false;
94
+ });
95
+ }
96
+
97
+ return $content;
98
+ }
99
+
100
+ /**
101
+ * @inheritDoc
102
+ */
103
+ public function in_context()
104
+ {
105
+ global $bp;
106
+ return isset($bp->displayed_user->domain) && $bp->displayed_user->domain;
107
+ }
108
+
109
+ /**
110
+ * @inheritDoc
111
+ */
112
+ public function get_context_data()
113
+ {
114
+ global $bp;
115
+ $data = [$this->default_value];
116
+ if (isset($bp->current_component)) {
117
+ $data[] = $bp->current_component;
118
+ if (isset($bp->current_action)) {
119
+ $data[] = $bp->current_component . '-' . $bp->current_action;
120
+ }
121
+ }
122
+ return $data;
123
+ }
124
+
125
+ /**
126
+ * Avoid collision with content of static module
127
+ * Somehow buddypress pages pass is_404()
128
+ *
129
+ * @since 1.0
130
+ * @param boolean $content
131
+ * @return boolean
132
+ */
133
+ public function static_is_content($content)
134
+ {
135
+ //TODO: test if deprecated
136
+ return $content && !$this->in_context();
137
+ }
138
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/wp-content-aware-engine/module/date.php CHANGED
@@ -1,77 +1,72 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- /**
12
- * Date Module
13
- *
14
- * @deprecated 9.1
15
- */
16
- class WPCAModule_date extends WPCAModule_Base
17
- {
18
-
19
- /**
20
- * Constructor
21
- */
22
- public function __construct()
23
- {
24
- parent::__construct(
25
- 'date',
26
- __('Dates', WPCA_DOMAIN)
27
- );
28
- $this->placeholder = __('Date Archives', WPCA_DOMAIN);
29
- $this->default_value = '0000-00-00';
30
- if (get_class() === 'WPCAModule_date') {
31
- $this->name .= ' (Legacy)';
32
- }
33
- //$this->query_name = 'cd';
34
- }
35
-
36
- /**
37
- * @inheritDoc
38
- */
39
- public function in_context()
40
- {
41
- return is_date();
42
- }
43
-
44
- /**
45
- * Get data from context
46
- *
47
- * @global object $wpdb
48
- * @since 1.0
49
- * @return array
50
- */
51
- public function get_context_data()
52
- {
53
- global $wpdb;
54
-
55
- $name = $this->get_query_name();
56
-
57
- return $wpdb->prepare(
58
- "($name.meta_value IS NULL OR '%s' = $name.meta_value)",
59
- '0000-00-00'
60
- );
61
- }
62
-
63
- /**
64
- * Get content
65
- *
66
- * @since 1.0
67
- * @return array
68
- */
69
- protected function _get_content($args = [])
70
- {
71
- $data = [];
72
- if ($args['include']) {
73
- $data = array_intersect_key($data, array_flip($args['include']));
74
- }
75
- return $data;
76
- }
77
- }
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ /**
12
+ * Date Module
13
+ *
14
+ * @deprecated 9.1
15
+ */
16
+ class WPCAModule_date extends WPCAModule_Base
17
+ {
18
+ /**
19
+ * Constructor
20
+ */
21
+ public function __construct()
22
+ {
23
+ parent::__construct(
24
+ 'date',
25
+ __('Dates', WPCA_DOMAIN)
26
+ );
27
+ $this->placeholder = __('Date Archives', WPCA_DOMAIN);
28
+ $this->default_value = '0000-00-00';
29
+ if (get_class() === 'WPCAModule_date') {
30
+ $this->name .= ' (Legacy)';
31
+ }
32
+ //$this->query_name = 'cd';
33
+ }
34
+
35
+ /**
36
+ * @inheritDoc
37
+ */
38
+ public function in_context()
39
+ {
40
+ return is_date();
41
+ }
42
+
43
+ /**
44
+ * @inheritDoc
45
+ */
46
+ public function get_context_data()
47
+ {
48
+ global $wpdb;
49
+
50
+ $name = $this->get_query_name();
51
+
52
+ return $wpdb->prepare(
53
+ "($name.meta_value IS NULL OR '%s' = $name.meta_value)",
54
+ '0000-00-00'
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Get content
60
+ *
61
+ * @since 1.0
62
+ * @return array
63
+ */
64
+ protected function _get_content($args = [])
65
+ {
66
+ $data = [];
67
+ if ($args['include']) {
68
+ $data = array_intersect_key($data, array_flip($args['include']));
69
+ }
70
+ return $data;
71
+ }
72
+ }
 
 
 
 
 
lib/wp-content-aware-engine/module/page_template.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
@@ -29,6 +29,7 @@ class WPCAModule_page_template extends WPCAModule_Base
29
  {
30
  parent::__construct('page_template', __('Page Templates', WPCA_DOMAIN));
31
  $this->placeholder = __('All Templates', WPCA_DOMAIN);
 
32
  $this->default_value = $this->id;
33
  $this->query_name = 'cpt';
34
  }
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
29
  {
30
  parent::__construct('page_template', __('Page Templates', WPCA_DOMAIN));
31
  $this->placeholder = __('All Templates', WPCA_DOMAIN);
32
+ $this->icon = 'dashicons-media-code';
33
  $this->default_value = $this->id;
34
  $this->query_name = 'cpt';
35
  }
lib/wp-content-aware-engine/module/pods.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
@@ -88,22 +88,11 @@ class WPCAModule_pods extends WPCAModule_Base
88
  $pods[$result['id']] = $result['name'];
89
  }
90
  if ($args['search']) {
91
- $this->search_string = $args['search'];
92
- $pods = array_filter($pods, [$this,'_filter_search']);
 
93
  }
94
 
95
  return $pods;
96
  }
97
-
98
- /**
99
- * Filter content based on search
100
- *
101
- * @since 2.0
102
- * @param string $value
103
- * @return boolean
104
- */
105
- protected function _filter_search($value)
106
- {
107
- return mb_stripos($value, $this->search_string) !== false;
108
- }
109
  }
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
88
  $pods[$result['id']] = $result['name'];
89
  }
90
  if ($args['search']) {
91
+ $pods = array_filter($pods, function ($pod) use ($args) {
92
+ return mb_stripos($pod, $args['search']) !== false;
93
+ });
94
  }
95
 
96
  return $pods;
97
  }
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
lib/wp-content-aware-engine/module/polylang.php CHANGED
@@ -1,111 +1,111 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- /**
12
- *
13
- * Polylang Module
14
- * Requires version 1.7+
15
- *
16
- * Detects if current content is:
17
- * a) in specific language
18
- *
19
- */
20
- class WPCAModule_polylang extends WPCAModule_Base
21
- {
22
-
23
- /**
24
- * @var string
25
- */
26
- protected $category = 'plugins';
27
-
28
- /**
29
- * Constructor
30
- */
31
- public function __construct()
32
- {
33
- parent::__construct('language', __('Languages', WPCA_DOMAIN));
34
- $this->query_name = 'cl';
35
- }
36
-
37
- /**
38
- * @inheritDoc
39
- */
40
- public function initiate()
41
- {
42
- parent::initiate();
43
- add_filter(
44
- 'pll_get_post_types',
45
- [$this,'remove_sidebar_multilingual']
46
- );
47
- }
48
-
49
- /**
50
- * @inheritDoc
51
- */
52
- public function can_enable()
53
- {
54
- return defined('POLYLANG_VERSION')
55
- && function_exists('pll_current_language');
56
- }
57
-
58
- /**
59
- * @inheritDoc
60
- */
61
- public function in_context()
62
- {
63
- return true;
64
- }
65
-
66
- /**
67
- * @inheritDoc
68
- */
69
- public function get_context_data()
70
- {
71
- $data = [$this->id];
72
- $data[] = pll_current_language();
73
- return $data;
74
- }
75
-
76
- /**
77
- * @inheritDoc
78
- */
79
- protected function _get_content($args = [])
80
- {
81
- global $polylang;
82
-
83
- $langs = [];
84
-
85
- if (isset($polylang->model) && method_exists($polylang->model, 'get_languages_list')) {
86
- foreach ($polylang->model->get_languages_list(['fields' => false]) as $lng) {
87
- $langs[$lng->slug] = $lng->name;
88
- }
89
- }
90
-
91
- if ($args['include']) {
92
- $langs = array_intersect_key($langs, array_flip($args['include']));
93
- }
94
- return $langs;
95
- }
96
-
97
- /**
98
- * Remove sidebars from multilingual list
99
- *
100
- * @since 1.0
101
- * @param array $post_types
102
- * @return array
103
- */
104
- public function remove_sidebar_multilingual($post_types)
105
- {
106
- foreach (WPCACore::types() as $post_type => $module) {
107
- unset($post_types[$post_type]);
108
- }
109
- return $post_types;
110
- }
111
- }
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ /**
12
+ *
13
+ * Polylang Module
14
+ * Requires version 1.7+
15
+ *
16
+ * Detects if current content is:
17
+ * a) in specific language
18
+ *
19
+ */
20
+ class WPCAModule_polylang extends WPCAModule_Base
21
+ {
22
+ /**
23
+ * @var string
24
+ */
25
+ protected $category = 'plugins';
26
+
27
+ /**
28
+ * Constructor
29
+ */
30
+ public function __construct()
31
+ {
32
+ parent::__construct('language', __('Languages', WPCA_DOMAIN));
33
+ $this->icon = 'dashicons-translation';
34
+ $this->query_name = 'cl';
35
+ }
36
+
37
+ /**
38
+ * @inheritDoc
39
+ */
40
+ public function initiate()
41
+ {
42
+ parent::initiate();
43
+ add_filter(
44
+ 'pll_get_post_types',
45
+ [$this,'remove_sidebar_multilingual']
46
+ );
47
+ }
48
+
49
+ /**
50
+ * @inheritDoc
51
+ */
52
+ public function can_enable()
53
+ {
54
+ return defined('POLYLANG_VERSION')
55
+ && function_exists('pll_current_language');
56
+ }
57
+
58
+ /**
59
+ * @inheritDoc
60
+ */
61
+ public function in_context()
62
+ {
63
+ return true;
64
+ }
65
+
66
+ /**
67
+ * @inheritDoc
68
+ */
69
+ public function get_context_data()
70
+ {
71
+ $data = [$this->id];
72
+ $data[] = pll_current_language();
73
+ return $data;
74
+ }
75
+
76
+ /**
77
+ * @inheritDoc
78
+ */
79
+ protected function _get_content($args = [])
80
+ {
81
+ global $polylang;
82
+
83
+ $langs = [];
84
+
85
+ if (isset($polylang->model) && method_exists($polylang->model, 'get_languages_list')) {
86
+ foreach ($polylang->model->get_languages_list(['fields' => false]) as $lng) {
87
+ $langs[$lng->slug] = $lng->name;
88
+ }
89
+ }
90
+
91
+ if ($args['include']) {
92
+ $langs = array_intersect_key($langs, array_flip($args['include']));
93
+ }
94
+ return $langs;
95
+ }
96
+
97
+ /**
98
+ * Remove sidebars from multilingual list
99
+ *
100
+ * @since 1.0
101
+ * @param array $post_types
102
+ * @return array
103
+ */
104
+ public function remove_sidebar_multilingual($post_types)
105
+ {
106
+ foreach (WPCACore::types() as $post_type => $module) {
107
+ unset($post_types[$post_type]);
108
+ }
109
+ return $post_types;
110
+ }
111
+ }
lib/wp-content-aware-engine/module/post_type.php CHANGED
@@ -1,527 +1,525 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
-
12
- /**
13
- *
14
- * Post Type Module
15
- *
16
- * Detects if current content is:
17
- * a) specific post type or specific post
18
- * b) specific post type archive or home
19
- *
20
- */
21
- class WPCAModule_post_type extends WPCAModule_Base
22
- {
23
- /**
24
- * @var string
25
- */
26
- protected $category = 'post_type';
27
-
28
- /**
29
- * Registered public post types
30
- *
31
- * @var array
32
- */
33
- private $_post_types;
34
-
35
- /**
36
- * Conditions to inherit from post ancestors
37
- * @var array
38
- */
39
- private $_post_ancestor_conditions;
40
-
41
- public function __construct()
42
- {
43
- parent::__construct('post_type', __('Post Types', WPCA_DOMAIN));
44
- $this->query_name = 'cp';
45
- }
46
-
47
- /**
48
- * @inheritDoc
49
- */
50
- public function initiate()
51
- {
52
- parent::initiate();
53
-
54
- add_action(
55
- 'transition_post_status',
56
- [$this,'post_ancestry_check'],
57
- 10,
58
- 3
59
- );
60
-
61
- if (is_admin()) {
62
- foreach ($this->post_types() as $post_type) {
63
- add_action(
64
- 'wp_ajax_wpca/module/'.$this->id.'-'.$post_type,
65
- [$this,'ajax_print_content']
66
- );
67
- }
68
- }
69
- }
70
-
71
- /**
72
- * @inheritDoc
73
- */
74
- protected function _get_content($args = [])
75
- {
76
- $walk_tree = false;
77
- $start = ($args['paged'] - 1) * $args['posts_per_page'];
78
- $end = $start + $args['posts_per_page'];
79
-
80
- //WordPress searches in title and content by default
81
- //We want to search in title and slug
82
- if (!empty($args['search'])) {
83
- $exclude_query = '';
84
- if (!empty($args['post__not_in'])) {
85
- $exclude_query = ' AND ID NOT IN ('.implode(',', $args['post__not_in']).')';
86
- }
87
-
88
- $columns = [
89
- ['post_title', 'LIKE', '%'.$args['search'].'%'],
90
- ['post_name', 'LIKE', '%'.$args['search'].'%'],
91
- ];
92
-
93
- if (is_numeric($args['search'])) {
94
- $columns[] = ['ID', '=', $args['search']];
95
- }
96
-
97
- $where = [];
98
- $values = [];
99
- foreach ($columns as $column_value) {
100
- list($column, $operator, $value) = $column_value;
101
- $prepared_value = is_numeric($value) ? '%d' : '%s';
102
- $where[] = "{$column} {$operator} '{$prepared_value}'";
103
- $values[] = $value;
104
- }
105
-
106
- //Using unprepared (safe) exclude because WP is not good at parsing arrays
107
- global $wpdb;
108
- $posts = $wpdb->get_results($wpdb->prepare(
109
- "
110
- SELECT ID, post_title, post_type, post_parent, post_status, post_password
111
- FROM {$wpdb->posts}
112
- WHERE (".implode(' OR ', $where).")
113
- AND post_status IN('".implode("','", $args['post_status'])."')
114
- AND post_type = '%s'
115
- $exclude_query
116
- ORDER BY post_title ASC
117
- LIMIT %d,%d
118
- ",
119
- array_merge($values, [
120
- $args['post_type'],
121
- $start,
122
- $args['posts_per_page']
123
- ])
124
- ));
125
- } else {
126
- if (is_post_type_hierarchical($args['post_type']) && !isset($args['post__in'])) {
127
- $args['posts_per_page'] = -1;
128
- $args['paged'] = 0;
129
- $args['orderby'] = 'menu_order title';
130
-
131
- $walk_tree = true;
132
- }
133
- $query = new WP_Query($args);
134
- $posts = $query->posts;
135
- }
136
-
137
- $retval = [];
138
-
139
- if ($walk_tree) {
140
- $pages_sorted = [];
141
- foreach ($posts as $post) {
142
- $pages_sorted[$post->post_parent][] = $post;
143
- }
144
- $i = 0;
145
- $this->_walk_tree($pages_sorted, $pages_sorted[0], $i, $start, $end, 0, $retval);
146
- } else {
147
- foreach ($posts as $post) {
148
- $retval[$post->ID] = $this->post_title($post);
149
- }
150
- }
151
-
152
- return $retval;
153
- }
154
-
155
- /**
156
- * Get hierarchical content with level param
157
- *
158
- * @since 3.7.2
159
- * @param array $all_pages
160
- * @param array $pages
161
- * @param int $i
162
- * @param int $start
163
- * @param int $end
164
- * @param int $level
165
- * @param array &$retval
166
- * @return void
167
- */
168
- protected function _walk_tree($all_pages, $pages, &$i, $start, $end, $level, &$retval)
169
- {
170
- foreach ($pages as $page) {
171
- if ($i >= $end) {
172
- break;
173
- }
174
-
175
- if ($i >= $start) {
176
- $retval[] = [
177
- 'id' => $page->ID,
178
- 'text' => $this->post_title($page),
179
- 'level' => $level
180
- ];
181
- }
182
-
183
- $i++;
184
-
185
- if (isset($all_pages[$page->ID])) {
186
- $this->_walk_tree($all_pages, $all_pages[$page->ID], $i, $start, $end, $level + 1, $retval);
187
- }
188
- }
189
- }
190
-
191
- /**
192
- * Get registered public post types
193
- *
194
- * @since 4.0
195
- * @return array
196
- */
197
- public function post_types()
198
- {
199
- if (!$this->_post_types) {
200
- // List public post types
201
- foreach (get_post_types(['public' => true], 'names') as $post_type) {
202
- $this->_post_types[$post_type] = $post_type;
203
- }
204
- }
205
- return $this->_post_types;
206
- }
207
-
208
- /**
209
- * @inheritDoc
210
- */
211
- public function get_group_data($group_data, $post_id)
212
- {
213
- $ids = get_post_custom_values(WPCACore::PREFIX . $this->id, $post_id);
214
- if ($ids) {
215
- $lookup = array_flip((array)$ids);
216
- foreach ($this->post_types() as $post_type) {
217
- $post_type_obj = get_post_type_object($post_type);
218
- $data = $this->get_content([
219
- 'include' => $ids,
220
- 'post_type' => $post_type
221
- ]);
222
-
223
- if ($data || isset($lookup[$post_type])) {
224
- $placeholder = $post_type_obj->labels->all_items;
225
- switch ($post_type) {
226
- case 'post':
227
- $placeholder .= ' / '.__('Blog Page', WPCA_DOMAIN);
228
- break;
229
- case 'product':
230
- $placeholder .= ' / '.__('Shop Page', WPCA_DOMAIN);
231
- break;
232
- default:
233
- if ($post_type_obj->has_archive) {
234
- $placeholder .= ' / '.sprintf(__('%s Archives', WPCA_DOMAIN), $post_type_obj->labels->singular_name);
235
- }
236
- break;
237
- }
238
-
239
- $group_data[$this->id.'-'.$post_type] = [
240
- 'label' => $post_type_obj->label,
241
- 'placeholder' => $placeholder,
242
- 'default_value' => $post_type
243
- ];
244
-
245
- if ($data) {
246
- $group_data[$this->id.'-'.$post_type]['data'] = $data;
247
- }
248
- }
249
- }
250
- }
251
- return $group_data;
252
- }
253
-
254
- /**
255
- * @inheritDoc
256
- */
257
- public function in_context()
258
- {
259
- return ((is_singular() || is_home()) && !is_front_page()) || is_post_type_archive();
260
- }
261
-
262
- /**
263
- * @inheritDoc
264
- */
265
- public function get_context_data()
266
- {
267
- if (is_singular()) {
268
- return [
269
- get_post_type(),
270
- get_queried_object_id()
271
- ];
272
- }
273
-
274
- // Home has post as default post type
275
- $post_type = get_query_var('post_type');
276
- if (is_array($post_type)) {
277
- $post_type = reset($post_type);
278
- } elseif (!$post_type) {
279
- $post_type = 'post';
280
- }
281
-
282
- return [
283
- $post_type
284
- ];
285
- }
286
-
287
- /**
288
- * @inheritDoc
289
- */
290
- protected function parse_query_args($args)
291
- {
292
- if (isset($args['item_object'])) {
293
- preg_match('/post_type-(.+)$/i', $args['item_object'], $matches);
294
- $post_type_name = isset($matches[1]) ? $matches[1] : '___';
295
- } else {
296
- $post_type_name = isset($args['post_type']) ? $args['post_type'] : 'category';
297
- }
298
-
299
- $exclude = [];
300
- if ($post_type_name == 'page' && 'page' == get_option('show_on_front')) {
301
- $exclude[] = intval(get_option('page_on_front'));
302
- $exclude[] = intval(get_option('page_for_posts'));
303
- }
304
-
305
- $post_status = ['publish','private','future','draft'];
306
- if ($post_type_name == 'attachment') {
307
- $post_status = ['inherit'];
308
- }
309
-
310
- $new_args = [
311
- 'post__not_in' => $exclude,
312
- 'post_type' => $post_type_name,
313
- 'post_status' => $post_status,
314
- 'orderby' => 'title',
315
- 'order' => 'ASC',
316
- 'paged' => $args['paged'],
317
- 'posts_per_page' => $args['limit'],
318
- 'search' => $args['search'],
319
- 'ignore_sticky_posts' => true,
320
- 'update_post_term_cache' => false,
321
- 'suppress_filters' => true,
322
- 'no_found_rows' => true,
323
- ];
324
-
325
- //future proof in case this is considered a bug https://core.trac.wordpress.org/ticket/28099
326
- if (!empty($args['include'])) {
327
- $new_args['post__in'] = $args['include'];
328
- }
329
-
330
- return $new_args;
331
- }
332
-
333
- /**
334
- * @inheritDoc
335
- */
336
- public function list_module($list)
337
- {
338
- foreach ($this->post_types() as $post_type) {
339
- $post_type_obj = get_post_type_object($post_type);
340
-
341
- $name = $post_type_obj->label;
342
- $placeholder = $post_type_obj->labels->all_items;
343
-
344
- switch ($post_type) {
345
- case 'post':
346
- $name .= ' / '.__('Blog', WPCA_DOMAIN);
347
- $placeholder .= ' / '.__('Blog Page', WPCA_DOMAIN);
348
- break;
349
- case 'product':
350
- $name .= ' / '.__('Shop', WPCA_DOMAIN);
351
- $placeholder .= ' / '.__('Shop Page', WPCA_DOMAIN);
352
- break;
353
- default:
354
- if ($post_type_obj->has_archive) {
355
- $placeholder .= ' / '.sprintf(__('%s Archives', WPCA_DOMAIN), $post_type_obj->labels->singular_name);
356
- }
357
- break;
358
- }
359
-
360
- $list[] = [
361
- 'id' => $this->id.'-'.$post_type,
362
- 'text' => $name,
363
- 'placeholder' => $placeholder,
364
- 'default_value' => $post_type
365
- ];
366
- }
367
- return $list;
368
- }
369
-
370
- /**
371
- * Get post title and state
372
- *
373
- * @since 3.7
374
- * @param WP_Post $post
375
- * @return string
376
- */
377
- public function post_title($post)
378
- {
379
- $post_states = [];
380
-
381
- if (!empty($post->post_password)) {
382
- $post_states['protected'] = __('Password protected');
383
- }
384
-
385
- if (is_sticky($post->ID)) {
386
- $post_states['sticky'] = __('Sticky');
387
- }
388
-
389
- switch ($post->post_status) {
390
- case 'private':
391
- $post_states['private'] = __('Private');
392
- break;
393
- case 'draft':
394
- $post_states['draft'] = __('Draft');
395
- break;
396
- case 'pending':
397
- /* translators: post state */
398
- $post_states['pending'] = _x('Pending', 'post state');
399
- break;
400
- case 'scheduled':
401
- $post_states['scheduled'] = __('Scheduled');
402
- break;
403
- default:
404
- break;
405
- }
406
-
407
- $post_title = $post->post_title ? $post->post_title : __('(no title)');
408
- $post_states = apply_filters('display_post_states', $post_states, $post);
409
-
410
- return $post_title . ' ' . ($post_states ? ' ('.implode(', ', $post_states).')' : '');
411
- }
412
-
413
- /**
414
- * @inheritDoc
415
- */
416
- public function save_data($post_id)
417
- {
418
- $meta_key = WPCACore::PREFIX . $this->id;
419
- $old = array_flip(get_post_meta($post_id, $meta_key, false));
420
- $new = [];
421
-
422
- foreach ($this->post_types() as $post_type) {
423
- $id = $this->id.'-'.$post_type;
424
- if (isset($_POST['conditions'][$id])) {
425
- $new = array_merge($new, $_POST['conditions'][$id]);
426
- }
427
- }
428
-
429
- if ($new) {
430
- //$new = array_unique($new);
431
- // Skip existing data or insert new data
432
- foreach ($new as $new_single) {
433
- if (isset($old[$new_single])) {
434
- unset($old[$new_single]);
435
- } else {
436
- add_post_meta($post_id, $meta_key, $new_single);
437
- }
438
- }
439
- // Remove existing data that have not been skipped
440
- foreach ($old as $old_key => $old_value) {
441
- delete_post_meta($post_id, $meta_key, $old_key);
442
- }
443
- } elseif (!empty($old)) {
444
- // Remove any old values when $new is empty
445
- delete_post_meta($post_id, $meta_key);
446
- }
447
- }
448
-
449
- /**
450
- * Check if post ancestors have sidebar conditions
451
- *
452
- * @since 1.0
453
- * @param string $new_status
454
- * @param string $old_status
455
- * @param WP_Post $post
456
- * @return void
457
- */
458
- public function post_ancestry_check($new_status, $old_status, $post)
459
- {
460
- if (!WPCACore::types()->has($post->post_type) && $post->post_type != WPCACore::TYPE_CONDITION_GROUP && $post->post_parent) {
461
- $status = [
462
- 'publish' => 1,
463
- 'private' => 1,
464
- 'future' => 1
465
- ];
466
-
467
- // Only new posts are relevant
468
- if (!isset($status[$old_status]) && isset($status[$new_status])) {
469
- $post_type = get_post_type_object($post->post_type);
470
- if ($post_type->hierarchical && $post_type->public) {
471
-
472
-
473
- // Get sidebars with post ancestor wanting to auto-select post
474
- $query = new WP_Query([
475
- 'post_type' => WPCACore::TYPE_CONDITION_GROUP,
476
- 'post_status' => [WPCACore::STATUS_OR,WPCACore::STATUS_EXCEPT,WPCACore::STATUS_PUBLISHED],
477
- 'meta_query' => [
478
- 'relation' => 'AND',
479
- [
480
- 'key' => WPCACore::PREFIX . 'autoselect',
481
- 'value' => 1,
482
- 'compare' => '='
483
- ],
484
- [
485
- 'key' => WPCACore::PREFIX . $this->id,
486
- 'value' => get_post_ancestors($post),
487
- 'type' => 'numeric',
488
- 'compare' => 'IN'
489
- ]
490
- ]
491
- ]);
492
-
493
- if ($query && $query->found_posts) {
494
-
495
- //Add conditions after Quick Select
496
- //otherwise they will be removed there
497
- $this->_post_ancestor_conditions = $query->posts;
498
- add_action(
499
- 'save_post_'.$post->post_type,
500
- [$this,'post_ancestry_add'],
501
- 99,
502
- 2
503
- );
504
- do_action('wpca/modules/auto-select/'.$this->category, $query->posts, $post);
505
- }
506
- }
507
- }
508
- }
509
- }
510
-
511
- /**
512
- * Add sidebar conditions from post ancestors
513
- *
514
- * @since 3.1.1
515
- * @param int $post_id
516
- * @param WP_Post $post
517
- * @return void
518
- */
519
- public function post_ancestry_add($post_id, $post)
520
- {
521
- if ($this->_post_ancestor_conditions) {
522
- foreach ($this->_post_ancestor_conditions as $condition) {
523
- add_post_meta($condition->ID, WPCACore::PREFIX.$this->id, $post_id);
524
- }
525
- }
526
- }
527
- }
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ /**
12
+ *
13
+ * Post Type Module
14
+ *
15
+ * Detects if current content is:
16
+ * a) specific post type or specific post
17
+ * b) specific post type archive or home
18
+ *
19
+ */
20
+ class WPCAModule_post_type extends WPCAModule_Base
21
+ {
22
+ /**
23
+ * @var string
24
+ */
25
+ protected $category = 'post_type';
26
+
27
+ /**
28
+ * Registered public post types
29
+ *
30
+ * @var array
31
+ */
32
+ private $_post_types;
33
+
34
+ /**
35
+ * Conditions to inherit from post ancestors
36
+ * @var array
37
+ */
38
+ private $_post_ancestor_conditions;
39
+
40
+ public function __construct()
41
+ {
42
+ parent::__construct('post_type', __('Post Types', WPCA_DOMAIN));
43
+ $this->query_name = 'cp';
44
+ }
45
+
46
+ /**
47
+ * @inheritDoc
48
+ */
49
+ public function initiate()
50
+ {
51
+ parent::initiate();
52
+
53
+ add_action(
54
+ 'transition_post_status',
55
+ [$this,'post_ancestry_check'],
56
+ 10,
57
+ 3
58
+ );
59
+
60
+ if (is_admin()) {
61
+ foreach ($this->post_types() as $post_type) {
62
+ add_action(
63
+ 'wp_ajax_wpca/module/' . $this->id . '-' . $post_type,
64
+ [$this,'ajax_print_content']
65
+ );
66
+ }
67
+ }
68
+ }
69
+
70
+ /**
71
+ * @inheritDoc
72
+ */
73
+ protected function _get_content($args = [])
74
+ {
75
+ $walk_tree = false;
76
+ $start = ($args['paged'] - 1) * $args['posts_per_page'];
77
+ $end = $start + $args['posts_per_page'];
78
+
79
+ //WordPress searches in title and content by default
80
+ //We want to search in title and slug
81
+ if (!empty($args['search'])) {
82
+ $exclude_query = '';
83
+ if (!empty($args['post__not_in'])) {
84
+ $exclude_query = ' AND ID NOT IN (' . implode(',', $args['post__not_in']) . ')';
85
+ }
86
+
87
+ $columns = [
88
+ ['post_title', 'LIKE', '%' . $args['search'] . '%'],
89
+ ['post_name', 'LIKE', '%' . $args['search'] . '%'],
90
+ ];
91
+
92
+ if (is_numeric($args['search'])) {
93
+ $columns[] = ['ID', '=', $args['search']];
94
+ }
95
+
96
+ $where = [];
97
+ $values = [];
98
+ foreach ($columns as $column_value) {
99
+ list($column, $operator, $value) = $column_value;
100
+ $prepared_value = is_numeric($value) ? '%d' : '%s';
101
+ $where[] = "$column $operator '$prepared_value'";
102
+ $values[] = $value;
103
+ }
104
+
105
+ //Using unprepared (safe) exclude because WP is not good at parsing arrays
106
+ global $wpdb;
107
+ $posts = $wpdb->get_results($wpdb->prepare(
108
+ "
109
+ SELECT ID, post_title, post_type, post_parent, post_status, post_password
110
+ FROM $wpdb->posts
111
+ WHERE (" . implode(' OR ', $where) . ")
112
+ AND post_status IN('" . implode("','", $args['post_status']) . "')
113
+ AND post_type = '%s'
114
+ $exclude_query
115
+ ORDER BY post_title ASC
116
+ LIMIT %d,%d
117
+ ",
118
+ array_merge($values, [
119
+ $args['post_type'],
120
+ $start,
121
+ $args['posts_per_page']
122
+ ])
123
+ ));
124
+ } else {
125
+ if (is_post_type_hierarchical($args['post_type']) && !isset($args['post__in'])) {
126
+ $args['posts_per_page'] = -1;
127
+ $args['paged'] = 0;
128
+ $args['orderby'] = 'menu_order title';
129
+
130
+ $walk_tree = true;
131
+ }
132
+ $query = new WP_Query($args);
133
+ $posts = $query->posts;
134
+ }
135
+
136
+ $retval = [];
137
+
138
+ if ($walk_tree) {
139
+ $pages_sorted = [];
140
+ foreach ($posts as $post) {
141
+ $pages_sorted[$post->post_parent][] = $post;
142
+ }
143
+ $i = 0;
144
+ $this->_walk_tree($pages_sorted, $pages_sorted[0], $i, $start, $end, 0, $retval);
145
+ } else {
146
+ foreach ($posts as $post) {
147
+ $retval[$post->ID] = $this->post_title($post);
148
+ }
149
+ }
150
+
151
+ return $retval;
152
+ }
153
+
154
+ /**
155
+ * Get hierarchical content with level param
156
+ *
157
+ * @since 3.7.2
158
+ * @param array $all_pages
159
+ * @param array $pages
160
+ * @param int $i
161
+ * @param int $start
162
+ * @param int $end
163
+ * @param int $level
164
+ * @param array &$retval
165
+ * @return void
166
+ */
167
+ protected function _walk_tree($all_pages, $pages, &$i, $start, $end, $level, &$retval)
168
+ {
169
+ foreach ($pages as $page) {
170
+ if ($i >= $end) {
171
+ break;
172
+ }
173
+
174
+ if ($i >= $start) {
175
+ $retval[] = [
176
+ 'id' => $page->ID,
177
+ 'text' => $this->post_title($page),
178
+ 'level' => $level
179
+ ];
180
+ }
181
+
182
+ $i++;
183
+
184
+ if (isset($all_pages[$page->ID])) {
185
+ $this->_walk_tree($all_pages, $all_pages[$page->ID], $i, $start, $end, $level + 1, $retval);
186
+ }
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Get registered public post types
192
+ *
193
+ * @since 4.0
194
+ * @return array
195
+ */
196
+ public function post_types()
197
+ {
198
+ if (!$this->_post_types) {
199
+ // List public post types
200
+ foreach (get_post_types(['public' => true], 'names') as $post_type) {
201
+ $this->_post_types[$post_type] = $post_type;
202
+ }
203
+ }
204
+ return $this->_post_types;
205
+ }
206
+
207
+ /**
208
+ * @inheritDoc
209
+ */
210
+ public function get_group_data($group_data, $post_id)
211
+ {
212
+ $ids = get_post_custom_values(WPCACore::PREFIX . $this->id, $post_id);
213
+ if ($ids) {
214
+ $lookup = array_flip((array)$ids);
215
+ foreach ($this->post_types() as $post_type) {
216
+ $post_type_obj = get_post_type_object($post_type);
217
+ $data = $this->get_content([
218
+ 'include' => $ids,
219
+ 'post_type' => $post_type
220
+ ]);
221
+
222
+ if ($data || isset($lookup[$post_type])) {
223
+ $placeholder = $post_type_obj->labels->all_items;
224
+ switch ($post_type) {
225
+ case 'post':
226
+ $placeholder .= ' / ' . __('Blog Page', WPCA_DOMAIN);
227
+ break;
228
+ case 'product':
229
+ $placeholder .= ' / ' . __('Shop Page', WPCA_DOMAIN);
230
+ break;
231
+ default:
232
+ if ($post_type_obj->has_archive) {
233
+ $placeholder .= ' / ' . sprintf(__('%s Archives', WPCA_DOMAIN), $post_type_obj->labels->singular_name);
234
+ }
235
+ break;
236
+ }
237
+
238
+ $group_data[$this->id . '-' . $post_type] = [
239
+ 'label' => $post_type_obj->label,
240
+ 'icon' => $post_type_obj->menu_icon,
241
+ 'placeholder' => $placeholder,
242
+ 'default_value' => $post_type
243
+ ];
244
+
245
+ if ($data) {
246
+ $group_data[$this->id . '-' . $post_type]['data'] = $data;
247
+ }
248
+ }
249
+ }
250
+ }
251
+ return $group_data;
252
+ }
253
+
254
+ /**
255
+ * @inheritDoc
256
+ */
257
+ public function in_context()
258
+ {
259
+ return ((is_singular() || is_home()) && !is_front_page()) || is_post_type_archive();
260
+ }
261
+
262
+ /**
263
+ * @inheritDoc
264
+ */
265
+ public function get_context_data()
266
+ {
267
+ if (is_singular()) {
268
+ return [
269
+ get_post_type(),
270
+ get_queried_object_id()
271
+ ];
272
+ }
273
+
274
+ // Home has post as default post type
275
+ $post_type = get_query_var('post_type');
276
+ if (is_array($post_type)) {
277
+ $post_type = reset($post_type);
278
+ } elseif (!$post_type) {
279
+ $post_type = 'post';
280
+ }
281
+
282
+ return [
283
+ $post_type
284
+ ];
285
+ }
286
+
287
+ /**
288
+ * @inheritDoc
289
+ */
290
+ protected function parse_query_args($args)
291
+ {
292
+ if (isset($args['item_object'])) {
293
+ preg_match('/post_type-(.+)$/i', $args['item_object'], $matches);
294
+ $post_type_name = isset($matches[1]) ? $matches[1] : '___';
295
+ } else {
296
+ $post_type_name = isset($args['post_type']) ? $args['post_type'] : 'category';
297
+ }
298
+
299
+ $exclude = [];
300
+ if ($post_type_name == 'page' && 'page' == get_option('show_on_front')) {
301
+ $exclude[] = intval(get_option('page_on_front'));
302
+ $exclude[] = intval(get_option('page_for_posts'));
303
+ }
304
+
305
+ $post_status = ['publish','private','future','draft'];
306
+ if ($post_type_name == 'attachment') {
307
+ $post_status = ['inherit'];
308
+ }
309
+
310
+ $new_args = [
311
+ 'post__not_in' => $exclude,
312
+ 'post_type' => $post_type_name,
313
+ 'post_status' => $post_status,
314
+ 'orderby' => 'title',
315
+ 'order' => 'ASC',
316
+ 'paged' => $args['paged'],
317
+ 'posts_per_page' => $args['limit'],
318
+ 'search' => $args['search'],
319
+ 'ignore_sticky_posts' => true,
320
+ 'update_post_term_cache' => false,
321
+ 'suppress_filters' => true,
322
+ 'no_found_rows' => true,
323
+ ];
324
+
325
+ //future proof in case this is considered a bug https://core.trac.wordpress.org/ticket/28099
326
+ if (!empty($args['include'])) {
327
+ $new_args['post__in'] = $args['include'];
328
+ }
329
+
330
+ return $new_args;
331
+ }
332
+
333
+ /**
334
+ * @inheritDoc
335
+ */
336
+ public function list_module($list)
337
+ {
338
+ foreach ($this->post_types() as $post_type) {
339
+ $post_type_obj = get_post_type_object($post_type);
340
+
341
+ $name = $post_type_obj->label;
342
+ $placeholder = $post_type_obj->labels->all_items;
343
+
344
+ switch ($post_type) {
345
+ case 'post':
346
+ $name .= ' / ' . __('Blog', WPCA_DOMAIN);
347
+ $placeholder .= ' / ' . __('Blog Page', WPCA_DOMAIN);
348
+ break;
349
+ case 'product':
350
+ $name .= ' / ' . __('Shop', WPCA_DOMAIN);
351
+ $placeholder .= ' / ' . __('Shop Page', WPCA_DOMAIN);
352
+ break;
353
+ default:
354
+ if ($post_type_obj->has_archive) {
355
+ $placeholder .= ' / ' . sprintf(__('%s Archives', WPCA_DOMAIN), $post_type_obj->labels->singular_name);
356
+ }
357
+ break;
358
+ }
359
+
360
+ $list[] = [
361
+ 'id' => $this->id . '-' . $post_type,
362
+ 'icon' => $post_type_obj->menu_icon,
363
+ 'text' => $name,
364
+ 'placeholder' => $placeholder,
365
+ 'default_value' => $post_type
366
+ ];
367
+ }
368
+ return $list;
369
+ }
370
+
371
+ /**
372
+ * Get post title and state
373
+ *
374
+ * @since 3.7
375
+ * @param WP_Post $post
376
+ * @return string
377
+ */
378
+ public function post_title($post)
379
+ {
380
+ $post_states = [];
381
+
382
+ if (!empty($post->post_password)) {
383
+ $post_states['protected'] = __('Password protected');
384
+ }
385
+
386
+ if (is_sticky($post->ID)) {
387
+ $post_states['sticky'] = __('Sticky');
388
+ }
389
+
390
+ switch ($post->post_status) {
391
+ case 'private':
392
+ $post_states['private'] = __('Private');
393
+ break;
394
+ case 'draft':
395
+ $post_states['draft'] = __('Draft');
396
+ break;
397
+ case 'pending':
398
+ /* translators: post state */
399
+ $post_states['pending'] = _x('Pending', 'post state');
400
+ break;
401
+ case 'scheduled':
402
+ $post_states['scheduled'] = __('Scheduled');
403
+ break;
404
+ default:
405
+ break;
406
+ }
407
+
408
+ $post_title = $post->post_title ? $post->post_title : __('(no title)');
409
+ //$post_states = apply_filters('display_post_states', $post_states, $post);
410
+
411
+ return $post_title . ' ' . ($post_states ? ' (' . implode(', ', $post_states) . ')' : '');
412
+ }
413
+
414
+ /**
415
+ * @inheritDoc
416
+ */
417
+ public function save_data($post_id)
418
+ {
419
+ $meta_key = WPCACore::PREFIX . $this->id;
420
+ $old = array_flip(get_post_meta($post_id, $meta_key, false));
421
+ $new = [];
422
+
423
+ foreach ($this->post_types() as $post_type) {
424
+ $id = $this->id . '-' . $post_type;
425
+ if (isset($_POST['conditions'][$id])) {
426
+ $new = array_merge($new, $_POST['conditions'][$id]);
427
+ }
428
+ }
429
+
430
+ if ($new) {
431
+ //$new = array_unique($new);
432
+ // Skip existing data or insert new data
433
+ foreach ($new as $new_single) {
434
+ if (isset($old[$new_single])) {
435
+ unset($old[$new_single]);
436
+ } else {
437
+ add_post_meta($post_id, $meta_key, $new_single);
438
+ }
439
+ }
440
+ // Remove existing data that have not been skipped
441
+ foreach ($old as $old_key => $old_value) {
442
+ delete_post_meta($post_id, $meta_key, $old_key);
443
+ }
444
+ } elseif (!empty($old)) {
445
+ // Remove any old values when $new is empty
446
+ delete_post_meta($post_id, $meta_key);
447
+ }
448
+ }
449
+
450
+ /**
451
+ * Check if post ancestors have sidebar conditions
452
+ *
453
+ * @since 1.0
454
+ * @param string $new_status
455
+ * @param string $old_status
456
+ * @param WP_Post $post
457
+ * @return void
458
+ */
459
+ public function post_ancestry_check($new_status, $old_status, $post)
460
+ {
461
+ if (!WPCACore::types()->has($post->post_type) && $post->post_type != WPCACore::TYPE_CONDITION_GROUP && $post->post_parent) {
462
+ $status = [
463
+ 'publish' => 1,
464
+ 'private' => 1,
465
+ 'future' => 1
466
+ ];
467
+
468
+ // Only new posts are relevant
469
+ if (!isset($status[$old_status]) && isset($status[$new_status])) {
470
+ $post_type = get_post_type_object($post->post_type);
471
+ if ($post_type->hierarchical && $post_type->public) {
472
+ // Get sidebars with post ancestor wanting to auto-select post
473
+ $query = new WP_Query([
474
+ 'post_type' => WPCACore::TYPE_CONDITION_GROUP,
475
+ 'post_status' => [WPCACore::STATUS_OR,WPCACore::STATUS_EXCEPT,WPCACore::STATUS_PUBLISHED],
476
+ 'meta_query' => [
477
+ 'relation' => 'AND',
478
+ [
479
+ 'key' => WPCACore::PREFIX . 'autoselect',
480
+ 'value' => 1,
481
+ 'compare' => '='
482
+ ],
483
+ [
484
+ 'key' => WPCACore::PREFIX . $this->id,
485
+ 'value' => get_post_ancestors($post),
486
+ 'type' => 'numeric',
487
+ 'compare' => 'IN'
488
+ ]
489
+ ]
490
+ ]);
491
+
492
+ if ($query && $query->found_posts) {
493
+ //Add conditions after Quick Select
494
+ //otherwise they will be removed there
495
+ $this->_post_ancestor_conditions = $query->posts;
496
+ add_action(
497
+ 'save_post_' . $post->post_type,
498
+ [$this,'post_ancestry_add'],
499
+ 99,
500
+ 2
501
+ );
502
+ do_action('wpca/modules/auto-select/' . $this->category, $query->posts, $post);
503
+ }
504
+ }
505
+ }
506
+ }
507
+ }
508
+
509
+ /**
510
+ * Add sidebar conditions from post ancestors
511
+ *
512
+ * @since 3.1.1
513
+ * @param int $post_id
514
+ * @param WP_Post $post
515
+ * @return void
516
+ */
517
+ public function post_ancestry_add($post_id, $post)
518
+ {
519
+ if ($this->_post_ancestor_conditions) {
520
+ foreach ($this->_post_ancestor_conditions as $condition) {
521
+ add_post_meta($condition->ID, WPCACore::PREFIX . $this->id, $post_id);
522
+ }
523
+ }
524
+ }
525
+ }
 
 
lib/wp-content-aware-engine/module/qtranslate.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
@@ -27,6 +27,7 @@ class WPCAModule_qtranslate extends WPCAModule_Base
27
  public function __construct()
28
  {
29
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
 
30
  $this->query_name = 'cl';
31
  }
32
 
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
27
  public function __construct()
28
  {
29
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
30
+ $this->icon = 'dashicons-translation';
31
  $this->query_name = 'cl';
32
  }
33
 
lib/wp-content-aware-engine/module/static.php CHANGED
@@ -1,92 +1,92 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- /**
12
- *
13
- * Static Pages Module
14
- *
15
- * Detects if current content is:
16
- * a) front page
17
- * b) search results
18
- * c) 404 page
19
- *
20
- */
21
- class WPCAModule_static extends WPCAModule_Base
22
- {
23
-
24
- /**
25
- * Cached search string
26
- * @var string
27
- */
28
- protected $search_string;
29
-
30
- public function __construct()
31
- {
32
- parent::__construct('static', __('Special Pages', WPCA_DOMAIN));
33
- $this->query_name = 'cs';
34
- }
35
-
36
- /**
37
- * @inheritDoc
38
- */
39
- protected function _get_content($args = [])
40
- {
41
- $static = [
42
- 'front-page' => __('Front Page', WPCA_DOMAIN),
43
- 'search' => __('Search Results', WPCA_DOMAIN),
44
- '404' => __('404 Page', WPCA_DOMAIN)
45
- ];
46
-
47
- if ($args['include']) {
48
- $static = array_intersect_key($static, array_flip($args['include']));
49
- } elseif ($args['search']) {
50
- $this->search_string = $args['search'];
51
- $static = array_filter($static, [$this,'_filter_search']);
52
- }
53
- return $static;
54
- }
55
-
56
- /**
57
- * Filter content based on search
58
- *
59
- * @since 2.0
60
- * @param string $value
61
- * @return boolean
62
- */
63
- protected function _filter_search($value)
64
- {
65
- return mb_stripos($value, $this->search_string) !== false;
66
- }
67
-
68
- /**
69
- * @inheritDoc
70
- */
71
- public function in_context()
72
- {
73
- return is_front_page() || is_search() || is_404();
74
- }
75
-
76
- /**
77
- * @inheritDoc
78
- */
79
- public function get_context_data()
80
- {
81
- if (is_front_page()) {
82
- $val = 'front-page';
83
- } elseif (is_search()) {
84
- $val = 'search';
85
- } else {
86
- $val = '404';
87
- }
88
- return [
89
- $val
90
- ];
91
- }
92
- }
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ /**
12
+ *
13
+ * Static Pages Module
14
+ *
15
+ * Detects if current content is:
16
+ * a) front page
17
+ * b) search results
18
+ * c) 404 page
19
+ *
20
+ */
21
+ class WPCAModule_static extends WPCAModule_Base
22
+ {
23
+ /**
24
+ * Cached search string
25
+ * @var string
26
+ */
27
+ protected $search_string;
28
+
29
+ public function __construct()
30
+ {
31
+ parent::__construct('static', __('Special Pages', WPCA_DOMAIN));
32
+ $this->icon = 'dashicons-layout';
33
+ $this->query_name = 'cs';
34
+ }
35
+
36
+ /**
37
+ * @inheritDoc
38
+ */
39
+ protected function _get_content($args = [])
40
+ {
41
+ $static = [
42
+ 'front-page' => __('Front Page', WPCA_DOMAIN),
43
+ 'search' => __('Search Results', WPCA_DOMAIN),
44
+ '404' => __('404 Page', WPCA_DOMAIN)
45
+ ];
46
+
47
+ if ($args['include']) {
48
+ $static = array_intersect_key($static, array_flip($args['include']));
49
+ } elseif ($args['search']) {
50
+ $this->search_string = $args['search'];
51
+ $static = array_filter($static, [$this,'_filter_search']);
52
+ }
53
+ return $static;
54
+ }
55
+
56
+ /**
57
+ * Filter content based on search
58
+ *
59
+ * @since 2.0
60
+ * @param string $value
61
+ * @return boolean
62
+ */
63
+ protected function _filter_search($value)
64
+ {
65
+ return mb_stripos($value, $this->search_string) !== false;
66
+ }
67
+
68
+ /**
69
+ * @inheritDoc
70
+ */
71
+ public function in_context()
72
+ {
73
+ return is_front_page() || is_search() || is_404();
74
+ }
75
+
76
+ /**
77
+ * @inheritDoc
78
+ */
79
+ public function get_context_data()
80
+ {
81
+ if (is_front_page()) {
82
+ $val = 'front-page';
83
+ } elseif (is_search()) {
84
+ $val = 'search';
85
+ } else {
86
+ $val = '404';
87
+ }
88
+ return [
89
+ $val
90
+ ];
91
+ }
92
+ }
lib/wp-content-aware-engine/module/taxonomy.php CHANGED
@@ -1,545 +1,545 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- /**
12
- *
13
- * Taxonomy Module
14
- *
15
- * Detects if current content has/is:
16
- * a) any term of specific taxonomy or specific term
17
- * b) taxonomy archive or specific term archive
18
- *
19
- */
20
- class WPCAModule_taxonomy extends WPCAModule_Base
21
- {
22
- /**
23
- * when condition has select terms,
24
- * set this value in postmeta
25
- * @see parent::filter_excluded_context()
26
- */
27
- const VALUE_HAS_TERMS = '-1';
28
-
29
- /**
30
- * @var string
31
- */
32
- protected $category = 'taxonomy';
33
-
34
- /**
35
- * Registered public taxonomies
36
- *
37
- * @var array
38
- */
39
- private $taxonomy_objects = [];
40
-
41
- /**
42
- * Terms of a given singular
43
- *
44
- * @var array
45
- */
46
- private $post_terms = [];
47
-
48
- /**
49
- * Taxonomies for a given singular
50
- * @var array
51
- */
52
- private $post_taxonomies = [];
53
-
54
- public function __construct()
55
- {
56
- parent::__construct('taxonomy', __('Taxonomies', WPCA_DOMAIN));
57
- $this->query_name = 'ct';
58
- }
59
-
60
- /**
61
- * @inheritDoc
62
- */
63
- public function initiate()
64
- {
65
- parent::initiate();
66
- add_action(
67
- 'created_term',
68
- [$this,'term_ancestry_check'],
69
- 10,
70
- 3
71
- );
72
-
73
- if (is_admin()) {
74
- foreach ($this->_get_taxonomies() as $taxonomy) {
75
- add_action(
76
- 'wp_ajax_wpca/module/'.$this->id.'-'.$taxonomy->name,
77
- [$this,'ajax_print_content']
78
- );
79
- }
80
- }
81
- }
82
-
83
- /**
84
- * @inheritDoc
85
- */
86
- public function in_context()
87
- {
88
- //check if post_taxonomies contains more than self::VALUE_HAS_TERMS
89
- return count($this->get_context_data()) > 1;
90
- }
91
-
92
- /**
93
- * @inheritDoc
94
- */
95
- public function get_context_data()
96
- {
97
- if (!empty($this->post_taxonomies)) {
98
- return $this->post_taxonomies;
99
- }
100
-
101
- $this->post_taxonomies[] = self::VALUE_HAS_TERMS;
102
-
103
- if (is_singular()) {
104
- $tax = $this->_get_taxonomies();
105
- // Check if content has any taxonomies supported
106
- foreach (get_object_taxonomies(get_post_type()) as $taxonomy) {
107
- //Only want taxonomies selectable in admin
108
- if (isset($tax[$taxonomy])) {
109
-
110
- //Check term caches, Core most likely used it
111
- $terms = get_object_term_cache(get_the_ID(), $taxonomy);
112
- if ($terms === false) {
113
- $terms = wp_get_object_terms(get_the_ID(), $taxonomy);
114
- }
115
- if ($terms) {
116
- $this->post_taxonomies[] = $taxonomy;
117
- $this->post_terms = array_merge($this->post_terms, $terms);
118
- }
119
- }
120
- }
121
- } elseif (is_tax() || is_category() || is_tag()) {
122
- $term = get_queried_object();
123
- $this->post_taxonomies[] = $term->taxonomy;
124
- $this->post_terms[] = $term;
125
- }
126
-
127
- return $this->post_taxonomies;
128
- }
129
-
130
- /**
131
- * @inheritDoc
132
- */
133
- public function filter_excluded_context($posts, $in_context = false)
134
- {
135
- $posts = parent::filter_excluded_context($posts, $in_context);
136
- if ($in_context) {
137
- $post_terms_by_tax = [];
138
- //@todo archive pages should be migrated to use AND as well, keep OR for now
139
- $legacy_use_or = is_archive();
140
- foreach ($this->post_terms as $term) {
141
- $post_terms_by_tax[$term->taxonomy][$term->term_taxonomy_id] = $term->term_taxonomy_id;
142
- }
143
-
144
- $check_terms = [];
145
- $keep_archive = [];
146
- $unset = [];
147
-
148
- //1. group's taxonomies must match all in post
149
- foreach ($posts as $condition_id => $condition_group) {
150
- $condition_taxonomies = get_post_meta($condition_id, '_ca_taxonomy', false);
151
- foreach ($condition_taxonomies as $taxonomy) {
152
- //if value==-1, group has individual terms, so goto 2
153
- if ($taxonomy == '-1') {
154
- $check_terms[$condition_id] = $condition_group;
155
- } elseif (isset($post_terms_by_tax[$taxonomy])) {
156
- //if on archive page, bail after 1st match
157
- if ($legacy_use_or) {
158
- $keep_archive[$condition_id] = 1;
159
- break;
160
- }
161
- } else {
162
- //if group has more taxonomies than post, unset
163
- $unset[$condition_id] = 1;
164
- //break;
165
- }
166
- }
167
- }
168
-
169
- //2. group's terms must match with minimum 1 in each taxonomy in post
170
- if (!empty($check_terms)) {
171
- //eager load groups terms
172
- $conditions_terms = wp_get_object_terms(array_keys($check_terms), array_keys($this->_get_taxonomies()), [
173
- 'fields' => 'all_with_object_id',
174
- 'orderby' => 'none',
175
- 'update_term_meta_cache' => false
176
- ]);
177
-
178
- $conditions_to_unset = [];
179
- foreach ($conditions_terms as $term) {
180
- if (!isset($conditions_to_unset[$term->object_id][$term->taxonomy])) {
181
- $conditions_to_unset[$term->object_id][$term->taxonomy] = 0;
182
- }
183
- $has_tax_term = isset($post_terms_by_tax[$term->taxonomy][$term->term_taxonomy_id]);
184
- $conditions_to_unset[$term->object_id][$term->taxonomy] |= $has_tax_term;
185
- if ($legacy_use_or && $has_tax_term) {
186
- $keep_archive[$term->object_id] = 1;
187
- }
188
- }
189
-
190
- foreach ($check_terms as $condition_id => $condition_group) {
191
-
192
- //if group has no terms in these taxonomies, it has terms in others, so unset
193
- if (!isset($conditions_to_unset[$condition_id])) {
194
- $unset[$condition_id] = 1;
195
- continue;
196
- }
197
-
198
- foreach ($conditions_to_unset[$condition_id] as $taxonomy => $should_keep) {
199
- //if group has a taxonomy with no term match, unset
200
- if (!$should_keep) {
201
- $unset[$condition_id] = 1;
202
- break;
203
- }
204
- }
205
- }
206
- }
207
-
208
- foreach ($unset as $id => $value) {
209
- if (!isset($keep_archive[$id])) {
210
- unset($posts[$id]);
211
- }
212
- }
213
- }
214
-
215
- return $posts;
216
- }
217
-
218
- /**
219
- * @inheritDoc
220
- */
221
- protected function _get_content($args = [])
222
- {
223
- $total_items = wp_count_terms($args['taxonomy'], [
224
- 'hide_empty' => $args['hide_empty']
225
- ]);
226
-
227
- $start = $args['offset'];
228
- $end = $start + $args['number'];
229
- $walk_tree = false;
230
- $retval = [];
231
-
232
- if ($total_items) {
233
- $taxonomy = get_taxonomy($args['taxonomy']);
234
-
235
- if ($taxonomy->hierarchical && !$args['search'] && !$args['include']) {
236
- $args['number'] = 0;
237
- $args['offset'] = 0;
238
-
239
- $walk_tree = true;
240
- }
241
-
242
- $terms = new WP_Term_Query($args);
243
-
244
- if ($walk_tree) {
245
- $sorted_terms = [];
246
- foreach ($terms->terms as $term) {
247
- $sorted_terms[$term->parent][] = $term;
248
- }
249
- $i = 0;
250
- $this->_walk_tree($sorted_terms, $sorted_terms[0], $i, $start, $end, 0, $retval);
251
- } else {
252
- //Hierarchical taxonomies use ids instead of slugs
253
- //see http://codex.wordpress.org/Function_Reference/wp_set_post_objects
254
- $value_var = ($taxonomy->hierarchical ? 'term_id' : 'slug');
255
-
256
- foreach ($terms->terms as $term) {
257
- //term names are encoded
258
- $retval[$term->$value_var] = htmlspecialchars_decode($term->name);
259
- }
260
- }
261
- }
262
- return $retval;
263
- }
264
-
265
- /**
266
- * Get hierarchical content with level param
267
- *
268
- * @since 3.7.2
269
- * @param array $all_terms
270
- * @param array $terms
271
- * @param int $i
272
- * @param int $start
273
- * @param int $end
274
- * @param int $level
275
- * @param array &$retval
276
- * @return void
277
- */
278
- protected function _walk_tree($all_terms, $terms, &$i, $start, $end, $level, &$retval)
279
- {
280
- foreach ($terms as $term) {
281
- if ($i >= $end) {
282
- break;
283
- }
284
-
285
- if ($i >= $start) {
286
- $retval[] = [
287
- 'id' => $term->term_id,
288
- 'text' => htmlspecialchars_decode($term->name),
289
- 'level' => $level
290
- ];
291
- }
292
-
293
- $i++;
294
-
295
- if (isset($all_terms[$term->term_id])) {
296
- $this->_walk_tree($all_terms, $all_terms[$term->term_id], $i, $start, $end, $level + 1, $retval);
297
- }
298
- }
299
- }
300
-
301
- /**
302
- * Get registered public taxonomies
303
- *
304
- * @since 1.0
305
- * @return array
306
- */
307
- protected function _get_taxonomies()
308
- {
309
- // List public taxonomies
310
- if (empty($this->taxonomy_objects)) {
311
- foreach (get_taxonomies(['public' => true], 'objects') as $tax) {
312
- $this->taxonomy_objects[$tax->name] = $tax;
313
- }
314
- if (defined('POLYLANG_VERSION')) {
315
- unset($this->taxonomy_objects['language']);
316
- }
317
- }
318
- return $this->taxonomy_objects;
319
- }
320
-
321
- /**
322
- * @inheritDoc
323
- */
324
- public function get_group_data($group_data, $post_id)
325
- {
326
- $ids = array_flip((array)get_post_custom_values(WPCACore::PREFIX . $this->id, $post_id));
327
-
328
- //Fetch all terms and group by tax to prevent lazy loading
329
- $terms = wp_get_object_terms(
330
- $post_id,
331
- array_keys($this->_get_taxonomies())
332
- // array(
333
- // 'update_term_meta_cache' => false
334
- // )
335
- );
336
- $terms_by_tax = [];
337
- foreach ($terms as $term) {
338
- $terms_by_tax[$term->taxonomy][] = $term;
339
- }
340
-
341
- $title_count = $this->get_title_count();
342
- foreach ($this->_get_taxonomies() as $taxonomy) {
343
- $posts = isset($terms_by_tax[$taxonomy->name]) ? $terms_by_tax[$taxonomy->name] : 0;
344
-
345
- if ($posts || isset($ids[$taxonomy->name])) {
346
- $group_data[$this->id.'-'.$taxonomy->name] = $this->get_list_data($taxonomy, $title_count[$taxonomy->label]);
347
- $group_data[$this->id.'-'.$taxonomy->name]['label'] = $group_data[$this->id.'-'.$taxonomy->name]['text'];
348
-
349
- if ($posts) {
350
- $retval = [];
351
-
352
- //Hierarchical taxonomies use ids instead of slugs
353
- //see http://codex.wordpress.org/Function_Reference/wp_set_post_objects
354
- $value_var = ($taxonomy->hierarchical ? 'term_id' : 'slug');
355
-
356
- foreach ($posts as $post) {
357
- $retval[$post->$value_var] = $post->name;
358
- }
359
- $group_data[$this->id.'-'.$taxonomy->name]['data'] = $retval;
360
- }
361
- }
362
- }
363
- return $group_data;
364
- }
365
-
366
- /**
367
- * Count taxonomy labels to find shared ones
368
- *
369
- * @return array
370
- */
371
- protected function get_title_count()
372
- {
373
- $title_count = [];
374
- foreach ($this->_get_taxonomies() as $taxonomy) {
375
- if (!isset($title_count[$taxonomy->label])) {
376
- $title_count[$taxonomy->label] = 0;
377
- }
378
- $title_count[$taxonomy->label]++;
379
- }
380
- return $title_count;
381
- }
382
-
383
- /**
384
- * @inheritDoc
385
- */
386
- protected function get_list_data($taxonomy, $title_count)
387
- {
388
- $placeholder = '/'.sprintf(__('%s Archives', WPCA_DOMAIN), $taxonomy->labels->singular_name);
389
- $placeholder = $taxonomy->labels->all_items.$placeholder;
390
- $label = $taxonomy->label;
391
-
392
- if (count($taxonomy->object_type) === 1 && $title_count > 1) {
393
- $post_type = get_post_type_object($taxonomy->object_type[0]);
394
- $label .= ' (' . $post_type->label . ')';
395
- }
396
-
397
- return [
398
- 'text' => $label,
399
- 'placeholder' => $placeholder,
400
- 'default_value' => $taxonomy->name
401
- ];
402
- }
403
-
404
- /**
405
- * @inheritDoc
406
- */
407
- public function list_module($list)
408
- {
409
- $title_count = $this->get_title_count();
410
- foreach ($this->_get_taxonomies() as $taxonomy) {
411
- $data = $this->get_list_data($taxonomy, $title_count[$taxonomy->label]);
412
- $data['id'] = $this->id.'-'.$taxonomy->name;
413
- $list[] = $data;
414
- }
415
- return $list;
416
- }
417
-
418
- /**
419
- * @inheritDoc
420
- */
421
- protected function parse_query_args($args)
422
- {
423
- if (isset($args['item_object'])) {
424
- preg_match('/taxonomy-(.+)$/i', $args['item_object'], $matches);
425
- $args['item_object'] = isset($matches[1]) ? $matches[1] : '___';
426
- $taxonomy_name = $args['item_object'];
427
- } else {
428
- $taxonomy_name = 'category';
429
- }
430
-
431
- return [
432
- 'include' => $args['include'],
433
- 'taxonomy' => $taxonomy_name,
434
- 'number' => $args['limit'],
435
- 'offset' => ($args['paged'] - 1) * $args['limit'],
436
- 'orderby' => 'name',
437
- 'order' => 'ASC',
438
- 'search' => $args['search'],
439
- 'hide_empty' => false,
440
- 'update_term_meta_cache' => false
441
- ];
442
- }
443
-
444
- /**
445
- * @inheritDoc
446
- */
447
- public function save_data($post_id)
448
- {
449
- $meta_key = WPCACore::PREFIX . $this->id;
450
- $old = array_flip(get_post_meta($post_id, $meta_key, false));
451
- $tax_input = $_POST['conditions'];
452
-
453
- $has_select_terms = false;
454
-
455
- //Save terms
456
- //Loop through each public taxonomy
457
- foreach ($this->_get_taxonomies() as $taxonomy) {
458
-
459
- //If no terms, maybe delete old ones
460
- if (!isset($tax_input[$this->id.'-'.$taxonomy->name])) {
461
- $terms = [];
462
- if (isset($old[$taxonomy->name])) {
463
- delete_post_meta($post_id, $meta_key, $taxonomy->name);
464
- }
465
- } else {
466
- $terms = $tax_input[$this->id.'-'.$taxonomy->name];
467
-
468
- $found_key = array_search($taxonomy->name, $terms);
469
- //If meta key found maybe add it
470
- if ($found_key !== false) {
471
- if (!isset($old[$taxonomy->name])) {
472
- add_post_meta($post_id, $meta_key, $taxonomy->name);
473
- }
474
- unset($terms[$found_key]);
475
- //Otherwise maybe delete it
476
- } elseif (isset($old[$taxonomy->name])) {
477
- delete_post_meta($post_id, $meta_key, $taxonomy->name);
478
- }
479
-
480
- //Hierarchical taxonomies use ids instead of slugs
481
- //see http://codex.wordpress.org/Function_Reference/wp_set_post_terms
482
- if ($taxonomy->hierarchical) {
483
- $terms = array_unique(array_map('intval', $terms));
484
- }
485
- }
486
-
487
- if (!empty($terms)) {
488
- $has_select_terms = true;
489
- }
490
-
491
- wp_set_object_terms($post_id, $terms, $taxonomy->name);
492
- }
493
-
494
- if ($has_select_terms && !isset($old[self::VALUE_HAS_TERMS])) {
495
- add_post_meta($post_id, $meta_key, self::VALUE_HAS_TERMS);
496
- } elseif (!$has_select_terms && isset($old[self::VALUE_HAS_TERMS])) {
497
- delete_post_meta($post_id, $meta_key, self::VALUE_HAS_TERMS);
498
- }
499
- }
500
-
501
- /**
502
- * Auto-select children of selected ancestor
503
- *
504
- * @since 1.0
505
- * @param int $term_id
506
- * @param int $tt_id
507
- * @param string $taxonomy
508
- * @return void
509
- */
510
- public function term_ancestry_check($term_id, $tt_id, $taxonomy)
511
- {
512
- if (is_taxonomy_hierarchical($taxonomy)) {
513
- $term = get_term($term_id, $taxonomy);
514
-
515
- if ($term->parent != '0') {
516
- // Get sidebars with term ancestor wanting to auto-select term
517
- $query = new WP_Query([
518
- 'post_type' => WPCACore::TYPE_CONDITION_GROUP,
519
- 'post_status' => [WPCACore::STATUS_OR,WPCACore::STATUS_EXCEPT,WPCACore::STATUS_PUBLISHED],
520
- 'meta_query' => [
521
- [
522
- 'key' => WPCACore::PREFIX . 'autoselect',
523
- 'value' => 1,
524
- 'compare' => '='
525
- ]
526
- ],
527
- 'tax_query' => [
528
- [
529
- 'taxonomy' => $taxonomy,
530
- 'field' => 'id',
531
- 'terms' => get_ancestors($term_id, $taxonomy),
532
- 'include_children' => false
533
- ]
534
- ]
535
- ]);
536
- if ($query && $query->found_posts) {
537
- foreach ($query->posts as $post) {
538
- wp_set_post_terms($post->ID, $term_id, $taxonomy, true);
539
- }
540
- do_action('wpca/modules/auto-select/'.$this->category, $query->posts, $term);
541
- }
542
- }
543
- }
544
- }
545
- }
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ /**
12
+ *
13
+ * Taxonomy Module
14
+ *
15
+ * Detects if current content has/is:
16
+ * a) any term of specific taxonomy or specific term
17
+ * b) taxonomy archive or specific term archive
18
+ *
19
+ */
20
+ class WPCAModule_taxonomy extends WPCAModule_Base
21
+ {
22
+ /**
23
+ * when condition has select terms,
24
+ * set this value in postmeta
25
+ * @see parent::filter_excluded_context()
26
+ */
27
+ const VALUE_HAS_TERMS = '-1';
28
+
29
+ /**
30
+ * @var string
31
+ */
32
+ protected $category = 'taxonomy';
33
+
34
+ /**
35
+ * Registered public taxonomies
36
+ *
37
+ * @var array
38
+ */
39
+ private $taxonomy_objects = [];
40
+
41
+ /**
42
+ * Terms of a given singular
43
+ *
44
+ * @var array
45
+ */
46
+ private $post_terms = [];
47
+
48
+ /**
49
+ * Taxonomies for a given singular
50
+ * @var array
51
+ */
52
+ private $post_taxonomies = [];
53
+
54
+ public function __construct()
55
+ {
56
+ parent::__construct('taxonomy', __('Taxonomies', WPCA_DOMAIN));
57
+ $this->query_name = 'ct';
58
+ }
59
+
60
+ /**
61
+ * @inheritDoc
62
+ */
63
+ public function initiate()
64
+ {
65
+ parent::initiate();
66
+ add_action(
67
+ 'created_term',
68
+ [$this,'term_ancestry_check'],
69
+ 10,
70
+ 3
71
+ );
72
+
73
+ if (is_admin()) {
74
+ foreach ($this->_get_taxonomies() as $taxonomy) {
75
+ add_action(
76
+ 'wp_ajax_wpca/module/' . $this->id . '-' . $taxonomy->name,
77
+ [$this,'ajax_print_content']
78
+ );
79
+ }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @inheritDoc
85
+ */
86
+ public function in_context()
87
+ {
88
+ //check if post_taxonomies contains more than self::VALUE_HAS_TERMS
89
+ return count($this->get_context_data()) > 1;
90
+ }
91
+
92
+ /**
93
+ * @inheritDoc
94
+ */
95
+ public function get_context_data()
96
+ {
97
+ if (!empty($this->post_taxonomies)) {
98
+ return $this->post_taxonomies;
99
+ }
100
+
101
+ $this->post_taxonomies[] = self::VALUE_HAS_TERMS;
102
+
103
+ if (is_singular()) {
104
+ $tax = $this->_get_taxonomies();
105
+ // Check if content has any taxonomies supported
106
+ foreach (get_object_taxonomies(get_post_type()) as $taxonomy) {
107
+ //Only want taxonomies selectable in admin
108
+ if (isset($tax[$taxonomy])) {
109
+ //Check term caches, Core most likely used it
110
+ $terms = get_object_term_cache(get_the_ID(), $taxonomy);
111
+ if ($terms === false) {
112
+ $terms = wp_get_object_terms(get_the_ID(), $taxonomy);
113
+ }
114
+ if ($terms) {
115
+ $this->post_taxonomies[] = $taxonomy;
116
+ $this->post_terms = array_merge($this->post_terms, $terms);
117
+ }
118
+ }
119
+ }
120
+ } elseif (is_tax() || is_category() || is_tag()) {
121
+ $term = get_queried_object();
122
+ $this->post_taxonomies[] = $term->taxonomy;
123
+ $this->post_terms[] = $term;
124
+ }
125
+
126
+ return $this->post_taxonomies;
127
+ }
128
+
129
+ /**
130
+ * @inheritDoc
131
+ */
132
+ public function filter_excluded_context($posts, $in_context = false)
133
+ {
134
+ $posts = parent::filter_excluded_context($posts, $in_context);
135
+ if ($in_context) {
136
+ $post_terms_by_tax = [];
137
+ //@todo archive pages should be migrated to use AND as well, keep OR for now
138
+ $legacy_use_or = is_archive();
139
+ foreach ($this->post_terms as $term) {
140
+ $post_terms_by_tax[$term->taxonomy][$term->term_taxonomy_id] = $term->term_taxonomy_id;
141
+ }
142
+
143
+ $check_terms = [];
144
+ $keep_archive = [];
145
+ $unset = [];
146
+
147
+ //1. group's taxonomies must match all in post
148
+ foreach ($posts as $condition_id => $condition_group) {
149
+ $condition_taxonomies = get_post_meta($condition_id, '_ca_taxonomy', false);
150
+ foreach ($condition_taxonomies as $taxonomy) {
151
+ //if value==-1, group has individual terms, so goto 2
152
+ if ($taxonomy == '-1') {
153
+ $check_terms[$condition_id] = $condition_group;
154
+ } elseif (isset($post_terms_by_tax[$taxonomy])) {
155
+ //if on archive page, bail after 1st match
156
+ if ($legacy_use_or) {
157
+ $keep_archive[$condition_id] = 1;
158
+ break;
159
+ }
160
+ } else {
161
+ //if group has more taxonomies than post, unset
162
+ $unset[$condition_id] = 1;
163
+ //break;
164
+ }
165
+ }
166
+ }
167
+
168
+ //2. group's terms must match with minimum 1 in each taxonomy in post
169
+ if (!empty($check_terms)) {
170
+ //eager load groups terms
171
+ $conditions_terms = wp_get_object_terms(array_keys($check_terms), array_keys($this->_get_taxonomies()), [
172
+ 'fields' => 'all_with_object_id',
173
+ 'orderby' => 'none',
174
+ 'update_term_meta_cache' => false
175
+ ]);
176
+
177
+ $conditions_to_unset = [];
178
+ foreach ($conditions_terms as $term) {
179
+ if (!isset($conditions_to_unset[$term->object_id][$term->taxonomy])) {
180
+ $conditions_to_unset[$term->object_id][$term->taxonomy] = 0;
181
+ }
182
+ $has_tax_term = isset($post_terms_by_tax[$term->taxonomy][$term->term_taxonomy_id]);
183
+ $conditions_to_unset[$term->object_id][$term->taxonomy] |= $has_tax_term;
184
+ if ($legacy_use_or && $has_tax_term) {
185
+ $keep_archive[$term->object_id] = 1;
186
+ }
187
+ }
188
+
189
+ foreach ($check_terms as $condition_id => $condition_group) {
190
+ //if group has no terms in these taxonomies, it has terms in others, so unset
191
+ if (!isset($conditions_to_unset[$condition_id])) {
192
+ $unset[$condition_id] = 1;
193
+ continue;
194
+ }
195
+
196
+ foreach ($conditions_to_unset[$condition_id] as $taxonomy => $should_keep) {
197
+ //if group has a taxonomy with no term match, unset
198
+ if (!$should_keep) {
199
+ $unset[$condition_id] = 1;
200
+ break;
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ foreach ($unset as $id => $value) {
207
+ if (!isset($keep_archive[$id])) {
208
+ unset($posts[$id]);
209
+ }
210
+ }
211
+ }
212
+
213
+ return $posts;
214
+ }
215
+
216
+ /**
217
+ * @inheritDoc
218
+ */
219
+ protected function _get_content($args = [])
220
+ {
221
+ $total_items = wp_count_terms($args['taxonomy'], [
222
+ 'hide_empty' => $args['hide_empty']
223
+ ]);
224
+
225
+ $start = $args['offset'];
226
+ $end = $start + $args['number'];
227
+ $walk_tree = false;
228
+ $retval = [];
229
+
230
+ if ($total_items) {
231
+ $taxonomy = get_taxonomy($args['taxonomy']);
232
+
233
+ if ($taxonomy->hierarchical && !$args['search'] && !$args['include']) {
234
+ $args['number'] = 0;
235
+ $args['offset'] = 0;
236
+
237
+ $walk_tree = true;
238
+ }
239
+
240
+ $terms = new WP_Term_Query($args);
241
+
242
+ if ($walk_tree) {
243
+ $sorted_terms = [];
244
+ foreach ($terms->terms as $term) {
245
+ $sorted_terms[$term->parent][] = $term;
246
+ }
247
+ $i = 0;
248
+ $this->_walk_tree($sorted_terms, $sorted_terms[0], $i, $start, $end, 0, $retval);
249
+ } else {
250
+ //Hierarchical taxonomies use ids instead of slugs
251
+ //see http://codex.wordpress.org/Function_Reference/wp_set_post_objects
252
+ $value_var = ($taxonomy->hierarchical ? 'term_id' : 'slug');
253
+
254
+ foreach ($terms->terms as $term) {
255
+ //term names are encoded
256
+ $retval[$term->$value_var] = htmlspecialchars_decode($term->name);
257
+ }
258
+ }
259
+ }
260
+ return $retval;
261
+ }
262
+
263
+ /**
264
+ * Get hierarchical content with level param
265
+ *
266
+ * @since 3.7.2
267
+ * @param array $all_terms
268
+ * @param array $terms
269
+ * @param int $i
270
+ * @param int $start
271
+ * @param int $end
272
+ * @param int $level
273
+ * @param array &$retval
274
+ * @return void
275
+ */
276
+ protected function _walk_tree($all_terms, $terms, &$i, $start, $end, $level, &$retval)
277
+ {
278
+ foreach ($terms as $term) {
279
+ if ($i >= $end) {
280
+ break;
281
+ }
282
+
283
+ if ($i >= $start) {
284
+ $retval[] = [
285
+ 'id' => $term->term_id,
286
+ 'text' => htmlspecialchars_decode($term->name),
287
+ 'level' => $level
288
+ ];
289
+ }
290
+
291
+ $i++;
292
+
293
+ if (isset($all_terms[$term->term_id])) {
294
+ $this->_walk_tree($all_terms, $all_terms[$term->term_id], $i, $start, $end, $level + 1, $retval);
295
+ }
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Get registered public taxonomies
301
+ *
302
+ * @since 1.0
303
+ * @return array
304
+ */
305
+ protected function _get_taxonomies()
306
+ {
307
+ // List public taxonomies
308
+ if (empty($this->taxonomy_objects)) {
309
+ foreach (get_taxonomies(['public' => true], 'objects') as $tax) {
310
+ $this->taxonomy_objects[$tax->name] = $tax;
311
+ }
312
+ if (defined('POLYLANG_VERSION')) {
313
+ unset($this->taxonomy_objects['language']);
314
+ }
315
+ }
316
+ return $this->taxonomy_objects;
317
+ }
318
+
319
+ /**
320
+ * @inheritDoc
321
+ */
322
+ public function get_group_data($group_data, $post_id)
323
+ {
324
+ $ids = array_flip((array)get_post_custom_values(WPCACore::PREFIX . $this->id, $post_id));
325
+
326
+ //Fetch all terms and group by tax to prevent lazy loading
327
+ $terms = wp_get_object_terms(
328
+ $post_id,
329
+ array_keys($this->_get_taxonomies())
330
+ // array(
331
+ // 'update_term_meta_cache' => false
332
+ // )
333
+ );
334
+ $terms_by_tax = [];
335
+ foreach ($terms as $term) {
336
+ $terms_by_tax[$term->taxonomy][] = $term;
337
+ }
338
+
339
+ $title_count = $this->get_title_count();
340
+ foreach ($this->_get_taxonomies() as $taxonomy) {
341
+ $posts = isset($terms_by_tax[$taxonomy->name]) ? $terms_by_tax[$taxonomy->name] : 0;
342
+
343
+ if ($posts || isset($ids[$taxonomy->name])) {
344
+ $group_data[$this->id . '-' . $taxonomy->name] = $this->get_list_data($taxonomy, $title_count[$taxonomy->label]);
345
+ $group_data[$this->id . '-' . $taxonomy->name]['label'] = $group_data[$this->id . '-' . $taxonomy->name]['text'];
346
+
347
+ if ($posts) {
348
+ $retval = [];
349
+
350
+ //Hierarchical taxonomies use ids instead of slugs
351
+ //see http://codex.wordpress.org/Function_Reference/wp_set_post_objects
352
+ $value_var = ($taxonomy->hierarchical ? 'term_id' : 'slug');
353
+
354
+ foreach ($posts as $post) {
355
+ $retval[$post->$value_var] = $post->name;
356
+ }
357
+ $group_data[$this->id . '-' . $taxonomy->name]['data'] = $retval;
358
+ }
359
+ }
360
+ }
361
+ return $group_data;
362
+ }
363
+
364
+ /**
365
+ * Count taxonomy labels to find shared ones
366
+ *
367
+ * @return array
368
+ */
369
+ protected function get_title_count()
370
+ {
371
+ $title_count = [];
372
+ foreach ($this->_get_taxonomies() as $taxonomy) {
373
+ if (!isset($title_count[$taxonomy->label])) {
374
+ $title_count[$taxonomy->label] = 0;
375
+ }
376
+ $title_count[$taxonomy->label]++;
377
+ }
378
+ return $title_count;
379
+ }
380
+
381
+ /**
382
+ * @param WP_Taxonomy $taxonomy
383
+ * @param int $title_count
384
+ * @return array
385
+ */
386
+ protected function get_list_data($taxonomy, $title_count)
387
+ {
388
+ $placeholder = '/' . sprintf(__('%s Archives', WPCA_DOMAIN), $taxonomy->labels->singular_name);
389
+ $placeholder = $taxonomy->labels->all_items . $placeholder;
390
+ $label = $taxonomy->label;
391
+
392
+ if (count($taxonomy->object_type) === 1 && $title_count > 1) {
393
+ $post_type = get_post_type_object($taxonomy->object_type[0]);
394
+ $label .= ' (' . $post_type->label . ')';
395
+ }
396
+
397
+ return [
398
+ 'text' => $label,
399
+ 'icon' => $taxonomy->hierarchical ? 'dashicons-category' : 'dashicons-tag',
400
+ 'placeholder' => $placeholder,
401
+ 'default_value' => $taxonomy->name
402
+ ];
403
+ }
404
+
405
+ /**
406
+ * @inheritDoc
407
+ */
408
+ public function list_module($list)
409
+ {
410
+ $title_count = $this->get_title_count();
411
+ foreach ($this->_get_taxonomies() as $taxonomy) {
412
+ $data = $this->get_list_data($taxonomy, $title_count[$taxonomy->label]);
413
+ $data['id'] = $this->id . '-' . $taxonomy->name;
414
+ $list[] = $data;
415
+ }
416
+ return $list;
417
+ }
418
+
419
+ /**
420
+ * @inheritDoc
421
+ */
422
+ protected function parse_query_args($args)
423
+ {
424
+ if (isset($args['item_object'])) {
425
+ preg_match('/taxonomy-(.+)$/i', $args['item_object'], $matches);
426
+ $args['item_object'] = isset($matches[1]) ? $matches[1] : '___';
427
+ $taxonomy_name = $args['item_object'];
428
+ } else {
429
+ $taxonomy_name = 'category';
430
+ }
431
+
432
+ return [
433
+ 'include' => $args['include'],
434
+ 'taxonomy' => $taxonomy_name,
435
+ 'number' => $args['limit'],
436
+ 'offset' => ($args['paged'] - 1) * $args['limit'],
437
+ 'orderby' => 'name',
438
+ 'order' => 'ASC',
439
+ 'search' => $args['search'],
440
+ 'hide_empty' => false,
441
+ 'update_term_meta_cache' => false
442
+ ];
443
+ }
444
+
445
+ /**
446
+ * @inheritDoc
447
+ */
448
+ public function save_data($post_id)
449
+ {
450
+ $meta_key = WPCACore::PREFIX . $this->id;
451
+ $old = array_flip(get_post_meta($post_id, $meta_key, false));
452
+ $tax_input = $_POST['conditions'];
453
+
454
+ $has_select_terms = false;
455
+
456
+ //Save terms
457
+ //Loop through each public taxonomy
458
+ foreach ($this->_get_taxonomies() as $taxonomy) {
459
+ //If no terms, maybe delete old ones
460
+ if (!isset($tax_input[$this->id . '-' . $taxonomy->name])) {
461
+ $terms = [];
462
+ if (isset($old[$taxonomy->name])) {
463
+ delete_post_meta($post_id, $meta_key, $taxonomy->name);
464
+ }
465
+ } else {
466
+ $terms = $tax_input[$this->id . '-' . $taxonomy->name];
467
+
468
+ $found_key = array_search($taxonomy->name, $terms);
469
+ //If meta key found maybe add it
470
+ if ($found_key !== false) {
471
+ if (!isset($old[$taxonomy->name])) {
472
+ add_post_meta($post_id, $meta_key, $taxonomy->name);
473
+ }
474
+ unset($terms[$found_key]);
475
+ //Otherwise maybe delete it
476
+ } elseif (isset($old[$taxonomy->name])) {
477
+ delete_post_meta($post_id, $meta_key, $taxonomy->name);
478
+ }
479
+
480
+ //Hierarchical taxonomies use ids instead of slugs
481
+ //see http://codex.wordpress.org/Function_Reference/wp_set_post_terms
482
+ if ($taxonomy->hierarchical) {
483
+ $terms = array_unique(array_map('intval', $terms));
484
+ }
485
+ }
486
+
487
+ if (!empty($terms)) {
488
+ $has_select_terms = true;
489
+ }
490
+
491
+ wp_set_object_terms($post_id, $terms, $taxonomy->name);
492
+ }
493
+
494
+ if ($has_select_terms && !isset($old[self::VALUE_HAS_TERMS])) {
495
+ add_post_meta($post_id, $meta_key, self::VALUE_HAS_TERMS);
496
+ } elseif (!$has_select_terms && isset($old[self::VALUE_HAS_TERMS])) {
497
+ delete_post_meta($post_id, $meta_key, self::VALUE_HAS_TERMS);
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Auto-select children of selected ancestor
503
+ *
504
+ * @since 1.0
505
+ * @param int $term_id
506
+ * @param int $tt_id
507
+ * @param string $taxonomy
508
+ * @return void
509
+ */
510
+ public function term_ancestry_check($term_id, $tt_id, $taxonomy)
511
+ {
512
+ if (is_taxonomy_hierarchical($taxonomy)) {
513
+ $term = get_term($term_id, $taxonomy);
514
+
515
+ if ($term->parent != '0') {
516
+ // Get sidebars with term ancestor wanting to auto-select term
517
+ $query = new WP_Query([
518
+ 'post_type' => WPCACore::TYPE_CONDITION_GROUP,
519
+ 'post_status' => [WPCACore::STATUS_OR,WPCACore::STATUS_EXCEPT,WPCACore::STATUS_PUBLISHED],
520
+ 'meta_query' => [
521
+ [
522
+ 'key' => WPCACore::PREFIX . 'autoselect',
523
+ 'value' => 1,
524
+ 'compare' => '='
525
+ ]
526
+ ],
527
+ 'tax_query' => [
528
+ [
529
+ 'taxonomy' => $taxonomy,
530
+ 'field' => 'id',
531
+ 'terms' => get_ancestors($term_id, $taxonomy),
532
+ 'include_children' => false
533
+ ]
534
+ ]
535
+ ]);
536
+ if ($query && $query->found_posts) {
537
+ foreach ($query->posts as $post) {
538
+ wp_set_post_terms($post->ID, $term_id, $taxonomy, true);
539
+ }
540
+ do_action('wpca/modules/auto-select/' . $this->category, $query->posts, $term);
541
+ }
542
+ }
543
+ }
544
+ }
545
+ }
lib/wp-content-aware-engine/module/translatepress.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
@@ -26,6 +26,7 @@ class WPCAModule_translatepress extends WPCAModule_Base
26
  public function __construct()
27
  {
28
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
 
29
  $this->query_name = 'cl';
30
  }
31
 
@@ -64,7 +65,6 @@ class WPCAModule_translatepress extends WPCAModule_Base
64
  */
65
  protected function _get_content($args = [])
66
  {
67
- $langs = [];
68
  $trp_instance = TRP_Translate_Press::get_trp_instance();
69
  $langs = $trp_instance->get_component('languages')->get_language_names(
70
  $trp_instance->get_component('settings')->get_setting('publish-languages')
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
26
  public function __construct()
27
  {
28
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
29
+ $this->icon = 'dashicons-translation';
30
  $this->query_name = 'cl';
31
  }
32
 
65
  */
66
  protected function _get_content($args = [])
67
  {
 
68
  $trp_instance = TRP_Translate_Press::get_trp_instance();
69
  $langs = $trp_instance->get_component('languages')->get_language_names(
70
  $trp_instance->get_component('settings')->get_setting('publish-languages')
lib/wp-content-aware-engine/module/transposh.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
@@ -27,6 +27,7 @@ class WPCAModule_transposh extends WPCAModule_Base
27
  public function __construct()
28
  {
29
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
 
30
  $this->query_name = 'cl';
31
  }
32
 
@@ -64,7 +65,6 @@ class WPCAModule_transposh extends WPCAModule_Base
64
  */
65
  protected function _get_content($args = [])
66
  {
67
- global $my_transposh_plugin;
68
  $langs = [];
69
 
70
  /**
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
27
  public function __construct()
28
  {
29
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
30
+ $this->icon = 'dashicons-translation';
31
  $this->query_name = 'cl';
32
  }
33
 
65
  */
66
  protected function _get_content($args = [])
67
  {
 
68
  $langs = [];
69
 
70
  /**
lib/wp-content-aware-engine/module/weglot.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
@@ -26,6 +26,7 @@ class WPCAModule_weglot extends WPCAModule_Base
26
  public function __construct()
27
  {
28
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
 
29
  $this->query_name = 'cl';
30
  }
31
 
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
26
  public function __construct()
27
  {
28
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
29
+ $this->icon = 'dashicons-translation';
30
  $this->query_name = 'cl';
31
  }
32
 
lib/wp-content-aware-engine/module/wpml.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
@@ -27,6 +27,7 @@ class WPCAModule_wpml extends WPCAModule_Base
27
  public function __construct()
28
  {
29
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
 
30
  $this->query_name = 'cl';
31
  }
32
 
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
27
  public function __construct()
28
  {
29
  parent::__construct('language', __('Languages', WPCA_DOMAIN));
30
+ $this->icon = 'dashicons-translation';
31
  $this->query_name = 'cl';
32
  }
33
 
lib/wp-content-aware-engine/modulemanager.php DELETED
@@ -1,26 +0,0 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- if (!class_exists('WPCAModuleManager')) {
12
- /**
13
- * Manage module objects
14
- */
15
- final class WPCAModuleManager extends WPCAObjectManager
16
- {
17
-
18
- /**
19
- * Constructor
20
- */
21
- public function __construct()
22
- {
23
- parent::__construct();
24
- }
25
- }
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/wp-content-aware-engine/objectmanager.php CHANGED
@@ -1,145 +1,18 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- if (!class_exists('WPCAObjectManager')) {
12
- /**
13
- * Manage a list of objects nicely
14
- */
15
- class WPCAObjectManager implements IteratorAggregate
16
- {
17
-
18
- /**
19
- * List of objects
20
- *
21
- * @var array
22
- */
23
- private $objects = [];
24
-
25
- /**
26
- * Constructor
27
- *
28
- * @since 1.0
29
- */
30
- public function __construct()
31
- {
32
- }
33
-
34
- /**
35
- * Add object to the manager if key is
36
- * not already added
37
- *
38
- * @since 1.0
39
- * @param mixed $object
40
- * @param string $name
41
- * @return WPCAObjectManager
42
- */
43
- public function add($object, $name)
44
- {
45
- if (!$this->has($name)) {
46
- $this->set($object, $name);
47
- }
48
- return $this;
49
- }
50
-
51
- /**
52
- * Remove object with key from manager
53
- *
54
- * @since 1.0
55
- * @param string $name
56
- * @return void
57
- */
58
- public function remove($name)
59
- {
60
- unset($this->objects[$name]);
61
- }
62
-
63
- /**
64
- * Check if manager has key
65
- *
66
- * @since 1.0
67
- * @param string $name
68
- * @return boolean
69
- */
70
- public function has($name)
71
- {
72
- return isset($this->objects[$name]);
73
- }
74
-
75
- /**
76
- * Get object with key
77
- * Returns null if not found
78
- *
79
- * @since 1.0
80
- * @param string $name
81
- * @return mixed|null
82
- */
83
- public function get($name)
84
- {
85
- return $this->has($name) ? $this->objects[$name] : null;
86
- }
87
-
88
- /**
89
- * Add object to manager regardless if
90
- * key exists already
91
- *
92
- * @since 1.0
93
- * @param mixed $object
94
- * @param string $name
95
- */
96
- public function set($object, $name)
97
- {
98
- $this->objects[$name] = $object;
99
- }
100
-
101
- /**
102
- * Get all objects in manager
103
- *
104
- * @since 1.0
105
- * @return array
106
- */
107
- public function get_all()
108
- {
109
- return $this->objects;
110
- }
111
-
112
- /**
113
- * Set all objects in manager
114
- *
115
- * @since 1.0
116
- * @param array $objects
117
- */
118
- public function set_all($objects)
119
- {
120
- $this->objects = (array)$objects;
121
- }
122
-
123
- /**
124
- * Count objects
125
- *
126
- * @since 1.0
127
- * @return int
128
- */
129
- public function count()
130
- {
131
- return count($this->objects);
132
- }
133
-
134
- /**
135
- * Make objects traversable
136
- *
137
- * @since 4.2
138
- * @return ArrayIterator
139
- */
140
- public function getIterator()
141
- {
142
- return new ArrayIterator($this->objects);
143
- }
144
- }
145
- }
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ if (!class_exists('WPCAObjectManager')) {
12
+ /**
13
+ * @deprecated
14
+ */
15
+ class WPCAObjectManager extends WPCACollection
16
+ {
17
+ }
18
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/wp-content-aware-engine/typemanager.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
@@ -12,7 +12,7 @@ if (!class_exists('WPCATypeManager')) {
12
  /**
13
  * Manage module objects
14
  */
15
- final class WPCATypeManager extends WPCAObjectManager
16
  {
17
  /**
18
  * Constructor
@@ -28,15 +28,12 @@ if (!class_exists('WPCATypeManager')) {
28
  }
29
 
30
  /**
31
- * Add module to manager
32
- *
33
- * @since 1.0
34
- * @param object $class
35
- * @param string $name
36
  */
37
- public function add($name, $arg = '')
38
  {
39
- parent::add(new WPCAObjectManager(), $name);
40
  }
41
 
42
  /**
@@ -80,8 +77,8 @@ if (!class_exists('WPCATypeManager')) {
80
  continue;
81
  }
82
 
83
- foreach ($this->get_all() as $post_type) {
84
- $post_type->add($class, $name);
85
  }
86
  }
87
 
@@ -89,12 +86,12 @@ if (!class_exists('WPCATypeManager')) {
89
 
90
  //initiate all modules once with backwards compatibility on can_enable()
91
  $initiated = [];
92
- foreach ($this->get_all() as $post_type_name => $post_type) {
93
  if (!WPCACore::get_option($post_type_name, 'legacy.date_module', false)) {
94
  $post_type->remove('date');
95
  }
96
 
97
- foreach ($post_type->get_all() as $key => $module) {
98
  if (!isset($initiated[$key])) {
99
  $initiated[$key] = 1;
100
  $module->initiate();
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
12
  /**
13
  * Manage module objects
14
  */
15
+ final class WPCATypeManager extends WPCACollection
16
  {
17
  /**
18
  * Constructor
28
  }
29
 
30
  /**
31
+ * @param string $name
32
+ * @return $this
 
 
 
33
  */
34
+ public function add($name)
35
  {
36
+ return parent::put($name, new WPCACollection());
37
  }
38
 
39
  /**
77
  continue;
78
  }
79
 
80
+ foreach ($this->all() as $post_type) {
81
+ $post_type->put($name, $class);
82
  }
83
  }
84
 
86
 
87
  //initiate all modules once with backwards compatibility on can_enable()
88
  $initiated = [];
89
+ foreach ($this->all() as $post_type_name => $post_type) {
90
  if (!WPCACore::get_option($post_type_name, 'legacy.date_module', false)) {
91
  $post_type->remove('date');
92
  }
93
 
94
+ foreach ($post_type->all() as $key => $module) {
95
  if (!isset($initiated[$key])) {
96
  $initiated[$key] = 1;
97
  $module->initiate();
lib/wp-content-aware-engine/view.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  defined('ABSPATH') || exit;
lib/wp-content-aware-engine/view/condition_options.php CHANGED
@@ -1,42 +1,42 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
- ?>
9
  <li>
10
  <label><?php _e('Page Types', WPCA_DOMAIN); ?></label>
11
  <div class="wpca-pull-right">
12
  <select data-vm="value:exposure">
13
- <option value="0"><?php _e('Single Pages', WPCA_DOMAIN); ?>
14
  </option>
15
- <option value="2"><?php _e('Archive Pages', WPCA_DOMAIN); ?>
16
  </option>
17
- <option value="1"><?php _e('All', WPCA_DOMAIN); ?>
18
  </option>
19
  </select>
20
  </div>
21
  </li>
22
- <?php if (WPCACore::get_option($post_type, 'legacy.negated_conditions', false)) : ?>
23
  <li>
24
  <label class="cae-toggle">
25
  <input data-vm="checked:statusNegated" type="checkbox" />
26
- <div class="cae-toggle-bar wpca-pull-right"></div><?php _e('Negate conditions', WPCA_DOMAIN); ?>
27
  (Legacy)
28
  </label>
29
  </li>
30
- <?php endif; ?>
31
  <li>
32
  <label class="cae-toggle">
33
  <input data-vm="checked:statusExcept" type="checkbox" />
34
- <div class="cae-toggle-bar wpca-pull-right"></div><?php _e('Exception', WPCA_DOMAIN); ?>
35
  </label>
36
  </li>
37
  <li>
38
  <label class="cae-toggle">
39
  <input data-vm="checked:binary(_ca_autoselect)" type="checkbox" />
40
- <div class="cae-toggle-bar wpca-pull-right"></div><?php _e('Auto-select new children of selected items', WPCA_DOMAIN); ?>
41
  </label>
42
  </li>
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+ ?>
9
  <li>
10
  <label><?php _e('Page Types', WPCA_DOMAIN); ?></label>
11
  <div class="wpca-pull-right">
12
  <select data-vm="value:exposure">
13
+ <option value="0"><?php _e('Single Pages', WPCA_DOMAIN); ?>
14
  </option>
15
+ <option value="2"><?php _e('Archive Pages', WPCA_DOMAIN); ?>
16
  </option>
17
+ <option value="1"><?php _e('All', WPCA_DOMAIN); ?>
18
  </option>
19
  </select>
20
  </div>
21
  </li>
22
+ <?php if (WPCACore::get_option($post_type, 'legacy.negated_conditions', false)) : ?>
23
  <li>
24
  <label class="cae-toggle">
25
  <input data-vm="checked:statusNegated" type="checkbox" />
26
+ <div class="cae-toggle-bar wpca-pull-right"></div><?php _e('Negate conditions', WPCA_DOMAIN); ?>
27
  (Legacy)
28
  </label>
29
  </li>
30
+ <?php endif; ?>
31
  <li>
32
  <label class="cae-toggle">
33
  <input data-vm="checked:statusExcept" type="checkbox" />
34
+ <div class="cae-toggle-bar wpca-pull-right"></div><?php _e('Exception', WPCA_DOMAIN); ?>
35
  </label>
36
  </li>
37
  <li>
38
  <label class="cae-toggle">
39
  <input data-vm="checked:binary(_ca_autoselect)" type="checkbox" />
40
+ <div class="cae-toggle-bar wpca-pull-right"></div><?php _e('Auto-select new children of selected items', WPCA_DOMAIN); ?>
41
  </label>
42
  </li>
lib/wp-content-aware-engine/view/condition_template.php CHANGED
@@ -3,11 +3,12 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
  ?>
9
  <script type="text/template" id="wpca-template-condition">
10
  <div class="cas-group-sep"><span><?php _e('And', WPCA_DOMAIN); ?></span></div>
 
11
  <div class='cas-group-label'>
12
  <span class='js-wpca-condition-remove wpca-condition-remove dashicons dashicons-trash'></span>
13
  <span data-vm="html:label"></span>
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
  ?>
9
  <script type="text/template" id="wpca-template-condition">
10
  <div class="cas-group-sep"><span><?php _e('And', WPCA_DOMAIN); ?></span></div>
11
+ <div class="cas-group-icon" data-vm="html:getIcon"></div>
12
  <div class='cas-group-label'>
13
  <span class='js-wpca-condition-remove wpca-condition-remove dashicons dashicons-trash'></span>
14
  <span data-vm="html:label"></span>
lib/wp-content-aware-engine/view/group_template.php CHANGED
@@ -1,11 +1,11 @@
1
- <?php
2
- /**
3
- * @package wp-content-aware-engine
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
- */
8
- ?>
9
  <script type="text/template" id="wpca-template-group">
10
  <div class="cas-group-sep" data-vm="classes:{'wpca-group-negate':any(statusExcept,statusNegated)}">
11
  <span data-vm="text:statusLabel"></span>
@@ -20,12 +20,12 @@
20
  <div class="alignright">
21
  <span class="spinner"></span>
22
  <button class="js-wpca-save-group button button-small hide-if-js" type="button"><?php _e('Save Changes', WPCA_DOMAIN); ?></button>
23
- <?php do_action('wpca/group/actions', $post_type); ?>
24
  <button type="button" class="button button-small js-wpca-options"><span class="dashicons dashicons-admin-generic"></span> <?php _e('Settings', WPCA_DOMAIN) ?></button>
25
  </div>
26
  </div>
27
  <ul class="cas-group-options hide-if-js">
28
- <?php do_action('wpca/group/settings', $post_type); ?>
29
  </ul>
30
  <div class="cas-group-cell">
31
  <div class="cas-content" data-vm="collection:$collection"></div>
1
+ <?php
2
+ /**
3
+ * @package wp-content-aware-engine
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+ ?>
9
  <script type="text/template" id="wpca-template-group">
10
  <div class="cas-group-sep" data-vm="classes:{'wpca-group-negate':any(statusExcept,statusNegated)}">
11
  <span data-vm="text:statusLabel"></span>
20
  <div class="alignright">
21
  <span class="spinner"></span>
22
  <button class="js-wpca-save-group button button-small hide-if-js" type="button"><?php _e('Save Changes', WPCA_DOMAIN); ?></button>
23
+ <?php do_action('wpca/group/actions', $post_type); ?>
24
  <button type="button" class="button button-small js-wpca-options"><span class="dashicons dashicons-admin-generic"></span> <?php _e('Settings', WPCA_DOMAIN) ?></button>
25
  </div>
26
  </div>
27
  <ul class="cas-group-options hide-if-js">
28
+ <?php do_action('wpca/group/settings', $post_type); ?>
29
  </ul>
30
  <div class="cas-group-cell">
31
  <div class="cas-content" data-vm="collection:$collection"></div>
lib/wp-content-aware-engine/view/meta_box.php CHANGED
@@ -3,7 +3,7 @@
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
- * @copyright 2021 by Joachim Jensen
7
  */
8
 
9
  $quick_links = [
3
  * @package wp-content-aware-engine
4
  * @author Joachim Jensen <joachim@dev.institute>
5
  * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
  */
8
 
9
  $quick_links = [
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: custom sidebars, sidebar, classic widgets, widget, bbpress, buddypress, si
5
  Requires at least: 5.0
6
  Requires PHP: 5.6
7
  Tested up to: 5.9
8
- Stable tag: 3.17
9
  License: GPLv3
10
 
11
  Display new sidebars on any post, page, category etc. Works with Classic Widgets, Block Widgets, and all themes!
@@ -186,6 +186,19 @@ Of course! Check out the links below:
186
 
187
  ####Highlights
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  = 3.17 =
190
 
191
  * [new] weglot display condition
5
  Requires at least: 5.0
6
  Requires PHP: 5.6
7
  Tested up to: 5.9
8
+ Stable tag: 3.17.1
9
  License: GPLv3
10
 
11
  Display new sidebars on any post, page, category etc. Works with Classic Widgets, Block Widgets, and all themes!
186
 
187
  ####Highlights
188
 
189
+ = 3.17.1 =
190
+
191
+ * [new] added icons and improved ui when editing conditions
192
+ * [new] performance improvements
193
+ * [fixed] quick-select could in some cases remove edited post from conditions
194
+ * [fixed] php8.1 deprecation warnings
195
+ * [updated] wp-content-aware-engine library
196
+ * [updated] freemius sdk
197
+
198
+ **Pro Plan:**
199
+
200
+ * [fixed] php8+ error when editing sidebar
201
+
202
  = 3.17 =
203
 
204
  * [new] weglot display condition
sidebar.php CHANGED
@@ -1,826 +1,820 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- defined('ABSPATH') || exit;
10
-
11
- final class CAS_Sidebar_Manager
12
- {
13
-
14
- /**
15
- * Sidebar metadata
16
- * @var WPCAObjectManager
17
- */
18
- protected $metadata;
19
-
20
- /**
21
- * Custom sidebars
22
- * @var array
23
- */
24
- public $sidebars = [];
25
-
26
- /**
27
- * Cache replaced sidebars
28
- * @var array
29
- */
30
- protected $replaced_sidebars = [];
31
-
32
- /**
33
- * Sidebar replacement map
34
- * @var array
35
- */
36
- protected $replace_map = [];
37
-
38
- /**
39
- * @var array
40
- * Constructor
41
- *
42
- * @since 3.1
43
- */
44
- public function __construct()
45
- {
46
- add_action(
47
- 'wpca/loaded',
48
- [$this,'late_init']
49
- );
50
- add_action(
51
- 'wp_head',
52
- [$this,'sidebar_notify_theme_customizer']
53
- );
54
- add_action(
55
- 'init',
56
- [$this,'init_sidebar_type'],
57
- 99
58
- );
59
- add_action(
60
- 'widgets_init',
61
- [$this,'create_sidebars'],
62
- 99
63
- );
64
- add_action(
65
- 'wp_loaded',
66
- [$this,'set_sidebar_styles'],
67
- 99
68
- );
69
-
70
- add_filter('get_edit_post_link', [$this,'get_edit_post_link'], 10, 3);
71
- add_filter('get_delete_post_link', [$this,'get_delete_post_link'], 10, 3);
72
-
73
- add_shortcode(
74
- 'ca-sidebar',
75
- [$this,'sidebar_shortcode']
76
- );
77
- }
78
-
79
- /**
80
- * Initialize after WPCA has been loaded
81
- * Makes sure the SDK can be used in actions/filters
82
- * forcefully called earlier
83
- *
84
- * @since 3.4
85
- * @return void
86
- */
87
- public function late_init()
88
- {
89
- if (!is_admin()) {
90
- add_filter(
91
- 'sidebars_widgets',
92
- [$this,'replace_sidebar']
93
- );
94
- add_filter(
95
- 'wpca/posts/sidebar',
96
- [__CLASS__,'filter_password_protection']
97
- );
98
- add_filter(
99
- 'wpca/posts/sidebar',
100
- [$this,'filter_visibility']
101
- );
102
- add_filter(
103
- 'cas/shortcode/display',
104
- [$this,'filter_shortcode_visibility'],
105
- 10,
106
- 2
107
- );
108
- add_action(
109
- 'dynamic_sidebar_before',
110
- [$this,'render_sidebar_before'],
111
- 9,
112
- 2
113
- );
114
- add_action(
115
- 'dynamic_sidebar_after',
116
- [$this,'render_sidebar_after'],
117
- 99,
118
- 2
119
- );
120
- }
121
- }
122
-
123
- /**
124
- * Get instance of metadata manager
125
- *
126
- * @since 3.0
127
- * @return WPCAObjectManager
128
- */
129
- public function metadata()
130
- {
131
- if (!$this->metadata) {
132
- $this->init_metadata();
133
- }
134
- return $this->metadata;
135
- }
136
-
137
- /**
138
- * Create post meta fields
139
- * @global array $wp_registered_sidebars
140
- * @return void
141
- */
142
- private function init_metadata()
143
- {
144
- $this->metadata = new WPCAObjectManager();
145
- $this->metadata
146
- ->add(new WPCAMeta(
147
- 'visibility',
148
- __('User Visibility', 'content-aware-sidebars'),
149
- [],
150
- 'multi',
151
- [
152
- 'general' => [
153
- 'label' => 'General',
154
- 'options' => [
155
- -1 => __('Logged-in', 'content-aware-sidebars')
156
- ]
157
- ]
158
- ]
159
- ), 'visibility')
160
- ->add(new WPCAMeta(
161
- 'handle',
162
- _x('Action', 'option', 'content-aware-sidebars'),
163
- CAS_App::ACTION_REPLACE,
164
- 'select',
165
- [
166
- CAS_App::ACTION_REPLACE => __('Replace', 'content-aware-sidebars'),
167
- CAS_App::ACTION_MERGE => __('Merge', 'content-aware-sidebars'),
168
- CAS_App::ACTION_REPLACE_FORCED => __('Forced replace', 'content-aware-sidebars'),
169
- CAS_App::ACTION_SHORTCODE => __('Shortcode')
170
- ],
171
- __('Replace host sidebar, merge with it or add sidebar manually.', 'content-aware-sidebars')
172
- ), 'handle')
173
- ->add(new WPCAMeta(
174
- 'host',
175
- __('Target Sidebar', 'content-aware-sidebars'),
176
- [],
177
- 'multi',
178
- []
179
- ), 'host')
180
- ->add(new WPCAMeta(
181
- 'merge_pos',
182
- __('Merge Position', 'content-aware-sidebars'),
183
- 1,
184
- 'select',
185
- [
186
- __('Top', 'content-aware-sidebars'),
187
- __('Bottom', 'content-aware-sidebars')
188
- ],
189
- __('Place sidebar on top or bottom of host when merging.', 'content-aware-sidebars')
190
- ), 'merge_pos')
191
- ->add(new WPCAMeta(
192
- 'html',
193
- __('HTML', 'content-aware-sidebars'),
194
- [],
195
- 'select',
196
- ['']
197
- ), 'html');
198
- apply_filters('cas/metadata/init', $this->metadata);
199
- }
200
-
201
- /**
202
- * Populate metadata with dynamic content
203
- * for use in admin
204
- *
205
- * @since 3.2
206
- * @return void
207
- */
208
- public function populate_metadata()
209
- {
210
- if ($this->metadata) {
211
- global $wp_registered_sidebars;
212
-
213
- // List of sidebars
214
- $sidebar_list = [];
215
- foreach ($wp_registered_sidebars as $sidebar) {
216
- $sidebar_list[$sidebar['id']] = $sidebar['name'];
217
- }
218
-
219
- // Remove ability to set self to host
220
- if (get_the_ID()) {
221
- unset($sidebar_list[CAS_App::SIDEBAR_PREFIX.get_the_ID()]);
222
- }
223
- asort($sidebar_list);
224
- $this->metadata->get('host')->set_input_list($sidebar_list);
225
-
226
- if (!cas_fs()->can_use_premium_code()) {
227
- $pro_label = ' (Pro)';
228
- $actions = $this->metadata->get('handle');
229
- $action_list = $actions->get_input_list();
230
- $action_list['__infuse'] = __('Infuse', 'content-aware-sidebars').$pro_label;
231
- $action_list['__after_paragraph'] = __('After Paragraph', 'content-aware-sidebars').$pro_label;
232
- $action_list['__totem'] = __('Totem - Floating Button', 'content-aware-sidebars').$pro_label;
233
- $actions->set_input_list($action_list);
234
- }
235
-
236
- apply_filters('cas/metadata/populate', $this->metadata);
237
- }
238
- }
239
-
240
- /**
241
- * Create sidebar post type
242
- * Add it to content aware engine
243
- *
244
- * @return void
245
- */
246
- public function init_sidebar_type()
247
- {
248
- register_post_type(CAS_App::TYPE_SIDEBAR, [
249
- 'labels' => [
250
- 'name' => __('Sidebars', 'content-aware-sidebars'),
251
- 'singular_name' => __('Sidebar', 'content-aware-sidebars'),
252
- 'add_new' => _x('Add New', 'sidebar', 'content-aware-sidebars'),
253
- 'add_new_item' => __('Add New Sidebar', 'content-aware-sidebars'),
254
- 'edit_item' => __('Edit Sidebar', 'content-aware-sidebars'),
255
- 'new_item' => __('New Sidebar', 'content-aware-sidebars'),
256
- 'all_items' => __('All Sidebars', 'content-aware-sidebars'),
257
- 'view_item' => __('View Sidebar', 'content-aware-sidebars'),
258
- 'search_items' => __('Search Sidebars', 'content-aware-sidebars'),
259
- 'not_found' => __('No sidebars found', 'content-aware-sidebars'),
260
- 'not_found_in_trash' => __('No sidebars found in Trash', 'content-aware-sidebars'),
261
- //wp-content-aware-engine specific
262
- 'ca_title' => __('Where to display', 'content-aware-sidebars')
263
- ],
264
- 'capabilities' => [
265
- 'edit_post' => CAS_App::CAPABILITY,
266
- 'read_post' => CAS_App::CAPABILITY,
267
- 'delete_post' => CAS_App::CAPABILITY,
268
- 'edit_posts' => CAS_App::CAPABILITY,
269
- 'delete_posts' => CAS_App::CAPABILITY,
270
- 'edit_others_posts' => CAS_App::CAPABILITY,
271
- 'publish_posts' => CAS_App::CAPABILITY,
272
- 'read_private_posts' => CAS_App::CAPABILITY
273
- ],
274
- 'public' => false,
275
- 'hierarchical' => false,
276
- 'exclude_from_search' => true,
277
- 'publicly_queryable' => false,
278
- 'show_ui' => false,
279
- 'show_in_menu' => false,
280
- 'show_in_nav_menus' => false,
281
- 'show_in_admin_bar' => false,
282
- 'has_archive' => false,
283
- 'rewrite' => false,
284
- 'query_var' => false,
285
- 'supports' => ['title','page-attributes'],
286
- 'menu_icon' => 'dashicons-welcome-widgets-menus',
287
- 'can_export' => false,
288
- 'delete_with_user' => false
289
- ]);
290
-
291
- WPCACore::types()->add(CAS_App::TYPE_SIDEBAR);
292
- }
293
-
294
- /**
295
- * Add sidebars to widgets area
296
- * Triggered in widgets_init to save location for each theme
297
- * @return void
298
- */
299
- public function create_sidebars()
300
- {
301
- $sidebars = get_posts([
302
- 'numberposts' => -1,
303
- 'post_type' => CAS_App::TYPE_SIDEBAR,
304
- 'post_status' => [
305
- CAS_App::STATUS_ACTIVE,
306
- CAS_App::STATUS_INACTIVE,
307
- CAS_App::STATUS_SCHEDULED
308
- ],
309
- 'orderby' => 'title',
310
- 'order' => 'ASC'
311
- ]);
312
-
313
- //Register sidebars to add them to the list
314
- foreach ($sidebars as $post) {
315
- $this->sidebars[CAS_App::SIDEBAR_PREFIX.$post->ID] = $post;
316
- register_sidebar([
317
- 'name' => $post->post_title ? $post->post_title : __('(no title)'),
318
- 'id' => CAS_App::SIDEBAR_PREFIX.$post->ID,
319
- 'before_sidebar' => '',
320
- 'after_sidebar' => ''
321
- ]);
322
- }
323
- }
324
-
325
- /**
326
- * Set styles of created sidebars
327
- *
328
- * @since 3.6
329
- */
330
- public function set_sidebar_styles()
331
- {
332
- global $wp_registered_sidebars;
333
-
334
- //todo: only for manual
335
- $default_styles = [
336
- 'before_widget' => '<div id="%1$s" class="widget-container %2$s">',
337
- 'after_widget' => '</div>',
338
- 'before_title' => '<h4 class="widget-title">',
339
- 'after_title' => '</h4>'
340
- ];
341
- $has_host = [
342
- CAS_App::ACTION_REPLACE => 1,
343
- CAS_App::ACTION_MERGE => 1,
344
- CAS_App::ACTION_REPLACE_FORCED => 1
345
- ];
346
- $metadata = $this->metadata();
347
-
348
- foreach ($this->sidebars as $id => $post) {
349
- $args = $default_styles;
350
-
351
- if (isset($has_host[$metadata->get('handle')->get_data($post->ID)])) {
352
- //Set style from host to fix when content aware sidebar
353
- //is called directly by other sidebar managers
354
- $host_id = $metadata->get('host')->get_data($post->ID);
355
- if (isset($wp_registered_sidebars[$host_id])) {
356
- foreach ([
357
- 'before_widget',
358
- 'after_widget',
359
- 'before_title',
360
- 'after_title',
361
- 'before_sidebar',
362
- 'after_sidebar'
363
- ] as $pos) {
364
- if (isset($wp_registered_sidebars[$host_id][$pos])) {
365
- $args[$pos] = $wp_registered_sidebars[$host_id][$pos];
366
- }
367
- }
368
- }
369
- }
370
-
371
- $wp_registered_sidebars[$id] = array_merge($wp_registered_sidebars[$id], $args);
372
- }
373
- }
374
-
375
- /**
376
- * @return array
377
- */
378
- public function get_replacement_map()
379
- {
380
- return $this->replace_map;
381
- }
382
-
383
- /**
384
- * @param string $i
385
- * @return void
386
- */
387
- public function override_sidebar_styles($i)
388
- {
389
- //Get nested styles
390
- $html = $this->get_sidebar_styles($i);
391
- if ($html) {
392
- global $wp_registered_sidebars;
393
- $styles = $wp_registered_sidebars[$i];
394
- //Set user styles
395
- foreach ([
396
- 'widget',
397
- 'title',
398
- 'sidebar'
399
- ] as $pos) {
400
- if (isset($html[$pos],$html[$pos.'_class'])) {
401
- $e = esc_html($html[$pos]);
402
- $class = esc_html($html[$pos.'_class']);
403
- $id = '';
404
- if (isset($html[$pos.'_id'])) {
405
- $id = ' id="'.$html[$pos.'_id'].'"';
406
- }
407
- $styles['before_'.$pos] = '<'.$e.$id.' class="'.$class.'">';
408
- $styles['after_'.$pos] = "</$e>";
409
- }
410
- }
411
- $wp_registered_sidebars[$i] = $styles;
412
- }
413
- }
414
-
415
-
416
- /**
417
- * Replace or merge a sidebar with content aware sidebars.
418
- * @since .
419
- * @param array $sidebars_widgets
420
- * @return array
421
- */
422
- public function replace_sidebar($sidebars_widgets)
423
- {
424
-
425
- //customizer requires sidebars_widgets filter. cache for repeat calls
426
- if ($this->replaced_sidebars) {
427
- return $this->replaced_sidebars;
428
- }
429
-
430
- $posts = WPCACore::get_posts(CAS_App::TYPE_SIDEBAR);
431
-
432
- if ($posts) {
433
- $metadata = $this->metadata();
434
- $has_host = [
435
- CAS_App::ACTION_REPLACE => 1,
436
- CAS_App::ACTION_MERGE => 1,
437
- CAS_App::ACTION_REPLACE_FORCED => 1
438
- ];
439
-
440
- //replace and merge widgets, build replacement map
441
- foreach ($posts as $post) {
442
- $id = CAS_App::SIDEBAR_PREFIX . $post->ID;
443
-
444
- // Check for correct handling
445
- if (!isset($has_host[$post->handle])) {
446
- continue;
447
- }
448
-
449
- $hosts = $metadata->get('host')->get_data($post->ID, false, false);
450
-
451
- foreach ($hosts as $host) {
452
-
453
- // Check if host exist
454
- if (!isset($sidebars_widgets[$host])) {
455
- continue;
456
- }
457
-
458
- $this->override_sidebar_styles($host);
459
-
460
- // Sidebar might not have any widgets. Get it anyway!
461
- if (!isset($sidebars_widgets[$id])) {
462
- $sidebars_widgets[$id] = [];
463
- }
464
-
465
- // If handle is merge or if handle is replace and host has already been replaced
466
- if ($post->handle == CAS_App::ACTION_MERGE || ($post->handle == CAS_App::ACTION_REPLACE && isset($handled_already[$host]))) {
467
- //do not merge forced replace
468
- //todo: maybe reverse order of fetched sidebars instead?
469
- if (isset($handled_already[$host]) && $handled_already[$host] == CAS_App::ACTION_REPLACE_FORCED) {
470
- continue;
471
- }
472
- if ($metadata->get('merge_pos')->get_data($post->ID)) {
473
- $sidebars_widgets[$host] = array_merge($sidebars_widgets[$host], $sidebars_widgets[$id]);
474
- } else {
475
- $sidebars_widgets[$host] = array_merge($sidebars_widgets[$id], $sidebars_widgets[$host]);
476
- }
477
- } else {
478
- $sidebars_widgets[$host] = $sidebars_widgets[$id];
479
- $handled_already[$host] = $post->handle;
480
- }
481
-
482
- //last replacement will take priority
483
- //todo: extend to work for widgets too
484
- $this->replace_map[$host] = $id;
485
- }
486
- }
487
- $this->replaced_sidebars = $sidebars_widgets;
488
-
489
- //override styles
490
- foreach ($posts as $post) {
491
- $id = CAS_App::SIDEBAR_PREFIX . $post->ID;
492
-
493
- // Check for correct handling
494
- if (!isset($has_host[$post->handle])) {
495
- $this->override_sidebar_styles($id);
496
- continue;
497
- }
498
-
499
- $hosts = $metadata->get('host')->get_data($post->ID, false, false);
500
-
501
- foreach ($hosts as $host) {
502
- // Check if host exist
503
- if (!isset($sidebars_widgets[$host])) {
504
- continue;
505
- }
506
-
507
- $this->override_sidebar_styles($host);
508
- }
509
- }
510
- }
511
- return $sidebars_widgets;
512
- }
513
-
514
- /**
515
- * Show manually handled content aware sidebars
516
- * @global array $_wp_sidebars_widgets
517
- * @param string|array $args
518
- * @return void
519
- */
520
- public function manual_sidebar($args)
521
- {
522
- global $_wp_sidebars_widgets;
523
-
524
- // Grab args or defaults
525
- $args = wp_parse_args($args, [
526
- 'include' => '',
527
- 'before' => '',
528
- 'after' => ''
529
- ]);
530
- extract($args, EXTR_SKIP);
531
-
532
- // Get sidebars
533
- $posts = WPCACore::get_posts(CAS_App::TYPE_SIDEBAR);
534
- if (!$posts) {
535
- return;
536
- }
537
-
538
- // Handle include argument
539
- if (!empty($include)) {
540
- if (!is_array($include)) {
541
- $include = explode(',', $include);
542
- }
543
- // Fast lookup
544
- $include = array_flip($include);
545
- }
546
-
547
- $i = $host = 0;
548
- foreach ($posts as $post) {
549
- $id = CAS_App::SIDEBAR_PREFIX . $post->ID;
550
-
551
- // Check for manual handling, if sidebar exists and if id should be included
552
- if ($post->handle != CAS_App::ACTION_SHORTCODE || !isset($_wp_sidebars_widgets[$id]) || (!empty($include) && !isset($include[$post->ID]))) {
553
- continue;
554
- }
555
-
556
- // Merge if more than one. First one is host.
557
- if ($i > 0) {
558
- if ($this->metadata()->get('merge_pos')->get_data($post->ID)) {
559
- $_wp_sidebars_widgets[$host] = array_merge($_wp_sidebars_widgets[$host], $_wp_sidebars_widgets[$id]);
560
- } else {
561
- $_wp_sidebars_widgets[$host] = array_merge($_wp_sidebars_widgets[$id], $_wp_sidebars_widgets[$host]);
562
- }
563
- } else {
564
- $host = $id;
565
- }
566
- $i++;
567
- }
568
-
569
- if ($host) {
570
- echo $before;
571
- dynamic_sidebar($host);
572
- echo $after;
573
- }
574
- }
575
-
576
- /**
577
- * Display sidebar with shortcode
578
- * @version 2.5
579
- * @param array $atts
580
- * @param string $content
581
- * @return string
582
- */
583
- public function sidebar_shortcode($atts, $content = '')
584
- {
585
- $a = shortcode_atts([
586
- 'id' => 0,
587
- ], $atts);
588
-
589
- $id = CAS_App::SIDEBAR_PREFIX.esc_attr($a['id']);
590
-
591
- if (!isset($this->sidebars[$id]) || $this->sidebars[$id]->post_status != CAS_App::STATUS_ACTIVE) {
592
- return $content;
593
- }
594
-
595
- if ($this->metadata()->get('handle')->get_data($a['id']) != CAS_App::ACTION_SHORTCODE) {
596
- return $content;
597
- }
598
-
599
- if (is_active_sidebar($id) && apply_filters('cas/shortcode/display', true, $a['id'])) {
600
- ob_start();
601
- do_action('cas/shortcode/before', $a['id']);
602
- $this->override_sidebar_styles($id);
603
- dynamic_sidebar($id);
604
- $content = ob_get_clean();
605
- }
606
- return $content;
607
- }
608
-
609
- /**
610
- * Get styles from nested sidebars
611
- *
612
- * @since 3.7
613
- * @param string $i
614
- * @param array $styles
615
- * @return array
616
- */
617
- public function get_sidebar_styles($i)
618
- {
619
- $styles = [];
620
-
621
- $metadata = $this->metadata()->get('html');
622
- while ($i) {
623
- if (isset($this->sidebars[$i])) {
624
- $style = apply_filters('cas/sidebar/html', $metadata->get_data($this->sidebars[$i]->ID, true), $this->sidebars[$i]->ID);
625
- if ($style) {
626
- $styles = array_merge($styles, $style);
627
- $styles['widget_id'] = '%1$s';
628
- $styles['sidebar_id'] = CAS_App::SIDEBAR_PREFIX.$this->sidebars[$i]->ID;
629
- }
630
- }
631
- $i = isset($this->replace_map[$i]) ? $this->replace_map[$i] : false;
632
- }
633
-
634
- return $styles;
635
- }
636
-
637
- /**
638
- * Render html if present before sidebar
639
- *
640
- * @deprecated since WP5.6
641
- * @since 3.6
642
- * @param string $i
643
- * @param boolean $has_widgets
644
- * @return void
645
- */
646
- public function render_sidebar_before($i, $has_widgets)
647
- {
648
- //wp5.6 introduced before_sidebar without notice
649
- if (version_compare(get_bloginfo('version'), '5.6', '>=')) {
650
- return;
651
- }
652
-
653
- global $wp_registered_sidebars;
654
- if ($has_widgets && isset($wp_registered_sidebars[$i]['before_sidebar'])) {
655
- echo $wp_registered_sidebars[$i]['before_sidebar'];
656
- }
657
- }
658
-
659
- /**
660
- * Render html if present after sidebar
661
- *
662
- * @deprecated since WP5.6
663
- * @since 3.6
664
- * @param string $i
665
- * @param boolean $has_widgets
666
- * @return void
667
- */
668
- public function render_sidebar_after($i, $has_widgets)
669
- {
670
- //wp5.6 introduced after_sidebar without notice
671
- if (version_compare(get_bloginfo('version'), '5.6', '>=')) {
672
- return;
673
- }
674
- global $wp_registered_sidebars;
675
- if ($has_widgets && isset($wp_registered_sidebars[$i]['after_sidebar'])) {
676
- echo $wp_registered_sidebars[$i]['after_sidebar'];
677
- }
678
- }
679
-
680
- /**
681
- * Filter out all sidebars if post is password protected
682
- *
683
- * @since 3.7
684
- * @param array $sidebars
685
- * @return array
686
- */
687
- public static function filter_password_protection($sidebars)
688
- {
689
- if (is_singular() && post_password_required()) {
690
- return [];
691
- }
692
- return $sidebars;
693
- }
694
-
695
- /**
696
- * Filter out sidebars based on current user
697
- *
698
- * @since 3.7
699
- * @param array $sidebars
700
- * @return array
701
- */
702
- public function filter_visibility($sidebars)
703
- {
704
- if ($sidebars) {
705
- $metadata = $this->metadata()->get('visibility');
706
-
707
- //temporary filter until WPCACore allows filtering
708
- $user_visibility = is_user_logged_in() ? [-1] : [];
709
- $user_visibility = apply_filters('cas/user_visibility', $user_visibility);
710
- foreach ($sidebars as $id => $sidebar) {
711
- $visibility = $metadata->get_data($id, true, false);
712
-
713
- // Check visibility
714
- if ($visibility && !array_intersect($visibility, $user_visibility)) {
715
- unset($sidebars[$id]);
716
- }
717
- }
718
- }
719
- return $sidebars;
720
- }
721
-
722
- /**
723
- * Filter shortcode sidebar based on current user
724
- *
725
- * @since 3.7.1
726
- * @param boolean $retval
727
- * @param int $id
728
- * @return boolean
729
- */
730
- public function filter_shortcode_visibility($retval, $id)
731
- {
732
- if ($retval) {
733
- $metadata = $this->metadata()->get('visibility');
734
-
735
- //temporary filter until WPCACore allows filtering
736
- $user_visibility = is_user_logged_in() ? [-1] : [];
737
- $user_visibility = apply_filters('cas/user_visibility', $user_visibility);
738
-
739
- $visibility = $metadata->get_data($id, true, false);
740
-
741
- // Check visibility
742
- if ($visibility && !array_intersect($visibility, $user_visibility)) {
743
- $retval = false;
744
- }
745
- }
746
- return $retval;
747
- }
748
-
749
-
750
- /**
751
- * Runs is_active_sidebar for sidebars
752
- * Widget management in Theme Customizer
753
- * expects this
754
- *
755
- * @global type $wp_customize
756
- * @since 2.2
757
- * @return void
758
- */
759
- public function sidebar_notify_theme_customizer()
760
- {
761
- global $wp_customize;
762
- if (!empty($wp_customize)) {
763
- $sidebars = WPCACore::get_posts(CAS_App::TYPE_SIDEBAR);
764
- if ($sidebars) {
765
- foreach ($sidebars as $sidebar) {
766
- is_active_sidebar(CAS_App::SIDEBAR_PREFIX . $sidebar->ID);
767
- }
768
- }
769
- }
770
- }
771
-
772
- /**
773
- * Get sidebar edit link
774
- * TODO: Consider changing post type _edit_link instead
775
- *
776
- * @since 3.4
777
- * @param string $link
778
- * @param int $post_id
779
- * @param string $context
780
- * @return string
781
- */
782
- public function get_edit_post_link($link, $post_id, $context)
783
- {
784
- $post = get_post($post_id);
785
- if ($post->post_type == CAS_App::TYPE_SIDEBAR) {
786
- $sep = '&';
787
- if ($context == 'display') {
788
- $sep = '&amp;';
789
- }
790
- $link = admin_url('admin.php?page=wpcas-edit'.$sep.'sidebar_id='.$post_id);
791
-
792
- //load page in all languages for wpml, polylang,
793
- //ensures post type conditions are not filtered
794
- if (defined('ICL_SITEPRESS_VERSION') || defined('POLYLANG_VERSION')) {
795
- $link .= $sep.'lang=all';
796
- }
797
- }
798
- return $link;
799
- }
800
-
801
- /**
802
- * Get sidebar delete link
803
- * TODO: Consider changing post type _edit_link instead
804
- *
805
- * @since 3.4
806
- * @param string $link
807
- * @param int $post_id
808
- * @param boolean $force_delete
809
- * @return string
810
- */
811
- public function get_delete_post_link($link, $post_id, $force_delete)
812
- {
813
- $post = get_post($post_id);
814
- if ($post->post_type == CAS_App::TYPE_SIDEBAR) {
815
- $action = ($force_delete || !EMPTY_TRASH_DAYS) ? 'delete' : 'trash';
816
-
817
- $link = add_query_arg(
818
- 'action',
819
- $action,
820
- admin_url('admin.php?page=wpcas-edit&sidebar_id='.$post_id)
821
- );
822
- $link = wp_nonce_url($link, "$action-post_{$post_id}");
823
- }
824
- return $link;
825
- }
826
- }
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ defined('ABSPATH') || exit;
10
+
11
+ final class CAS_Sidebar_Manager
12
+ {
13
+ /**
14
+ * Sidebar metadata
15
+ * @var WPCACollection
16
+ */
17
+ protected $metadata;
18
+
19
+ /**
20
+ * Custom sidebars
21
+ * @var array
22
+ */
23
+ public $sidebars = [];
24
+
25
+ /**
26
+ * Cache replaced sidebars
27
+ * @var array
28
+ */
29
+ protected $replaced_sidebars = [];
30
+
31
+ /**
32
+ * Sidebar replacement map
33
+ * @var array
34
+ */
35
+ protected $replace_map = [];
36
+
37
+ /**
38
+ * @var array
39
+ * Constructor
40
+ *
41
+ * @since 3.1
42
+ */
43
+ public function __construct()
44
+ {
45
+ add_action(
46
+ 'wpca/loaded',
47
+ [$this,'late_init']
48
+ );
49
+ add_action(
50
+ 'wp_head',
51
+ [$this,'sidebar_notify_theme_customizer']
52
+ );
53
+ add_action(
54
+ 'init',
55
+ [$this,'init_sidebar_type'],
56
+ 99
57
+ );
58
+ add_action(
59
+ 'widgets_init',
60
+ [$this,'create_sidebars'],
61
+ 99
62
+ );
63
+ add_action(
64
+ 'wp_loaded',
65
+ [$this,'set_sidebar_styles'],
66
+ 99
67
+ );
68
+
69
+ add_filter('get_edit_post_link', [$this,'get_edit_post_link'], 10, 3);
70
+ add_filter('get_delete_post_link', [$this,'get_delete_post_link'], 10, 3);
71
+
72
+ add_shortcode(
73
+ 'ca-sidebar',
74
+ [$this,'sidebar_shortcode']
75
+ );
76
+ }
77
+
78
+ /**
79
+ * Initialize after WPCA has been loaded
80
+ * Makes sure the SDK can be used in actions/filters
81
+ * forcefully called earlier
82
+ *
83
+ * @since 3.4
84
+ * @return void
85
+ */
86
+ public function late_init()
87
+ {
88
+ if (!is_admin()) {
89
+ add_filter(
90
+ 'sidebars_widgets',
91
+ [$this,'replace_sidebar']
92
+ );
93
+ add_filter(
94
+ 'wpca/posts/sidebar',
95
+ [__CLASS__,'filter_password_protection']
96
+ );
97
+ add_filter(
98
+ 'wpca/posts/sidebar',
99
+ [$this,'filter_visibility']
100
+ );
101
+ add_filter(
102
+ 'cas/shortcode/display',
103
+ [$this,'filter_shortcode_visibility'],
104
+ 10,
105
+ 2
106
+ );
107
+ add_action(
108
+ 'dynamic_sidebar_before',
109
+ [$this,'render_sidebar_before'],
110
+ 9,
111
+ 2
112
+ );
113
+ add_action(
114
+ 'dynamic_sidebar_after',
115
+ [$this,'render_sidebar_after'],
116
+ 99,
117
+ 2
118
+ );
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Get instance of metadata manager
124
+ *
125
+ * @since 3.0
126
+ * @return WPCACollection
127
+ */
128
+ public function metadata()
129
+ {
130
+ if (!$this->metadata) {
131
+ $this->init_metadata();
132
+ }
133
+ return $this->metadata;
134
+ }
135
+
136
+ /**
137
+ * Create post meta fields
138
+ * @global array $wp_registered_sidebars
139
+ * @return void
140
+ */
141
+ private function init_metadata()
142
+ {
143
+ $this->metadata = new WPCACollection();
144
+ $this->metadata
145
+ ->add(new WPCAMeta(
146
+ 'visibility',
147
+ __('User Visibility', 'content-aware-sidebars'),
148
+ [],
149
+ 'multi',
150
+ [
151
+ 'general' => [
152
+ 'label' => 'General',
153
+ 'options' => [
154
+ -1 => __('Logged-in', 'content-aware-sidebars')
155
+ ]
156
+ ]
157
+ ]
158
+ ), 'visibility')
159
+ ->add(new WPCAMeta(
160
+ 'handle',
161
+ _x('Action', 'option', 'content-aware-sidebars'),
162
+ CAS_App::ACTION_REPLACE,
163
+ 'select',
164
+ [
165
+ CAS_App::ACTION_REPLACE => __('Replace', 'content-aware-sidebars'),
166
+ CAS_App::ACTION_MERGE => __('Merge', 'content-aware-sidebars'),
167
+ CAS_App::ACTION_REPLACE_FORCED => __('Forced replace', 'content-aware-sidebars'),
168
+ CAS_App::ACTION_SHORTCODE => __('Shortcode')
169
+ ],
170
+ __('Replace host sidebar, merge with it or add sidebar manually.', 'content-aware-sidebars')
171
+ ), 'handle')
172
+ ->add(new WPCAMeta(
173
+ 'host',
174
+ __('Target Sidebar', 'content-aware-sidebars'),
175
+ [],
176
+ 'multi',
177
+ []
178
+ ), 'host')
179
+ ->add(new WPCAMeta(
180
+ 'merge_pos',
181
+ __('Merge Position', 'content-aware-sidebars'),
182
+ 1,
183
+ 'select',
184
+ [
185
+ __('Top', 'content-aware-sidebars'),
186
+ __('Bottom', 'content-aware-sidebars')
187
+ ],
188
+ __('Place sidebar on top or bottom of host when merging.', 'content-aware-sidebars')
189
+ ), 'merge_pos')
190
+ ->add(new WPCAMeta(
191
+ 'html',
192
+ __('HTML', 'content-aware-sidebars'),
193
+ [],
194
+ 'select',
195
+ ['']
196
+ ), 'html');
197
+ apply_filters('cas/metadata/init', $this->metadata);
198
+ }
199
+
200
+ /**
201
+ * Populate metadata with dynamic content
202
+ * for use in admin
203
+ *
204
+ * @since 3.2
205
+ * @return void
206
+ */
207
+ public function populate_metadata()
208
+ {
209
+ if ($this->metadata) {
210
+ global $wp_registered_sidebars;
211
+
212
+ // List of sidebars
213
+ $sidebar_list = [];
214
+ foreach ($wp_registered_sidebars as $sidebar) {
215
+ $sidebar_list[$sidebar['id']] = $sidebar['name'];
216
+ }
217
+
218
+ // Remove ability to set self to host
219
+ if (get_the_ID()) {
220
+ unset($sidebar_list[CAS_App::SIDEBAR_PREFIX . get_the_ID()]);
221
+ }
222
+ asort($sidebar_list);
223
+ $this->metadata->get('host')->set_input_list($sidebar_list);
224
+
225
+ if (!cas_fs()->can_use_premium_code()) {
226
+ $pro_label = ' (Pro)';
227
+ $actions = $this->metadata->get('handle');
228
+ $action_list = $actions->get_input_list();
229
+ $action_list['__infuse'] = __('Infuse', 'content-aware-sidebars') . $pro_label;
230
+ $action_list['__after_paragraph'] = __('After Paragraph', 'content-aware-sidebars') . $pro_label;
231
+ $action_list['__totem'] = __('Totem - Floating Button', 'content-aware-sidebars') . $pro_label;
232
+ $actions->set_input_list($action_list);
233
+ }
234
+
235
+ apply_filters('cas/metadata/populate', $this->metadata);
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Create sidebar post type
241
+ * Add it to content aware engine
242
+ *
243
+ * @return void
244
+ */
245
+ public function init_sidebar_type()
246
+ {
247
+ register_post_type(CAS_App::TYPE_SIDEBAR, [
248
+ 'labels' => [
249
+ 'name' => __('Sidebars', 'content-aware-sidebars'),
250
+ 'singular_name' => __('Sidebar', 'content-aware-sidebars'),
251
+ 'add_new' => _x('Add New', 'sidebar', 'content-aware-sidebars'),
252
+ 'add_new_item' => __('Add New Sidebar', 'content-aware-sidebars'),
253
+ 'edit_item' => __('Edit Sidebar', 'content-aware-sidebars'),
254
+ 'new_item' => __('New Sidebar', 'content-aware-sidebars'),
255
+ 'all_items' => __('All Sidebars', 'content-aware-sidebars'),
256
+ 'view_item' => __('View Sidebar', 'content-aware-sidebars'),
257
+ 'search_items' => __('Search Sidebars', 'content-aware-sidebars'),
258
+ 'not_found' => __('No sidebars found', 'content-aware-sidebars'),
259
+ 'not_found_in_trash' => __('No sidebars found in Trash', 'content-aware-sidebars'),
260
+ //wp-content-aware-engine specific
261
+ 'ca_title' => __('Where to display', 'content-aware-sidebars')
262
+ ],
263
+ 'capabilities' => [
264
+ 'edit_post' => CAS_App::CAPABILITY,
265
+ 'read_post' => CAS_App::CAPABILITY,
266
+ 'delete_post' => CAS_App::CAPABILITY,
267
+ 'edit_posts' => CAS_App::CAPABILITY,
268
+ 'delete_posts' => CAS_App::CAPABILITY,
269
+ 'edit_others_posts' => CAS_App::CAPABILITY,
270
+ 'publish_posts' => CAS_App::CAPABILITY,
271
+ 'read_private_posts' => CAS_App::CAPABILITY
272
+ ],
273
+ 'public' => false,
274
+ 'hierarchical' => false,
275
+ 'exclude_from_search' => true,
276
+ 'publicly_queryable' => false,
277
+ 'show_ui' => false,
278
+ 'show_in_menu' => false,
279
+ 'show_in_nav_menus' => false,
280
+ 'show_in_admin_bar' => false,
281
+ 'has_archive' => false,
282
+ 'rewrite' => false,
283
+ 'query_var' => false,
284
+ 'supports' => ['title','page-attributes'],
285
+ 'menu_icon' => 'dashicons-welcome-widgets-menus',
286
+ 'can_export' => false,
287
+ 'delete_with_user' => false
288
+ ]);
289
+
290
+ WPCACore::types()->add(CAS_App::TYPE_SIDEBAR);
291
+ }
292
+
293
+ /**
294
+ * Add sidebars to widgets area
295
+ * Triggered in widgets_init to save location for each theme
296
+ * @return void
297
+ */
298
+ public function create_sidebars()
299
+ {
300
+ $sidebars = get_posts([
301
+ 'numberposts' => -1,
302
+ 'post_type' => CAS_App::TYPE_SIDEBAR,
303
+ 'post_status' => [
304
+ CAS_App::STATUS_ACTIVE,
305
+ CAS_App::STATUS_INACTIVE,
306
+ CAS_App::STATUS_SCHEDULED
307
+ ],
308
+ 'orderby' => 'title',
309
+ 'order' => 'ASC'
310
+ ]);
311
+
312
+ //Register sidebars to add them to the list
313
+ foreach ($sidebars as $post) {
314
+ $this->sidebars[CAS_App::SIDEBAR_PREFIX . $post->ID] = $post;
315
+ register_sidebar([
316
+ 'name' => $post->post_title ? $post->post_title : __('(no title)'),
317
+ 'id' => CAS_App::SIDEBAR_PREFIX . $post->ID,
318
+ 'before_sidebar' => '',
319
+ 'after_sidebar' => ''
320
+ ]);
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Set styles of created sidebars
326
+ *
327
+ * @since 3.6
328
+ */
329
+ public function set_sidebar_styles()
330
+ {
331
+ global $wp_registered_sidebars;
332
+
333
+ //todo: only for manual
334
+ $default_styles = [
335
+ 'before_widget' => '<div id="%1$s" class="widget-container %2$s">',
336
+ 'after_widget' => '</div>',
337
+ 'before_title' => '<h4 class="widget-title">',
338
+ 'after_title' => '</h4>'
339
+ ];
340
+ $has_host = [
341
+ CAS_App::ACTION_REPLACE => 1,
342
+ CAS_App::ACTION_MERGE => 1,
343
+ CAS_App::ACTION_REPLACE_FORCED => 1
344
+ ];
345
+ $metadata = $this->metadata();
346
+
347
+ foreach ($this->sidebars as $id => $post) {
348
+ $args = $default_styles;
349
+
350
+ if (isset($has_host[$metadata->get('handle')->get_data($post->ID)])) {
351
+ //Set style from host to fix when content aware sidebar
352
+ //is called directly by other sidebar managers
353
+ $host_id = $metadata->get('host')->get_data($post->ID);
354
+ if (isset($wp_registered_sidebars[$host_id])) {
355
+ foreach ([
356
+ 'before_widget',
357
+ 'after_widget',
358
+ 'before_title',
359
+ 'after_title',
360
+ 'before_sidebar',
361
+ 'after_sidebar'
362
+ ] as $pos) {
363
+ if (isset($wp_registered_sidebars[$host_id][$pos])) {
364
+ $args[$pos] = $wp_registered_sidebars[$host_id][$pos];
365
+ }
366
+ }
367
+ }
368
+ }
369
+
370
+ $wp_registered_sidebars[$id] = array_merge($wp_registered_sidebars[$id], $args);
371
+ }
372
+ }
373
+
374
+ /**
375
+ * @return array
376
+ */
377
+ public function get_replacement_map()
378
+ {
379
+ return $this->replace_map;
380
+ }
381
+
382
+ /**
383
+ * @param string $i
384
+ * @return void
385
+ */
386
+ public function override_sidebar_styles($i)
387
+ {
388
+ //Get nested styles
389
+ $html = $this->get_sidebar_styles($i);
390
+ if ($html) {
391
+ global $wp_registered_sidebars;
392
+ $styles = $wp_registered_sidebars[$i];
393
+ //Set user styles
394
+ foreach ([
395
+ 'widget',
396
+ 'title',
397
+ 'sidebar'
398
+ ] as $pos) {
399
+ if (isset($html[$pos],$html[$pos . '_class'])) {
400
+ $e = esc_html($html[$pos]);
401
+ $class = esc_html($html[$pos . '_class']);
402
+ $id = '';
403
+ if (isset($html[$pos . '_id'])) {
404
+ $id = ' id="' . $html[$pos . '_id'] . '"';
405
+ }
406
+ $styles['before_' . $pos] = '<' . $e . $id . ' class="' . $class . '">';
407
+ $styles['after_' . $pos] = "</$e>";
408
+ }
409
+ }
410
+ $wp_registered_sidebars[$i] = $styles;
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Replace or merge a sidebar with content aware sidebars.
416
+ * @since .
417
+ * @param array $sidebars_widgets
418
+ * @return array
419
+ */
420
+ public function replace_sidebar($sidebars_widgets)
421
+ {
422
+ //customizer requires sidebars_widgets filter. cache for repeat calls
423
+ if ($this->replaced_sidebars) {
424
+ return $this->replaced_sidebars;
425
+ }
426
+
427
+ $posts = WPCACore::get_posts(CAS_App::TYPE_SIDEBAR);
428
+
429
+ if ($posts) {
430
+ $metadata = $this->metadata();
431
+ $has_host = [
432
+ CAS_App::ACTION_REPLACE => 1,
433
+ CAS_App::ACTION_MERGE => 1,
434
+ CAS_App::ACTION_REPLACE_FORCED => 1
435
+ ];
436
+
437
+ //replace and merge widgets, build replacement map
438
+ foreach ($posts as $post) {
439
+ $id = CAS_App::SIDEBAR_PREFIX . $post->ID;
440
+
441
+ // Check for correct handling
442
+ if (!isset($has_host[$post->handle])) {
443
+ continue;
444
+ }
445
+
446
+ $hosts = $metadata->get('host')->get_data($post->ID, false, false);
447
+
448
+ foreach ($hosts as $host) {
449
+ // Check if host exist
450
+ if (!isset($sidebars_widgets[$host])) {
451
+ continue;
452
+ }
453
+
454
+ $this->override_sidebar_styles($host);
455
+
456
+ // Sidebar might not have any widgets. Get it anyway!
457
+ if (!isset($sidebars_widgets[$id])) {
458
+ $sidebars_widgets[$id] = [];
459
+ }
460
+
461
+ // If handle is merge or if handle is replace and host has already been replaced
462
+ if ($post->handle == CAS_App::ACTION_MERGE || ($post->handle == CAS_App::ACTION_REPLACE && isset($handled_already[$host]))) {
463
+ //do not merge forced replace
464
+ //todo: maybe reverse order of fetched sidebars instead?
465
+ if (isset($handled_already[$host]) && $handled_already[$host] == CAS_App::ACTION_REPLACE_FORCED) {
466
+ continue;
467
+ }
468
+ if ($metadata->get('merge_pos')->get_data($post->ID)) {
469
+ $sidebars_widgets[$host] = array_merge($sidebars_widgets[$host], $sidebars_widgets[$id]);
470
+ } else {
471
+ $sidebars_widgets[$host] = array_merge($sidebars_widgets[$id], $sidebars_widgets[$host]);
472
+ }
473
+ } else {
474
+ $sidebars_widgets[$host] = $sidebars_widgets[$id];
475
+ $handled_already[$host] = $post->handle;
476
+ }
477
+
478
+ //last replacement will take priority
479
+ //todo: extend to work for widgets too
480
+ $this->replace_map[$host] = $id;
481
+ }
482
+ }
483
+ $this->replaced_sidebars = $sidebars_widgets;
484
+
485
+ //override styles
486
+ foreach ($posts as $post) {
487
+ $id = CAS_App::SIDEBAR_PREFIX . $post->ID;
488
+
489
+ // Check for correct handling
490
+ if (!isset($has_host[$post->handle])) {
491
+ $this->override_sidebar_styles($id);
492
+ continue;
493
+ }
494
+
495
+ $hosts = $metadata->get('host')->get_data($post->ID, false, false);
496
+
497
+ foreach ($hosts as $host) {
498
+ // Check if host exist
499
+ if (!isset($sidebars_widgets[$host])) {
500
+ continue;
501
+ }
502
+
503
+ $this->override_sidebar_styles($host);
504
+ }
505
+ }
506
+ }
507
+ return $sidebars_widgets;
508
+ }
509
+
510
+ /**
511
+ * Show manually handled content aware sidebars
512
+ * @global array $_wp_sidebars_widgets
513
+ * @param string|array $args
514
+ * @return void
515
+ */
516
+ public function manual_sidebar($args)
517
+ {
518
+ global $_wp_sidebars_widgets;
519
+
520
+ // Grab args or defaults
521
+ $args = wp_parse_args($args, [
522
+ 'include' => '',
523
+ 'before' => '',
524
+ 'after' => ''
525
+ ]);
526
+ extract($args, EXTR_SKIP);
527
+
528
+ // Get sidebars
529
+ $posts = WPCACore::get_posts(CAS_App::TYPE_SIDEBAR);
530
+ if (!$posts) {
531
+ return;
532
+ }
533
+
534
+ // Handle include argument
535
+ if (!empty($include)) {
536
+ if (!is_array($include)) {
537
+ $include = explode(',', $include);
538
+ }
539
+ // Fast lookup
540
+ $include = array_flip($include);
541
+ }
542
+
543
+ $i = $host = 0;
544
+ foreach ($posts as $post) {
545
+ $id = CAS_App::SIDEBAR_PREFIX . $post->ID;
546
+
547
+ // Check for manual handling, if sidebar exists and if id should be included
548
+ if ($post->handle != CAS_App::ACTION_SHORTCODE || !isset($_wp_sidebars_widgets[$id]) || (!empty($include) && !isset($include[$post->ID]))) {
549
+ continue;
550
+ }
551
+
552
+ // Merge if more than one. First one is host.
553
+ if ($i > 0) {
554
+ if ($this->metadata()->get('merge_pos')->get_data($post->ID)) {
555
+ $_wp_sidebars_widgets[$host] = array_merge($_wp_sidebars_widgets[$host], $_wp_sidebars_widgets[$id]);
556
+ } else {
557
+ $_wp_sidebars_widgets[$host] = array_merge($_wp_sidebars_widgets[$id], $_wp_sidebars_widgets[$host]);
558
+ }
559
+ } else {
560
+ $host = $id;
561
+ }
562
+ $i++;
563
+ }
564
+
565
+ if ($host) {
566
+ echo $before;
567
+ dynamic_sidebar($host);
568
+ echo $after;
569
+ }
570
+ }
571
+
572
+ /**
573
+ * Display sidebar with shortcode
574
+ * @version 2.5
575
+ * @param array $atts
576
+ * @param string $content
577
+ * @return string
578
+ */
579
+ public function sidebar_shortcode($atts, $content = '')
580
+ {
581
+ $a = shortcode_atts([
582
+ 'id' => 0,
583
+ ], $atts);
584
+
585
+ $id = CAS_App::SIDEBAR_PREFIX . esc_attr($a['id']);
586
+
587
+ if (!isset($this->sidebars[$id]) || $this->sidebars[$id]->post_status != CAS_App::STATUS_ACTIVE) {
588
+ return $content;
589
+ }
590
+
591
+ if ($this->metadata()->get('handle')->get_data($a['id']) != CAS_App::ACTION_SHORTCODE) {
592
+ return $content;
593
+ }
594
+
595
+ if (is_active_sidebar($id) && apply_filters('cas/shortcode/display', true, $a['id'])) {
596
+ ob_start();
597
+ do_action('cas/shortcode/before', $a['id']);
598
+ $this->override_sidebar_styles($id);
599
+ dynamic_sidebar($id);
600
+ $content = ob_get_clean();
601
+ }
602
+ return $content;
603
+ }
604
+
605
+ /**
606
+ * Get styles from nested sidebars
607
+ *
608
+ * @since 3.7
609
+ * @param string $i
610
+ * @return array
611
+ */
612
+ public function get_sidebar_styles($i)
613
+ {
614
+ $styles = [];
615
+
616
+ $metadata = $this->metadata()->get('html');
617
+ while ($i) {
618
+ if (isset($this->sidebars[$i])) {
619
+ $style = apply_filters('cas/sidebar/html', $metadata->get_data($this->sidebars[$i]->ID, true), $this->sidebars[$i]->ID);
620
+ if ($style) {
621
+ $styles = array_merge($styles, $style);
622
+ $styles['widget_id'] = '%1$s';
623
+ $styles['sidebar_id'] = CAS_App::SIDEBAR_PREFIX . $this->sidebars[$i]->ID;
624
+ }
625
+ }
626
+ $i = isset($this->replace_map[$i]) ? $this->replace_map[$i] : false;
627
+ }
628
+
629
+ return $styles;
630
+ }
631
+
632
+ /**
633
+ * Render html if present before sidebar
634
+ *
635
+ * @deprecated since WP5.6
636
+ * @since 3.6
637
+ * @param string $i
638
+ * @param boolean $has_widgets
639
+ * @return void
640
+ */
641
+ public function render_sidebar_before($i, $has_widgets)
642
+ {
643
+ //wp5.6 introduced before_sidebar without notice
644
+ if (version_compare(get_bloginfo('version'), '5.6', '>=')) {
645
+ return;
646
+ }
647
+
648
+ global $wp_registered_sidebars;
649
+ if ($has_widgets && isset($wp_registered_sidebars[$i]['before_sidebar'])) {
650
+ echo $wp_registered_sidebars[$i]['before_sidebar'];
651
+ }
652
+ }
653
+
654
+ /**
655
+ * Render html if present after sidebar
656
+ *
657
+ * @deprecated since WP5.6
658
+ * @since 3.6
659
+ * @param string $i
660
+ * @param boolean $has_widgets
661
+ * @return void
662
+ */
663
+ public function render_sidebar_after($i, $has_widgets)
664
+ {
665
+ //wp5.6 introduced after_sidebar without notice
666
+ if (version_compare(get_bloginfo('version'), '5.6', '>=')) {
667
+ return;
668
+ }
669
+ global $wp_registered_sidebars;
670
+ if ($has_widgets && isset($wp_registered_sidebars[$i]['after_sidebar'])) {
671
+ echo $wp_registered_sidebars[$i]['after_sidebar'];
672
+ }
673
+ }
674
+
675
+ /**
676
+ * Filter out all sidebars if post is password protected
677
+ *
678
+ * @since 3.7
679
+ * @param array $sidebars
680
+ * @return array
681
+ */
682
+ public static function filter_password_protection($sidebars)
683
+ {
684
+ if (is_singular() && post_password_required()) {
685
+ return [];
686
+ }
687
+ return $sidebars;
688
+ }
689
+
690
+ /**
691
+ * Filter out sidebars based on current user
692
+ *
693
+ * @since 3.7
694
+ * @param array $sidebars
695
+ * @return array
696
+ */
697
+ public function filter_visibility($sidebars)
698
+ {
699
+ if ($sidebars) {
700
+ $metadata = $this->metadata()->get('visibility');
701
+
702
+ //temporary filter until WPCACore allows filtering
703
+ $user_visibility = is_user_logged_in() ? [-1] : [];
704
+ $user_visibility = apply_filters('cas/user_visibility', $user_visibility);
705
+ foreach ($sidebars as $id => $sidebar) {
706
+ $visibility = $metadata->get_data($id, true, false);
707
+
708
+ // Check visibility
709
+ if ($visibility && !array_intersect($visibility, $user_visibility)) {
710
+ unset($sidebars[$id]);
711
+ }
712
+ }
713
+ }
714
+ return $sidebars;
715
+ }
716
+
717
+ /**
718
+ * Filter shortcode sidebar based on current user
719
+ *
720
+ * @since 3.7.1
721
+ * @param boolean $retval
722
+ * @param int $id
723
+ * @return boolean
724
+ */
725
+ public function filter_shortcode_visibility($retval, $id)
726
+ {
727
+ if ($retval) {
728
+ $metadata = $this->metadata()->get('visibility');
729
+
730
+ //temporary filter until WPCACore allows filtering
731
+ $user_visibility = is_user_logged_in() ? [-1] : [];
732
+ $user_visibility = apply_filters('cas/user_visibility', $user_visibility);
733
+
734
+ $visibility = $metadata->get_data($id, true, false);
735
+
736
+ // Check visibility
737
+ if ($visibility && !array_intersect($visibility, $user_visibility)) {
738
+ $retval = false;
739
+ }
740
+ }
741
+ return $retval;
742
+ }
743
+
744
+ /**
745
+ * Runs is_active_sidebar for sidebars
746
+ * Widget management in Theme Customizer
747
+ * expects this
748
+ *
749
+ * @global WP_Customize_Manager $wp_customize
750
+ * @since 2.2
751
+ * @return void
752
+ */
753
+ public function sidebar_notify_theme_customizer()
754
+ {
755
+ global $wp_customize;
756
+ if (!empty($wp_customize)) {
757
+ $sidebars = WPCACore::get_posts(CAS_App::TYPE_SIDEBAR);
758
+ if ($sidebars) {
759
+ foreach ($sidebars as $sidebar) {
760
+ is_active_sidebar(CAS_App::SIDEBAR_PREFIX . $sidebar->ID);
761
+ }
762
+ }
763
+ }
764
+ }
765
+
766
+ /**
767
+ * Get sidebar edit link
768
+ * TODO: Consider changing post type _edit_link instead
769
+ *
770
+ * @since 3.4
771
+ * @param string $link
772
+ * @param int $post_id
773
+ * @param string $context
774
+ * @return string
775
+ */
776
+ public function get_edit_post_link($link, $post_id, $context)
777
+ {
778
+ $post = get_post($post_id);
779
+ if ($post->post_type == CAS_App::TYPE_SIDEBAR) {
780
+ $sep = '&';
781
+ if ($context == 'display') {
782
+ $sep = '&amp;';
783
+ }
784
+ $link = admin_url('admin.php?page=wpcas-edit' . $sep . 'sidebar_id=' . $post_id);
785
+
786
+ //load page in all languages for wpml, polylang,
787
+ //ensures post type conditions are not filtered
788
+ if (defined('ICL_SITEPRESS_VERSION') || defined('POLYLANG_VERSION')) {
789
+ $link .= $sep . 'lang=all';
790
+ }
791
+ }
792
+ return $link;
793
+ }
794
+
795
+ /**
796
+ * Get sidebar delete link
797
+ * TODO: Consider changing post type _edit_link instead
798
+ *
799
+ * @since 3.4
800
+ * @param string $link
801
+ * @param int $post_id
802
+ * @param boolean $force_delete
803
+ * @return string
804
+ */
805
+ public function get_delete_post_link($link, $post_id, $force_delete)
806
+ {
807
+ $post = get_post($post_id);
808
+ if ($post->post_type == CAS_App::TYPE_SIDEBAR) {
809
+ $action = ($force_delete || !EMPTY_TRASH_DAYS) ? 'delete' : 'trash';
810
+
811
+ $link = add_query_arg(
812
+ 'action',
813
+ $action,
814
+ admin_url('admin.php?page=wpcas-edit&sidebar_id=' . $post_id)
815
+ );
816
+ $link = wp_nonce_url($link, "$action-post_$post_id");
817
+ }
818
+ return $link;
819
+ }
820
+ }
 
 
 
 
 
 
view/meta_box_html.php CHANGED
@@ -1,41 +1,41 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- $data = CAS_App::instance()->manager()->metadata()->get('html')->get_data($post->ID, true);
10
-
11
- $data_default = array_merge([
12
- 'sidebar' => '',
13
- 'sidebar_class' => '',
14
- 'widget' => '',
15
- 'widget_class' => 'widget %2$s',
16
- 'title' => '',
17
- 'title_class' => 'widget-title'
18
- ], $data);
19
-
20
- $sidebar_opts = [
21
- 'div' => 'div',
22
- 'aside' => 'aside',
23
- 'ul' => 'ul'
24
- ];
25
- $widget_opts = [
26
- 'div' => 'div',
27
- 'li' => 'li',
28
- 'section' => 'section'
29
- ];
30
- $widget_title_opts = [
31
- 'h2' => 'h2',
32
- 'h3' => 'h3',
33
- 'h4' => 'h4',
34
- 'h5' => 'h5',
35
- 'h6' => 'h6'
36
- ];
37
-
38
- ?>
39
 
40
  <table class="form-table cas-form-table" role="presentation">
41
  <tbody>
@@ -43,15 +43,15 @@ $widget_title_opts = [
43
  <th scope="row"><?php _e('Sidebar'); ?></th>
44
  <td>
45
  <label class="cae-toggle">
46
- <input class="js-cas-html" type="checkbox" <?php checked(isset($data['sidebar'],$data['sidebar_class']), true); ?> data-target=".js-cas-html-sidebar" />
47
  <div class="cae-toggle-bar"></div>
48
  </label>
49
  <code style="display:inline-block;">
50
  <<select style="width:80px;" name="html[sidebar]" class="js-cas-html-sidebar">
51
- <?php foreach ($sidebar_opts as $k => $v) {
52
- echo '<option value="'.$k.'"'.selected($data_default['sidebar'], $k, false).'>'.$v.'</option>';
53
- }
54
- ?>
55
  </select>
56
  class="<input type="text" name="html[sidebar_class]" value="<?php echo esc_html($data_default['sidebar_class']); ?>" class="js-cas-html-sidebar" />">
57
  </code>
@@ -61,15 +61,15 @@ $widget_title_opts = [
61
  <th scope="row"><?php _e('Widget'); ?></th>
62
  <td>
63
  <label class="cae-toggle">
64
- <input class="js-cas-html" type="checkbox" <?php checked(isset($data['widget'],$data['widget_class']), true); ?> data-target=".js-cas-html-widget" />
65
  <div class="cae-toggle-bar"></div>
66
  </label>
67
  <code style="display:inline-block;">
68
  <<select style="width:80px;" name="html[widget]" class="js-cas-html-widget">
69
- <?php foreach ($widget_opts as $k => $v) {
70
- echo '<option value="'.$k.'"'.selected($data_default['widget'], $k, false).'>'.$v.'</option>';
71
- }
72
- ?>
73
  </select>
74
  class="<input type="text" name="html[widget_class]" value="<?php echo esc_html($data_default['widget_class']); ?>" class="js-cas-html-widget" />">
75
  </code>
@@ -79,15 +79,15 @@ $widget_title_opts = [
79
  <th scope="row"><?php _e('Widget Title'); ?></th>
80
  <td>
81
  <label class="cae-toggle">
82
- <input class="js-cas-html" type="checkbox" <?php checked(isset($data['title'],$data['title_class']), true); ?> data-target=".js-cas-html-widget-title" />
83
  <div class="cae-toggle-bar"></div>
84
  </label>
85
  <code style="display:inline-block;">
86
  <<select style="width:80px;" name="html[title]" class="js-cas-html-widget-title">
87
- <?php foreach ($widget_title_opts as $k => $v) {
88
- echo '<option value="'.$k.'"'.selected($data_default['title'], $k, false).'>'.$v.'</option>';
89
- }
90
- ?>
91
  </select>
92
  class="<input type="text" name="html[title_class]" value="<?php echo esc_html($data_default['title_class']); ?>" class="js-cas-html-widget-title" />">
93
  </code>
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ $data = CAS_App::instance()->manager()->metadata()->get('html')->get_data($post->ID, true);
10
+
11
+ $data_default = array_merge([
12
+ 'sidebar' => '',
13
+ 'sidebar_class' => '',
14
+ 'widget' => '',
15
+ 'widget_class' => 'widget %2$s',
16
+ 'title' => '',
17
+ 'title_class' => 'widget-title'
18
+ ], $data);
19
+
20
+ $sidebar_opts = [
21
+ 'div' => 'div',
22
+ 'aside' => 'aside',
23
+ 'ul' => 'ul'
24
+ ];
25
+ $widget_opts = [
26
+ 'div' => 'div',
27
+ 'li' => 'li',
28
+ 'section' => 'section'
29
+ ];
30
+ $widget_title_opts = [
31
+ 'h2' => 'h2',
32
+ 'h3' => 'h3',
33
+ 'h4' => 'h4',
34
+ 'h5' => 'h5',
35
+ 'h6' => 'h6'
36
+ ];
37
+
38
+ ?>
39
 
40
  <table class="form-table cas-form-table" role="presentation">
41
  <tbody>
43
  <th scope="row"><?php _e('Sidebar'); ?></th>
44
  <td>
45
  <label class="cae-toggle">
46
+ <input class="js-cas-html" type="checkbox" <?php checked(isset($data['sidebar'],$data['sidebar_class'])); ?>data-target=".js-cas-html-sidebar" />
47
  <div class="cae-toggle-bar"></div>
48
  </label>
49
  <code style="display:inline-block;">
50
  <<select style="width:80px;" name="html[sidebar]" class="js-cas-html-sidebar">
51
+ <?php foreach ($sidebar_opts as $k => $v) {
52
+ echo '<option value="' . $k . '"' . selected($data_default['sidebar'], $k, false) . '>' . $v . '</option>';
53
+ }
54
+ ?>
55
  </select>
56
  class="<input type="text" name="html[sidebar_class]" value="<?php echo esc_html($data_default['sidebar_class']); ?>" class="js-cas-html-sidebar" />">
57
  </code>
61
  <th scope="row"><?php _e('Widget'); ?></th>
62
  <td>
63
  <label class="cae-toggle">
64
+ <input class="js-cas-html" type="checkbox" <?php checked(isset($data['widget'],$data['widget_class'])); ?>data-target=".js-cas-html-widget" />
65
  <div class="cae-toggle-bar"></div>
66
  </label>
67
  <code style="display:inline-block;">
68
  <<select style="width:80px;" name="html[widget]" class="js-cas-html-widget">
69
+ <?php foreach ($widget_opts as $k => $v) {
70
+ echo '<option value="' . $k . '"' . selected($data_default['widget'], $k, false) . '>' . $v . '</option>';
71
+ }
72
+ ?>
73
  </select>
74
  class="<input type="text" name="html[widget_class]" value="<?php echo esc_html($data_default['widget_class']); ?>" class="js-cas-html-widget" />">
75
  </code>
79
  <th scope="row"><?php _e('Widget Title'); ?></th>
80
  <td>
81
  <label class="cae-toggle">
82
+ <input class="js-cas-html" type="checkbox" <?php checked(isset($data['title'],$data['title_class'])); ?>data-target=".js-cas-html-widget-title" />
83
  <div class="cae-toggle-bar"></div>
84
  </label>
85
  <code style="display:inline-block;">
86
  <<select style="width:80px;" name="html[title]" class="js-cas-html-widget-title">
87
+ <?php foreach ($widget_title_opts as $k => $v) {
88
+ echo '<option value="' . $k . '"' . selected($data_default['title'], $k, false) . '>' . $v . '</option>';
89
+ }
90
+ ?>
91
  </select>
92
  class="<input type="text" name="html[title_class]" value="<?php echo esc_html($data_default['title_class']); ?>" class="js-cas-html-widget-title" />">
93
  </code>
view/meta_box_submit.php CHANGED
@@ -39,7 +39,7 @@ if (post_type_supports($post->post_type, 'revisions')) {
39
  <div class="wpca-pull-right">
40
  <a class="js-nav-link" href="#top#section-schedule"><?php _ex('Schedule', 'verb', 'content-aware-sidebars'); ?></a>
41
  <label class="cae-toggle">
42
- <input class="js-cas-status" type="checkbox" name="post_status" value="<?php echo CAS_App::STATUS_ACTIVE; ?>" <?php checked(in_array($post->post_status, [CAS_App::STATUS_ACTIVE,'auto-draft']), true); ?> />
43
  <div class="cae-toggle-bar"></div>
44
  </label>
45
  </div>
39
  <div class="wpca-pull-right">
40
  <a class="js-nav-link" href="#top#section-schedule"><?php _ex('Schedule', 'verb', 'content-aware-sidebars'); ?></a>
41
  <label class="cae-toggle">
42
+ <input class="js-cas-status" type="checkbox" name="post_status" value="<?php echo CAS_App::STATUS_ACTIVE; ?>" <?php checked(in_array($post->post_status, [CAS_App::STATUS_ACTIVE,'auto-draft'])); ?> />
43
  <div class="cae-toggle-bar"></div>
44
  </label>
45
  </div>
view/sidebars_quick_select.php CHANGED
@@ -1,42 +1,42 @@
1
- <?php
2
- /**
3
- * @package Content Aware Sidebars
4
- * @author Joachim Jensen <joachim@dev.institute>
5
- * @license GPLv3
6
- * @copyright 2022 by Joachim Jensen
7
- */
8
-
9
- $i = 0;
10
- $user_can = current_user_can(CAS_App::CAPABILITY);
11
- echo $nonce;
12
- foreach ($sidebars as $id => $sidebar) :
13
- if ($i == $limit) : ?>
14
- <div class="cas-more" style="display:none;">
15
- <?php endif; ?>
16
-
17
- <div>
18
- <label style="display:block;padding:8px 0 4px;font-weight:bold;" for="ca_sidebars_'<?php echo $id; ?>"><?php echo $sidebar['label']; ?></label>
19
- <select style="width:100%;" id="ca_sidebars_<?php echo $id; ?>" class="js-cas-sidebars" name="cas_sidebars[<?php echo $id; ?>][]" multiple data-tags="<?php echo $user_can; ?>" data-placeholder="<?php esc_attr_e('Default'); ?>">
20
- <?php foreach ($sidebar['options'] as $sidebar) : ?>
21
- <option value="<?php echo $sidebar['id']; ?>"<?php selected(isset($sidebar['select']), true); ?>><?php echo $sidebar['text']; ?></option>
22
- <?php endforeach;
23
- if ($user_can) : ?>
24
- <option value="0" disabled="disabled"><?php _e('Type to Add New Sidebar', 'content-aware-sidebars'); ?></option>
25
- <?php endif; ?>
26
- </select>
27
- </div>
28
- <?php
29
- $i++;
30
- endforeach;
31
-
32
- if ($i > $limit) : ?>
33
- </div>
34
- <div style="text-align:center;"><button class="js-cas-more button button-small" data-toggle=".cas-more"><span class="dashicons dashicons-arrow-down-alt2"></span></button></div>
35
- <?php endif; ?>
36
-
37
- <p class="howto"><?php echo sprintf(__('Note: Selected Sidebars are displayed on this %s specifically.', 'content-aware-sidebars'), strtolower($singular)).sprintf(
38
- __('Display sidebars per %s etc. with the %s.', 'content-aware-sidebars'),
39
- strtolower(implode(', ', array_slice($content, 0, 3))),
40
- '<a href="'.admin_url('admin.php?page=wpcas').'">'.__('Sidebar Manager', 'content-aware-sidebars').'</a>'
41
- ); ?>
42
- </p>
1
+ <?php
2
+ /**
3
+ * @package Content Aware Sidebars
4
+ * @author Joachim Jensen <joachim@dev.institute>
5
+ * @license GPLv3
6
+ * @copyright 2022 by Joachim Jensen
7
+ */
8
+
9
+ $i = 0;
10
+ $user_can = current_user_can(CAS_App::CAPABILITY);
11
+ echo $nonce;
12
+ foreach ($sidebars as $id => $sidebar) :
13
+ if ($i == $limit) : ?>
14
+ <div class="cas-more" style="display:none;">
15
+ <?php endif; ?>
16
+
17
+ <div>
18
+ <label style="display:block;padding:8px 0 4px;font-weight:bold;" for="ca_sidebars_'<?php echo $id; ?>"><?php echo $sidebar['label']; ?></label>
19
+ <select style="width:100%;" id="ca_sidebars_<?php echo $id; ?>" class="js-cas-sidebars" name="cas_sidebars[<?php echo $id; ?>][]" multiple data-tags="<?php echo $user_can; ?>" data-placeholder="<?php esc_attr_e('Default'); ?>">
20
+ <?php foreach ($sidebar['options'] as $sidebar_option) : ?>
21
+ <option value="<?php echo $sidebar_option['id']; ?>"<?php selected(isset($sidebar_option['select'])); ?>><?php echo $sidebar_option['text']; ?></option>
22
+ <?php endforeach;
23
+ if ($user_can) : ?>
24
+ <option value="0" disabled="disabled"><?php _e('Type to Add New Sidebar', 'content-aware-sidebars'); ?></option>
25
+ <?php endif; ?>
26
+ </select>
27
+ </div>
28
+ <?php
29
+ $i++;
30
+ endforeach;
31
+
32
+ if ($i > $limit) : ?>
33
+ </div>
34
+ <div style="text-align:center;"><button class="js-cas-more button button-small" data-toggle=".cas-more"><span class="dashicons dashicons-arrow-down-alt2"></span></button></div>
35
+ <?php endif; ?>
36
+
37
+ <p class="howto"><?php echo sprintf(__('Note: Selected Sidebars are displayed on this %s specifically.', 'content-aware-sidebars'), strtolower($singular)) . sprintf(
38
+ __('Display sidebars per %s etc. with the %s.', 'content-aware-sidebars'),
39
+ strtolower(implode(', ', array_slice($content, 0, 3))),
40
+ '<a href="' . admin_url('admin.php?page=wpcas') . '">' . __('Sidebar Manager', 'content-aware-sidebars') . '</a>'
41
+ ); ?>
42
+ </p>