PluginProbe
MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor / 0.6.0
MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor v0.6.0
0.6.0 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.0 0.3.3 0.3.2 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.1.0 0.1.1 0.1.2 0.2.0 0.2.1 0.2.2 0.2.3 0.3.0 0.3.1
marqueex / includes / Traits / PostQuery / PostQueryControls.php

PostQueryControls.php in MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor 0.6.0, at includes/Traits/PostQuery/PostQueryControls.php

351 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Wpxero\Marqueex\Traits\PostQuery;
4
5 use Elementor\Controls_Manager;
6 use Wpxero\Marqueex\Core\Upsell;
7
8 if (!defined('ABSPATH')) {
9 exit;
10 }
11
12 /**
13 * Reusable Post Query Controls Trait (Free Version)
14 *
15 * Provides basic query controls and query builder for any Elementor widget.
16 * Premium features (Manual Selection, Include/Exclude) are shown as
17 * disabled controls with PRO badges. All premium logic lives in marqueex-pro.
18 *
19 * Usage:
20 * use PostQueryControls;
21 * // Inside register_controls():
22 * $this->register_query_controls();
23 * // Inside render():
24 * $args = $this->get_query_args( $this->get_settings_for_display() );
25 */
26 trait PostQueryControls {
27
28 // ─── Helpers ─────────────────────────────────────────────────
29
30 /**
31 * Get available public post types for the Source dropdown
32 */
33 protected function get_post_types_options() {
34 $post_types = get_post_types(['public' => true], 'objects');
35 $options = [];
36 $ignore = ['elementor_library', 'attachment'];
37
38 foreach ($post_types as $post_type) {
39 if (!in_array($post_type->name, $ignore, true)) {
40 $options[$post_type->name] = $post_type->label;
41 }
42 }
43
44 return $options;
45 }
46
47 // ─── Controls Registration ───────────────────────────────────
48
49 /**
50 * Register query controls
51 *
52 * Call this inside your widget's register_controls() method.
53 * Registers:
54 * - Basic controls (free)
55 * - Disabled PRO placeholders (when marqueex-pro is not active)
56 * - Hook for marqueex-pro to inject working premium controls
57 */
58 protected function register_query_controls() {
59 $this->start_controls_section(
60 'query_section',
61 ['label' => __('Query', 'marqueex')]
62 );
63
64 // ── Posts per page ──
65 $this->add_control(
66 'posts_per_page',
67 [
68 'label' => __('Number of Posts', 'marqueex'),
69 'type' => Controls_Manager::NUMBER,
70 'default' => 6,
71 'min' => 1,
72 'max' => 50,
73 ]
74 );
75
76 // ── Source (post types only in free) ──
77 $this->add_control(
78 'posts_source',
79 [
80 'label' => __('Source', 'marqueex'),
81 'type' => Controls_Manager::SELECT,
82 'options' => $this->get_post_types_options(),
83 'default' => 'post',
84 ]
85 );
86
87 // ── Locked premium query controls ──
88 Upsell::add_elementor_notice($this, 'pro_query_notice', [
89 'title' => __('Choose exactly which posts scroll', 'marqueex'),
90 'description' => __('Hand-pick the entries yourself, or narrow an automatic query down to the authors and categories you want — and keep the ones you do not out of it.', 'marqueex'),
91 'items' => [
92 __('Manual selection — pick posts one by one', 'marqueex'),
93 __('Include by author or taxonomy term', 'marqueex'),
94 __('Exclude specific posts, authors or terms', 'marqueex'),
95 ],
96 'cta' => __('Unlock the query builder', 'marqueex'),
97 'source' => 'elementor-post-query',
98 ]);
99
100 /**
101 * Hook for marqueex-pro to inject premium controls.
102 *
103 * When Pro is active the placeholders above are skipped and
104 * this action registers the real working controls instead:
105 * - manual_selection option added to posts_source
106 * - posts_selected_ids (post picker)
107 * - Include / Exclude tabs (authors, terms)
108 *
109 * @param \Elementor\Widget_Base $widget
110 */
111 do_action('marqueex/query/premium_controls', $this);
112
113 // ── Basic Controls ──
114 $this->add_control(
115 'posts_divider',
116 ['type' => Controls_Manager::DIVIDER]
117 );
118
119 $this->add_control(
120 'posts_offset',
121 [
122 'label' => __('Offset', 'marqueex'),
123 'type' => Controls_Manager::NUMBER,
124 'default' => 0,
125 'condition' => ['posts_source!' => 'manual_selection'],
126 ]
127 );
128
129 $this->add_control(
130 'posts_select_date',
131 [
132 'label' => __('Date', 'marqueex'),
133 'type' => Controls_Manager::SELECT,
134 'default' => 'anytime',
135 'options' => [
136 'anytime' => __('All', 'marqueex'),
137 'today' => __('Past Day', 'marqueex'),
138 'week' => __('Past Week', 'marqueex'),
139 'month' => __('Past Month', 'marqueex'),
140 'quarter' => __('Past Quarter', 'marqueex'),
141 'year' => __('Past Year', 'marqueex'),
142 'exact' => __('Custom', 'marqueex'),
143 ],
144 'condition' => ['posts_source!' => 'manual_selection'],
145 ]
146 );
147
148 $this->add_control(
149 'posts_date_before',
150 [
151 'label' => __('Before', 'marqueex'),
152 'type' => Controls_Manager::DATE_TIME,
153 'description' => __('Setting a Before date will show all posts published until the chosen date (inclusive).', 'marqueex'),
154 'condition' => [
155 'posts_select_date' => 'exact',
156 'posts_source!' => 'manual_selection',
157 ],
158 ]
159 );
160
161 $this->add_control(
162 'posts_date_after',
163 [
164 'label' => __('After', 'marqueex'),
165 'type' => Controls_Manager::DATE_TIME,
166 'description' => __('Setting an After date will show all posts published since the chosen date (inclusive).', 'marqueex'),
167 'condition' => [
168 'posts_select_date' => 'exact',
169 'posts_source!' => 'manual_selection',
170 ],
171 ]
172 );
173
174 $this->add_control(
175 'posts_orderby',
176 [
177 'label' => __('Order By', 'marqueex'),
178 'type' => Controls_Manager::SELECT,
179 'default' => 'date',
180 'options' => [
181 'title' => __('Title', 'marqueex'),
182 'ID' => __('ID', 'marqueex'),
183 'date' => __('Date', 'marqueex'),
184 'author' => __('Author', 'marqueex'),
185 'comment_count' => __('Comment Count', 'marqueex'),
186 'menu_order' => __('Menu Order', 'marqueex'),
187 'modified' => __('Last Modified', 'marqueex'),
188 'rand' => __('Random', 'marqueex'),
189 ],
190 ]
191 );
192
193 $this->add_control(
194 'posts_order',
195 [
196 'label' => __('Order', 'marqueex'),
197 'type' => Controls_Manager::SELECT,
198 'default' => 'desc',
199 'options' => [
200 'asc' => __('ASC', 'marqueex'),
201 'desc' => __('DESC', 'marqueex'),
202 ],
203 ]
204 );
205
206 $this->add_control(
207 'posts_ignore_sticky_posts',
208 [
209 'label' => __('Ignore Sticky Posts', 'marqueex'),
210 'type' => Controls_Manager::SWITCHER,
211 'return_value' => 'yes',
212 'default' => 'yes',
213 'condition' => ['posts_source' => 'post'],
214 ]
215 );
216
217 $this->add_control(
218 'posts_only_with_featured_image',
219 [
220 'label' => __('Only Featured Image Post', 'marqueex'),
221 'description' => __('Enable to display posts only when featured image is present.', 'marqueex'),
222 'type' => Controls_Manager::SWITCHER,
223 'return_value' => 'yes',
224 'condition' => ['posts_source!' => 'manual_selection'],
225 ]
226 );
227
228 $this->end_controls_section();
229 }
230
231 // ─── Query Builder ───────────────────────────────────────────
232
233 /**
234 * Build WP_Query arguments from widget settings (free version)
235 *
236 * Handles basic parameters only. Premium logic (manual selection,
237 * include/exclude) is applied by marqueex-pro via the
238 * `marqueex/query/args` filter.
239 *
240 * @param array $settings Widget settings from get_settings_for_display()
241 * @return array WP_Query compatible arguments
242 */
243 protected function get_query_args($settings = []) {
244 $settings = wp_parse_args($settings, [
245 'posts_per_page' => 6,
246 'posts_source' => 'post',
247 'posts_offset' => 0,
248 'posts_select_date' => 'anytime',
249 'posts_date_before' => '',
250 'posts_date_after' => '',
251 'posts_orderby' => 'date',
252 'posts_order' => 'desc',
253 'posts_ignore_sticky_posts' => 'yes',
254 'posts_only_with_featured_image' => '',
255 'post_categories' => [],
256 ]);
257
258 $posts_per_page = min(50, max(1, intval($settings['posts_per_page'])));
259 $post_source = sanitize_text_field($settings['posts_source']);
260
261 $args = [
262 'post_status' => 'publish',
263 'posts_per_page' => $posts_per_page,
264 'no_found_rows' => true,
265 'post_type' => $post_source ?: 'post',
266 ];
267
268 // Ignore sticky posts
269 if ($post_source === 'post' && $settings['posts_ignore_sticky_posts'] === 'yes') {
270 $args['ignore_sticky_posts'] = true;
271 }
272
273 // Backward compat: old post_categories setting
274 if (!empty($settings['post_categories'])) {
275 $category_ids = array_map('intval', (array) $settings['post_categories']);
276 $category_ids = array_filter($category_ids, function ($id) {
277 return $id > 0;
278 });
279 if (!empty($category_ids)) {
280 $args['category__in'] = $category_ids;
281 }
282 }
283
284 // Order
285 $args['orderby'] = sanitize_text_field($settings['posts_orderby']);
286 $args['order'] = strtoupper(sanitize_text_field($settings['posts_order']));
287
288 // Offset
289 if (!empty($settings['posts_offset']) && intval($settings['posts_offset']) > 0) {
290 $args['offset'] = intval($settings['posts_offset']);
291 }
292
293 // Only with featured image. Filtering by the `_thumbnail_id` meta key is the
294 // standard way to limit a query to posts that have a featured image; the
295 // slow-query notice is acceptable for this opt-in, bounded marquee query.
296 if ($settings['posts_only_with_featured_image'] === 'yes') {
297 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
298 $args['meta_key'] = '_thumbnail_id';
299 }
300
301 // Date filter
302 $selected_date = $settings['posts_select_date'];
303 if (!empty($selected_date) && $selected_date !== 'anytime') {
304 $date_query = [];
305 switch ($selected_date) {
306 case 'today':
307 $date_query['after'] = '-1 day';
308 break;
309 case 'week':
310 $date_query['after'] = '-1 week';
311 break;
312 case 'month':
313 $date_query['after'] = '-1 month';
314 break;
315 case 'quarter':
316 $date_query['after'] = '-3 month';
317 break;
318 case 'year':
319 $date_query['after'] = '-1 year';
320 break;
321 case 'exact':
322 if (!empty($settings['posts_date_after'])) {
323 $date_query['after'] = $settings['posts_date_after'];
324 }
325 if (!empty($settings['posts_date_before'])) {
326 $date_query['before'] = $settings['posts_date_before'];
327 }
328 $date_query['inclusive'] = true;
329 break;
330 }
331 if (!empty($date_query)) {
332 $args['date_query'] = $date_query;
333 }
334 }
335
336 /**
337 * Filter query arguments.
338 *
339 * marqueex-pro hooks here to apply premium logic:
340 * - Manual selection (post__in, post_type = any)
341 * - Include by authors / terms
342 * - Exclude by posts / authors / terms / current post
343 *
344 * @param array $args WP_Query arguments
345 * @param array $settings Widget settings
346 * @param \Elementor\Widget_Base $widget Current widget instance
347 */
348 return apply_filters('marqueex/query/args', $args, $settings, $this);
349 }
350 }
351