PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.24
Advanced Ads – Ad Manager & AdSense v2.0.24
2.0.25 2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / includes / placements / class-placement-repository.php
advanced-ads / includes / placements Last commit date
types 3 months ago class-placement-after-content.php 1 year ago class-placement-before-content.php 1 year ago class-placement-content.php 1 year ago class-placement-factory.php 2 weeks ago class-placement-footer.php 1 year ago class-placement-header.php 1 year ago class-placement-repository.php 2 weeks ago class-placement-sidebar-widget.php 1 year ago class-placement-standard.php 1 year ago class-placement-types.php 3 months ago class-placements.php 1 year ago index.php 1 year ago
class-placement-repository.php
463 lines
1 <?php
2 /**
3 * The placement repository.
4 *
5 * @package AdvancedAds
6 * @author Advanced Ads <info@wpadvancedads.com>
7 * @since 1.50.0
8 */
9
10 namespace AdvancedAds\Placements;
11
12 use AdvancedAds\Abstracts\Placement;
13 use AdvancedAds\Constants;
14 use AdvancedAds\Cache_Invalidator;
15 use AdvancedAds\Utilities\Cache;
16 use AdvancedAds\Utilities\WordPress;
17 use Exception;
18 use WP_Query;
19
20 defined( 'ABSPATH' ) || exit;
21
22 /**
23 * Placements Repository.
24 */
25 class Placement_Repository {
26
27 /* CRUD Methods ------------------- */
28
29 /**
30 * Create a new placement in the database.
31 *
32 * @param Placement $placement Placement object.
33 *
34 * @return Placement
35 */
36 public function create( &$placement ): Placement {
37 $id = wp_insert_post(
38 apply_filters(
39 'advanced-ads-new-placement-data',
40 [
41 'post_type' => Constants::POST_TYPE_PLACEMENT,
42 'post_name' => $placement->get_slug(),
43 'post_status' => $placement->get_status() ? $placement->get_status() : 'publish',
44 'post_author' => get_current_user_id(),
45 'post_title' => $placement->get_title() ? $placement->get_title() : __( 'New Placement', 'advanced-ads' ),
46 'post_content' => $placement->get_content() ? $placement->get_content() : __( 'New placement content goes here', 'advanced-ads' ),
47 'comment_status' => 'closed',
48 'ping_status' => 'closed',
49 ]
50 ),
51 true
52 );
53
54 if ( $id && ! is_wp_error( $id ) ) {
55 $placement->set_id( $id );
56 $this->update_post_meta( $placement );
57 $placement->apply_changes();
58
59 Cache_Invalidator::invalidate_placements();
60 }
61
62 return $placement;
63 }
64
65 /**
66 * Read a placement from the database.
67 *
68 * @param Placement $placement Placement object.
69 *
70 * @throws Exception If invalid placement.
71 *
72 * @return void
73 */
74 public function read( &$placement ): void {
75 $placement->set_defaults();
76 $post_object = get_post( $placement->get_id() );
77
78 if ( ! $placement->get_id() || ! $post_object || Constants::POST_TYPE_PLACEMENT !== $post_object->post_type ) {
79 throw new Exception( esc_html__( 'Invalid placement.', 'advanced-ads' ) );
80 }
81
82 $placement->set_props(
83 [
84 'title' => $post_object->post_title,
85 'slug' => $post_object->post_name,
86 'status' => $post_object->post_status,
87 'content' => $post_object->post_content,
88 ]
89 );
90
91 $this->read_placement_data( $placement );
92 $placement->set_object_read( true );
93 }
94
95 /**
96 * Update an existing placement in the database.
97 *
98 * @param Placement $placement Placement object.
99 *
100 * @return void
101 */
102 public function update( &$placement ): void {
103 global $wpdb;
104
105 $changes = $placement->get_changes();
106
107 // Only update the post when the post data changes.
108 if ( array_intersect( [ 'title', 'status', 'content' ], array_keys( $changes ) ) ) {
109 $post_data = [
110 'post_title' => $placement->get_title( 'edit' ),
111 'post_status' => $placement->get_status( 'edit' ) ? $placement->get_status( 'edit' ) : 'publish',
112 'post_type' => Constants::POST_TYPE_PLACEMENT,
113 'post_content' => wp_unslash(
114 apply_filters(
115 'content_save_pre',
116 $placement->get_content( 'edit' )
117 )
118 ),
119 ];
120
121 /**
122 * When updating this object, to prevent infinite loops, use $wpdb
123 * to update data, since wp_update_post spawns more calls to the
124 * save_post action.
125 *
126 * This ensures hooks are fired by either WP itself (admin screen save),
127 * or an update purely from CRUD.
128 */
129 if ( doing_action( 'save_post' ) ) {
130 $GLOBALS['wpdb']->update( $GLOBALS['wpdb']->posts, $post_data, [ 'ID' => $placement->get_id() ] );
131 clean_post_cache( $placement->get_id() );
132 } else {
133 wp_update_post( array_merge( [ 'ID' => $placement->get_id() ], $post_data ) );
134 }
135 } else { // Only update post modified time to record this save event.
136 $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
137 $wpdb->posts,
138 [
139 'post_modified' => current_time( 'mysql' ),
140 'post_modified_gmt' => current_time( 'mysql', 1 ),
141 ],
142 [
143 'ID' => $placement->get_id(),
144 ]
145 );
146 clean_post_cache( $placement->get_id() );
147 }
148
149 $this->update_post_meta( $placement );
150
151 $placement->apply_changes();
152
153 Cache_Invalidator::invalidate_placements();
154 }
155
156 /**
157 * Delete an placement from the database.
158 *
159 * @param Placement $placement Placement object or id.
160 * @param bool $force_delete Whether to bypass Trash and force deletion. Default false.
161 *
162 * @return void
163 */
164 public function delete( &$placement, $force_delete = false ): void {
165 // Early bail!!
166 if ( ! $placement || ! $placement->get_id() ) {
167 return;
168 }
169
170 if ( $force_delete ) {
171 wp_delete_post( $placement->get_id(), true );
172 $placement->set_id( 0 );
173 } else {
174 wp_trash_post( $placement->get_id() );
175 $placement->set_status( 'trash' );
176 }
177
178 Cache_Invalidator::invalidate_placements();
179 }
180
181 /* Finder Methods ------------------- */
182
183 /**
184 * Find placement by item id.
185 *
186 * Example: Find all placements that are connected to a specific ad.
187 * In this case $item_id = "ad_1234"
188 *
189 * Example: Find all placements that are connected to a specific group.
190 * In this case $item_id = "group_1234"
191 *
192 * @param string $item_id Item id to search for.
193 *
194 * @return Placement[]
195 */
196 public function find_by_item_id( $item_id ): array {
197 if ( ! is_string( $item_id ) || '' === $item_id ) {
198 return [];
199 }
200
201 $placement_ids = [];
202
203 foreach ( $this->get_placement_summaries() as $id => $summary ) {
204 if ( $item_id === $summary['item'] ) {
205 $placement_ids[] = (int) $id;
206 }
207 }
208
209 return $this->hydrate_placements( $placement_ids );
210 }
211
212 /**
213 * Retrieves placements by type.
214 *
215 * Filters cached placement summaries by type and hydrates matches.
216 *
217 * @param string|array $types Placement types to query.
218 * @param string $output The required return type. One of OBJECT or ids,
219 * which correspond to an Placement object or an array containing post ids respectively.
220 * @param bool $published_only Whether to return only published placements.
221 *
222 * @return array An associative array of placement IDs as keys and their corresponding placement objects as values.
223 */
224 public function find_by_types( $types, $output = OBJECT, $published_only = false ): array {
225 $types = array_values( array_filter( array_map( 'sanitize_key', (array) $types ) ) );
226
227 if ( empty( $types ) ) {
228 return [];
229 }
230
231 $placement_ids = [];
232
233 foreach ( $this->get_placement_summaries() as $id => $summary ) {
234 if ( $published_only && 'publish' !== $summary['status'] ) {
235 continue;
236 }
237
238 if ( in_array( $summary['type'], $types, true ) ) {
239 $placement_ids[] = (int) $id;
240 }
241 }
242
243 if ( 'ids' === $output ) {
244 return $placement_ids;
245 }
246
247 return $this->hydrate_placements( $placement_ids );
248 }
249
250 /**
251 * Get array of all placements.
252 *
253 * Prefer get_placement_summaries() for admin lists that only need id, title, type,
254 * status, or author — avoids constructing a full Placement instance per row.
255 *
256 * @return Placement[]
257 */
258 public function get_all_placements(): array {
259 return $this->hydrate_placements( array_keys( $this->get_placement_summaries() ) );
260 }
261
262 /**
263 * Get array of all published placements.
264 *
265 * @return Placement[]
266 */
267 public function get_all_published(): array {
268 return $this->hydrate_placements( $this->get_published_ids() );
269 }
270
271 /**
272 * Get lightweight placement summaries for list UIs (cached cross-request).
273 *
274 * @return array<int, array{id: int, title: string, type: string, status: string, author_id: int, item: string}>
275 */
276 public function get_placement_summaries(): array {
277 $summaries = Cache::get( Cache::PREFIX_PLACEMENTS, Cache::KEY_SUMMARIES );
278
279 if ( null === $summaries ) {
280 $summaries = $this->query_placement_summaries();
281 Cache::set( Cache::PREFIX_PLACEMENTS, Cache::KEY_SUMMARIES, $summaries );
282 }
283
284 return $summaries;
285 }
286
287 /**
288 * Get all placement as ID => title, derived from cached summaries.
289 *
290 * @return array<int, string>
291 */
292 public function get_placements_dropdown(): array {
293 $summaries = $this->get_placement_summaries();
294
295 if ( empty( $summaries ) ) {
296 return [];
297 }
298
299 return wp_list_pluck( $summaries, 'title', 'id' );
300 }
301
302 /**
303 * Get array of all published placement IDs, derived from cached summaries.
304 *
305 * @return int[]
306 */
307 public function get_published_ids(): array {
308 $published_ids = [];
309
310 foreach ( $this->get_placement_summaries() as $id => $summary ) {
311 if ( 'publish' === $summary['status'] ) {
312 $published_ids[] = (int) $id;
313 }
314 }
315
316 return $published_ids;
317 }
318
319 /**
320 * Query lightweight placement summaries without hydrating Placement objects.
321 *
322 * @return array<int, array{id: int, title: string, type: string, status: string, author_id: int, item: string}>
323 */
324 private function query_placement_summaries(): array {
325 $query = new WP_Query(
326 WordPress::improve_wp_query(
327 [
328 'post_type' => Constants::POST_TYPE_PLACEMENT,
329 'posts_per_page' => -1,
330 'post_status' => 'any',
331 'orderby' => 'title',
332 'order' => 'ASC',
333 'suppress_filters' => defined( 'ICL_SITEPRESS_VERSION' ) ? true : false, // Suppress filters if WPML is present.
334 ]
335 )
336 );
337
338 if ( ! $query->have_posts() ) {
339 return [];
340 }
341
342 $post_ids = wp_list_pluck( $query->posts, 'ID' );
343 _prime_post_caches( $post_ids, false, true );
344 update_meta_cache( 'post', $post_ids );
345
346 $summaries = [];
347 foreach ( $query->posts as $post ) {
348 $type = get_post_meta( $post->ID, 'type', true );
349 $item = get_post_meta( $post->ID, 'item', true );
350
351 $summaries[ (int) $post->ID ] = [
352 'id' => (int) $post->ID,
353 'title' => $post->post_title,
354 'type' => $type ? $type : 'default',
355 'status' => $post->post_status,
356 'author_id' => (int) $post->post_author,
357 'item' => is_string( $item ) ? $item : '',
358 ];
359 }
360
361 return $summaries;
362 }
363
364 /**
365 * Hydrate placement objects for the given post IDs.
366 *
367 * @param int[] $post_ids Placement post IDs.
368 *
369 * @return Placement[]
370 */
371 private function hydrate_placements( array $post_ids ): array {
372 $post_ids = array_values( array_filter( array_map( 'absint', $post_ids ) ) );
373
374 if ( ! empty( $post_ids ) ) {
375 _prime_post_caches( $post_ids, false, true );
376 }
377
378 $placements = [];
379 foreach ( $post_ids as $post_id ) {
380 $placement_object = wp_advads_get_placement( $post_id );
381 if ( false !== $placement_object ) {
382 $placements[ $post_id ] = $placement_object;
383 }
384 }
385
386 return $placements;
387 }
388
389 /* Additional Methods ------------------- */
390
391 /**
392 * Read placement data. Can be overridden by child classes to load other props.
393 *
394 * @param Ad $placement Ad object.
395 *
396 * @return void
397 */
398 private function read_placement_data( &$placement ): void {
399 $item = get_post_meta( $placement->get_id(), 'item', true );
400 $options = get_post_meta( $placement->get_id(), 'options', true );
401 $type = get_post_meta( $placement->get_id(), 'type', true );
402
403 if ( empty( $options ) || ! is_array( $options ) ) {
404 $options = [];
405 }
406
407 $display_conditions = $options['display'] ?? [];
408 $visitor_conditions = $options['visitors'] ?? [];
409 unset( $options['display'], $options['visitors'] );
410
411 $placement->set_item( $item );
412 $placement->set_type( $type ?? 'default' );
413 $placement->set_props( $options );
414 $placement->set_display_conditions( $display_conditions );
415 $placement->set_visitor_conditions( $visitor_conditions );
416 }
417
418 /**
419 * Update placement data. Can be overridden by child classes to load other props.
420 *
421 * @param Placement $placement Placement object.
422 *
423 * @return void
424 */
425 private function update_post_meta( &$placement ): void {
426 do_action( 'advanced-ads-placement-pre-save', $placement );
427
428 $meta_keys = $placement->get_data_keys();
429 $meta_keys = array_combine( $meta_keys, $meta_keys );
430
431 $meta_values = [];
432 foreach ( $meta_keys as $meta_key => $prop ) {
433 $value = method_exists( $placement, "get_$prop" )
434 ? $placement->{"get_$prop"}( 'edit' )
435 : $placement->get_prop( $prop, 'edit' );
436
437 $value = is_string( $value ) ? wp_slash( $value ) : $value;
438
439 switch ( $prop ) {
440 case 'display':
441 case 'visitors':
442 $value = WordPress::sanitize_conditions( $value );
443 break;
444 }
445
446 $meta_values[ $meta_key ] = $value;
447 }
448
449 $meta_values = array_diff_key(
450 $meta_values,
451 [
452 'type' => true,
453 'slug' => true,
454 'item' => true,
455 ]
456 );
457
458 update_post_meta( $placement->get_id(), 'item', $placement->get_item() );
459 update_post_meta( $placement->get_id(), 'type', $placement->get_type() );
460 update_post_meta( $placement->get_id(), 'options', $meta_values );
461 }
462 }
463