source = $source ?? new ApiClient(); $this->reconciler = $reconciler ?? new DeletedItemsReconciler(); } /** * Checking whether the site holds any statuses. * * @return bool Whether anything is cached. */ public static function is_populated(): bool { return array() !== Options::get_array( self::ACTIVE_OPTION ) || array() !== Options::get_array( self::DEACTIVATED_OPTION ); } /** * Fetching the list afresh and caching it. * * @return bool Whether a new list was stored. */ public function refresh(): bool { return RefreshOutcome::UPDATED === $this->refresh_outcome(); } /** * Fetching the list afresh, caching it, and saying how that went. * * @return string One of the RefreshOutcome constants. */ public function refresh_outcome(): string { $platform_id = Options::get_int( PublicationResolver::PLATFORM_OPTION ); if ( $platform_id <= 0 ) { // The resource is addressed per platform. return RefreshOutcome::FAILED; } $response = $this->source->fetch_statuses( $platform_id ); if ( null === $response ) { return RefreshOutcome::FAILED; } $active = array(); $deactivated = array(); // A fetch is one list with an "active" flag; a push carries two lists. foreach ( $response as $status ) { if ( ! is_array( $status ) || ! isset( $status['id'] ) ) { continue; } if ( array_key_exists( 'active', $status ) && false === $status['active'] ) { $deactivated[] = $status; } else { $active[] = $status; } } if ( array() === $active && array() === $deactivated ) { $this->forget_deleted( array() ); Options::delete( self::ACTIVE_OPTION ); Options::delete( self::DEACTIVATED_OPTION ); return RefreshOutcome::EMPTY; } list( $active, $deactivated ) = $this->prepare( $active, $deactivated ); // Write both, even if empty: a fetch is the whole list. Options::set( self::ACTIVE_OPTION, $active ); Options::set( self::DEACTIVATED_OPTION, $deactivated ); return RefreshOutcome::UPDATED; } /** * Making an incoming list ready to store. * * Must run before storing: deleted statuses are found by comparing with the stored list. * * @param array> $active Active statuses. * @param array> $deactivated Deactivated statuses. * * @return array{0: array>, 1: array>} The adjusted lists. */ public function prepare( array $active, array $deactivated ): array { list( $active, $deactivated ) = self::apply_export_triggers( $active, $deactivated ); $this->forget_deleted( array_merge( $active, $deactivated ) ); return array( $active, $deactivated ); } /** * Moving statuses that no longer trigger an export into the deactivated list. * * @param array> $active Active statuses. * @param array> $deactivated Deactivated statuses. * * @return array{0: array>, 1: array>} The adjusted lists. */ private static function apply_export_triggers( array $active, array $deactivated ): array { $still_active = array(); foreach ( $active as $status ) { if ( array_key_exists( 'triggersExport', $status ) && false === $status['triggersExport'] ) { array_unshift( $deactivated, $status ); continue; } $still_active[] = $status; } return array( $still_active, $deactivated ); } /** * Removing mappings for statuses Kordiam no longer has. * * @param array> $incoming Every status in the incoming list. */ private function forget_deleted( array $incoming ): void { $stored = Options::get_array( self::ACTIVE_OPTION ); if ( array() === $stored ) { return; } $this->reconciler->reconcile( $stored, $incoming, array_keys( StatusMapper::selectable_statuses() ), DeletedItemsReconciler::TYPE_STATUS ); } }