get_option( 'phplist_url' ); $base = rtrim( $base, '/' ); if ( empty( $base ) ) { return new WP_Error( 'woolist_no_url', __( 'phpList base URL is not configured.', 'woolist-phplist' ) ); } $params = array_merge( [ 'page' => 'call', 'pi' => 'restapi', 'login' => $this->get_option( 'phplist_login' ), 'password' => $this->get_option( 'phplist_password' ), 'cmd' => $cmd, ], $extra ); return $base . '/admin/?' . http_build_query( $params ); } /** * Execute an API call and return decoded JSON data. * * @param string $cmd phpList API command. * @param array $extra Additional query parameters. * @return array|WP_Error Decoded response data or WP_Error. */ public function call( string $cmd, array $extra = [] ) { $url = $this->build_url( $cmd, $extra ); if ( is_wp_error( $url ) ) { return $url; } $response = wp_remote_post( $url, [ 'timeout' => 15 ] ); if ( is_wp_error( $response ) ) { error_log( '[WooList] API request failed for cmd=' . $cmd . ': ' . $response->get_error_message() ); return $response; } $code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); if ( $code < 200 || $code >= 300 ) { error_log( '[WooList] API returned HTTP ' . $code . ' for cmd=' . $cmd ); return new WP_Error( 'woolist_http_error', 'HTTP error ' . $code ); } $data = json_decode( $body, true ); if ( json_last_error() !== JSON_ERROR_NONE ) { error_log( '[WooList] API returned invalid JSON for cmd=' . $cmd . ': ' . $body ); return new WP_Error( 'woolist_json_error', 'Invalid JSON response from phpList.' ); } // phpList REST API signals errors via a "status" field. if ( isset( $data['status'] ) && strtolower( $data['status'] ) === 'error' ) { $message = $data['errormessage'] ?? $data['message'] ?? 'Unknown API error'; error_log( '[WooList] API error for cmd=' . $cmd . ': ' . $message ); return new WP_Error( 'woolist_api_error', $message ); } return $data; } /** * Retrieve a subscriber by email address. * * @param string $email Subscriber email. * @return array|WP_Error */ public function subscriber_get_by_email( string $email ) { return $this->call( 'subscriberGetByEmail', [ 'email' => rawurlencode( $email ) ] ); } /** * Add a new confirmed subscriber. * * @param string $email Subscriber email. * @return array|WP_Error */ public function subscriber_add( string $email ) { return $this->call( 'subscriberAdd', [ 'email' => rawurlencode( $email ), 'confirmed' => 1, 'htmlemail' => 1, ] ); } /** * Add a subscriber (by ID) to a phpList list. * * @param int $list_id phpList list ID. * @param int $subscriber_id phpList subscriber ID. * @return array|WP_Error */ public function list_subscriber_add( int $list_id, int $subscriber_id ) { return $this->call( 'listSubscriberAdd', [ 'listid' => $list_id, 'subscriberid' => $subscriber_id, ] ); } /** * High-level helper: subscribe an email to a list. * * Gets or creates the subscriber, then adds them to the list. * * @param string $email Subscriber email address. * @param int $list_id phpList list ID. * @return array{success: bool, subscriber_id: int|null} */ public function subscribe_email_to_list( string $email, int $list_id ): array { // Try to find existing subscriber. $subscriber_id = null; $existing = $this->subscriber_get_by_email( $email ); if ( ! is_wp_error( $existing ) && ! empty( $existing['id'] ) ) { $subscriber_id = (int) $existing['id']; } else { // Create new subscriber. $added = $this->subscriber_add( $email ); if ( is_wp_error( $added ) ) { error_log( '[WooList] Could not add subscriber ' . $email . ': ' . $added->get_error_message() ); return [ 'success' => false, 'subscriber_id' => null ]; } $subscriber_id = isset( $added['id'] ) ? (int) $added['id'] : null; } if ( ! $subscriber_id ) { error_log( '[WooList] Could not determine subscriber ID for ' . $email ); return [ 'success' => false, 'subscriber_id' => null ]; } // Add subscriber to the list. $result = $this->list_subscriber_add( $list_id, $subscriber_id ); if ( is_wp_error( $result ) ) { error_log( '[WooList] Could not add subscriber ' . $subscriber_id . ' to list ' . $list_id . ': ' . $result->get_error_message() ); return [ 'success' => false, 'subscriber_id' => $subscriber_id ]; } return [ 'success' => true, 'subscriber_id' => $subscriber_id ]; } /** * Retrieve all lists (used for connection testing). * * @return array|WP_Error */ public function lists_get() { return $this->call( 'listsGet' ); } }