repository = $repository ?? new CalendarConnectionsRepository();
}
public function register(): void {
add_action( 'admin_menu', array( $this, 'menu' ) );
add_action( 'abientot_staff_extension_panels', array( $this, 'panel' ) );
add_action( 'admin_post_abientot_calendar_disconnect_local', array( $this, 'handle' ) );
}
public function menu(): void {
if ( get_option( 'abientot_calendar_inbound_schema_version', '' ) !== '' ) {
add_submenu_page( '', __( 'Retained external calendar protection', 'abientot' ), __( 'Retained external calendar protection', 'abientot' ), 'abientot_manage_settings', 'abientot-retained-calendars', array( $this, 'page' ) );
}
}
public function page(): void {
if ( ! current_user_can( 'abientot_manage_settings' ) ) {
wp_die( esc_html__( 'You do not have permission to manage settings.', 'abientot' ) );
}
echo '
' . esc_html__( 'Retained external calendar protection', 'abientot' ) . '
';
try {
$page = self::integer( \Abientot\Support\Request::view( 'calendar_staff_page' ) ?? 1 );
$ids = $this->repository->staff_ids( $page );
$staff_id = \Abientot\Support\Request::view( 'staff_id' );
$selected = null !== $staff_id ? self::integer( $staff_id ) : ( $ids[0] ?? 0 );
echo '
';
if ( $selected ) {
$this->panel( array( 'id' => $selected ) );
}
foreach ( array(
-1 => __( 'Previous', 'abientot' ),
1 => __( 'Next', 'abientot' )
) as $delta => $label ) {
if ( $page + $delta > 0 && ( $delta < 0 ? $page > 1 : count( $ids ) > 25 ) ) {
echo '
' . esc_html( $label ) . ' ';
}
}
} catch ( \Throwable $e ) {
echo '
' . esc_html__( 'Retained calendar data is unavailable. Existing protection remains blocked until its storage is repaired or an authorized disconnect succeeds.', 'abientot' ) . '
';
}
echo '
';
}
private static function integer( $value ): int {
if ( ( ! is_int( $value ) && ! is_string( $value ) ) || ! preg_match( '/^[1-9][0-9]*$/D', (string) $value ) || false === ( $number = filter_var(
$value,
FILTER_VALIDATE_INT,
array(
'options' => array(
'min_range' => 1,
'max_range' => PHP_INT_MAX - 1
)
)
) ) ) {
throw new \DomainException( 'calendar_retained_input' );
}
return $number;
}
/**
* Disconnects one retained calendar connection. The caller has verified the nonce and the
* POST method; $input holds sanitized fields only.
*/
public function request( array $input ): array {
try {
if ( ! current_user_can( 'abientot_manage_settings' ) ) {
throw new \DomainException( 'calendar_retained_permission' );
}
if ( ( $input['risk_accepted'] ?? '' ) !== '1' ) {
throw new \DomainException( 'calendar_retained_permission' );
}
return $this->repository->disconnect( self::integer( $input['connection_id'] ?? null ), self::integer( $input['revision'] ?? null ), true );
} catch ( \Throwable $e ) {
return array( 'error' => 'calendar_retained_unavailable' );
}
}
public function handle(): void {
check_admin_referer( 'abientot_calendar_disconnect_local' );
if ( ! current_user_can( 'abientot_manage_settings' ) ) {
wp_die( esc_html__( 'You do not have permission to manage settings.', 'abientot' ), '', array( 'response' => 403 ) );
}
nocache_headers();
$result = \Abientot\Support\Request::is_post() ? $this->request( \Abientot\Support\Request::post_fields( 'abientot_calendar_disconnect_local', array( 'connection_id', 'revision', 'risk_accepted' ) ) ) : array( 'error' => 'calendar_retained_unavailable' );
if ( isset( $result['error'] ) ) {
wp_die(
esc_html__( 'Retained calendar protection could not be disconnected. Check permission, confirmation and current connection revision.', 'abientot' ),
'',
array(
'response' => 400,
'back_link' => true
)
);
}
wp_safe_redirect( admin_url( 'admin.php?page=abientot-staff&calendar_disconnected=1' ) );
exit;
}
public function panel( array $staff ): void {
if ( ! current_user_can( 'abientot_manage_settings' ) ) {
return;
}
try {
$staffId = self::integer( $staff['id'] ?? null );
$page = self::integer( \Abientot\Support\Request::view( 'calendar_connection_page' ) ?? 1 );
if ( ! $this->repository->present() ) {
return;
}
$rows = $this->repository->listing( $staffId, $page );
if ( ! $rows ) {
return;
}
if ( \Abientot\Support\Request::page() !== 'abientot-retained-calendars' ) {
echo '' . esc_html__( 'View all retained calendar connections', 'abientot' ) . '
';
}
$more = count( $rows ) > 25;
$rows = array_slice( $rows, 0, 25 );
echo '' . esc_html__( 'Retained external calendar protection', 'abientot' ) . '
' . esc_html__( 'Existing calendar protection remains active after changing edition. Pending, incomplete or older-than-15-minute coverage blocks new time changes. This local control cannot connect accounts or refresh provider data.', 'abientot' ) . '
';
if ( ! $rows ) {
echo '' . esc_html__( 'No retained calendar connections for this staff profile.', 'abientot' ) . '
';
}
foreach ( $rows as $row ) {
$enabled = (int) $row['enabled'] === 1;
echo '' . esc_html( ( $row['provider'] === 'google' ? 'Google' : 'Outlook' ) ) . '
' . esc_html__( 'Protection', 'abientot' ) . ': ' . esc_html( $enabled ? __( 'Enabled', 'abientot' ) : __( 'Disabled', 'abientot' ) ) . ' · ' . esc_html__( 'Timezone', 'abientot' ) . ': ' . esc_html( $row['timezone'] ) . '
' . esc_html__( 'Technical details', 'abientot' ) . '
' . esc_html__( 'Complete coverage (UTC)', 'abientot' ) . ': ' . esc_html( $row['coverage_start'] !== null ? $row['coverage_start'] . ' — ' . $row['coverage_end'] : __( 'No complete coverage', 'abientot' ) ) . '
' . esc_html__( 'Last successful sync (UTC)', 'abientot' ) . ': ' . esc_html( $row['last_success'] ?? __( 'Never', 'abientot' ) ) . '
';
if ( $row['error_code'] !== '' ) {
echo '' . esc_html__( 'Connection status code', 'abientot' ) . ': ' . esc_html( $row['error_code'] ) . '
';
}
echo ' ';
if ( $enabled ) {
$success = $row['last_success'] !== null ? strtotime( $row['last_success'] . ' UTC' ) : false;
if ( $row['error_code'] !== '' || $success === false || time() - $success > 900 || $success - time() > 60 || $row['coverage_start'] === null || (int) $row['sync_revision'] < 1 ) {
echo '' . esc_html__( 'Protection blocks new time changes because complete fresh coverage is unavailable. Restore a complete sync or explicitly disconnect after accepting the conflict risk.', 'abientot' ) . '
';
}
}
if ( $row['authority'] !== 'revoked' || $enabled ) {
echo '';
}
echo '';
}
echo '';
foreach ( array(
-1 => __( 'Previous', 'abientot' ),
1 => __( 'Next', 'abientot' )
) as $delta => $label ) {
if ( $page + $delta > 0 && ( $delta < 0 ? $page > 1 : $more ) ) {
echo '' . esc_html( $label ) . ' ';
}
}
echo '
';
} catch ( \Throwable $e ) {
echo '' . esc_html__( 'Retained calendar data is unavailable. Existing protection remains blocked until its storage is repaired or an authorized disconnect succeeds.', 'abientot' ) . '
';
}
}
}