404 ) ); } } } add_action( 'template_redirect', 'projects_portfolio_handle_download_redirect' ); /** * Add Download Count Column to Projects Admin Table * * @since 1.0.0 * @param array $columns The columns array. * @return mixed */ function projects_portfolio_add_download_count_column( $columns ) { $columns['download_count'] = esc_html__( 'Download Count', 'projects-wp' ); return $columns; } add_filter( 'manage_projects_posts_columns', 'projects_portfolio_add_download_count_column' ); /** * Display Download Count in Admin Column * * @since 1.0.0 * @param string $column The column name. * @param int $post_id The post ID. * @return void */ function projects_portfolio_display_download_count_column( $column, $post_id ) { if ( 'download_count' === $column ) { $download_count = (int) get_post_meta( $post_id, '_projects_portfolio_download_count', true ); echo $download_count ? esc_html( $download_count ) : esc_html__( '0', 'projects-wp' ); } } add_action( 'manage_projects_posts_custom_column', 'projects_portfolio_display_download_count_column', 10, 2 ); /** * Make the Download Count Column Sortable * * @since 1.0.0 * @param array $columns The columns array. * @return mixed */ function projects_portfolio_sortable_columns( $columns ) { $columns['download_count'] = 'download_count'; return $columns; } add_filter( 'manage_edit-projects_sortable_columns', 'projects_portfolio_sortable_columns' ); /** * Handle Sorting for Download Count * * @since 1.0.0 * @param WP_Query $query The query object. * @return void */ function projects_portfolio_sort_download_count_column( $query ) { if ( ! is_admin() || ! $query->is_main_query() ) { return; } if ( 'download_count' === $query->get( 'orderby' ) ) { $query->set( 'meta_key', '_projects_portfolio_download_count' ); $query->set( 'orderby', 'meta_value_num' ); } } add_action( 'pre_get_posts', 'projects_portfolio_sort_download_count_column' ); /** * Register custom REST API endpoint for projects. * * @since 1.0.0 * @return void */ function projects_portfolio_register_custom_endpoint() { register_rest_route( 'projects/v1', '/projects', array( 'methods' => WP_REST_Server::READABLE, 'callback' => 'projects_portfolio_get_projects_data', 'permission_callback' => '__return_true', ) ); } add_action( 'rest_api_init', 'projects_portfolio_register_custom_endpoint' ); /** * Load custom templates for single and archive project views. * * @since 1.0.0 * @param string $template The template file path. * @return mixed */ function projects_portfolio_template_loader( $template ) { if ( is_singular( 'projects' ) ) { // Check for a single-projects.php template in the theme $theme_template = locate_template( 'single-projects.php' ); return $theme_template ? $theme_template : plugin_dir_path( __FILE__ ) . 'templates/single-projects.php'; } if ( is_post_type_archive( 'projects' ) ) { // Check for an archive-projects.php template in the theme $theme_template = locate_template( 'archive-projects.php' ); return $theme_template ? $theme_template : plugin_dir_path( __FILE__ ) . 'templates/archive-projects.php'; } if ( is_tax( 'project-type' ) ) { // Check for a taxonomy-project-type.php template in the theme $theme_template = locate_template( 'archive-project-type.php' ); return $theme_template ? $theme_template : plugin_dir_path( __FILE__ ) . 'templates/taxonomy-project-type.php'; } return $template; } add_filter( 'template_include', 'projects_portfolio_template_loader' ); /** * Enqueue styles for project single and archive templates. * * @since 1.0.0 * @return void */ function projects_portfolio_enqueue_project_styles() { if ( is_singular( 'projects' ) || is_post_type_archive( 'projects' ) || is_tax( 'project-type' ) ) { $is_fse_theme = wp_theme_has_theme_json() && function_exists( 'wp_is_block_theme' ) && wp_is_block_theme(); // Register stylesheet for both theme types wp_register_style( 'projects-wp-styles', plugin_dir_url( __FILE__ ) . 'assets/css/style.css', array(), PROJECTS_PORTFOLIO_VERSION ); if ( $is_fse_theme ) { // Inline block style enqueue (needed for FSE themes) add_action( 'wp_footer', function () { echo ''; } ); } else { // Classic theme enqueue wp_enqueue_style( 'projects-wp-styles' ); } } if ( is_singular( 'projects' ) ) { // assets/js/buttons.js was used to render GitHub-style follow buttons. // The Follow button now uses a plain styled link (works for any provider), // so the script is no longer enqueued. } } add_action( 'wp_enqueue_scripts', 'projects_portfolio_enqueue_project_styles' ); /** * Enqueue admin styles. * * @since 1.0.0 * @return void */ function projects_portfolio_admin_styles() { wp_enqueue_style( 'projects-wp-admin-css', plugin_dir_url( __FILE__ ) . 'assets/css/admin-styles.css' ); } add_action( 'admin_enqueue_scripts', 'projects_portfolio_admin_styles' ); /** * Modify the number of posts per page for the 'projects' post type archive. * * Ensures only the 'projects' archive page is affected without changing global settings. * * @param WP_Query $query The WordPress query object. * * @since 1.0.0 * @return void */ function projects_portfolio_custom_posts_per_page( $query ) { if ( is_admin() || ! $query->is_main_query() ) { return; } $projects_per_page = apply_filters( 'projects_portfolio_archives_projects_per_page', 12 ); if ( is_post_type_archive( 'projects' ) ) { $query->set( 'posts_per_page', $projects_per_page ); } } add_action( 'pre_get_posts', 'projects_portfolio_custom_posts_per_page', 1 ); /** * Modify the posts per page for the 'projects' post type archive in Full Site Editing (FSE) themes. * * This function ensures that the 'projects' post type archive displays 12 posts per page, * even when using an FSE theme with a Query Loop block. * * @param array $query The existing query variables for the Query Loop block. * @param object $block The current block object (not used but available if needed). * * @since 1.0.0 * @return array Modified query variables with 'posts_per_page' set for the projects archive. */ function modify_projects_posts_per_page_fse( $query, $block ) { // phpcs:ignore // Ensure we are modifying the archive query for the 'projects' post type. if ( isset( $query['post_type'] ) && $query['post_type'] === 'projects' ) { $query['posts_per_page'] = 12; } return $query; } add_filter( 'query_loop_block_query_vars', 'modify_projects_posts_per_page_fse', 10, 2 ); /** * Add social sharing buttons with Tabler icons. * * @param int $project_id The project post ID. * * @since 1.0.0 * @return void */ function projects_portfolio_social_sharing_buttons( $project_id ) { $url = rawurlencode( get_permalink( $project_id ) ); $title = rawurlencode( get_the_title( $project_id ) ); $html = '
'; // wp_kses_post() strips