diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 0000000..5556d7e --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,31 @@ +name: PHP Code Linting + +on: + pull_request: + push: + branches: + - main + - master + +jobs: + php-lint: + name: PHP Lint + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + + - name: Validate Composer configuration + run: composer validate + + - name: Install PHP dependencies + uses: ramsey/composer-install@83af392bf5f031813d25e6fe4cd626cdba9a2df6 + with: + composer-options: '--prefer-dist --no-progress --no-interaction' + + - name: Run tests + run: composer run-script phpcs diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index 73081f2..ded2c42 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -90,6 +90,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/devtools/composer.json b/devtools/composer.json index 79d3d90..ce64f8d 100644 --- a/devtools/composer.json +++ b/devtools/composer.json @@ -2,6 +2,7 @@ "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "phpcompatibility/phpcompatibility-wp": "^2.1", + "slevomat/coding-standard": "^8.15", "wp-cli/i18n-command": "^2.6", "wp-coding-standards/wpcs": "^3.1" }, diff --git a/includes/classes/class-ns-featured-posts-admin.php b/includes/classes/class-ns-featured-posts-admin.php index c9c50c8..a4b8110 100644 --- a/includes/classes/class-ns-featured-posts-admin.php +++ b/includes/classes/class-ns-featured-posts-admin.php @@ -5,6 +5,7 @@ * @package NS_Featured_Posts */ +use Nilambar\AdminNotice\Notice; use Nilambar\Optioner\Optioner; /** @@ -73,8 +74,6 @@ private function __construct() { $this->options = $plugin->get_options(); - - // Add an action link pointing to the options page. $base_file = $this->plugin_slug . '/' . $this->plugin_slug . '.php'; add_filter( 'plugin_action_links_' . $base_file, array( $this, 'add_plugin_action_links' ) ); @@ -107,9 +106,13 @@ private function __construct() { add_action( 'wp_ajax_nsfp_nsbl_get_posts', array( $this, 'get_posts_ajax_callback' ) ); } + /** + * Setup admin notice. + * + * @since 2.0.10 + */ public function setup_custom_notice() { - // Setup notice. - \Nilambar\AdminNotice\Notice::init( + Notice::init( array( 'slug' => $this->plugin_slug, 'name' => esc_html__( 'NS Featured Posts', 'ns-featured-posts' ), @@ -366,7 +369,7 @@ public function ajax_handler_featured_toggle() { ); // Nonce check. - $nonce = isset( $_POST['nonce'] ) ? $_POST['nonce'] : null; // phpcs:ignore WordPress.Security.NonceVerification + $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification if ( ! wp_verify_nonce( $nonce, 'ajax-nonce' ) ) { $output['message'] = esc_html__( 'Nonce verification failed.', 'ns-featured-posts' ); @@ -374,23 +377,23 @@ public function ajax_handler_featured_toggle() { wp_send_json( $output ); } - $uno = isset( $_POST['uno'] ) ? rest_sanitize_boolean( $_POST['uno'] ) : false; + $uno = isset( $_POST['uno'] ) ? rest_sanitize_boolean( sanitize_text_field( wp_unslash( $_POST['uno'] ) ) ) : false; - $max_posts = isset( $_POST['max_posts'] ) ? absint( $_POST['max_posts'] ) : 0; - $max_status = isset( $_POST['max_status'] ) ? rest_sanitize_boolean( $_POST['max_status'] ) : false; + $max_posts = isset( $_POST['max_posts'] ) ? absint( sanitize_text_field( wp_unslash( $_POST['max_posts'] ) ) ) : 0; + $max_status = isset( $_POST['max_status'] ) ? rest_sanitize_boolean( sanitize_text_field( wp_unslash( $_POST['max_status'] ) ) ) : false; - $ns_featured = isset( $_POST['ns_featured'] ) ? $_POST['ns_featured'] : null; + $ns_featured = isset( $_POST['ns_featured'] ) ? sanitize_text_field( wp_unslash( $_POST['ns_featured'] ) ) : null; $post_id = 0; if ( isset( $_POST['post_id'] ) ) { - $post_id = (int) $_POST['post_id']; + $post_id = (int) sanitize_text_field( wp_unslash( $_POST['post_id'] ) ); } $post_type = null; if ( isset( $_POST['post_type'] ) ) { - $post_type = (string) $_POST['post_type']; + $post_type = (string) sanitize_text_field( wp_unslash( $_POST['post_type'] ) ); } if ( ! empty( $post_id ) && ! empty( $post_type ) && null !== $ns_featured ) { @@ -476,8 +479,8 @@ private function get_other_posts( $post_id, $post_type ) { $qargs = array( 'posts_per_page' => -1, 'post__not_in' => array( $post_id ), - 'meta_key' => '_is_ns_featured_post', - 'meta_value' => 'yes', + 'meta_key' => '_is_ns_featured_post', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + 'meta_value' => 'yes', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value 'post_type' => $post_type, 'post_status' => array( 'publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash' ), ); @@ -491,6 +494,13 @@ private function get_other_posts( $post_id, $post_type ) { return $output; } + /** + * Load settings assets. + * + * @since 2.0.0 + * + * @param string $hook Hook name. + */ public function load_settings_assets( $hook ) { if ( 'settings_page_ns-featured-posts' !== $hook ) { return; @@ -524,7 +534,6 @@ public function load_assets() { ); wp_localize_script( 'nspf-admin', 'NSFP_OBJ', $localize_args ); - } /** @@ -592,7 +601,7 @@ public function save_featured_meta_box( $post_id ) { } // If our nonce isn't there, or we can't verify it, bail. - if ( ! isset( $_POST['nsfp_featured_metabox_nonce'] ) || ! wp_verify_nonce( $_POST['nsfp_featured_metabox_nonce'], plugin_basename( __FILE__ ) ) ) { + if ( ! isset( $_POST['nsfp_featured_metabox_nonce'] ) || ! wp_verify_nonce( $_POST['nsfp_featured_metabox_nonce'], plugin_basename( __FILE__ ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput return $post_id; } @@ -662,7 +671,7 @@ public function custom_table_filtering() { $selected_now = ''; if ( isset( $_GET['filter-ns-featured-posts'] ) ) { - $selected_now = esc_attr( $_GET['filter-ns-featured-posts'] ); + $selected_now = sanitize_text_field( wp_unslash( $_GET['filter-ns-featured-posts'] ) ); } echo '