PluginProbe
Zone Manager (Zoninator) / 0.6
Zone Manager (Zoninator) v0.6
trunk 0.1 0.10.0 0.10.1 0.10.2 0.11.0 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9
zoninator / zoninator.php

zoninator.php in Zone Manager (Zoninator) 0.6, at zoninator.php

1,558 lines 48.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Zone Manager (Zoninator)
4 Description: Curation made easy! Create "zones" then add and order your content!
5 Author: Mohammad Jangda, Automattic
6 Version: 0.6
7 Author URI: http://vip.wordpress.com
8 Text Domain: zoninator
9 Domain Path: /language/
10
11 Copyright 2010-2015 Mohammad Jangda, Automattic
12
13 This plugin was built by Mohammad Jangda in conjunction with William Davis and the Bangor Daily News.
14
15 GNU General Public License, Free Software Foundation <http://creativecommons.org/licenses/GPL/2.0/>
16
17 This program is free software; you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation; either version 2 of the License, or
20 (at your option) any later version.
21
22 This program is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30
31 */
32
33 if( ! class_exists( 'Zoninator' ) ) :
34
35 define( 'ZONINATOR_VERSION', '0.6' );
36 define( 'ZONINATOR_PATH', dirname( __FILE__ ) );
37 define( 'ZONINATOR_URL', trailingslashit( plugins_url( '', __FILE__ ) ) );
38
39 require_once( ZONINATOR_PATH . '/functions.php' );
40 require_once( ZONINATOR_PATH . '/widget.zone-posts.php');
41
42 class Zoninator
43 {
44 var $key = 'zoninator';
45 var $zone_taxonomy = 'zoninator_zones';
46 var $zone_term_prefix = 'zone-';
47 var $zone_meta_prefix = '_zoninator_order_';
48 var $zone_nonce_prefix = 'zone-nonce';
49 var $zone_ajax_nonce_action = 'ajax-action';
50 var $zone_lock_period = 30; // number of seconds a lock is valid for
51 var $zone_max_lock_period = 600; // max number of seconds for all locks in a session
52 var $post_types = null;
53 var $zone_detail_defaults = array(
54 'description' => ''
55 // Add additional properties here!
56 );
57 var $zone_messages = null;
58 var $posts_per_page = 10;
59
60 function __construct() {
61 add_action( 'init', array( $this, 'init' ), 99 ); // init later after other post types have been registered
62
63 add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
64
65 add_action( 'widgets_init', array( $this, 'widgets_init' ) );
66
67 add_action( 'init', array( $this, 'add_zone_feed' ) );
68
69 add_action( 'template_redirect', array( $this, 'do_zoninator_feeds' ) );
70
71 add_action( 'split_shared_term', array( $this, 'split_shared_term' ), 10, 4 );
72
73 $this->default_post_types = array( 'post' );
74 }
75
76 function add_zone_feed() {
77 add_rewrite_tag( '%' . $this->zone_taxonomy . '%', '([^&]+)' );
78 add_rewrite_rule( '^zones/([^/]+)/feed.json/?$', 'index.php?' . $this->zone_taxonomy . '=$matches[1]', 'top' );
79 }
80
81 function init() {
82 $this->zone_messages = array(
83 'insert-success' => __( 'The zone was successfully created.', 'zoninator' ),
84 'update-success' => __( 'The zone was successfully updated.', 'zoninator' ),
85 'delete-success' => __( 'The zone was successfully deleted.', 'zoninator' ),
86 'error-general' => __( 'Sorry, something went wrong! Please try again?', 'zoninator' ),
87 'error-zone-lock' => __( 'Sorry, this zone is in use by %s and is currently locked. Please try again later.', 'zoninator' ),
88 'error-zone-lock-max' => __( 'Sorry, you have reached the maximum idle limit and will now be redirected to the Dashboard.', 'zoninator' ),
89 );
90
91 $this->zone_lock_period = apply_filters( 'zoninator_zone_lock_period', $this->zone_lock_period );
92 $this->zone_max_lock_period = apply_filters( 'zoninator_zone_max_lock_period', $this->zone_max_lock_period );
93 $this->posts_per_page = apply_filters( 'zoninator_posts_per_page', $this->posts_per_page );
94
95 do_action( 'zoninator_pre_init' );
96
97 // Default post type support
98 foreach( $this->default_post_types as $post_type )
99 add_post_type_support( $post_type, $this->zone_taxonomy );
100
101 // Register taxonomy
102 if( ! taxonomy_exists( $this->zone_taxonomy ) ) {
103 register_taxonomy( $this->zone_taxonomy, $this->get_supported_post_types(), array(
104 'label' => __( 'Zones', 'zoninator' ),
105 'hierarchical' => false,
106 'query_var' => false,
107 'rewrite' => false,
108 'public' => false,
109
110 ) );
111 }
112
113 add_action( 'admin_init', array( $this, 'admin_controller' ) );
114 add_action( 'admin_init', array( $this, 'admin_init' ) );
115
116 add_action( 'admin_menu', array( $this, 'admin_page_init' ) );
117
118 # Add default advanced search fields
119 add_action( 'zoninator_advanced_search_fields', array( $this, 'zone_advanced_search_cat_filter' ) );
120 add_action( 'zoninator_advanced_search_fields', array( $this, 'zone_advanced_search_date_filter' ), 20 );
121
122 do_action( 'zoninator_post_init' );
123 }
124
125 public function load_textdomain() {
126 load_plugin_textdomain( 'zoninator', false, basename( ZONINATOR_PATH ) . '/language' );
127 }
128
129 function widgets_init() {
130 register_widget( 'Zoninator_ZonePosts_Widget' );
131 }
132
133 // Add necessary AJAX actions
134 function admin_ajax_init( ) {
135 add_action( 'wp_ajax_zoninator_reorder_posts', array( $this, 'ajax_reorder_posts' ) );
136 add_action( 'wp_ajax_zoninator_add_post', array( $this, 'ajax_add_post' ) );
137 add_action( 'wp_ajax_zoninator_remove_post', array( $this, 'ajax_remove_post' ) );
138 add_action( 'wp_ajax_zoninator_search_posts', array( $this, 'ajax_search_posts' ) );
139 add_action( 'wp_ajax_zoninator_update_lock', array( $this, 'ajax_update_lock' ) );
140 add_action( 'wp_ajax_zoninator_update_recent', array( $this, 'ajax_recent_posts' ) );
141
142 }
143
144 function admin_init() {
145
146 $this->admin_ajax_init();
147
148 // Enqueue Scripts and Styles
149 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
150 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ) );
151 }
152
153 function admin_page_init() {
154 // Set up page
155 add_menu_page( __( 'Zoninator', 'zoninator' ), __( 'Zones', 'zoninator' ), $this->_get_manage_zones_cap(), $this->key, array( $this, 'admin_page' ), '', 11 );
156 }
157
158 function admin_enqueue_scripts() {
159 if( $this->is_zoninator_page() ) {
160 wp_enqueue_script( 'zoninator-js', ZONINATOR_URL . 'js/zoninator.js', array( 'jquery', 'jquery-ui-core', 'jquery-ui-widget', 'jquery-ui-mouse', 'jquery-ui-position', 'jquery-ui-sortable', 'jquery-ui-autocomplete' ), ZONINATOR_VERSION, true );
161
162 $options = array(
163 'baseUrl' => $this->_get_zone_page_url(),
164 'adminUrl' => admin_url(),
165 'ajaxNonceAction' => $this->_get_nonce_key( $this->zone_ajax_nonce_action ),
166 'errorGeneral' => $this->_get_message( 'error-general' ),
167 'errorZoneLock' => sprintf( $this->_get_message( 'error-zone-lock' ), __( 'another user', 'zoninator' ) ),
168 'errorZoneLockMax' => $this->_get_message( 'error-zone-lock-max' ),
169 'zoneLockPeriod' => $this->zone_lock_period,
170 'zoneLockPeriodMax' => $this->zone_max_lock_period,
171 );
172 wp_localize_script( 'zoninator-js', 'zoninatorOptions', $options );
173
174 // For mobile support
175 // http://github.com/furf/jquery-ui-touch-punch
176 wp_enqueue_script( 'jquery-ui-touch-punch', ZONINATOR_URL . 'js/jquery.ui.touch-punch.min.js', array( 'jquery-ui-core', 'jquery-ui-widget', 'jquery-ui-mouse' ) );
177
178 }
179 }
180
181 function admin_enqueue_styles() {
182 if( $this->is_zoninator_page() ) {
183 wp_enqueue_style( 'zoninator-jquery-ui', ZONINATOR_URL . 'css/jquery-ui/smoothness/jquery-ui-zoninator.css', false, ZONINATOR_VERSION, 'all' );
184 wp_enqueue_style( 'zoninator-styles', ZONINATOR_URL . 'css/zoninator.css', false, ZONINATOR_VERSION, 'all' );
185 }
186 }
187
188 function admin_controller() {
189 if( $this->is_zoninator_page() ) {
190 $action = $this->_get_request_var( 'action' );
191
192 switch( $action ) {
193
194 case 'insert':
195 case 'update':
196 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
197
198 $this->verify_nonce( $action );
199 $this->verify_access( $action, $zone_id );
200
201 $name = $this->_get_post_var( 'name' );
202 $slug = $this->_get_post_var( 'slug', sanitize_title( $name ) );
203 $details = array(
204 'description' => $this->_get_post_var( 'description', '', 'strip_tags' )
205 );
206
207 // TODO: handle additional properties
208 if( $zone_id ) {
209 $result = $this->update_zone( $zone_id, array(
210 'name' => $name,
211 'slug' => $slug,
212 'details' => $details
213 ) );
214
215 } else {
216 $result = $this->insert_zone( $slug, $name, $details );
217 }
218
219 if( is_wp_error( $result ) ) {
220 wp_redirect( add_query_arg( 'message', 'error-general' ) );
221 exit;
222 } else {
223 if( ! $zone_id && isset( $result['term_id'] ) )
224 $zone_id = $result['term_id'];
225
226 // Redirect with success message
227 $message = sprintf( '%s-success', $action );
228 wp_redirect( $this->_get_zone_page_url( array( 'action' => 'edit', 'zone_id' => $zone_id, 'message' => $message ) ) );
229 exit;
230 }
231 break;
232
233 case 'delete':
234 $zone_id = $this->_get_request_var( 'zone_id', 0, 'absint' );
235
236 $this->verify_nonce( $action );
237 $this->verify_access( $action, $zone_id );
238
239 if( $zone_id ) {
240 $result = $this->delete_zone( $zone_id );
241 }
242
243 if( is_wp_error( $result ) ) {
244 $redirect_args = array( 'error' => $result->get_error_messages() );
245 } else {
246 $redirect_args = array( 'message' => 'delete-success' );
247 }
248
249 wp_redirect( $this->_get_zone_page_url( $redirect_args ) );
250 exit;
251 }
252 }
253 }
254
255 function admin_page() {
256 global $zoninator_admin_page;
257
258 $view = $this->_get_value_or_default( 'view', $zoninator_admin_page, 'edit.php' );
259 $view = sprintf( '%s/views/%s', ZONINATOR_PATH, $view );
260 $title = __( 'Zones', 'zoninator' );
261
262 $zones = $this->get_zones();
263
264 $default_active_zone = 0;
265 if( ! $this->_current_user_can_add_zones() ) {
266 if( ! empty( $zones ) )
267 $default_active_zone = $zones[0]->term_id;
268 }
269
270 $active_zone_id = $this->_get_request_var( 'zone_id', $default_active_zone, 'absint' );
271 $active_zone = ! empty( $active_zone_id ) ? $this->get_zone( $active_zone_id ) : array();
272 if ( ! empty( $active_zone ) )
273 $title = __( 'Edit Zone', 'zoninator' );
274
275 $message = $this->_get_message( $this->_get_get_var( 'message', '', 'urldecode' ) );
276 $error = $this->_get_get_var( 'error', '', 'urldecode' );
277
278 ?>
279 <div class="wrap zoninator-page">
280 <div id="icon-edit-pages" class="icon32"><br /></div>
281 <h2>
282 <?php echo esc_html( $title ); ?>
283 <?php if( $this->_current_user_can_add_zones() ) :
284 $new_link = $this->_get_zone_page_url( array( 'action' => 'new' ) ); ?>
285 <?php if( $active_zone_id ) : ?>
286 <a href="<?php echo esc_url( $new_link ); ?>" class="add-new-h2 zone-button-add-new"><?php esc_html_e( 'Add New', 'zoninator' ); ?></a>
287 <?php else : ?>
288 <span class="nav-tab nav-tab-active zone-tab zone-tab-active"><?php esc_html_e( 'Add New', 'zoninator' ); ?></span>
289 <?php endif; ?>
290 <?php endif; ?>
291 </h2>
292
293 <?php if( $message ) : ?>
294 <div id="zone-message" class="updated below-h2">
295 <p><?php echo esc_html( $message ); ?></p>
296 </div>
297 <?php endif; ?>
298 <?php if( $error ) : ?>
299 <div id="zone-message" class="error below-h2">
300 <p><?php echo esc_html( $error ); ?></p>
301 </div>
302 <?php endif; ?>
303
304 <div id="zoninator-wrap">
305
306 <?php $this->admin_page_zone_tabs( $zones, $active_zone_id ); ?>
307 <?php $this->admin_page_zone_edit( $active_zone ); ?>
308
309 </div>
310 </div>
311 <?php
312 }
313
314 function admin_page_zone_tabs( $zones, $active_zone_id = 0 ) {
315 $new_link = $this->_get_zone_page_url( array( 'action' => 'new' ) );
316 ?>
317 <div class="nav-tabs-container zone-tabs-container">
318 <div class="nav-tabs-nav-wrapper zone-tabs-nav-wrapper">
319 <div class="nav-tabs-wrapper zone-tabs-wrapper">
320 <div class="nav-tabs zone-tabs">
321 <?php foreach( $zones as $zone ) : ?>
322 <?php $zone_id = $this->get_zone_id( $zone ); ?>
323 <?php $zone_link = $this->_get_zone_page_url( array( 'action' => 'edit', 'zone_id' => $zone_id ) ); ?>
324
325 <?php if( $active_zone_id && $zone_id == $active_zone_id ) : ?>
326 <span class="nav-tab nav-tab-active zone-tab zone-tab-active"><?php echo esc_html( $zone->name ); ?></span>
327 <?php else : ?>
328 <a href="<?php echo esc_url( $zone_link ); ?>" class="nav-tab zone-tab"><?php echo esc_html( $zone->name ); ?></a>
329 <?php endif; ?>
330 <?php endforeach; ?>
331 </div>
332 </div>
333 </div>
334 </div>
335 <?php
336 }
337
338 function admin_page_zone_edit( $zone = null ) {
339 $zone_id = $this->_get_value_or_default( 'term_id', $zone, 0, 'absint' );
340 $zone_name = $this->_get_value_or_default( 'name', $zone );
341 $zone_slug = $this->_get_value_or_default( 'slug', $zone, '', array( $this, 'get_unformatted_zone_slug' ) );
342 $zone_description = $this->_get_value_or_default( 'description', $zone );
343
344 $zone_posts = $zone_id ? $this->get_zone_posts( $zone ) : array();
345
346 $zone_locked = $this->is_zone_locked( $zone_id );
347
348 $delete_link = $this->_get_zone_page_url( array( 'action' => 'delete', 'zone_id' => $zone_id ) );
349 $delete_link = wp_nonce_url( $delete_link, $this->_get_nonce_key( 'delete' ) );
350 ?>
351 <div id="zone-edit-wrapper">
352 <?php if( ( $zone_id == 0 && $this->_current_user_can_add_zones() ) || ( $zone_id != 0 && $this->_current_user_can_manage_zones() ) ) : ?>
353 <?php if( $zone_locked ) : ?>
354 <?php $locking_user = get_userdata( $zone_locked ); ?>
355 <div class="updated below-h2">
356 <p><?php echo sprintf( $this->_get_message( 'error-zone-lock' ), sprintf( '<a href="mailto:%s">%s</a>', esc_attr( $locking_user->user_email ), esc_html( $locking_user->display_name ) ) ); ?></p>
357 </div>
358 <input type="hidden" id="zone-locked" name="zone-locked" value="1" />
359 <?php endif; ?>
360 <div class="col-wrap zone-col zone-info-col">
361 <div class="form-wrap zone-form zone-info-form">
362
363 <?php if( $this->_current_user_can_edit_zones( $zone_id ) && ! $zone_locked ) : ?>
364
365 <form id="zone-info" method="post">
366
367 <?php do_action( 'zoninator_pre_zone_fields', $zone ); ?>
368
369 <div class="form-field zone-field">
370 <label for="zone-name"><?php esc_html_e( 'Name', 'zoninator' ); ?></label>
371 <input type="text" id="zone-name" name="name" value="<?php echo esc_attr( $zone_name ); ?>" />
372 </div>
373
374 <?php if( $zone_id ) : ?>
375 <div class="form-field zone-field">
376 <label for="zone-slug"><?php esc_html_e( 'Slug', 'zoninator' ); ?></label>
377 <span><?php echo esc_attr( $zone_slug ); ?></span>
378 <input type="hidden" id="zone-slug" name="slug" value="<?php echo esc_attr( $zone_slug ); ?>" />
379 </div>
380 <?php endif; ?>
381
382 <div class="form-field zone-field">
383 <label for="zone-description"><?php esc_html_e( 'Description', 'zoninator' ); ?></label>
384 <textarea id="zone-description" name="description"><?php echo esc_html( $zone_description ); ?></textarea>
385 </div>
386
387 <?php do_action( 'zoninator_post_zone_fields', $zone ); ?>
388
389 <?php if( $zone_id ) : ?>
390 <input type="hidden" name="zone_id" id="zone_id" value="<?php echo esc_attr( $zone_id ) ?>" />
391 <?php wp_nonce_field( $this->_get_nonce_key( 'update' ) ); ?>
392 <?php else : ?>
393 <?php wp_nonce_field( $this->_get_nonce_key( 'insert' ) ); ?>
394 <?php endif; ?>
395
396 <div class="submit-field submitbox">
397 <input type="submit" value="<?php esc_attr_e('Save', 'zoninator'); ?>" name="submit" class="button-primary" />
398
399 <?php if( $zone_id ) : ?>
400 <a href="<?php echo $delete_link ?>" class="submitdelete" onclick="return confirm('<?php echo esc_js( 'Are you sure you want to delete this zone?', 'zoninator' ); ?>')"><?php esc_html_e('Delete', 'zoninator') ?></a>
401 <?php endif; ?>
402 </div>
403
404 <input type="hidden" name="action" value="<?php echo $zone_id ? 'update' : 'insert'; ?>">
405 <input type="hidden" name="page" value="<?php echo $this->key; ?>">
406
407 </form>
408 <?php else : ?>
409 <div id="zone-info-readonly" class="readonly">
410 <?php do_action( 'zoninator_pre_zone_readonly', $zone ); ?>
411
412 <div class="form-field zone-field">
413 <label for="zone-name"><?php esc_html_e( 'Name', 'zoninator' ); ?></label>
414 <span><?php echo esc_attr( $zone_name ); ?></span>
415 </div>
416
417 <!--
418 <div class="form-field zone-field">
419 <label for="zone-slug"><?php esc_html_e( 'Slug', 'zoninator' ); ?></label>
420 <span><?php echo esc_attr( $zone_slug ); ?></span>
421 </div>
422 -->
423
424 <div class="form-field zone-field">
425 <label for="zone-description"><?php esc_html_e( 'Description', 'zoninator' ); ?></label>
426 <span><?php echo esc_html( $zone_description ); ?></span>
427 </div>
428
429 <input type="hidden" name="zone_id" id="zone_id" value="<?php echo esc_attr( $zone_id ) ?>" />
430
431 <?php do_action( 'zoninator_post_zone_readonly', $zone ); ?>
432 </div>
433 <?php endif; ?>
434
435 <?php // Ideally, we should seperate nonces for each action. But this will do for simplicity. ?>
436 <?php wp_nonce_field( $this->_get_nonce_key( $this->zone_ajax_nonce_action ), $this->_get_nonce_key( $this->zone_ajax_nonce_action ), false ); ?>
437 </div>
438
439 </div>
440
441 <div class="col-wrap zone-col zone-posts-col">
442 <div class="zone-posts-wrapper <?php echo ! $this->_current_user_can_manage_zones( $zone_id ) || $zone_locked ? 'readonly' : ''; ?>">
443 <?php if( $zone_id ) : ?>
444 <h3><?php esc_html_e( 'Zone Content', 'zoninator' ); ?></h3>
445
446 <?php $this->zone_advanced_search_filters(); ?>
447
448 <?php $this->zone_admin_recent_posts_dropdown( $zone_id ); ?>
449
450 <?php $this->zone_admin_search_form(); ?>
451
452 <div class="zone-posts-list">
453 <?php foreach( $zone_posts as $post ) : ?>
454 <?php $this->admin_page_zone_post( $post, $zone ); ?>
455 <?php endforeach; ?>
456 </div>
457
458 <?php else : ?>
459 <p class="description"><?php esc_html_e( 'To create a zone, enter a name (and any other info) to to left and click "Save". You can then choose content items to add to the zone.', 'zoninator' ); ?></p>
460 <?php endif; ?>
461 </div>
462 </div>
463 <?php endif; ?>
464 </div>
465
466 <?php
467 }
468
469 function admin_page_zone_post( $post, $zone ) {
470 $columns = apply_filters( 'zoninator_zone_post_columns', array(
471 'position' => array( $this, 'admin_page_zone_post_col_position' ),
472 'info' => array( $this, 'admin_page_zone_post_col_info' )
473 ), $post, $zone );
474 ?>
475 <div id="zone-post-<?php echo $post->ID; ?>" class="zone-post" data-post-id="<?php echo $post->ID; ?>">
476 <table>
477 <tr>
478 <?php foreach( $columns as $column_key => $column_callback ) : ?>
479 <?php if( is_callable( $column_callback ) ) : ?>
480 <td class="zone-post-col zone-post-<?php echo $column_key; ?>">
481 <?php call_user_func( $column_callback, $post, $zone ); ?>
482 </td>
483 <?php endif; ?>
484 <?php endforeach; ?>
485 </tr>
486 </table>
487 <input type="hidden" name="zone-post-id" value="<?php echo $post->ID; ?>" />
488 </div>
489 <?php
490 }
491
492 function admin_page_zone_post_col_position( $post, $zone ) {
493 $current_position = $this->get_post_order( $post->ID, $zone );
494 ?>
495 <span title="<?php esc_attr_e( 'Click and drag to change the position of this item.', 'zoninator' ); ?>">
496 <?php echo esc_html( $current_position ); ?>
497 </span>
498 <?php
499 }
500 function admin_page_zone_post_col_info( $post, $zone ) {
501 $action_links = array(
502 sprintf( '<a href="%s" class="edit" target="_blank" title="%s">%s</a>', get_edit_post_link( $post->ID ), __( 'Opens in new window', 'zoninator' ), __( 'Edit', 'zoninator' ) ),
503 sprintf( '<a href="#" class="delete" title="%s">%s</a>', __( 'Remove this item from the zone', 'zoninator' ), __( 'Remove', 'zoninator' ) ),
504 sprintf( '<a href="%s" class="view" target="_blank" title="%s">%s</a>', get_permalink( $post->ID ), __( 'Opens in new window', 'zoninator' ), __( 'View', 'zoninator' ) ),
505 // Move To
506 // Copy To
507 );
508 ?>
509 <?php echo sprintf( '%s <span class="zone-post-status">(%s)</span>', esc_html( $post->post_title ), esc_html( $post->post_status ) ); ?>
510
511 <div class="row-actions">
512 <?php echo implode( ' | ', $action_links ); ?>
513 </div>
514 <?php
515 }
516
517 function zone_advanced_search_filters() {
518 ?>
519 <div class="zone-advanced-search-filters-heading">
520 <span class="zone-toggle-advanced-search" data-alt-label="<?php esc_attr_e( 'Hide', 'zoninator' ); ?>"><?php esc_html_e( 'Show Filters', 'zoninator' ); ?></span>
521 </div>
522 <div class="zone-advanced-search-filters-wrapper">
523 <?php do_action( 'zoninator_advanced_search_fields' ); ?>
524 </div>
525 <?php
526 }
527
528 function zone_advanced_search_cat_filter() {
529 $current_cat = $this->_get_post_var( 'zone_advanced_filter_taxonomy', '', 'absint' );
530 ?>
531 <label for="zone_advanced_filter_taxonomy"><?php esc_html_e( 'Filter:', 'zoninator' ); ?></label>
532 <?php
533 wp_dropdown_categories( apply_filters( 'zoninator_advanced_filter_category', array(
534 'show_option_all' => __( 'Show all Categories', 'zoninator' ),
535 'selected' => $current_cat,
536 'name' => 'zone_advanced_filter_taxonomy',
537 'id' => 'zone_advanced_filter_taxonomy',
538 'hide_if_empty' => true,
539 ) ) );
540 }
541
542 function zone_advanced_search_date_filter() {
543 $current_date = $this->_get_post_var( 'zone_advanced_filter_date', '', 'striptags' );
544 $date_filters = apply_filters( 'zoninator_advanced_filter_date', array( 'all', 'today', 'yesterday') );
545 ?>
546 <select name="zone_advanced_filter_date" id="zone_advanced_filter_date">
547 <?php
548 // Convert string dates into actual dates
549 foreach( $date_filters as $date ) :
550 $timestamp = strtotime( $date );
551 $output = ( $timestamp ) ? date( 'Y-m-d', $timestamp ) : 0;
552 echo sprintf( '<option value="%s" %s>%s</option>', esc_attr( $output ), selected( $output, $current_date, false ), esc_html( $date ) );
553 endforeach;
554 ?>
555 </select>
556 <?php
557 }
558
559 function ajax_recent_posts() {
560
561 $cat = $this->_get_post_var( 'cat', '', 'absint' );
562 $date = $this->_get_post_var( 'date', '', 'striptags' );
563 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
564
565 $limit = $this->posts_per_page;
566 $post_types = $this->get_supported_post_types();
567 $zone_posts = $this->get_zone_posts( $zone_id );
568 $zone_post_ids = wp_list_pluck( $zone_posts, 'ID' );
569
570
571 // Verify nonce
572 $this->verify_nonce( $this->zone_ajax_nonce_action );
573 $this->verify_access( '', $zone_id );
574
575 if( is_wp_error( $zone_posts ) ) {
576 $status = 0;
577 $content = $zone_posts->get_error_message();
578 } else {
579 $args = apply_filters( 'zoninator_recent_posts_args', array(
580 'posts_per_page' => $limit,
581 'order' => 'DESC',
582 'orderby' => 'post_date',
583 'post_type' => $post_types,
584 'ignore_sticky_posts' => true,
585 'post_status' => array( 'publish', 'future' ),
586 'post__not_in' => $zone_post_ids,
587 ) );
588
589 if ( $this->_validate_category_filter( $cat ) ) {
590 $args['cat'] = $cat;
591 }
592
593 if ( $this->_validate_date_filter( $date ) ) {
594 $filter_date_parts = explode( '-', $date );
595 $args['year'] = $filter_date_parts[0];
596 $args['monthnum'] = $filter_date_parts[1];
597 $args['day'] = $filter_date_parts[2];
598 }
599
600 $content = '';
601 $recent_posts = get_posts( $args );
602 foreach ( $recent_posts as $post ) :
603 $content .= sprintf( '<option value="%d">%s</option>', $post->ID, get_the_title( $post->ID ) . ' (' . $post->post_status . ')' );
604 endforeach;
605 wp_reset_postdata();
606 $status = 1;
607 }
608
609 $empty_label = '';
610 if ( ! $content ) {
611 $empty_label = __( 'No results found', 'zoninator' );
612 } elseif ( $cat ) {
613 $empty_label = sprintf( __( 'Choose post from %s', 'zoninator' ), get_the_category_by_ID( $cat ) );
614 } else {
615 $empty_label = __( 'Choose a post', 'zoninator' );
616 }
617
618 $content = '<option value="">' . esc_html( $empty_label ) . '</option>' . $content;
619
620 $this->ajax_return( $status, $content );
621 }
622
623 function zone_admin_recent_posts_dropdown( $zone_id ) {
624
625 $limit = $this->posts_per_page;
626 $post_types = $this->get_supported_post_types();
627 $zone_posts = $this->get_zone_posts( $zone_id );
628 $zone_post_ids = wp_list_pluck( $zone_posts, 'ID' );
629
630 $args = apply_filters( 'zoninator_recent_posts_args', array(
631 'posts_per_page' => $limit,
632 'order' => 'DESC',
633 'orderby' => 'post_date',
634 'post_type' => $post_types,
635 'ignore_sticky_posts' => true,
636 'post_status' => array( 'publish', 'future' ),
637 'post__not_in' => $zone_post_ids,
638 ) );
639
640
641
642 $recent_posts = get_posts( $args );
643 ?>
644 <div class="zone-search-wrapper">
645 <label for="zone-post-search-latest"><?php esc_html_e( 'Add Recent Content', 'zoninator' );?></label><br />
646 <select name="search-posts" id="zone-post-latest">
647 <option value=""><?php esc_html_e( 'Choose a post', 'zoninator' ); ?></option>
648 <?php
649 foreach ( $recent_posts as $post ) :
650 echo sprintf( '<option value="%d">%s</option>', $post->ID, esc_html( get_the_title( $post->ID ) . ' (' . $post->post_status . ')' ) );
651 endforeach;
652 wp_reset_postdata();
653 ?>
654 </select>
655 </div>
656 <?php
657 }
658
659 function zone_admin_search_form() {
660 ?>
661 <div class="zone-search-wrapper">
662 <label for="zone-post-search"><?php esc_html_e( 'Search for content', 'zoninator' );?></label>
663 <input type="text" id="zone-post-search" name="search" />
664 <p class="description"><?php esc_html_e( 'Enter a term or phrase in the text box above to search for and add content to this zone.', 'zoninator' ); ?></p>
665 </div>
666 <?php
667 }
668
669 function is_zoninator_page() {
670 global $current_screen;
671
672 if( function_exists( 'get_current_screen' ) )
673 $screen = get_current_screen();
674
675 if( empty( $screen ) ) {
676 return ! empty( $_REQUEST['page'] ) && sanitize_key( $_REQUEST['page'] ) == $this->key;
677 } else {
678 return ! empty( $screen->id ) && strstr( $screen->id, $this->key );
679 }
680 }
681
682 function ajax_return( $status, $content = '', $action = '' ) {
683 $action = ! empty( $action ) ? $action : $this->zone_ajax_nonce_action;
684 $nonce = wp_create_nonce( $this->_get_nonce_key( $action ) );
685
686 echo json_encode( array(
687 'status' => $status,
688 'content' => $content,
689 'nonce' => $nonce,
690 ) );
691 exit;
692 }
693
694 function ajax_add_post() {
695 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
696 $post_id = $this->_get_post_var( 'post_id', 0, 'absint' );
697
698 // Verify nonce
699 $this->verify_nonce( $this->zone_ajax_nonce_action );
700 $this->verify_access( '', $zone_id );
701
702 // Validate
703 if( ! $zone_id || ! $post_id )
704 $this->ajax_return( 0 );
705
706 $result = $this->add_zone_posts( $zone_id, $post_id, true );
707
708 if( is_wp_error( $result ) ) {
709 $status = 0;
710 $content = $result->get_error_message();
711 } else {
712 $post = get_post( $post_id );
713 $zone = $this->get_zone( $zone_id );
714
715 ob_start();
716 $this->admin_page_zone_post( $post, $zone );
717 $content = ob_get_contents();
718 ob_end_clean();
719
720 $status = 1;
721 }
722
723 $this->ajax_return( $status, $content );
724 }
725
726 function ajax_remove_post() {
727 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
728 $post_id = $this->_get_post_var( 'post_id', 0, 'absint' );
729
730 // Verify nonce
731 $this->verify_nonce( $this->zone_ajax_nonce_action );
732 $this->verify_access( '', $zone_id );
733
734 // Validate
735 if( ! $zone_id || ! $post_id )
736 $this->ajax_return( 0 );
737
738 $result = $this->remove_zone_posts( $zone_id, $post_id );
739
740 if( is_wp_error( $result ) ) {
741 $status = 0;
742 $content = $result->get_error_message();
743 } else {
744 $status = 1;
745 $content = '';
746 }
747
748 $this->ajax_return( $status, $content );
749 }
750
751 function ajax_reorder_posts() {
752 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
753 $post_ids = (array) $this->_get_post_var( 'posts', array(), 'absint' );
754
755 // Verify nonce
756 $this->verify_nonce( $this->zone_ajax_nonce_action );
757 $this->verify_access( '', $zone_id );
758
759 // validate
760 if( ! $zone_id || empty( $post_ids ) )
761 $this->ajax_return( 0 );
762
763 $result = $this->add_zone_posts( $zone_id, $post_ids, false );
764
765 if( is_wp_error( $result ) ) {
766 $status = 0;
767 $content = $result->get_error_message();
768 } else {
769 $status = 1;
770 $content = '';
771 }
772
773 $this->ajax_return( $status, $content );
774 }
775
776 // TODO: implement in front-end
777 function ajax_move_zone_post( $from_zone, $to_zone, $post_id ) {
778 $from_zone_id = $this->_get_post_var( 'from_zone_id', 0, 'absint' );
779 $to_zone_id = $this->_get_post_var( 'to_zone_id', 0, 'absint' );
780
781 $this->verify_nonce( $this->zone_ajax_nonce_action );
782
783 // TODO: validate both zones exist, post exists
784
785 // Add to new zone
786 $this->add_zone_posts( $to_zone_id, $post_id, true );
787
788 // Remove from old zone
789 $this->remove_zone_posts( $from_zone_id, $post_id );
790 }
791
792 function ajax_search_posts() {
793
794 $q = $this->_get_request_var( 'term', '', 'stripslashes' );
795
796 if( ! empty( $q ) ) {
797
798 $filter_cat = $this->_get_request_var( 'cat', '', 'absint' );
799 $filter_date = $this->_get_request_var( 'date', '', 'striptags' );
800
801 $post_types = $this->get_supported_post_types();
802 $limit = $this->_get_request_var( 'limit', $this->posts_per_page );
803 if( $limit <= 0 )
804 $limit = $this->posts_per_page;
805 $exclude = (array) $this->_get_request_var( 'exclude', array(), 'absint' );
806
807 $args = apply_filters( 'zoninator_search_args', array(
808 's' => $q,
809 'post__not_in' => $exclude,
810 'posts_per_page' => $limit,
811 'post_type' => $post_types,
812 'post_status' => array( 'publish', 'future' ),
813 'order' => 'DESC',
814 'orderby' => 'post_date',
815 'suppress_filters' => true,
816 ) );
817
818 if ( $this->_validate_category_filter( $filter_cat ) ) {
819 $args['cat'] = $filter_cat;
820 }
821
822 if ( $this->_validate_date_filter( $filter_date ) ) {
823 $filter_date_parts = explode( '-', $filter_date );
824 $args['year'] = $filter_date_parts[0];
825 $args['monthnum'] = $filter_date_parts[1];
826 $args['day'] = $filter_date_parts[2];
827 }
828
829 $query = new WP_Query( $args );
830 $stripped_posts = array();
831
832 if ( ! $query->have_posts() )
833 exit;
834
835 foreach( $query->posts as $post ) {
836 $stripped_posts[] = apply_filters( 'zoninator_search_results_post', array(
837 'title' => ! empty( $post->post_title ) ? $post->post_title : __( '(no title)', 'zoninator' ),
838 'post_id' => $post->ID,
839 'date' => get_the_time( get_option( 'date_format' ), $post ),
840 'post_type' => $post->post_type,
841 'post_status' => $post->post_status,
842 ), $post );
843 }
844
845 echo json_encode( $stripped_posts );
846 exit;
847 }
848 }
849
850 function ajax_update_lock() {
851 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
852
853 $this->verify_nonce( $this->zone_ajax_nonce_action );
854 $this->verify_access( '', $zone_id );
855
856 if( ! $zone_id )
857 exit;
858
859 if( ! $this->is_zone_locked( $zone_id ) ) {
860 $this->lock_zone( $zone_id );
861 $this->ajax_return( 1, '' );
862 }
863 }
864
865 function get_supported_post_types() {
866 if( isset( $this->post_types ) )
867 return $this->post_types;
868
869 $this->post_types = array();
870
871 foreach( get_post_types() as $post_type ) {
872 if( post_type_supports( $post_type, $this->zone_taxonomy ) )
873 array_push( $this->post_types, $post_type );
874 }
875
876 return $this->post_types;
877 }
878
879 function insert_zone( $slug, $name = '', $details = array() ) {
880
881 // slug cannot be empty
882 if( empty( $slug ) )
883 return new WP_Error( 'zone-empty-slug', __( 'Slug is a required field.', 'zoninator' ) );
884
885 $slug = $this->get_formatted_zone_slug( $slug );
886 $name = ! empty( $name ) ? $name : $slug;
887
888 $details = wp_parse_args( $details, $this->zone_detail_defaults );
889 $details = maybe_serialize( stripslashes_deep( $details ) );
890
891 $args = array(
892 'slug' => $slug,
893 'description' => $details,
894 );
895
896 // Filterize to allow other inputs
897 $args = apply_filters( 'zoninator_insert_zone', $args );
898
899 return wp_insert_term( $name, $this->zone_taxonomy, $args );
900 }
901
902 function update_zone( $zone, $data = array() ) {
903 $zone_id = $this->get_zone_id( $zone );
904
905 if( $this->zone_exists( $zone_id ) ) {
906 $zone = $this->get_zone( $zone );
907
908 $name = $this->_get_value_or_default( 'name', $data, $zone->name );
909 $slug = $this->_get_value_or_default( 'slug', $data, $zone->slug, array( $this, 'get_formatted_zone_slug' ) );
910 $details = $this->_get_value_or_default( 'details', $data, array() );
911
912 // TODO: Back-fill current zone details
913 //$details = wp_parse_args( $details, $this->zone_detail_defaults );
914 $details = wp_parse_args( $details, $this->zone_detail_defaults );
915 $details = maybe_serialize( stripslashes_deep( $details ) );
916
917 $args = array(
918 'name' => $name,
919 'slug' => $slug,
920 'description' => $details
921 );
922
923 // Filterize to allow other inputs
924 $args = apply_filters( 'zoninator_update_zone', $args, $zone_id, $zone );
925
926 return wp_update_term( $zone_id, $this->zone_taxonomy, $args );
927 }
928 return new WP_Error( 'invalid-zone', __( 'Sorry, that zone doesn\'t exist.', 'zoninator' ) );
929 }
930
931 function delete_zone( $zone ) {
932 $zone_id = $this->get_zone_id( $zone );
933 $meta_key = $this->get_zone_meta_key( $zone );
934
935 $this->_empty_zone_posts_cache( $meta_key );
936
937 if( $this->zone_exists( $zone_id ) ) {
938 // Delete all post associations for the zone
939 $this->remove_zone_posts( $zone_id );
940
941 // Delete the term
942 $delete = wp_delete_term( $zone_id, $this->zone_taxonomy );
943
944 if( ! $delete ) {
945 return new WP_Error( 'delete-zone', __( 'Sorry, we couldn\'t delete the zone.', 'zoninator' ) );
946 } else {
947 do_action( 'zoninator_delete_zone', $zone_id );
948 return $delete;
949 }
950 }
951 return new WP_Error( 'invalid-zone', __( 'Sorry, that zone doesn\'t exist.', 'zoninator' ) );
952 }
953
954 function add_zone_posts( $zone, $posts, $append = false ) {
955 $zone = $this->get_zone( $zone );
956 $meta_key = $this->get_zone_meta_key( $zone );
957
958 $this->_empty_zone_posts_cache( $meta_key );
959
960 if( $append ) {
961 // Order should be the highest post order
962 $last_post = $this->get_last_post_in_zone( $zone );
963 if( $last_post )
964 $order = $this->get_post_order( $last_post, $zone );
965 else
966 $order = 0;
967 } else {
968 $order = 0;
969 $this->remove_zone_posts( $zone );
970 }
971
972 foreach( (array) $posts as $post ) {
973 $post_id = $this->get_post_id( $post );
974 if( $post_id ) {
975 $order++;
976 update_metadata( 'post', $post_id, $meta_key, $order, true );
977 }
978 // TODO: remove_object_terms -- but need remove object terms function :(
979 }
980
981 clean_term_cache( $this->get_zone_id( $zone ), $this->zone_taxonomy ); // flush cache for our zone term and related APC caches
982
983 do_action( 'zoninator_add_zone_posts', $posts, $zone );
984 }
985
986 function remove_zone_posts( $zone, $posts = null ) {
987 $zone = $this->get_zone( $zone );
988 $meta_key = $this->get_zone_meta_key( $zone );
989
990 $this->_empty_zone_posts_cache( $meta_key );
991
992 // if null, delete all
993 if( ! $posts )
994 $posts = $this->get_zone_posts( $zone );
995
996 foreach( (array) $posts as $post ) {
997 $post_id = $this->get_post_id( $post );
998 if( $post_id )
999 delete_metadata( 'post', $post_id, $meta_key );
1000 }
1001
1002 clean_term_cache( $this->get_zone_id( $zone ), $this->zone_taxonomy ); // flush cache for our zone term and related APC caches
1003
1004 do_action( 'zoninator_remove_zone_posts', $posts, $zone );
1005 }
1006
1007 function get_zone_posts( $zone, $args = array() ) {
1008 // Check cache first
1009 if( $posts = $this->get_zone_posts_from_cache( $zone, $args ) )
1010 return $posts;
1011
1012 $query = $this->get_zone_query( $zone, $args );
1013 $posts = $query->get_posts();
1014
1015 // Add posts to cache
1016 $this->add_zone_posts_to_cache( $posts, $zone, $args );
1017
1018 return $posts;
1019 }
1020
1021 function get_zone_query( $zone, $args = array() ) {
1022 $meta_key = $this->get_zone_meta_key( $zone );
1023
1024 $defaults = array(
1025 'order' => 'ASC',
1026 'posts_per_page' => -1,
1027 'post_type' => $this->get_supported_post_types(),
1028 'ignore_sticky_posts' => '1', // don't want sticky posts messing up our order
1029 );
1030
1031 // Default to published posts on the front-end
1032 if ( ! is_admin() )
1033 $defaults['post_status'] = array( 'publish' );
1034
1035 if ( is_admin() ) // skip APC in the admin
1036 $defaults['suppress_filters'] = true;
1037
1038 $args = wp_parse_args( $args, $defaults );
1039
1040 // Un-overridable args
1041 $args['orderby'] = 'meta_value_num';
1042 $args['meta_key'] = $meta_key;
1043
1044 /* // 3.1-friendly, though missing sort support which is why we're using the old way
1045 if( function_exists( 'get_post_format' ) ) {
1046 $args['meta_query'] = array(
1047 array(
1048 'key' => $meta_key,
1049 'type' => 'NUMERIC'
1050 )
1051 );
1052 } else {
1053 $args['meta_key'] = $meta_key;
1054 }
1055 */
1056 return new WP_Query( $args );
1057 }
1058
1059 function get_last_post_in_zone( $zone ) {
1060 return $this->get_single_post_in_zone( $zone, array(
1061 'order' => 'DESC',
1062 ) );
1063 }
1064
1065 function get_first_post_in_zone( $zone ) {
1066 return $this->get_single_post_in_zone( $zone );
1067 }
1068
1069 function get_prev_post_in_zone( $zone, $post_id ) {
1070 // TODO: test this works
1071 $order = $this->get_post_order_in_zone( $zone, $post_id );
1072
1073 return $this->get_single_post_in_zone( $zone, array(
1074 'meta_value' => $order,
1075 'meta_compare' => '<='
1076 ) );
1077 }
1078
1079 function get_next_post_in_zone( $zone, $post_id ) {
1080 // TODO: test this works
1081 $order = $this->get_post_order_in_zone( $zone, $post_id );
1082
1083 return $this->get_single_post_in_zone( $zone, array(
1084 'meta_value' => $order,
1085 'meta_compare' => '>='
1086 ) );
1087
1088 }
1089
1090 function get_single_post_in_zone( $zone, $args = array() ) {
1091
1092 $args = wp_parse_args( $args, array(
1093 'posts_per_page' => 1,
1094 'showposts' => 1,
1095 ) );
1096
1097 $post = $this->get_zone_posts( $zone, $args );
1098
1099 if( is_array( $post ) && ! empty( $post ) )
1100 return array_pop( $post );
1101
1102 return false;
1103 }
1104
1105 function get_zones_for_post( $post_id ) {
1106 // TODO: build this out
1107
1108 // get_object_terms
1109 // get_terms
1110
1111 // OR
1112
1113 // get all meta_keys that match the prefix
1114 // strip the prefix
1115
1116 // OR
1117
1118 // get all zones and see if there's a matching meta entry
1119 // strip the prefix from keys
1120
1121 // array_map( 'absint', $zone_ids )
1122 // $zones = array();
1123 // foreach( $zone_ids as $zone_id ) {
1124 // $zones[] = get_zone( $zone_id );
1125 //}
1126 //return $zones;
1127 }
1128
1129 function get_zones( $args = array() ) {
1130
1131 $args = wp_parse_args( $args, array(
1132 'orderby' => 'id',
1133 'order' => 'ASC',
1134 'hide_empty' => 0,
1135 ) );
1136
1137 $zones = get_terms( $this->zone_taxonomy, $args );
1138
1139 // Add extra fields in description as properties
1140 foreach( $zones as $zone ) {
1141 $zone = $this->_fill_zone_details( $zone );
1142 }
1143
1144 return $zones;
1145 }
1146
1147 function get_zone( $zone ) {
1148 if( is_int( $zone ) ) {
1149 $field = 'id';
1150 } elseif( is_string( $zone ) ) {
1151 $field = 'slug';
1152 $zone = $this->get_zone_slug( $zone );
1153 } elseif( is_object( $zone ) ) {
1154 return $zone;
1155 } else {
1156 return false;
1157 }
1158
1159 $zone = get_term_by( $field, $zone, $this->zone_taxonomy );
1160
1161 if( ! $zone )
1162 return false;
1163
1164 return $this->_fill_zone_details( $zone );
1165 }
1166
1167 function lock_zone( $zone, $user_id = 0 ) {
1168 $zone_id = $this->get_zone_id( $zone );
1169
1170 if( ! $zone_id )
1171 return false;
1172
1173 if( ! $user_id ) {
1174 $user = wp_get_current_user();
1175 $user_id = $user->ID;
1176 }
1177
1178 $lock_key = $this->get_zone_meta_key( $zone );
1179 $expiry = $this->zone_lock_period + 1; // Add a one to avoid most race condition issues between lock expiry and ajax call
1180 set_transient( $lock_key, $user->ID, $expiry );
1181
1182 // Possible alternative: set zone lock as property with time and user
1183 }
1184
1185 // Not really needed with transients...
1186 function unlock_zone( $zone ) {
1187 $zone_id = $this->get_zone_id( $zone );
1188
1189 if( ! $zone_id )
1190 return false;
1191
1192 $lock_key = $this->get_zone_meta_key( $zone );
1193
1194 delete_transient( $lock_key );
1195 }
1196
1197 function is_zone_locked( $zone ) {
1198 $zone_id = $this->get_zone_id( $zone );
1199 if( ! $zone_id )
1200 return false;
1201
1202 $user = wp_get_current_user();
1203 $lock_key = $this->get_zone_meta_key( $zone );
1204
1205 $lock = get_transient( $lock_key );
1206
1207 // If lock doesn't exist, or check if current user same as lock user
1208 if( ! $lock || absint( $lock ) === absint( $user->ID ) )
1209 return false;
1210 else
1211 // return user_id of locking user
1212 return absint( $lock );
1213 }
1214
1215 function zone_exists( $zone ) {
1216 $zone_id = $this->get_zone_id( $zone );
1217
1218 if( term_exists( $zone_id, $this->zone_taxonomy ) )
1219 return true;
1220
1221 return false;
1222 }
1223
1224 function get_zone_id( $zone ) {
1225 if( is_int( $zone ) )
1226 return $zone;
1227
1228 $zone = $this->get_zone( $zone );
1229 if( is_object( $zone ) )
1230 $zone = $zone->term_id;
1231
1232 return (int)$zone;
1233 }
1234
1235 function get_zone_meta_key( $zone ) {
1236 $zone_id = $this->get_zone_id( $zone );
1237 return $this->zone_meta_prefix . $zone_id;
1238 }
1239
1240 function get_zone_slug( $zone ) {
1241 if( is_int( $zone ) )
1242 $zone = $this->get_zone( $zone );
1243
1244 if( is_object( $zone ) )
1245 $zone = $zone->slug;
1246
1247 return $this->get_formatted_zone_slug( $zone );
1248 }
1249
1250 function get_formatted_zone_slug( $slug ) {
1251 return $slug; // legacy function -- slugs can no longer be changed
1252 }
1253 function get_unformatted_zone_slug( $slug ) {
1254 return $slug; // legacy function -- slugs can no longer be changed
1255 }
1256
1257 function get_post_id( $post ) {
1258 if( is_int( $post ) )
1259 return $post;
1260 elseif( is_array( $post ) )
1261 return absint( $post['ID'] );
1262 elseif( is_object( $post ) )
1263 return $post->ID;
1264
1265 return false;
1266 }
1267
1268 function get_post_order( $post, $zone ) {
1269 $post_id = $this->get_post_id( $post );
1270 $meta_key = $this->get_zone_meta_key( $zone );
1271
1272 return get_metadata( 'post', $post_id, $meta_key, true );
1273 }
1274
1275 function verify_nonce( $action ) {
1276 $action = $this->_get_nonce_key( $action );
1277 $nonce = $this->_get_request_var( $action );
1278
1279 if( empty( $nonce ) )
1280 $nonce = $this->_get_request_var( '_wpnonce' );
1281
1282 if( ! wp_verify_nonce( $nonce, $action ) )
1283 $this->_unauthorized_access();
1284 }
1285
1286 function verify_access( $action = '', $zone_id = null ) {
1287 // TODO: should check if zone locked
1288
1289 $verify_function = '';
1290 switch( $action ) {
1291 case 'insert':
1292 $verify_function = '_current_user_can_add_zones';
1293 break;
1294 case 'update':
1295 case 'delete':
1296 $verify_function = '_current_user_can_edit_zones';
1297 break;
1298 default:
1299 $verify_function = '_current_user_can_manage_zones';
1300 break;
1301 }
1302
1303 if( ! call_user_func( array( $this, $verify_function ), $zone_id ) )
1304 $this->_unauthorized_access();
1305 }
1306
1307 function _unauthorized_access() {
1308 wp_die( __( 'Sorry, you\'re not supposed to do that...', 'zoninator' ) );
1309 }
1310
1311 function _fill_zone_details( $zone ) {
1312 if( ! empty( $zone->zoninator_parsed ) && $zone->zoninator_parsed )
1313 return $zone;
1314
1315 $details = array();
1316
1317 if( ! empty( $zone->description ) )
1318 $details = maybe_unserialize( $zone->description );
1319
1320 $details = wp_parse_args( $details, $this->zone_detail_defaults );
1321
1322 foreach( $details as $detail_key => $detail_value ) {
1323 $zone->$detail_key = $detail_value;
1324 }
1325
1326 $zone->zoninator_parsed = true;
1327
1328 return $zone;
1329 }
1330
1331 function do_zoninator_feeds() {
1332
1333 global $wp_query;
1334
1335 $query_var = get_query_var( $this->zone_taxonomy );
1336
1337 if ( ! empty( $query_var ) ) {
1338 $zone_slug = get_query_var( $this->zone_taxonomy );
1339 $zone_id = $this->get_zone( $zone_slug );
1340
1341 if ( empty( $zone_id ) ) {
1342 $this->send_user_error( __( 'Invalid zone supplied', 'zoninator' ) );
1343 }
1344
1345 $results = $this->get_zone_posts( $zone_id, apply_filters( 'zoninator_json_feed_fields', array(), $zone_slug ) );
1346
1347 if ( empty( $results ) ) {
1348 $this->send_user_error( __( 'No zone posts found', 'zoninator' ) );
1349 }
1350
1351 $filtered_results = $this->filter_zone_feed_fields( $results );
1352
1353 $this->json_return( apply_filters( 'zoninator_json_feed_results', $filtered_results, $zone_slug ), false );
1354 }
1355
1356 return;
1357
1358 }
1359
1360 private function filter_zone_feed_fields( $results ) {
1361
1362 $whitelisted_fields = array( 'ID', 'post_date', 'post_title', 'post_content', 'post_excerpt', 'post_status', 'guid' );
1363
1364 $i = 0;
1365 foreach ( $results as $result ) {
1366 foreach( $whitelisted_fields as $field ) {
1367 $filtered_results[$i]->$field = $result->$field;
1368 }
1369 $i++;
1370 }
1371
1372
1373 return $filtered_results;
1374 }
1375
1376 /**
1377 * Encode some data and echo it (possibly without cached headers)
1378 *
1379 * @param array $data
1380 */
1381 private function json_return( $data ) {
1382
1383 if ( $data == NULL )
1384 return false;
1385
1386 header( 'Content-Type: application/json' );
1387 echo json_encode( $data );
1388 exit();
1389 }
1390
1391 private static function send_user_error( $message ) {
1392 self::status_header_with_message( 406, $message );
1393 exit();
1394 }
1395
1396 /**
1397 * Modify the header and description in the global array
1398 *
1399 * @global array $wp_header_to_desc
1400 * @param int $status
1401 * @param string $message
1402 */
1403 private static function status_header_with_message( $status, $message ) {
1404 global $wp_header_to_desc;
1405
1406 $status = absint( $status );
1407 $official_message = isset( $wp_header_to_desc[$status] ) ? $wp_header_to_desc[$status] : '';
1408 $wp_header_to_desc[$status] = $message;
1409
1410 status_header( $status );
1411
1412 $wp_header_to_desc[$status] = $official_message;
1413 }
1414
1415
1416
1417
1418 // TODO: Caching needs to be testing properly before being implemented!
1419 function get_zone_cache_key( $zone, $args = array() ) {
1420 return '';
1421
1422 $meta_key = $this->get_zone_meta_key( $zone );
1423 $hash = md5( serialize( $args ) );
1424 return $meta_key . $hash;
1425 }
1426
1427 function get_zone_posts_from_cache( $zone, $args = array() ) {
1428 return false; // TODO: implement
1429
1430 $meta_key = $this->get_zone_meta_key( $zone );
1431 $cache_key = $this->get_zone_cache_key( $zone, $args );
1432 if( $posts = wp_cache_get( $cache_key, $meta_key ) )
1433 return $posts;
1434 return false;
1435 }
1436
1437 function add_zone_posts_to_cache( $posts, $zone, $args = array() ) {
1438 return; // TODO: implement
1439
1440 $meta_key = $this->get_zone_meta_key( $zone );
1441 $cache_key = $this->get_zone_cache_key( $zone, $args );
1442 wp_cache_set( $cache_key, $posts, $meta_key );
1443 }
1444
1445 // Handle 4.2 term-splitting
1446 function split_shared_term( $old_term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
1447 if ( $this->zone_taxonomy === $taxonomy ) {
1448 do_action( 'zoninator_split_shared_term', $old_term_id, $new_term_id, $term_taxonomy_id );
1449
1450 // Quick, easy switcheroo; add posts to new zone id and remove from the old one.
1451 $posts = $this->get_zone_posts( $old_term_id );
1452 if ( ! empty( $posts ) ) {
1453 $this->add_zone_posts( $new_term_id, $posts );
1454 $this->remove_zone_posts( $old_term_id );
1455 }
1456
1457 do_action( 'zoninator_did_split_shared_term', $old_term_id, $new_term_id, $term_taxonomy_id );
1458 }
1459 }
1460
1461 function _empty_zone_posts_cache( $meta_key ) {
1462 return; // TODO: implement
1463 }
1464
1465 function _get_message( $message_id, $encode = false ) {
1466 $message = '';
1467
1468 if( ! empty( $this->zone_messages[$message_id] ) )
1469 $message = $this->zone_messages[$message_id];
1470
1471 if( $encode )
1472 $message = urlencode( $message );
1473
1474 return $message;
1475 }
1476
1477 function _get_nonce_key( $action ) {
1478 return sprintf( '%s-%s', $this->zone_nonce_prefix, $action );
1479 }
1480
1481 function _current_user_can_add_zones() {
1482 return current_user_can( $this->_get_add_zones_cap() );
1483 }
1484
1485 function _current_user_can_edit_zones( $zone_id ) {
1486 $has_cap = current_user_can( $this->_get_edit_zones_cap() );
1487 return apply_filters( 'zoninator_current_user_can_edit_zone', $has_cap, $zone_id );
1488 }
1489
1490 function _current_user_can_manage_zones() {
1491 return current_user_can( $this->_get_manage_zones_cap() );
1492 }
1493
1494 function _get_add_zones_cap() {
1495 return apply_filters( 'zoninator_add_zone_cap', 'edit_others_posts' );
1496 }
1497
1498 function _get_edit_zones_cap() {
1499 return apply_filters( 'zoninator_edit_zone_cap', 'edit_others_posts' );
1500 }
1501
1502 function _get_manage_zones_cap() {
1503 return apply_filters( 'zoninator_manage_zone_cap', 'edit_others_posts' );
1504 }
1505
1506 function _get_zone_page_url( $args = array() ) {
1507 $url = menu_page_url( $this->key, false );
1508
1509 foreach( $args as $arg_key => $arg_value ) {
1510 $url = add_query_arg( $arg_key, $arg_value, $url );
1511 }
1512
1513 return $url;
1514 }
1515
1516 function _validate_date_filter( $date ) {
1517 return preg_match( '/([0-9]{4})-([0-9]{2})-([0-9]{2})/', $date );
1518 }
1519
1520 function _validate_category_filter( $cat ) {
1521 return $cat && get_term_by( 'id', $cat, 'category' );
1522 }
1523
1524 function _get_value_or_default( $var, $object, $default = '', $sanitize_callback = '' ) {
1525 if( is_object( $object ) )
1526 $value = ! empty( $object->$var ) ? $object->$var : $default;
1527 elseif( is_array( $object ) )
1528 $value = ! empty( $object[$var] ) ? $object[$var] : $default;
1529 else
1530 $value = $default;
1531
1532 if( is_callable( $sanitize_callback ) ) {
1533 if( is_array( $value ) )
1534 $value = array_map( $sanitize_callback, $value );
1535 else
1536 $value = call_user_func( $sanitize_callback, $value );
1537 }
1538
1539 return $value;
1540 }
1541
1542 function _get_request_var( $var, $default = '', $sanitize_callback = '' ) {
1543 return $this->_get_value_or_default( $var, $_REQUEST, $default, $sanitize_callback );
1544 }
1545
1546 function _get_get_var( $var, $default = '', $sanitize_callback = '' ) {
1547 return $this->_get_value_or_default( $var, $_GET, $default, $sanitize_callback );
1548 }
1549 function _get_post_var( $var, $default = '', $sanitize_callback = '' ) {
1550 return $this->_get_value_or_default( $var, $_POST, $default, $sanitize_callback );
1551 }
1552 }
1553
1554 global $zoninator;
1555 $zoninator = new Zoninator;
1556
1557 endif;
1558