PluginProbe
Zone Manager (Zoninator) / 0.7
Zone Manager (Zoninator) v0.7
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.7, at zoninator.php

1,576 lines 48.6 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( apply_filters( 'zoninator_admin_page_get_zones_args', array() ) );
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 zone info', 'zoninator'); ?>" name="submit" class="button" />
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-save-input">
453 <input type="button" value="Save zone posts" name="zone-posts-save" id="zone-posts-save" class="button-primary" />
454 <p id="zone-posts-save-info" class="zone-posts-save-info"></p>
455 </div>
456
457 <div class="zone-posts-list">
458 <?php foreach( $zone_posts as $post ) : ?>
459 <?php $this->admin_page_zone_post( $post, $zone ); ?>
460 <?php endforeach; ?>
461 </div>
462
463 <?php else : ?>
464 <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>
465 <?php endif; ?>
466 </div>
467 </div>
468 <?php endif; ?>
469 </div>
470
471 <?php
472 }
473
474 function admin_page_zone_post( $post, $zone ) {
475 $columns = apply_filters( 'zoninator_zone_post_columns', array(
476 'position' => array( $this, 'admin_page_zone_post_col_position' ),
477 'info' => array( $this, 'admin_page_zone_post_col_info' )
478 ), $post, $zone );
479 ?>
480 <div id="zone-post-<?php echo $post->ID; ?>" class="zone-post" data-post-id="<?php echo $post->ID; ?>">
481 <table>
482 <tr>
483 <?php foreach( $columns as $column_key => $column_callback ) : ?>
484 <?php if( is_callable( $column_callback ) ) : ?>
485 <td class="zone-post-col zone-post-<?php echo $column_key; ?>">
486 <?php call_user_func( $column_callback, $post, $zone ); ?>
487 </td>
488 <?php endif; ?>
489 <?php endforeach; ?>
490 </tr>
491 </table>
492 <input type="hidden" name="zone-post-id" value="<?php echo $post->ID; ?>" />
493 </div>
494 <?php
495 }
496
497 function admin_page_zone_post_col_position( $post, $zone ) {
498 $current_position = $this->get_post_order( $post->ID, $zone );
499 ?>
500 <span title="<?php esc_attr_e( 'Click and drag to change the position of this item.', 'zoninator' ); ?>">
501 <?php echo esc_html( $current_position ); ?>
502 </span>
503 <?php
504 }
505 function admin_page_zone_post_col_info( $post, $zone ) {
506 $action_links = array(
507 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' ) ),
508 sprintf( '<a href="#" class="delete" title="%s">%s</a>', __( 'Remove this item from the zone', 'zoninator' ), __( 'Remove', 'zoninator' ) ),
509 sprintf( '<a href="%s" class="view" target="_blank" title="%s">%s</a>', get_permalink( $post->ID ), __( 'Opens in new window', 'zoninator' ), __( 'View', 'zoninator' ) ),
510 // Move To
511 // Copy To
512 );
513 ?>
514 <?php echo sprintf( '%s <span class="zone-post-status">(%s)</span>', esc_html( $post->post_title ), esc_html( $post->post_status ) ); ?>
515
516 <div class="row-actions">
517 <?php echo implode( ' | ', $action_links ); ?>
518 </div>
519 <?php
520 }
521
522 function zone_advanced_search_filters() {
523 ?>
524 <div class="zone-advanced-search-filters-heading">
525 <span class="zone-toggle-advanced-search" data-alt-label="<?php esc_attr_e( 'Hide', 'zoninator' ); ?>"><?php esc_html_e( 'Show Filters', 'zoninator' ); ?></span>
526 </div>
527 <div class="zone-advanced-search-filters-wrapper">
528 <?php do_action( 'zoninator_advanced_search_fields' ); ?>
529 </div>
530 <?php
531 }
532
533 function zone_advanced_search_cat_filter() {
534 $current_cat = $this->_get_post_var( 'zone_advanced_filter_taxonomy', '', 'absint' );
535 ?>
536 <label for="zone_advanced_filter_taxonomy"><?php esc_html_e( 'Filter:', 'zoninator' ); ?></label>
537 <?php
538 wp_dropdown_categories( apply_filters( 'zoninator_advanced_filter_category', array(
539 'show_option_all' => __( 'Show all Categories', 'zoninator' ),
540 'selected' => $current_cat,
541 'name' => 'zone_advanced_filter_taxonomy',
542 'id' => 'zone_advanced_filter_taxonomy',
543 'hide_if_empty' => true,
544 ) ) );
545 }
546
547 function zone_advanced_search_date_filter() {
548 $current_date = $this->_get_post_var( 'zone_advanced_filter_date', apply_filters( 'zoninator_advanced_filter_date_default', '' ), 'striptags' );
549 $date_filters = apply_filters( 'zoninator_advanced_filter_date', array( 'all', 'today', 'yesterday') );
550 ?>
551 <select name="zone_advanced_filter_date" id="zone_advanced_filter_date">
552 <?php
553 // Convert string dates into actual dates
554 foreach( $date_filters as $date ) :
555 if ( true === is_array( $date ) ) {
556 $output = array_key_exists( 'value', $date ) ? $date['value'] : 0;
557 $label = array_key_exists( 'label', $date ) ? $date['label'] : $output;
558 } else {
559 $timestamp = strtotime( $date );
560 $output = ( $timestamp ) ? date( 'Y-m-d', $timestamp ) : 0;
561 $label = $date;
562 }
563 echo sprintf( '<option value="%s" %s>%s</option>', esc_attr( $output ), selected( $output, $current_date, false ), esc_html( $label ) );
564 endforeach;
565 ?>
566 </select>
567 <?php
568 }
569
570 function ajax_recent_posts() {
571
572 $cat = $this->_get_post_var( 'cat', '', 'absint' );
573 $date = $this->_get_post_var( 'date', '', 'striptags' );
574 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
575
576 $limit = $this->posts_per_page;
577 $post_types = $this->get_supported_post_types();
578 $zone_posts = $this->get_zone_posts( $zone_id );
579 $zone_post_ids = wp_list_pluck( $zone_posts, 'ID' );
580
581
582 // Verify nonce
583 $this->verify_nonce( $this->zone_ajax_nonce_action );
584 $this->verify_access( '', $zone_id );
585
586 if( is_wp_error( $zone_posts ) ) {
587 $status = 0;
588 $content = $zone_posts->get_error_message();
589 } else {
590 $args = apply_filters( 'zoninator_recent_posts_args', array(
591 'posts_per_page' => $limit,
592 'order' => 'DESC',
593 'orderby' => 'post_date',
594 'post_type' => $post_types,
595 'ignore_sticky_posts' => true,
596 'post_status' => array( 'publish', 'future' ),
597 'post__not_in' => $zone_post_ids,
598 'suppress_filters' => false,
599 ) );
600
601 if ( $this->_validate_category_filter( $cat ) ) {
602 $args['cat'] = $cat;
603 }
604
605 if ( $this->_validate_date_filter( $date ) ) {
606 $filter_date_parts = explode( '-', $date );
607 $args['year'] = $filter_date_parts[0];
608 $args['monthnum'] = $filter_date_parts[1];
609 $args['day'] = $filter_date_parts[2];
610 }
611
612 $content = '';
613 $recent_posts = get_posts( $args );
614 foreach ( $recent_posts as $post ) :
615 $content .= sprintf( '<option value="%d">%s</option>', $post->ID, get_the_title( $post->ID ) . ' (' . $post->post_status . ')' );
616 endforeach;
617 wp_reset_postdata();
618 $status = 1;
619 }
620
621 $empty_label = '';
622 if ( ! $content ) {
623 $empty_label = __( 'No results found', 'zoninator' );
624 } elseif ( $cat ) {
625 $empty_label = sprintf( __( 'Choose post from %s', 'zoninator' ), get_the_category_by_ID( $cat ) );
626 } else {
627 $empty_label = __( 'Choose a post', 'zoninator' );
628 }
629
630 $content = '<option value="">' . esc_html( $empty_label ) . '</option>' . $content;
631
632 $this->ajax_return( $status, $content );
633 }
634
635 function zone_admin_recent_posts_dropdown( $zone_id ) {
636
637 $limit = $this->posts_per_page;
638 $post_types = $this->get_supported_post_types();
639 $zone_posts = $this->get_zone_posts( $zone_id );
640 $zone_post_ids = wp_list_pluck( $zone_posts, 'ID' );
641
642 $args = apply_filters( 'zoninator_recent_posts_args', array(
643 'posts_per_page' => $limit,
644 'order' => 'DESC',
645 'orderby' => 'post_date',
646 'post_type' => $post_types,
647 'ignore_sticky_posts' => true,
648 'post_status' => array( 'publish', 'future' ),
649 'post__not_in' => $zone_post_ids,
650 'suppress_filters' => false,
651 ) );
652
653
654
655 $recent_posts = get_posts( $args );
656 ?>
657 <div class="zone-search-wrapper">
658 <label for="zone-post-search-latest"><?php esc_html_e( 'Add Recent Content', 'zoninator' );?></label><br />
659 <select name="search-posts" id="zone-post-latest">
660 <option value=""><?php esc_html_e( 'Choose a post', 'zoninator' ); ?></option>
661 <?php
662 foreach ( $recent_posts as $post ) :
663 echo sprintf( '<option value="%d">%s</option>', $post->ID, esc_html( get_the_title( $post->ID ) . ' (' . $post->post_status . ')' ) );
664 endforeach;
665 wp_reset_postdata();
666 ?>
667 </select>
668 </div>
669 <?php
670 }
671
672 function zone_admin_search_form() {
673 ?>
674 <div class="zone-search-wrapper">
675 <label for="zone-post-search"><?php esc_html_e( 'Search for content', 'zoninator' );?></label>
676 <input type="text" id="zone-post-search" name="search" />
677 <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>
678 </div>
679 <?php
680 }
681
682 function is_zoninator_page() {
683 global $current_screen;
684
685 if( function_exists( 'get_current_screen' ) )
686 $screen = get_current_screen();
687
688 if( empty( $screen ) ) {
689 return ! empty( $_REQUEST['page'] ) && sanitize_key( $_REQUEST['page'] ) == $this->key;
690 } else {
691 return ! empty( $screen->id ) && strstr( $screen->id, $this->key );
692 }
693 }
694
695 function ajax_return( $status, $content = '', $action = '' ) {
696 $action = ! empty( $action ) ? $action : $this->zone_ajax_nonce_action;
697 $nonce = wp_create_nonce( $this->_get_nonce_key( $action ) );
698
699 echo json_encode( array(
700 'status' => $status,
701 'content' => $content,
702 'nonce' => $nonce,
703 ) );
704 exit;
705 }
706
707 function ajax_add_post() {
708 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
709 $post_id = $this->_get_post_var( 'post_id', 0, 'absint' );
710
711 // Verify nonce
712 $this->verify_nonce( $this->zone_ajax_nonce_action );
713 $this->verify_access( '', $zone_id );
714
715 // Validate
716 if( ! $zone_id || ! $post_id )
717 $this->ajax_return( 0 );
718
719 $result = $this->add_zone_posts( $zone_id, $post_id, true );
720
721 if( is_wp_error( $result ) ) {
722 $status = 0;
723 $content = $result->get_error_message();
724 } else {
725 $post = get_post( $post_id );
726 $zone = $this->get_zone( $zone_id );
727
728 ob_start();
729 $this->admin_page_zone_post( $post, $zone );
730 $content = ob_get_contents();
731 ob_end_clean();
732
733 $status = 1;
734 }
735
736 $this->ajax_return( $status, $content );
737 }
738
739 function ajax_remove_post() {
740 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
741 $post_id = $this->_get_post_var( 'post_id', 0, 'absint' );
742
743 // Verify nonce
744 $this->verify_nonce( $this->zone_ajax_nonce_action );
745 $this->verify_access( '', $zone_id );
746
747 // Validate
748 if( ! $zone_id || ! $post_id )
749 $this->ajax_return( 0 );
750
751 $result = $this->remove_zone_posts( $zone_id, $post_id );
752
753 if( is_wp_error( $result ) ) {
754 $status = 0;
755 $content = $result->get_error_message();
756 } else {
757 $status = 1;
758 $content = '';
759 }
760
761 $this->ajax_return( $status, $content );
762 }
763
764 function ajax_reorder_posts() {
765 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
766 $post_ids = (array) $this->_get_post_var( 'posts', array(), 'absint' );
767
768 // Verify nonce
769 $this->verify_nonce( $this->zone_ajax_nonce_action );
770 $this->verify_access( '', $zone_id );
771
772 // validate
773 if( ! $zone_id || empty( $post_ids ) )
774 $this->ajax_return( 0 );
775
776 $result = $this->add_zone_posts( $zone_id, $post_ids, false );
777
778 if( is_wp_error( $result ) ) {
779 $status = 0;
780 $content = $result->get_error_message();
781 } else {
782 $status = 1;
783 $content = '';
784 }
785
786 $this->ajax_return( $status, $content );
787 }
788
789 // TODO: implement in front-end
790 function ajax_move_zone_post( $from_zone, $to_zone, $post_id ) {
791 $from_zone_id = $this->_get_post_var( 'from_zone_id', 0, 'absint' );
792 $to_zone_id = $this->_get_post_var( 'to_zone_id', 0, 'absint' );
793
794 $this->verify_nonce( $this->zone_ajax_nonce_action );
795
796 // TODO: validate both zones exist, post exists
797
798 // Add to new zone
799 $this->add_zone_posts( $to_zone_id, $post_id, true );
800
801 // Remove from old zone
802 $this->remove_zone_posts( $from_zone_id, $post_id );
803 }
804
805 function ajax_search_posts() {
806
807 $q = $this->_get_request_var( 'term', '', 'stripslashes' );
808
809 if( ! empty( $q ) ) {
810
811 $filter_cat = $this->_get_request_var( 'cat', '', 'absint' );
812 $filter_date = $this->_get_request_var( 'date', '', 'striptags' );
813
814 $post_types = $this->get_supported_post_types();
815 $limit = $this->_get_request_var( 'limit', $this->posts_per_page );
816 if( $limit <= 0 )
817 $limit = $this->posts_per_page;
818 $exclude = (array) $this->_get_request_var( 'exclude', array(), 'absint' );
819
820 $args = apply_filters( 'zoninator_search_args', array(
821 's' => $q,
822 'post__not_in' => $exclude,
823 'posts_per_page' => $limit,
824 'post_type' => $post_types,
825 'post_status' => array( 'publish', 'future' ),
826 'order' => 'DESC',
827 'orderby' => 'post_date',
828 'suppress_filters' => true,
829 'no_found_rows' => true,
830 ) );
831
832 if ( $this->_validate_category_filter( $filter_cat ) ) {
833 $args['cat'] = $filter_cat;
834 }
835
836 if ( $this->_validate_date_filter( $filter_date ) ) {
837 $filter_date_parts = explode( '-', $filter_date );
838 $args['year'] = $filter_date_parts[0];
839 $args['monthnum'] = $filter_date_parts[1];
840 $args['day'] = $filter_date_parts[2];
841 }
842
843 $query = new WP_Query( $args );
844 $stripped_posts = array();
845
846 if ( ! $query->have_posts() )
847 exit;
848
849 foreach( $query->posts as $post ) {
850 $stripped_posts[] = apply_filters( 'zoninator_search_results_post', array(
851 'title' => ! empty( $post->post_title ) ? $post->post_title : __( '(no title)', 'zoninator' ),
852 'post_id' => $post->ID,
853 'date' => get_the_time( get_option( 'date_format' ), $post ),
854 'post_type' => $post->post_type,
855 'post_status' => $post->post_status,
856 ), $post );
857 }
858
859 echo json_encode( $stripped_posts );
860 exit;
861 }
862 }
863
864 function ajax_update_lock() {
865 $zone_id = $this->_get_post_var( 'zone_id', 0, 'absint' );
866
867 $this->verify_nonce( $this->zone_ajax_nonce_action );
868 $this->verify_access( '', $zone_id );
869
870 if( ! $zone_id )
871 exit;
872
873 if( ! $this->is_zone_locked( $zone_id ) ) {
874 $this->lock_zone( $zone_id );
875 $this->ajax_return( 1, '' );
876 }
877 }
878
879 function get_supported_post_types() {
880 if( isset( $this->post_types ) )
881 return $this->post_types;
882
883 $this->post_types = array();
884
885 foreach( get_post_types() as $post_type ) {
886 if( post_type_supports( $post_type, $this->zone_taxonomy ) )
887 array_push( $this->post_types, $post_type );
888 }
889
890 return $this->post_types;
891 }
892
893 function insert_zone( $slug, $name = '', $details = array() ) {
894
895 // slug cannot be empty
896 if( empty( $slug ) )
897 return new WP_Error( 'zone-empty-slug', __( 'Slug is a required field.', 'zoninator' ) );
898
899 $slug = $this->get_formatted_zone_slug( $slug );
900 $name = ! empty( $name ) ? $name : $slug;
901
902 $details = wp_parse_args( $details, $this->zone_detail_defaults );
903 $details = maybe_serialize( stripslashes_deep( $details ) );
904
905 $args = array(
906 'slug' => $slug,
907 'description' => $details,
908 );
909
910 // Filterize to allow other inputs
911 $args = apply_filters( 'zoninator_insert_zone', $args );
912
913 return wp_insert_term( $name, $this->zone_taxonomy, $args );
914 }
915
916 function update_zone( $zone, $data = array() ) {
917 $zone_id = $this->get_zone_id( $zone );
918
919 if( $this->zone_exists( $zone_id ) ) {
920 $zone = $this->get_zone( $zone );
921
922 $name = $this->_get_value_or_default( 'name', $data, $zone->name );
923 $slug = $this->_get_value_or_default( 'slug', $data, $zone->slug, array( $this, 'get_formatted_zone_slug' ) );
924 $details = $this->_get_value_or_default( 'details', $data, array() );
925
926 // TODO: Back-fill current zone details
927 //$details = wp_parse_args( $details, $this->zone_detail_defaults );
928 $details = wp_parse_args( $details, $this->zone_detail_defaults );
929 $details = maybe_serialize( stripslashes_deep( $details ) );
930
931 $args = array(
932 'name' => $name,
933 'slug' => $slug,
934 'description' => $details
935 );
936
937 // Filterize to allow other inputs
938 $args = apply_filters( 'zoninator_update_zone', $args, $zone_id, $zone );
939
940 return wp_update_term( $zone_id, $this->zone_taxonomy, $args );
941 }
942 return new WP_Error( 'invalid-zone', __( 'Sorry, that zone doesn\'t exist.', 'zoninator' ) );
943 }
944
945 function delete_zone( $zone ) {
946 $zone_id = $this->get_zone_id( $zone );
947 $meta_key = $this->get_zone_meta_key( $zone );
948
949 $this->_empty_zone_posts_cache( $meta_key );
950
951 if( $this->zone_exists( $zone_id ) ) {
952 // Delete all post associations for the zone
953 $this->remove_zone_posts( $zone_id );
954
955 // Delete the term
956 $delete = wp_delete_term( $zone_id, $this->zone_taxonomy );
957
958 if( ! $delete ) {
959 return new WP_Error( 'delete-zone', __( 'Sorry, we couldn\'t delete the zone.', 'zoninator' ) );
960 } else {
961 do_action( 'zoninator_delete_zone', $zone_id );
962 return $delete;
963 }
964 }
965 return new WP_Error( 'invalid-zone', __( 'Sorry, that zone doesn\'t exist.', 'zoninator' ) );
966 }
967
968 function add_zone_posts( $zone, $posts, $append = false ) {
969 $zone = $this->get_zone( $zone );
970 $meta_key = $this->get_zone_meta_key( $zone );
971
972 $this->_empty_zone_posts_cache( $meta_key );
973
974 if( $append ) {
975 // Order should be the highest post order
976 $last_post = $this->get_last_post_in_zone( $zone );
977 if( $last_post )
978 $order = $this->get_post_order( $last_post, $zone );
979 else
980 $order = 0;
981 } else {
982 $order = 0;
983 $this->remove_zone_posts( $zone );
984 }
985
986 foreach( (array) $posts as $post ) {
987 $post_id = $this->get_post_id( $post );
988 if( $post_id ) {
989 $order++;
990 update_metadata( 'post', $post_id, $meta_key, $order, true );
991 }
992 // TODO: remove_object_terms -- but need remove object terms function :(
993 }
994
995 clean_term_cache( $this->get_zone_id( $zone ), $this->zone_taxonomy ); // flush cache for our zone term and related APC caches
996
997 do_action( 'zoninator_add_zone_posts', $posts, $zone );
998 }
999
1000 function remove_zone_posts( $zone, $posts = null ) {
1001 $zone = $this->get_zone( $zone );
1002 $meta_key = $this->get_zone_meta_key( $zone );
1003
1004 $this->_empty_zone_posts_cache( $meta_key );
1005
1006 // if null, delete all
1007 if( ! $posts )
1008 $posts = $this->get_zone_posts( $zone );
1009
1010 foreach( (array) $posts as $post ) {
1011 $post_id = $this->get_post_id( $post );
1012 if( $post_id )
1013 delete_metadata( 'post', $post_id, $meta_key );
1014 }
1015
1016 clean_term_cache( $this->get_zone_id( $zone ), $this->zone_taxonomy ); // flush cache for our zone term and related APC caches
1017
1018 do_action( 'zoninator_remove_zone_posts', $posts, $zone );
1019 }
1020
1021 function get_zone_posts( $zone, $args = array() ) {
1022 // Check cache first
1023 if( $posts = $this->get_zone_posts_from_cache( $zone, $args ) )
1024 return $posts;
1025
1026 $query = $this->get_zone_query( $zone, $args );
1027 $posts = $query->posts;
1028
1029 // Add posts to cache
1030 $this->add_zone_posts_to_cache( $posts, $zone, $args );
1031
1032 return $posts;
1033 }
1034
1035 function get_zone_query( $zone, $args = array() ) {
1036 $meta_key = $this->get_zone_meta_key( $zone );
1037
1038 $defaults = array(
1039 'order' => 'ASC',
1040 'posts_per_page' => -1,
1041 'post_type' => $this->get_supported_post_types(),
1042 'ignore_sticky_posts' => '1', // don't want sticky posts messing up our order
1043 );
1044
1045 // Default to published posts on the front-end
1046 if ( ! is_admin() )
1047 $defaults['post_status'] = array( 'publish' );
1048
1049 if ( is_admin() ) // skip APC in the admin
1050 $defaults['suppress_filters'] = true;
1051
1052 $args = wp_parse_args( $args, $defaults );
1053
1054 // Un-overridable args
1055 $args['orderby'] = 'meta_value_num';
1056 $args['meta_key'] = $meta_key;
1057
1058 /* // 3.1-friendly, though missing sort support which is why we're using the old way
1059 if( function_exists( 'get_post_format' ) ) {
1060 $args['meta_query'] = array(
1061 array(
1062 'key' => $meta_key,
1063 'type' => 'NUMERIC'
1064 )
1065 );
1066 } else {
1067 $args['meta_key'] = $meta_key;
1068 }
1069 */
1070 return new WP_Query( $args );
1071 }
1072
1073 function get_last_post_in_zone( $zone ) {
1074 return $this->get_single_post_in_zone( $zone, array(
1075 'order' => 'DESC',
1076 ) );
1077 }
1078
1079 function get_first_post_in_zone( $zone ) {
1080 return $this->get_single_post_in_zone( $zone );
1081 }
1082
1083 function get_prev_post_in_zone( $zone, $post_id ) {
1084 // TODO: test this works
1085 $order = $this->get_post_order_in_zone( $zone, $post_id );
1086
1087 return $this->get_single_post_in_zone( $zone, array(
1088 'meta_value' => $order,
1089 'meta_compare' => '<='
1090 ) );
1091 }
1092
1093 function get_next_post_in_zone( $zone, $post_id ) {
1094 // TODO: test this works
1095 $order = $this->get_post_order_in_zone( $zone, $post_id );
1096
1097 return $this->get_single_post_in_zone( $zone, array(
1098 'meta_value' => $order,
1099 'meta_compare' => '>='
1100 ) );
1101
1102 }
1103
1104 function get_single_post_in_zone( $zone, $args = array() ) {
1105
1106 $args = wp_parse_args( $args, array(
1107 'posts_per_page' => 1,
1108 'showposts' => 1,
1109 ) );
1110
1111 $post = $this->get_zone_posts( $zone, $args );
1112
1113 if( is_array( $post ) && ! empty( $post ) )
1114 return array_pop( $post );
1115
1116 return false;
1117 }
1118
1119 function get_zones_for_post( $post_id ) {
1120 // TODO: build this out
1121
1122 // get_object_terms
1123 // get_terms
1124
1125 // OR
1126
1127 // get all meta_keys that match the prefix
1128 // strip the prefix
1129
1130 // OR
1131
1132 // get all zones and see if there's a matching meta entry
1133 // strip the prefix from keys
1134
1135 // array_map( 'absint', $zone_ids )
1136 // $zones = array();
1137 // foreach( $zone_ids as $zone_id ) {
1138 // $zones[] = get_zone( $zone_id );
1139 //}
1140 //return $zones;
1141 }
1142
1143 function get_zones( $args = array() ) {
1144
1145 $args = wp_parse_args( $args, array(
1146 'orderby' => 'id',
1147 'order' => 'ASC',
1148 'hide_empty' => 0,
1149 ) );
1150
1151 $zones = get_terms( $this->zone_taxonomy, $args );
1152
1153 // Add extra fields in description as properties
1154 foreach( $zones as $zone ) {
1155 $zone = $this->_fill_zone_details( $zone );
1156 }
1157
1158 return $zones;
1159 }
1160
1161 function get_zone( $zone ) {
1162 if( is_int( $zone ) ) {
1163 $field = 'id';
1164 } elseif( is_string( $zone ) ) {
1165 $field = 'slug';
1166 $zone = $this->get_zone_slug( $zone );
1167 } elseif( is_object( $zone ) ) {
1168 return $zone;
1169 } else {
1170 return false;
1171 }
1172
1173 if ( function_exists( 'wpcom_vip_get_term_by' ) ) {
1174 $zone = wpcom_vip_get_term_by( $field, $zone, $this->zone_taxonomy );
1175 } else {
1176 $zone = get_term_by( $field, $zone, $this->zone_taxonomy );
1177 }
1178
1179 if( ! $zone )
1180 return false;
1181
1182 return $this->_fill_zone_details( $zone );
1183 }
1184
1185 function lock_zone( $zone, $user_id = 0 ) {
1186 $zone_id = $this->get_zone_id( $zone );
1187
1188 if( ! $zone_id )
1189 return false;
1190
1191 if( ! $user_id ) {
1192 $user = wp_get_current_user();
1193 $user_id = $user->ID;
1194 }
1195
1196 $lock_key = $this->get_zone_meta_key( $zone );
1197 $expiry = $this->zone_lock_period + 1; // Add a one to avoid most race condition issues between lock expiry and ajax call
1198 set_transient( $lock_key, $user->ID, $expiry );
1199
1200 // Possible alternative: set zone lock as property with time and user
1201 }
1202
1203 // Not really needed with transients...
1204 function unlock_zone( $zone ) {
1205 $zone_id = $this->get_zone_id( $zone );
1206
1207 if( ! $zone_id )
1208 return false;
1209
1210 $lock_key = $this->get_zone_meta_key( $zone );
1211
1212 delete_transient( $lock_key );
1213 }
1214
1215 function is_zone_locked( $zone ) {
1216 $zone_id = $this->get_zone_id( $zone );
1217 if( ! $zone_id )
1218 return false;
1219
1220 $user = wp_get_current_user();
1221 $lock_key = $this->get_zone_meta_key( $zone );
1222
1223 $lock = get_transient( $lock_key );
1224
1225 // If lock doesn't exist, or check if current user same as lock user
1226 if( ! $lock || absint( $lock ) === absint( $user->ID ) )
1227 return false;
1228 else
1229 // return user_id of locking user
1230 return absint( $lock );
1231 }
1232
1233 function zone_exists( $zone ) {
1234 $zone_id = $this->get_zone_id( $zone );
1235
1236 if( term_exists( $zone_id, $this->zone_taxonomy ) )
1237 return true;
1238
1239 return false;
1240 }
1241
1242 function get_zone_id( $zone ) {
1243 if( is_int( $zone ) )
1244 return $zone;
1245
1246 $zone = $this->get_zone( $zone );
1247 if( is_object( $zone ) )
1248 $zone = $zone->term_id;
1249
1250 return (int)$zone;
1251 }
1252
1253 function get_zone_meta_key( $zone ) {
1254 $zone_id = $this->get_zone_id( $zone );
1255 return $this->zone_meta_prefix . $zone_id;
1256 }
1257
1258 function get_zone_slug( $zone ) {
1259 if( is_int( $zone ) )
1260 $zone = $this->get_zone( $zone );
1261
1262 if( is_object( $zone ) )
1263 $zone = $zone->slug;
1264
1265 return $this->get_formatted_zone_slug( $zone );
1266 }
1267
1268 function get_formatted_zone_slug( $slug ) {
1269 return $slug; // legacy function -- slugs can no longer be changed
1270 }
1271 function get_unformatted_zone_slug( $slug ) {
1272 return $slug; // legacy function -- slugs can no longer be changed
1273 }
1274
1275 function get_post_id( $post ) {
1276 if( is_int( $post ) )
1277 return $post;
1278 elseif( is_array( $post ) )
1279 return absint( $post['ID'] );
1280 elseif( is_object( $post ) )
1281 return $post->ID;
1282
1283 return false;
1284 }
1285
1286 function get_post_order( $post, $zone ) {
1287 $post_id = $this->get_post_id( $post );
1288 $meta_key = $this->get_zone_meta_key( $zone );
1289
1290 return get_metadata( 'post', $post_id, $meta_key, true );
1291 }
1292
1293 function verify_nonce( $action ) {
1294 $action = $this->_get_nonce_key( $action );
1295 $nonce = $this->_get_request_var( $action );
1296
1297 if( empty( $nonce ) )
1298 $nonce = $this->_get_request_var( '_wpnonce' );
1299
1300 if( ! wp_verify_nonce( $nonce, $action ) )
1301 $this->_unauthorized_access();
1302 }
1303
1304 function verify_access( $action = '', $zone_id = null ) {
1305 // TODO: should check if zone locked
1306
1307 $verify_function = '';
1308 switch( $action ) {
1309 case 'insert':
1310 $verify_function = '_current_user_can_add_zones';
1311 break;
1312 case 'update':
1313 case 'delete':
1314 $verify_function = '_current_user_can_edit_zones';
1315 break;
1316 default:
1317 $verify_function = '_current_user_can_manage_zones';
1318 break;
1319 }
1320
1321 if( ! call_user_func( array( $this, $verify_function ), $zone_id ) )
1322 $this->_unauthorized_access();
1323 }
1324
1325 function _unauthorized_access() {
1326 wp_die( __( 'Sorry, you\'re not supposed to do that...', 'zoninator' ) );
1327 }
1328
1329 function _fill_zone_details( $zone ) {
1330 if( ! empty( $zone->zoninator_parsed ) && $zone->zoninator_parsed )
1331 return $zone;
1332
1333 $details = array();
1334
1335 if( ! empty( $zone->description ) )
1336 $details = maybe_unserialize( $zone->description );
1337
1338 $details = wp_parse_args( $details, $this->zone_detail_defaults );
1339
1340 foreach( $details as $detail_key => $detail_value ) {
1341 $zone->$detail_key = $detail_value;
1342 }
1343
1344 $zone->zoninator_parsed = true;
1345
1346 return $zone;
1347 }
1348
1349 function do_zoninator_feeds() {
1350
1351 global $wp_query;
1352
1353 $query_var = get_query_var( $this->zone_taxonomy );
1354
1355 if ( ! empty( $query_var ) ) {
1356 $zone_slug = get_query_var( $this->zone_taxonomy );
1357 $zone_id = $this->get_zone( $zone_slug );
1358
1359 if ( empty( $zone_id ) ) {
1360 $this->send_user_error( __( 'Invalid zone supplied', 'zoninator' ) );
1361 }
1362
1363 $results = $this->get_zone_posts( $zone_id, apply_filters( 'zoninator_json_feed_fields', array(), $zone_slug ) );
1364
1365 if ( empty( $results ) ) {
1366 $this->send_user_error( __( 'No zone posts found', 'zoninator' ) );
1367 }
1368
1369 $filtered_results = $this->filter_zone_feed_fields( $results );
1370
1371 $this->json_return( apply_filters( 'zoninator_json_feed_results', $filtered_results, $zone_slug ), false );
1372 }
1373
1374 return;
1375
1376 }
1377
1378 private function filter_zone_feed_fields( $results ) {
1379
1380 $whitelisted_fields = array( 'ID', 'post_date', 'post_title', 'post_content', 'post_excerpt', 'post_status', 'guid' );
1381
1382 $i = 0;
1383 foreach ( $results as $result ) {
1384 foreach( $whitelisted_fields as $field ) {
1385 $filtered_results[$i]->$field = $result->$field;
1386 }
1387 $i++;
1388 }
1389
1390
1391 return $filtered_results;
1392 }
1393
1394 /**
1395 * Encode some data and echo it (possibly without cached headers)
1396 *
1397 * @param array $data
1398 */
1399 private function json_return( $data ) {
1400
1401 if ( $data == NULL )
1402 return false;
1403
1404 header( 'Content-Type: application/json' );
1405 echo json_encode( $data );
1406 exit();
1407 }
1408
1409 private static function send_user_error( $message ) {
1410 self::status_header_with_message( 406, $message );
1411 exit();
1412 }
1413
1414 /**
1415 * Modify the header and description in the global array
1416 *
1417 * @global array $wp_header_to_desc
1418 * @param int $status
1419 * @param string $message
1420 */
1421 private static function status_header_with_message( $status, $message ) {
1422 global $wp_header_to_desc;
1423
1424 $status = absint( $status );
1425 $official_message = isset( $wp_header_to_desc[$status] ) ? $wp_header_to_desc[$status] : '';
1426 $wp_header_to_desc[$status] = $message;
1427
1428 status_header( $status );
1429
1430 $wp_header_to_desc[$status] = $official_message;
1431 }
1432
1433
1434
1435
1436 // TODO: Caching needs to be testing properly before being implemented!
1437 function get_zone_cache_key( $zone, $args = array() ) {
1438 return '';
1439
1440 $meta_key = $this->get_zone_meta_key( $zone );
1441 $hash = md5( serialize( $args ) );
1442 return $meta_key . $hash;
1443 }
1444
1445 function get_zone_posts_from_cache( $zone, $args = array() ) {
1446 return false; // TODO: implement
1447
1448 $meta_key = $this->get_zone_meta_key( $zone );
1449 $cache_key = $this->get_zone_cache_key( $zone, $args );
1450 if( $posts = wp_cache_get( $cache_key, $meta_key ) )
1451 return $posts;
1452 return false;
1453 }
1454
1455 function add_zone_posts_to_cache( $posts, $zone, $args = array() ) {
1456 return; // TODO: implement
1457
1458 $meta_key = $this->get_zone_meta_key( $zone );
1459 $cache_key = $this->get_zone_cache_key( $zone, $args );
1460 wp_cache_set( $cache_key, $posts, $meta_key );
1461 }
1462
1463 // Handle 4.2 term-splitting
1464 function split_shared_term( $old_term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
1465 if ( $this->zone_taxonomy === $taxonomy ) {
1466 do_action( 'zoninator_split_shared_term', $old_term_id, $new_term_id, $term_taxonomy_id );
1467
1468 // Quick, easy switcheroo; add posts to new zone id and remove from the old one.
1469 $posts = $this->get_zone_posts( $old_term_id );
1470 if ( ! empty( $posts ) ) {
1471 $this->add_zone_posts( $new_term_id, $posts );
1472 $this->remove_zone_posts( $old_term_id );
1473 }
1474
1475 do_action( 'zoninator_did_split_shared_term', $old_term_id, $new_term_id, $term_taxonomy_id );
1476 }
1477 }
1478
1479 function _empty_zone_posts_cache( $meta_key ) {
1480 return; // TODO: implement
1481 }
1482
1483 function _get_message( $message_id, $encode = false ) {
1484 $message = '';
1485
1486 if( ! empty( $this->zone_messages[$message_id] ) )
1487 $message = $this->zone_messages[$message_id];
1488
1489 if( $encode )
1490 $message = urlencode( $message );
1491
1492 return $message;
1493 }
1494
1495 function _get_nonce_key( $action ) {
1496 return sprintf( '%s-%s', $this->zone_nonce_prefix, $action );
1497 }
1498
1499 function _current_user_can_add_zones() {
1500 return current_user_can( $this->_get_add_zones_cap() );
1501 }
1502
1503 function _current_user_can_edit_zones( $zone_id ) {
1504 $has_cap = current_user_can( $this->_get_edit_zones_cap() );
1505 return apply_filters( 'zoninator_current_user_can_edit_zone', $has_cap, $zone_id );
1506 }
1507
1508 function _current_user_can_manage_zones() {
1509 return current_user_can( $this->_get_manage_zones_cap() );
1510 }
1511
1512 function _get_add_zones_cap() {
1513 return apply_filters( 'zoninator_add_zone_cap', 'edit_others_posts' );
1514 }
1515
1516 function _get_edit_zones_cap() {
1517 return apply_filters( 'zoninator_edit_zone_cap', 'edit_others_posts' );
1518 }
1519
1520 function _get_manage_zones_cap() {
1521 return apply_filters( 'zoninator_manage_zone_cap', 'edit_others_posts' );
1522 }
1523
1524 function _get_zone_page_url( $args = array() ) {
1525 $url = menu_page_url( $this->key, false );
1526
1527 foreach( $args as $arg_key => $arg_value ) {
1528 $url = add_query_arg( $arg_key, $arg_value, $url );
1529 }
1530
1531 return $url;
1532 }
1533
1534 function _validate_date_filter( $date ) {
1535 return preg_match( '/([0-9]{4})-([0-9]{2})-([0-9]{2})/', $date );
1536 }
1537
1538 function _validate_category_filter( $cat ) {
1539 return $cat && get_term_by( 'id', $cat, 'category' );
1540 }
1541
1542 function _get_value_or_default( $var, $object, $default = '', $sanitize_callback = '' ) {
1543 if( is_object( $object ) )
1544 $value = ! empty( $object->$var ) ? $object->$var : $default;
1545 elseif( is_array( $object ) )
1546 $value = ! empty( $object[$var] ) ? $object[$var] : $default;
1547 else
1548 $value = $default;
1549
1550 if( is_callable( $sanitize_callback ) ) {
1551 if( is_array( $value ) )
1552 $value = array_map( $sanitize_callback, $value );
1553 else
1554 $value = call_user_func( $sanitize_callback, $value );
1555 }
1556
1557 return $value;
1558 }
1559
1560 function _get_request_var( $var, $default = '', $sanitize_callback = '' ) {
1561 return $this->_get_value_or_default( $var, $_REQUEST, $default, $sanitize_callback );
1562 }
1563
1564 function _get_get_var( $var, $default = '', $sanitize_callback = '' ) {
1565 return $this->_get_value_or_default( $var, $_GET, $default, $sanitize_callback );
1566 }
1567 function _get_post_var( $var, $default = '', $sanitize_callback = '' ) {
1568 return $this->_get_value_or_default( $var, $_POST, $default, $sanitize_callback );
1569 }
1570 }
1571
1572 global $zoninator;
1573 $zoninator = new Zoninator;
1574
1575 endif;
1576