ce( $template ) { if ( ! empty( $template['source'] ) ) { return $template['source']; } $source = 'wpforms-addon'; static $addons = null; if ( $addons === null ) { $addons = array_keys( $this->addons_obj->get_all() ); } if ( $template['plugin_dir'] === 'wpforms' || $template['plugin_dir'] === 'wpforms-lite' ) { $source = 'wpforms-core'; } if ( $source !== 'wpforms-core' && ! in_array( $template['plugin_dir'], $addons, true ) ) { $source = 'wpforms-custom'; } return $source; } /** * Determine template categories. * * @since 1.7.7 * * @param array $template Template data. * * @return string Template categories coma separated. */ private function get_template_categories( $template ) { $categories = ! empty( $template['categories'] ) ? (array) $template['categories'] : []; $source = $this->get_template_source( $template ); if ( $source === 'wpforms-addon' ) { $categories[] = 'addons'; } if ( $source === 'wpforms-custom' ) { $categories[] = 'custom'; } if ( isset( $template['created_at'] ) && strtotime( $template['created_at'] ) > strtotime( '-3 Months' ) ) { $categories[] = 'new'; } return implode( ',', $categories ); } /** * Determine template subcategories. * * @since 1.8.4 * * @param array $template Template data. * * @return string Template subcategories coma separated. */ private function get_template_subcategories( $template ) { $subcategories = ! empty( $template['subcategories'] ) ? (array) $template['subcategories'] : []; $subcategories = array_keys( $subcategories ); return implode( ',', $subcategories ); } /** * Determine template fields. * * @since 1.8.6 * * @param array $template Template data. * * @return string Template fields, comma separated. */ private function get_template_fields( array $template ): string { $fields = ! empty( $template['fields'] ) ? (array) $template['fields'] : []; /** * Filter template fields. * * @since 1.8.6 * * @param array $fields Template fields. */ $fields = (array) apply_filters( 'wpforms_setup_template_fields', $fields ); /* * Keywords are appended after the filter on purpose. * WPForms_Field::enhance_template_fields_with_keywords() replaces every entry that exactly matches * a field type slug ( e.g. "password", "email", "name" ) with that field's display name, * so a keyword merged before the filter could be silently rewritten. */ if ( ! empty( $template['keywords'] ) ) { $fields = array_merge( $fields, array_map( 'trim', explode( ',', $template['keywords'] ) ) ); } /* * The wpforms_setup_template_fields filter can return non-string entries, so cast before * filtering: strlen() raises a TypeError on an array where implode() only ever cast it. * Filtering on length rather than truthiness keeps a field literally labelled "0". */ $fields = array_filter( array_map( 'strval', $fields ), 'strlen' ); // Drop the repeated terms, the way WPForms_Field::enhance_template_fields_with_keywords() does. return implode( ',', array_unique( $fields ) ); } /** * Get categories templates count. * * @since 1.7.7 * * @return array */ private function get_count_in_categories() { $all_categories = []; $available_templates_count = 0; $favorites_templates_count = 0; $user_templates_count = 0; foreach ( $this->prepared_templates as $template_data ) { $template = $template_data['template']; $categories = explode( ',', $template_data['categories'] ); if ( $template['has_access'] ) { ++$available_templates_count; } if ( $template['favorite'] ) { ++$favorites_templates_count; } if ( $template['source'] === 'wpforms-user-template' ) { ++$user_templates_count; } if ( is_array( $categories ) ) { array_push( $all_categories, ...$categories ); continue; } $all_categories[] = $categories; } $categories_count = array_count_values( $all_categories ); $categories_count['all'] = count( $this->prepared_templates ); $categories_count['available'] = $available_templates_count; $categories_count['favorites'] = $favorites_templates_count; $categories_count['user'] = $user_templates_count; $categories_count['subcategories'] = $this->get_count_in_subcategories(); return $categories_count; } /** * Get subcategories templates count. * * @since 1.8.7 * * @return array */ private function get_count_in_subcategories(): array { $all_subcategories = []; foreach ( $this->prepared_templates as $template_data ) { $subcategories = explode( ',', $template_data['subcategories'] ); if ( is_array( $subcategories ) ) { array_push( $all_subcategories, ...$subcategories ); continue; } $all_subcategories[] = $subcategories; } return array_count_values( $all_subcategories ); } /** * Generate templates content with infinite scroll support. * * @since 2.0.0 * * @return string HTML content. */ private function generate_templates_content_with_infinite_scroll(): string { $this->prepare_templates_data(); $total_templates = count( $this->prepared_templates ); $initial_limit = 11; // Get the first batch of templates (11 because 1 additional item is added dynamically in JS). $initial_batch = array_slice( $this->prepared_templates, 0, $initial_limit ); ob_start(); /** This action is documented in src/Admin/Traits/FormTemplates.php. */ do_action( 'wpforms_admin_form_templates_list_before', [] ); foreach ( $initial_batch as $template ) { echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 'builder/templates-item', $template, true ); } $list_content = ob_get_clean(); $list_attributes = sprintf( 'data-total-templates="%d" data-loaded-templates="%d"', absint( $total_templates ), absint( count( $initial_batch ) ) ); return $this->get_templates_wrapper_html( $list_content, $list_attributes, true ); } /** * Get filtered templates with pagination. * * @since 2.0.0 * * @param int $offset Offset for pagination. * @param int $limit Number of templates to return. * @param string $category Category filter. * @param string $subcategory Subcategory filter. * @param string $search Search query. * @param bool $favorite Filter favorites only. * * @return array { * Filtered templates' data. * * @type array $templates Array of template data. * @type int $total Total matching templates. * @type bool $has_more More templates available. * } */ public function get_filtered_templates( int $offset = 0, int $limit = 12, string $category = 'all', string $subcategory = '', string $search = '', bool $favorite = false ): array { $filtered = []; // Apply filters to prepared templates. foreach ( $this->prepared_templates as $template_data ) { if ( ! $this->template_matches_filters( $template_data, $category, $subcategory, $search, $favorite ) ) { continue; } $filtered[] = $template_data; } $total = count( $filtered ); $slice = array_slice( $filtered, $offset, $limit ); $has_more = ( $offset + $limit ) < $total; return [ 'templates' => $slice, 'total' => $total, 'has_more' => $has_more, ]; } /** * Check if the template matches filters. * * @since 2.0.0 * * @param array $template_data Template data. * @param string $category Category filter. * @param string $subcategory Subcategory filter. * @param string $search Search query. * @param bool $favorite Favorites filter. * * @return bool True if the template matches all filters. */ private function template_matches_filters( array $template_data, string $category, string $subcategory, string $search, bool $favorite ): bool { // Favorite filter. if ( $favorite && empty( $template_data['template']['favorite'] ) ) { return false; } // Category filter. if ( $category !== 'all' ) { $categories = explode( ',', $template_data['categories'] ); if ( $category === 'available' && empty( $template_data['template']['has_access'] ) ) { return false; } if ( $category === 'favorites' && empty( $template_data['template']['favorite'] ) ) { return false; } if ( ! in_array( $category, [ 'available', 'favorites' ], true ) && ! in_array( $category, $categories, true ) ) { return false; } } // Subcategory filter. if ( ! empty( $subcategory ) ) { $subcategories = explode( ',', $template_data['subcategories'] ); if ( ! in_array( $subcategory, $subcategories, true ) ) { return false; } } // Search filter. if ( ! empty( $search ) ) { $search_lower = strtolower( $search ); $name_lower = strtolower( $template_data['template']['name'] ); $desc_lower = strtolower( $template_data['template']['description'] ); $fields_lower = strtolower( $template_data['fields'] ); if ( strpos( $name_lower, $search_lower ) === false && strpos( $desc_lower, $search_lower ) === false && strpos( $fields_lower, $search_lower ) === false ) { return false; } } return true; } }