connection->is_connected() ) {
$this->render_first_run();
return;
}
$sync = new Sync( $this->connection );
// Counted once and passed down: two cards read the same figures, and a
// card that disagreed with the one beside it would be worse than either.
$stats = $sync->stats();
echo '
';
$this->render_workspace_card( $stats );
$this->render_entries_card( $stats );
$this->render_platform_card();
$this->render_access_card();
echo '
';
// Between the cards and the panels, which is where the Drupal module
// puts them (StatusController::page() builds the cards, then #actions,
// then the activity and index panels). They are the same three buttons
// in the same order, so they should be in the same place.
$this->render_actions( $sync );
$this->render_sync_card( $sync );
$this->render_searches_card();
}
/**
* The invitation, when there is nothing to report yet.
*
* A port of the Drupal module's first-run page: what this does, in two
* features, then the three steps of connecting, then one button. The button
* is a form because starting the flow is a POST behind a nonce.
*/
private function render_first_run(): void {
?>
render_locked_notice(); ?>
render_service_notice(); ?>
' . esc_html( wp_parse_url( $this->connection->api_base(), PHP_URL_HOST ) ?? 'api.sharely.ai' ) . '',
'' . esc_html__( 'Terms of Service', 'sharelyai' ) . '',
'' . esc_html__( 'Privacy Policy', 'sharelyai' ) . ''
);
?>
connection->is_locked( 'workspace_id' ) && ! $this->connection->is_locked( 'api_key' ) ) {
return;
}
?>
connection->get( 'workspace_name' );
$id = $this->connection->workspace_id();
$last4 = (string) $this->connection->get( 'key_last4' );
$since = $this->connection->connected_at();
$app = $this->connection->app_base();
$rbac = (bool) $this->connection->get( 'rbac_enabled' );
$locked = $this->connection->is_locked( 'api_key' ) || $this->connection->is_locked( 'workspace_id' );
?>
0 ) {
return sprintf(
'%s',
esc_html(
sprintf(
/* translators: %s: number of entries. */
_n( '%s failed', '%s failed', $stats['failed'], 'sharelyai' ),
number_format_i18n( $stats['failed'] )
)
)
);
}
if ( $stats['queued'] > 0 ) {
return '' . esc_html__( 'syncing', 'sharelyai' ) . '';
}
if ( $stats['in_scope'] > 0 && $stats['synced'] >= $stats['in_scope'] ) {
return '' . esc_html__( 'up to date', 'sharelyai' ) . '';
}
return '';
}
/**
* How much of what this site would send has been sent.
*
* @param array{in_scope:int, synced:int, queued:int, failed:int} $stats Sync counters.
*/
private function render_entries_card( array $stats ): void {
$inventory = new Inventory( $this->connection );
$types = $inventory->by_post_type();
$percent = $stats['in_scope'] > 0
? min( 100, (int) round( ( $stats['synced'] / $stats['in_scope'] ) * 100 ) )
: 0;
?>
connection ) )->stats();
if ( ! $stats['ok'] || ! is_array( $stats['data'] ) ) {
// Deliberately quiet: a reachability problem belongs to whatever the
// administrator was actually trying to do, not to a status card.
return;
}
$data = $stats['data'];
$by_status = is_array( $data['byStatus'] ?? null ) ? $data['byStatus'] : array();
$total = (int) ( $data['total'] ?? 0 );
$done = (int) ( $by_status['COMPLETED'] ?? 0 );
$working = (int) ( $by_status['BACKGROUND_START'] ?? 0 );
$failed = (int) ( $by_status['BACKGROUND_ERROR'] ?? 0 );
$untagged = (int) ( $data['withoutRoles'] ?? 0 );
?>
get_names() ) + 1;
?>
/
connection ) )->private_included()
? __( 'Published entries are tagged for anonymous visitors; private ones for the roles that may read them. A visitor’s search runs as the roles they hold.', 'sharelyai' )
: __( 'Published entries are tagged for anonymous visitors. Private entries are not sent — turn that on under Settings if members-only content should be searchable.', 'sharelyai' )
);
?>
0 ) : ?>
run();
$running = $sync->is_running();
$total = (int) ( $run['total'] ?? 0 );
$done = (int) ( $run['done'] ?? 0 );
$percent = $total > 0 ? min( 100, (int) round( ( $done / $total ) * 100 ) ) : 0;
?>
0 && ! empty( $run['finished'] ) ) : ?>
render_failures( $sync ); ?>
render_log( $sync ); ?>
connection->is_locked( 'api_key' ) || $this->connection->is_locked( 'workspace_id' );
?>
render_locked_notice(); ?>
failures();
if ( array() === $failures ) {
return;
}
?>
log();
if ( array() === $log ) {
return;
}
?>
__( 'Sent', 'sharelyai' ),
'updated' => __( 'Updated', 'sharelyai' ),
'unchanged' => __( 'No change', 'sharelyai' ),
'removed' => __( 'Removed', 'sharelyai' ),
'skipped' => __( 'Not in scope', 'sharelyai' ),
'failed' => __( 'Failed', 'sharelyai' ),
);
return $labels[ $outcome ] ?? $outcome;
}
/**
* What visitors searched for.
*/
private function render_searches_card(): void {
$stats = new Stats( $this->connection );
if ( ! $stats->enabled() ) {
return;
}
$days = max( 1, (int) $this->connection->get( 'stats_retention_days', 30 ) );
$summary = $stats->summary( $days );
?>
' . esc_html( number_format_i18n( $summary['searches'] ) ) . '',
'' . esc_html( number_format_i18n( $summary['terms'] ) ) . '',
'' . esc_html( number_format_i18n( $summary['empty'] ) ) . '',
esc_html( number_format_i18n( $percent ) )
);
?>
render_query_table( __( 'Most searched', 'sharelyai' ), $stats->top( $days, 10 ), true ); ?>
render_query_table( __( 'Found nothing', 'sharelyai' ), $stats->top( $days, 10, true ), false ); ?>
$rows Rows.
* @param bool $average Show the average result count.
*/
private function render_query_table( string $caption, array $rows, bool $average ): void {
if ( array() === $rows ) {
return;
}
?>