> The option definitions. */ public function options(): array { return array( $this->url_option( __( 'Incoming webhook URL', 'devplugins-custom-form-builder' ), 'https://hooks.slack.com/services/…' ), array( 'key' => 'title', 'type' => 'text', 'label' => __( 'Message heading', 'devplugins-custom-form-builder' ), 'description' => __( 'Leave empty to use the form title.', 'devplugins-custom-form-builder' ), ), ); } /** * Builds the body sent to Slack. * * @param IntegrationContext $context Submission being delivered. * @param array $config Stored values for the form. * * @return array The payload. */ public function payload( IntegrationContext $context, array $config ): array { $heading = $this->option( $config, 'title' ); $heading = '' !== $heading ? $heading : $context->form()->title(); $blocks = array( array( 'type' => 'header', 'text' => array( 'type' => 'plain_text', 'text' => $this->truncate( $heading, 150 ), ), ), ); /* * Slack allows ten fields per section and truncates a field at 2000 * characters, so the answers are chunked rather than sent as one block * that the API would refuse outright. */ foreach ( array_chunk( $this->pairs( $context ), 10, true ) as $chunk ) { $fields = array(); foreach ( $chunk as $label => $value ) { $fields[] = array( 'type' => 'mrkdwn', 'text' => '*' . $this->escape( (string) $label ) . "*\n" . $this->escape( $this->truncate( $value, 1800 ) ), ); } $blocks[] = array( 'type' => 'section', 'fields' => $fields, ); } $url = $context->entry_url(); if ( '' !== $url ) { $blocks[] = array( 'type' => 'context', 'elements' => array( array( 'type' => 'mrkdwn', 'text' => '<' . $url . '|' . __( 'View the entry', 'devplugins-custom-form-builder' ) . '>', ), ), ); } return array( // Sent alongside the blocks because it is what a notification preview // and a screen reader announce; blocks alone leave both empty. 'text' => $heading, 'blocks' => $blocks, ); } /** * Returns the answers, keyed by label, with the empty ones dropped. * * @param IntegrationContext $context Submission being delivered. * * @return array Answers keyed by label. */ private function pairs( IntegrationContext $context ): array { $pairs = array_filter( $context->labelled(), static fn( string $value ): bool => '' !== trim( $value ) ); return array() === $pairs ? array( __( 'Submission', 'devplugins-custom-form-builder' ) => __( 'No answers were recorded.', 'devplugins-custom-form-builder' ) ) : $pairs; } /** * Escapes the three characters Slack treats as markup. * * @param string $text Raw text. */ private function escape( string $text ): string { return str_replace( array( '&', '<', '>' ), array( '&', '<', '>' ), $text ); } /** * Shortens text to a number of characters. * * @param string $text Raw text. * @param int $limit Largest length. */ private function truncate( string $text, int $limit ): string { return mb_strlen( $text ) > $limit ? mb_substr( $text, 0, $limit - 1 ) . '…' : $text; } }