public function admin_batch_page()
    {
        // boost our upper limit
        ini_set('memory_limit', '512M');
        # batch start date
        if (!empty($_REQUEST['start_date'])) {
            $batch_args['start_date'] = date('Y-m-d', strtotime($_REQUEST['start_date']));
        } elseif (empty($_REQUEST['batch'])) {
            $batch_args['start_date'] = $this->get_last_batch_date();
        }
        echo $this->admin_wrapper_open((!empty($_GET['batch']) ? 'Edit' : 'New') . ' Batch');
        if (!$this->sufficiently_configured()) {
            $this->show_admin_messages();
            echo $this->admin_wrapper_close();
            return;
        }
        # get batch
        if (empty($this->batch)) {
            # batch id
            if (!empty($_REQUEST['batch'])) {
                $batch_args['ID'] = intval($_REQUEST['batch']);
            }
            // pull in duplicate data
            if (!empty($_GET['from_batch']) && is_numeric($_GET['from_batch'])) {
                try {
                    $from_batch = new cfd_batch(array('ID' => intval($_GET['from_batch'])));
                    $batch_args['data'] = $from_batch->get_data();
                } catch (Exception $e) {
                    // boo!
                }
            }
            try {
                $this->batch = new cfd_batch($batch_args);
            } catch (Exception $e) {
                echo '<div class="error batch-error"><p><b>' . __('Cannot edit batch', 'cf-deploy') . ':</b> ' . $e->getMessage() . '</p></div>';
                $this->admin_wrapper_close();
                return;
            }
        }
        # do comparison
        $this->batch->init_comparison_data();
        $this->do_server_comparison($this->batch);
        $this->show_admin_messages();
        echo '
			<form class="cf-form" method="post" name="batch-form">
				<input type="hidden" name="batch_ID" value="' . intval($this->batch->ID) . '" />
				' . wp_nonce_field($this->deploy_batch_action_base, '_wpnonce', true, false);
        # batch details
        echo '
			<div class="form-section">
				<fieldset class="cf-lbl-pos-left" id="batch-details">
					<div class="cf-elm-block cf-elm-width-full">
						<label for="batch-title">' . __('Name', 'cf-deploy') . '</label>
						<input type="text" name="batch_title" id="batch-title" class="cf-elm-text" value="' . htmlspecialchars($this->batch->title) . '" />';
        if ($this->batch->date) {
            echo '
						<span class="cf-elm-help cf-elm-align-bottom">' . __(sprintf('Created on %s by %s', $this->batch->date, $this->batch->author->user_nicename), 'cf-deploy') . '</span>';
        }
        echo '
					</div>
					<div class="cf-elm-block cf-elm-width-full">
						<label for="batch-description">' . __('Description', 'cf-deploy') . '</label>
						<textarea name="batch_description" class="cf-elm-textarea" id="batch-description">' . htmlspecialchars($this->batch->description) . '</textarea>
					</div>
					<div class="cf-elm-block">
						<label for="batch-start-date">' . __('Start Date', 'cf-deploy') . ':</label>
						<input type="text" name="batch_items[start_date]" id="batch-start-date" class="cf-elm-text" value="' . date('Y-m-d', strtotime($this->batch->start_date)) . '" />
						<input type="button" name="batch_refresh_date" id="batch-refresh-button" class="button-secondary" value="' . __('Refresh', 'cf-deploy') . '" />
						<span class="cf-elm-help cf-elm-align-bottom">' . __('YYYY-MM-DD format, default start date is set to the last completed batch', 'cf-deploy') . '</span>
					</div>
					<p class="submit">
						<input type="submit" name="cf_deploy_save_batch" id="batch-save-button" class="button-primary" value="' . __('Save Batch', 'cf-deploy') . '" /> 
					</p>
				</fieldset>
			</div><!-- .form-section -->
			<div  id="batch-contents">';
        # batch contents: post-types
        $post_types = $this->batch->get_comparison_data('post_types');
        if ($post_types && count($post_types)) {
            foreach ($post_types as $type => $objects) {
                $p_type = get_post_type_object($type);
                if (!empty($objects['__error__'])) {
                    $error = $objects['__error__'];
                    unset($objects['__error__']);
                }
                echo '
						<fieldset class="cf-lbl-pos-left">
							<legend>' . $p_type->labels->name . '</legend>';
                if (!empty($error)) {
                    echo $this->comparison_error($error);
                    unset($error);
                }
                if (count($objects)) {
                    echo $this->batch_items_table_header($type);
                    foreach ($objects as $object) {
                        echo '
								<tr>
									<td class="has-cb">';
                        if (empty($object->errors)) {
                            echo '<input class="item-select" type="checkbox" name="batch_items[post_types][' . esc_attr($type) . '][]" id="' . esc_attr($type) . '-' . $object->id() . '" ' . 'value="' . intval($object->id()) . '" ' . (!empty($object->selected) && $object->selected ? ' checked="checked"' : '') . '/>';
                        }
                        echo '
									</td> 
									<td><b><a href="' . $object->edit_url() . '">' . $object->name() . '</a></b>';
                        if ($object->post_parent !== 0) {
                            echo '<br />Child of: <a href="' . get_edit_post_link($object->post_parent) . '">' . get_the_title($object->post_parent) . '</a>';
                        }
                        if (!empty($object->modified)) {
                            echo '<br /><span class="item-status-text">' . __('Change' . (count($object->modified) > 1 ? 's' : '') . ' detected', 'cf-deploy') . ': ' . implode(', ', $object->modified) . '</span>';
                        }
                        if (!empty($object->errors)) {
                            echo '<div class="cfd-error message">' . sprintf(__('<b>The following errors were encountered with this item and the item cannot be transferred:</b> %s', 'cf-deploy'), implode(', ', $object->errors)) . '</div>';
                        }
                        echo '</td>
									<td>' . __($object->post_date == $object->post_modified ? 'Added' : 'Modified', 'cf-deploy') . ': <span class="item-date">' . date('Y-m-d', strtotime($object->post_modified)) . '</span>' . '<br /><span class="item-time">' . date('g:i a', strtotime($object->post_modified)) . '</span>
									<td>';
                        if (!empty($object->status) && !empty($object->status['remote_status'])) {
                            echo __($object->status['remote_status']['post_date'] == $object->status['remote_status']['post_modified'] ? 'Added' : 'Modified', 'cf-deploy') . ': ' . '<span class="item-date">' . date('Y-m-d', strtotime($object->status['remote_status']['post_modified'])) . '</span><br />' . '<span class="item-time">' . date('g:i a', strtotime($object->status['remote_status']['post_modified'])) . '</span>';
                            if ($object->status['remote_status']['profile']['post']['post_status'] == 'trash') {
                                echo '<br /><span class="item-trash">' . __('Post Status: trash', 'cf-deploy') . '</span>';
                            }
                        } else {
                            echo '--';
                        }
                        echo '</td>
								</tr>';
                    }
                    echo $this->batch_items_table_footer();
                } else {
                    echo '
							<div class="message cf-mar-top-none"><p>' . __(sprintf('No new or modified %s found.', $p_type->labels->name), 'cf-deploy') . '</p></div>';
                }
                echo '
						</fieldset><!-- /' . $type . ' -->';
            }
        }
        # batch contents: menus
        $menus = $this->batch->get_comparison_data('menus');
        echo '
					<fieldset class="cf-lbl-pos-left">
						<legend>' . __('Menus', 'cf-deploy') . '</legend>';
        if (!empty($menus)) {
            echo $this->batch_items_table_header('menus');
            foreach ($menus as $menu) {
                echo '
							<tr>
								<td class="has-cb"><input class="item-selected" type="checkbox" name="batch_items[menus][]" id="menus-' . $menu->id() . '" value="' . $menu->id() . '"' . (!empty($menu->selected) && $menu->selected == true ? ' checked="checked"' : '') . ' /></td>
								<td><b><a href="' . $menu->edit_url() . '">' . $menu->name() . '</a></b></td>
								<td>' . __('Modified', 'cf-deploy') . ': <span class="item-date">' . mysql2date('Y-m-d', $menu->last_modified()) . '</span><br />' . '<span class="item-time">' . mysql2date('g:i a', $menu->last_modified()) . '</td>
								<td>';
                if (!empty($menu->status['remote_status'])) {
                    echo __('Modified', 'cf-deploy') . ': <span class="item-date">' . mysql2date('Y-m-d', $menu->status['remote_status']['last_modified']) . '</span><br />' . '<span class="item-time">' . mysql2date('g:i a', $menu->status['remote_status']['last_modified']);
                } else {
                    echo '--';
                }
                echo '
								</td>
							</tr>
					';
            }
            echo $this->batch_items_table_footer();
        } else {
            echo '
						<div class="message cf-mar-top-none"><p>' . __('No new or modified Menus found.', 'cf-deploy') . '</p></div>';
        }
        echo '
					</fieldset><!-- menus -->';
        # batch contents: users
        echo '
					<fieldset class="cf-lbl-pos-left">
						<legend>' . __('Users', 'cf-deploy') . '</legend>';
        $users = $this->batch->get_comparison_data('users');
        if ($users && count($users)) {
            echo $this->batch_items_table_header('users');
            foreach ($users as $user) {
                echo '
							<tr>
								<td class="has-cb"><input class="item-select" type="checkbox" name="batch_items[users][]" id="user-' . $user->ID . '" value="' . $user->ID . '" ' . (!empty($user->selected) && $user->selected == true ? ' checked="checked"' : '') . '/></td>
								<td><b><a href="' . $this->user_edit_url($user->ID) . '">' . $user->user_login . '</a></b><br />' . implode(', ', $user->roles) . '</td>
								<td>' . __('Added') . ': <span class="item-date">' . mysql2date('Y-m-d', $user->user_registered) . '</span><br />
									<span class="item-time">' . mysql2date('g:i a', $user->user_registered) . '</span></td>
								<td>	';
                if (!empty($user->modified) && $user->modified == 'profile') {
                    echo __('Local &amp; Remote Differ', 'cf-deploy');
                } elseif (!empty($user->modified) && $user->modified == 'new') {
                    echo __('New User', 'cf-deploy');
                } else {
                    echo '--';
                }
                echo '</td>
							</tr>';
            }
            echo $this->batch_items_table_footer();
        } else {
            echo '
						<div class="message cf-mar-top-none"><p>' . __('No new or modified Users found.', 'cf-deploy') . '</p></div>';
        }
        echo '
					</fieldset><!-- /users -->';
        # That ends the REAL date based stuff, the rest is just wether it exists or not
        # batch contents: taxonomies
        $taxonomies = $this->batch->get_comparison_data('taxonomies');
        if (!empty($taxonomies)) {
            foreach ($taxonomies as $type => $objects) {
                $tax = get_taxonomy($type);
                echo '
						<fieldset class="cf-lbl-pos-left">
							<legend>' . $tax->labels->name . '</legend>';
                if (count($objects)) {
                    echo $this->batch_items_table_header($type, 'short');
                    foreach ($objects as $term) {
                        $term_edit_link = 'edit-tags.php?action=edit&amp;taxonomy=' . $type . '&amp;post_type=' . $term->post_type . '&amp;tag_ID=' . $term->term_id;
                        echo '
									<tr>
										<td class="has-cb"><input class="item-select" type="checkbox" name="batch_items[taxonomies][' . esc_attr($type) . '][]" id="' . esc_attr($type) . '-' . $term->term_id . '" value="' . $term->term_id . '" ' . (!empty($term->selected) && $term->selected == true ? ' checked="checked"' : '') . '/></td>
										<td><b><a href="' . $term_edit_link . '">' . $term->name . '</a></b><br /><span class="item-status-text">Post Count: ' . $term->count . '</span>';
                        if ($term->parent > 0) {
                            $parent = get_term($term->parent, $term->taxonomy);
                            $parent_edit_link = 'edit-tags.php?action=edit&amp;taxonomy=' . $parent->taxonomy . '&amp;tag_ID=' . $parent->term_id;
                            echo '<br /><span class="item-status-text">Child of: <a href="' . $parent_edit_link . '">' . $parent->name . '</a></span>';
                        }
                        echo '</td>
										<td><span class="item-status-text">' . ($term->modified == 'new' ? __('New', 'cf-deploy') : __('Local &amp; Remote Differ', 'cf-deploy')) . '</span></td>
									</tr>';
                    }
                    echo $this->batch_items_table_footer();
                } else {
                    echo '
							<div class="message cf-mar-top-none"><p>' . __(sprintf('No new or modified %s found.', $tax->labels->name), 'cf-deploy') . '</p></div>';
                }
                echo '
						</fieldset><!-- /' . $type . ' -->';
            }
        }
        # batch contents: bookmarks
        $bookmarks = $this->batch->get_comparison_data('bookmarks');
        echo '
					<fieldset class="cf-lbl-pos-left">
						<legend>' . __('Links', 'cf-deploy') . '</legend>';
        if (!empty($bookmarks)) {
            echo $this->batch_items_table_header('bookmarks', 'short');
            foreach ($bookmarks as $bookmark) {
                $bmark_local_status = $bmark_remote_status = '--';
                echo '
							<tr>
								<td class="has-cb"><input class="item-selected" type="checkbox" name="batch_items[bookmarks][]" id="bookmarks-' . $bookmark->link_id . '" value="' . $bookmark->link_id . '" ' . (!empty($bookmark->selected) && $bookmark->selected == true ? ' checked="checked"' : '') . '/></td>
								<td><b><a href="' . get_edit_bookmark_link($bookmark->link_id) . '">' . esc_html($bookmark->link_name) . '</a></b><br /><span class="item-status-text">' . esc_html($bookmark->link_url) . '</span></td>
								<td><span class="item-status-text">';
                switch (true) {
                    case empty($bookmark->status['remote_status']):
                        echo __('New', 'cf-deploy');
                        break;
                    default:
                        echo __('Local &amp; Remote differ', 'cf-deploy');
                        break;
                }
                echo '</span></td>
							</tr>';
            }
            echo $this->batch_items_table_footer();
        } else {
            echo '
						<div class="message cf-mar-top-none"><p>' . __('No new or modified Links found.', 'cf-deploy') . '</p></div>';
        }
        echo '
					</fieldset><!-- bookmarks -->';
        # Informational only items
        # plugins
        $plugins = $this->get_plugin_data();
        echo '
				<fieldset class="cf-lbl-pos-left">
					<legend>' . __('Plugins', 'cf-deploy') . '</legend>';
        if (!empty($plugins)) {
            ob_start();
            foreach ($plugins as $plugin) {
                echo '
					<tr>
						<td><b>' . $plugin['Name'] . '</b> by ' . $plugin['Author'] . '<br /><span class="item-status-text">' . $plugin['Description'] . '</span></td>
						<td>' . __('Active', 'cf-deploy') . '<br /><span class="item-status-text">' . __('Version', 'cf-deploy') . ': ' . $plugin['Version'] . '</span></td>
						<td>';
                if (!empty($plugin['remote_status'])) {
                    echo 'Active<br /><span class="item-status-text">Version: ' . $plugin['remote_status']['Version'] . '</span>';
                } else {
                    echo '<div class="notice message"><p>' . __('Not Active', 'cf-deploy') . '</span></div>';
                }
                echo '</td>
					</td>
					';
            }
            $plugins_rows = ob_get_clean();
            if (!empty($plugins_rows)) {
                echo $this->batch_items_table_header('plugins') . $plugins_rows . $this->batch_items_table_footer();
            } else {
                echo '
						<div class="message cf-mar-top-none"><p>' . __('No Plugin differences found.', 'cf-deploy') . '</p></div>';
            }
        } else {
            echo '
					<div class="message cf-mar-top-none"><p>' . __('No Plugin differences found.', 'cf-deploy') . '</p></div>';
        }
        echo '
				</fieldset><!-- /plugins -->';
        $preflight_disabled = '';
        if (!empty($this->admin_messages['__error__'])) {
            $preflight_disabled = ' disabled="disabled" ';
        }
        # close it out
        echo '
				</div><!-- #batch-contents -->
				<div class="cf-footer">
					<p class="submit">
						<input type="hidden" name="cf_deploy_action" value="save-batch" />
						<input type="submit" name="cf_deploy_save_batch" id="batch-save-button" class="button-primary" value="' . __('Save Batch', 'cf-deploy') . '" />&nbsp;
						<input type="submit" name="cf_deploy_preflight_batch" id="batch-preflight-button" class="button-secondary" value="' . __('Pre-flight Check', 'cf-deploy') . '" ' . $preflight_disabled . '/>
						<input type="submit" name="cf_deploy_delete_batch" id="batch-delete-button" class="cf-btn-delete batch-delete" value="' . __('Delete Batch', 'cf-deploy') . '" />
					</p>
				</div>
			</form>
			';
        echo $this->admin_wrapper_close();
    }
 /**
  * Gateway function for Testing Comms
  *
  * @param array $args 
  * @return object cfd_message
  */
 public function ajax_test_comms($args)
 {
     $server = $args['cfd_settings']['remote_server'][0]['address'];
     $auth_key = $args['cfd_settings']['remote_server'][0]['key'];
     $method = 'ajax_test_' . $args['test_action'];
     if ($method == 'ajax_test_say_hello' && method_exists($this, $method)) {
         // simple "hello world" comms test
         $ret = $this->{$method}($server, $auth_key, $args);
         if (empty($ret) || !$ret instanceof cfd_message) {
             $ret = new cfd_message(array('success' => false, 'type' => 'unknown-return-value', 'message' => '<div class="error"><p>Unexpected return value from "' . esc_html($args['test_action']) . '"</p></div>'));
         } else {
             return $ret;
         }
     } elseif (method_exists($this, $method)) {
         // batch data testing
         $batch_data = $this->{$method}($args, array());
         if ($batch_data instanceof cfd_message) {
             return $batch_data;
         }
         $batch = new cfd_batch(array('ID' => 0, 'data' => $batch_data));
         $params = array('server' => $server, 'auth_key' => $auth_key, 'method' => 'import_batch', 'args' => array('batch' => $batch->get_deploy_data()));
         $ret = $this->send($params);
         if (is_array($ret->message)) {
             $post_type_messages = $ret->message;
             $ret->message = '';
             foreach ($post_type_messages as $post_type => $messages) {
                 $ret->message .= $this->parse_message_response($messages, $post_type);
             }
         }
         if ($ret->success) {
             $ret->message = '<div class="success message"><h3>Import Successful</h3></div>' . $ret->message;
         } else {
             $ret->message = '<div class="error message"><h3>Import Failed</h3></div>' . $ret->message;
         }
         if (empty($ret) || !$ret instanceof cfd_message) {
             $ret = new cfd_message(array('success' => false, 'type' => 'unknown-return-value', 'message' => '<div class="error message"><p>Unexpected return value from "' . esc_html($args['test_action']) . '"</p></div>'));
         }
     } else {
         $ret = new cfd_message(array('success' => false, 'type' => 'bad-method', 'message' => '<div class="error message"><p>Method "' . esc_html($args['test_action']) . '" does not exist</p></div>'));
     }
     return $ret;
 }