@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
/**
|
||||
* Class which represnt event parent/child relationship.
|
||||
*
|
||||
* @author Time.ly Network, Inc.
|
||||
* @since 2.0
|
||||
* @package Ai1EC
|
||||
* @subpackage Ai1EC.Model
|
||||
*/
|
||||
class Ai1ec_Event_Parent extends Ai1ec_Base {
|
||||
|
||||
/**
|
||||
* event_parent method
|
||||
*
|
||||
* Get/set event parent
|
||||
*
|
||||
* @param int $event_id ID of checked event
|
||||
* @param int $parent_id ID of new parent [optional=NULL, acts as getter]
|
||||
* @param int $instance_id ID of old instance id
|
||||
*
|
||||
* @return int|bool Value depends on mode:
|
||||
* Getter: {@see self::get_parent_event()} for details
|
||||
* Setter: true on success.
|
||||
*/
|
||||
public function event_parent(
|
||||
$event_id,
|
||||
$parent_id = null,
|
||||
$instance_id = null
|
||||
) {
|
||||
$meta_key = '_ai1ec_event_parent';
|
||||
if ( null === $parent_id ) {
|
||||
return $this->get_parent_event( $event_id );
|
||||
}
|
||||
$meta_value = json_encode( array(
|
||||
'created' => $this->_registry->get( 'date.system' )->current_time(),
|
||||
'instance' => $instance_id,
|
||||
) );
|
||||
return add_post_meta( $event_id, $meta_key, $meta_value, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent ID for given event
|
||||
*
|
||||
* @param int $current_id Current event ID
|
||||
*
|
||||
* @return int|bool ID of parent event or bool(false)
|
||||
*/
|
||||
public function get_parent_event( $current_id ) {
|
||||
static $parents = null;
|
||||
if ( null === $parents ) {
|
||||
$parents = $this->_registry->get( 'cache.memory' );
|
||||
}
|
||||
$current_id = (int)$current_id;
|
||||
if ( null === ( $parent_id = $parents->get( $current_id ) ) ) {
|
||||
$db = $this->_registry->get( 'dbi.dbi' );
|
||||
/* @var $db Ai1ec_Dbi */
|
||||
$query = '
|
||||
SELECT parent.ID, parent.post_status
|
||||
FROM
|
||||
' . $db->get_table_name( 'posts' ) . ' AS child
|
||||
INNER JOIN ' . $db->get_table_name( 'posts' ) . ' AS parent
|
||||
ON ( parent.ID = child.post_parent )
|
||||
WHERE child.ID = ' . $current_id;
|
||||
$parent = $db->get_row( $query );
|
||||
if (
|
||||
empty( $parent ) ||
|
||||
'trash' === $parent->post_status
|
||||
) {
|
||||
$parent_id = false;
|
||||
} else {
|
||||
$parent_id = $parent->ID;
|
||||
}
|
||||
$parents->set( $current_id, $parent_id );
|
||||
unset( $query );
|
||||
}
|
||||
return $parent_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of modified (children) event objects
|
||||
*
|
||||
* @param int $parent_id ID of parent event
|
||||
* @param bool $include_trash Includes trashed when `true` [optional=false]
|
||||
*
|
||||
* @return array List (might be empty) of Ai1ec_Event objects
|
||||
*/
|
||||
public function get_child_event_objects(
|
||||
$parent_id,
|
||||
$include_trash = false
|
||||
) {
|
||||
$db = $this->_registry->get( 'dbi.dbi' );
|
||||
/* @var $db Ai1ec_Dbi */
|
||||
$parent_id = (int)$parent_id;
|
||||
$sql_query = 'SELECT ID FROM ' . $db->get_table_name( 'posts' ) .
|
||||
' WHERE post_parent = ' . $parent_id;
|
||||
$children = (array)$db->get_col( $sql_query );
|
||||
$objects = array();
|
||||
foreach ( $children as $child_id ) {
|
||||
try {
|
||||
$instance = $this->_registry->get( 'model.event', $child_id );
|
||||
if (
|
||||
$include_trash ||
|
||||
'trash' !== $instance->get( 'post' )->post_status
|
||||
) {
|
||||
$objects[$child_id] = $instance;
|
||||
}
|
||||
} catch ( Ai1ec_Event_Not_Found_Exception $exception ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* admin_init_post method
|
||||
*
|
||||
* Bind to admin_action_editpost action to override default save
|
||||
* method when user is editing single instance.
|
||||
* New post is created with some fields unset.
|
||||
*/
|
||||
public function admin_init_post( ) {
|
||||
if (
|
||||
isset( $_POST['ai1ec_instance_id'] ) &&
|
||||
isset( $_POST['action'] ) &&
|
||||
'editpost' === $_POST['action']
|
||||
) {
|
||||
$old_post_id = $_POST['post_ID'];
|
||||
$instance_id = $_POST['ai1ec_instance_id'];
|
||||
$post_id = $this->_registry->get( 'model.event.creating' )
|
||||
->create_duplicate_post();
|
||||
if ( false !== $post_id ) {
|
||||
$this->_handle_instances(
|
||||
$this->_registry->get( 'model.event', $post_id ),
|
||||
$this->_registry->get( 'model.event', $old_post_id ),
|
||||
$instance_id
|
||||
);
|
||||
$this->_registry->get( 'model.event.instance' )->clean(
|
||||
$old_post_id,
|
||||
$instance_id
|
||||
);
|
||||
$location = add_query_arg(
|
||||
'message',
|
||||
1,
|
||||
get_edit_post_link( $post_id, 'url' )
|
||||
);
|
||||
wp_redirect(
|
||||
apply_filters(
|
||||
'redirect_post_location',
|
||||
$location,
|
||||
$post_id
|
||||
)
|
||||
);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject base event edit link for modified instances
|
||||
*
|
||||
* Modified instances are events, belonging to some parent having recurrence
|
||||
* rule, and having some of it's properties altered.
|
||||
*
|
||||
* @param array $actions List of defined actions
|
||||
* @param stdClass $post Instance being rendered (WP_Post class instance in WP 3.5+)
|
||||
*
|
||||
* @return array Optionally modified $actions list
|
||||
*/
|
||||
public function post_row_actions( $actions, $post ) {
|
||||
if ( $this->_registry->get( 'acl.aco' )->is_our_post_type( $post ) ) {
|
||||
$parent_post_id = $this->event_parent( $post->ID );
|
||||
if (
|
||||
$parent_post_id &&
|
||||
NULL !== ( $parent_post = get_post( $parent_post_id ) ) &&
|
||||
isset( $parent_post->post_status ) &&
|
||||
'trash' !== $parent_post->post_status
|
||||
) {
|
||||
$parent_link = get_edit_post_link(
|
||||
$parent_post_id,
|
||||
'display'
|
||||
);
|
||||
$actions['ai1ec_parent'] = sprintf(
|
||||
'<a href="%s" title="%s">%s</a>',
|
||||
wp_nonce_url( $parent_link ),
|
||||
sprintf(
|
||||
__( 'Edit “%s”', AI1EC_PLUGIN_NAME ),
|
||||
apply_filters(
|
||||
'the_title',
|
||||
$parent_post->post_title,
|
||||
$parent_post->ID
|
||||
)
|
||||
),
|
||||
__( 'Base Event', AI1EC_PLUGIN_NAME )
|
||||
);
|
||||
}
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* add_exception_date method
|
||||
*
|
||||
* Add exception (date) to event.
|
||||
*
|
||||
* @param int $post_id Event edited post ID
|
||||
* @param mixed $date Parseable date representation to exclude
|
||||
*
|
||||
* @return bool Success
|
||||
*/
|
||||
public function add_exception_date( $post_id, Ai1ec_Date_Time $date ) {
|
||||
$event = $this->_registry->get( 'model.event', $post_id );
|
||||
$dates_list = explode( ',', $event->get( 'exception_dates' ) );
|
||||
if ( empty( $dates_list[0] ) ) {
|
||||
unset( $dates_list[0] );
|
||||
}
|
||||
$date->set_time( 0, 0, 0 );
|
||||
$dates_list[] = $date->format(
|
||||
'Ymd\THis\Z'
|
||||
);
|
||||
$event->set( 'exception_dates', implode( ',', $dates_list ) );
|
||||
return $event->save( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles instances saving and switching if needed. If original event
|
||||
* and created event have different start dates proceed in old style
|
||||
* otherwise find next instance, switch original start date to next
|
||||
* instance start date. If there are no next instances mark event as
|
||||
* non recurring. Filter also exception dates if are past.
|
||||
*
|
||||
* @param Ai1ec_Event $created_event Created event object.
|
||||
* @param Ai1ec_Event $original_event Original event object.
|
||||
*
|
||||
* @return void Method does not return.
|
||||
*/
|
||||
protected function _handle_instances(
|
||||
Ai1ec_Event $created_event,
|
||||
Ai1ec_Event $original_event,
|
||||
$instance_id
|
||||
) {
|
||||
$ce_start = $created_event->get( 'start' );
|
||||
$oe_start = $original_event->get( 'start' );
|
||||
if (
|
||||
$ce_start->format() !== $oe_start->format()
|
||||
) {
|
||||
$this->add_exception_date(
|
||||
$original_event->get( 'post_id' ),
|
||||
$ce_start
|
||||
);
|
||||
return;
|
||||
}
|
||||
$next_instance = $this->_find_next_instance(
|
||||
$original_event->get( 'post_id' ),
|
||||
$instance_id
|
||||
);
|
||||
if ( ! $next_instance ) {
|
||||
$original_event->set( 'recurrence_rules', null );
|
||||
$original_event->save( true );
|
||||
return;
|
||||
}
|
||||
$original_event->set(
|
||||
'start',
|
||||
$this->_registry->get( 'date.time', $next_instance->get( 'start' ) )
|
||||
);
|
||||
$original_event->set(
|
||||
'end',
|
||||
$this->_registry->get( 'date.time', $next_instance->get( 'end' ) )
|
||||
);
|
||||
$edates = $this->_filter_exception_dates( $original_event );
|
||||
$original_event->set( 'exception_dates', implode( ',', $edates ) );
|
||||
$recurrence_rules = $original_event->get( 'recurrence_rules' );
|
||||
$rules_info = $this->_registry->get( 'recurrence.rule' )
|
||||
->build_recurrence_rules_array( $recurrence_rules );
|
||||
if ( isset( $rules_info['COUNT'] ) ) {
|
||||
$next_instances_count = $this->_count_next_instances(
|
||||
$original_event->get( 'post_id' ),
|
||||
$instance_id
|
||||
);
|
||||
$rules_info['COUNT'] = (int)$next_instances_count + count( $edates );
|
||||
$rules = '';
|
||||
if ( $rules_info['COUNT'] <= 1 ) {
|
||||
$rules_info = array();
|
||||
}
|
||||
foreach ( $rules_info as $key => $value ) {
|
||||
$rules .= $key . '=' . $value . ';';
|
||||
}
|
||||
$original_event->set(
|
||||
'recurrence_rules',
|
||||
$rules
|
||||
);
|
||||
}
|
||||
$original_event->save( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns next instance.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param int $instance_id Instance ID.
|
||||
*
|
||||
* @return null|Ai1ec_Event Result.
|
||||
*/
|
||||
protected function _find_next_instance( $post_id, $instance_id ) {
|
||||
$dbi = $this->_registry->get( 'dbi.dbi' );
|
||||
$table_instances = $dbi->get_table_name( 'ai1ec_event_instances' );
|
||||
$table_posts = $dbi->get_table_name( 'posts' );
|
||||
$query = $dbi->prepare(
|
||||
'SELECT i.id FROM ' . $table_instances . ' i JOIN ' .
|
||||
$table_posts . ' p ON (p.ID = i.post_id) ' .
|
||||
'WHERE i.post_id = %d AND i.id > %d ' .
|
||||
'AND p.post_status = \'publish\' ' .
|
||||
'ORDER BY id ASC LIMIT 1',
|
||||
$post_id,
|
||||
$instance_id
|
||||
);
|
||||
$next_instance_id = $dbi->get_var( $query );
|
||||
if ( ! $next_instance_id ) {
|
||||
return null;
|
||||
}
|
||||
return $this->_registry->get( 'model.search' )
|
||||
->get_event( $post_id, $next_instance_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts future instances.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param int $instance_id Instance ID.
|
||||
*
|
||||
* @return int Result.
|
||||
*/
|
||||
protected function _count_next_instances( $post_id, $instance_id ) {
|
||||
$dbi = $this->_registry->get( 'dbi.dbi' );
|
||||
$table_instances = $dbi->get_table_name( 'ai1ec_event_instances' );
|
||||
$table_posts = $dbi->get_table_name( 'posts' );
|
||||
$query = $dbi->prepare(
|
||||
'SELECT COUNT(i.id) FROM ' . $table_instances . ' i JOIN ' .
|
||||
$table_posts . ' p ON (p.ID = i.post_id) ' .
|
||||
'WHERE i.post_id = %d AND i.id > %d ' .
|
||||
'AND p.post_status = \'publish\'',
|
||||
$post_id,
|
||||
$instance_id
|
||||
);
|
||||
return (int)$dbi->get_var( $query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters past or out of range exception dates.
|
||||
*
|
||||
* @param Ai1ec_Event $event Event.
|
||||
*
|
||||
* @return array Filtered exception dates.
|
||||
*/
|
||||
protected function _filter_exception_dates( Ai1ec_Event $event ) {
|
||||
$start = (int)$event->get( 'start' )->format();
|
||||
$exception_dates = explode( ',', $event->get( 'exception_dates' ) );
|
||||
$dates = array();
|
||||
foreach ( $exception_dates as $date ) {
|
||||
$ex_date = (int)$this->_registry->get( 'date.time', $date )->format();
|
||||
if ( $ex_date > $start ) {
|
||||
$dates[] = $date;
|
||||
}
|
||||
}
|
||||
return $dates;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user