PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / frontend / comments.php

comments.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/frontend/comments.php

176 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace StoreEngine\Frontend;
4
5 use StoreEngine\Utils\Helper;
6
7 if ( ! defined( 'ABSPATH' ) ) {
8 exit;
9 }
10
11 class Comments {
12 public static function init() {
13 $self = new self();
14 add_filter( 'preprocess_comment', [ $self, 'validate_review_submission' ] );
15 add_action( 'comment_post', array( $self, 'add_comment_rating' ), 1 );
16 add_filter( 'comments_template_query_args', [ __CLASS__, 'comments_template_query_args' ] );
17 add_filter( 'pre_comment_approved', [ $self, 'set_review_approval' ], 20, 2 );
18 }
19
20 /**
21 * Enforce review eligibility on the native comment endpoint.
22 *
23 * The on-product review form submits through wp-comments-post.php, so the
24 * template's own gate is not enough — a crafted POST could bypass it. Reviews
25 * are identified by the presence of the storeengine_rating field; plain
26 * product comments are left untouched.
27 *
28 * @param array $commentdata Comment data.
29 *
30 * @return array
31 */
32 public function validate_review_submission( $commentdata ) {
33 // phpcs:disable WordPress.Security.NonceVerification.Missing -- core handles the comment submission nonce/flood.
34 $post_id = (int) ( $commentdata['comment_post_ID'] ?? 0 );
35
36 if ( ! $post_id
37 || ! isset( $_POST['storeengine_rating'] )
38 || 'storeengine_product' !== get_post_type( $post_id )
39 || ! function_exists( 'storeengine_can_review_product' ) ) {
40 // phpcs:enable WordPress.Security.NonceVerification.Missing
41 return $commentdata;
42 }
43 // phpcs:enable WordPress.Security.NonceVerification.Missing
44
45 if ( ! storeengine_can_review_product( $post_id ) ) {
46 wp_die(
47 esc_html__( 'You are not allowed to review this product.', 'storeengine' ),
48 esc_html__( 'Review not allowed', 'storeengine' ),
49 [ 'response' => 403, 'back_link' => true ]
50 );
51 }
52
53 if ( storeengine_get_user_review( $post_id ) ) {
54 wp_die(
55 esc_html__( 'You have already reviewed this product.', 'storeengine' ),
56 esc_html__( 'Already reviewed', 'storeengine' ),
57 [ 'response' => 409, 'back_link' => true ]
58 );
59 }
60
61 return $commentdata;
62 }
63
64 /**
65 * Apply the store's review-approval setting to a product review submitted
66 * through the on-product form (identified by the storeengine_rating field).
67 *
68 * @param int|string $approved Current approval status.
69 * @param array $commentdata Comment data.
70 *
71 * @return int|string
72 */
73 public function set_review_approval( $approved, $commentdata ) {
74 // phpcs:disable WordPress.Security.NonceVerification.Missing -- core already flood/nonce checks comment submission.
75 if ( 'spam' === $approved || 'trash' === $approved ) {
76 return $approved;
77 }
78
79 $post_id = (int) ( $commentdata['comment_post_ID'] ?? 0 );
80
81 if ( $post_id
82 && isset( $_POST['storeengine_rating'] )
83 && 'storeengine_product' === get_post_type( $post_id )
84 && function_exists( 'storeengine_review_auto_approve' ) ) {
85 return storeengine_review_auto_approve() ? 1 : 0;
86 }
87 // phpcs:enable WordPress.Security.NonceVerification.Missing
88
89 return $approved;
90 }
91
92 /**
93 * Rating field for comments.
94 *
95 * @param int $comment_id Comment ID.
96 */
97 public function add_comment_rating( $comment_id ) {
98 if ( isset( $_POST['comment_post_ID'] ) && 'storeengine_product' === get_post_type( absint( $_POST['comment_post_ID'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
99 $comment_post_ID = absint( sanitize_text_field( wp_unslash( $_POST['comment_post_ID'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
100 $storeengine_rating = isset( $_POST['storeengine_rating'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['storeengine_rating'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
101
102
103 if ( ! $storeengine_rating ) { // phpcs:ignore input var ok, CSRF ok.
104 return;
105 }
106
107 wp_update_comment( [ 'comment_ID' => $comment_id, 'comment_type' => 'storeengine_product' ] );
108
109 add_comment_meta( $comment_id, 'storeengine_rating', $storeengine_rating, true );
110
111 // Media (images/videos) uploaded ahead of submit are carried in a
112 // hidden field as a comma-separated list of attachment IDs. Keep only
113 // IDs that are review-media attachments the buyer just uploaded.
114 $this->save_review_media( $comment_id, $comment_post_ID );
115
116 /**
117 * Fires after adding product rating.
118 *
119 * @param int $comment_id Comment id.
120 * @param int $comment_post_ID Post id.
121 * @param int $storeengine_rating Rating.
122 */
123 do_action( 'storeengine/frontend/after_product_rating', $comment_id, $comment_post_ID, $storeengine_rating );
124 }
125 }
126
127 /**
128 * Persist review media (attachment IDs) submitted with a review.
129 *
130 * @param int $comment_id Comment id.
131 * @param int $comment_post_ID Product id the comment belongs to.
132 */
133 protected function save_review_media( int $comment_id, int $comment_post_ID ) {
134 if ( empty( $_POST['storeengine_review_media'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- core comment submission already nonce/flood checked.
135 return;
136 }
137
138 $raw = sanitize_text_field( wp_unslash( $_POST['storeengine_review_media'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
139 $ids = array_filter( array_map( 'absint', explode( ',', $raw ) ) );
140
141 if ( empty( $ids ) ) {
142 return;
143 }
144
145 $user_id = get_current_user_id();
146 $valid = [];
147 foreach ( array_unique( $ids ) as $attachment_id ) {
148 // Only accept review-media attachments the current user uploaded for
149 // this product (tagged by the upload_review_media AJAX handler).
150 if ( (int) get_post_meta( $attachment_id, '_storeengine_review_media', true ) === $comment_post_ID
151 && (int) get_post_field( 'post_author', $attachment_id ) === $user_id ) {
152 $valid[] = $attachment_id;
153 wp_update_post( [ 'ID' => $attachment_id, 'post_parent' => $comment_post_ID ] );
154 }
155 }
156
157 // Enforce the configured per-review media cap (0 = unlimited).
158 $max = function_exists( 'storeengine_review_media_max' ) ? storeengine_review_media_max() : 0;
159 if ( $max > 0 && count( $valid ) > $max ) {
160 $valid = array_slice( $valid, 0, $max );
161 }
162
163 if ( ! empty( $valid ) ) {
164 add_comment_meta( $comment_id, 'storeengine_review_media', $valid, true );
165 }
166 }
167
168 public static function comments_template_query_args( array $args ): array {
169 if ( Helper::is_product() ) {
170 $args['type'] = 'comment';
171 }
172
173 return $args;
174 }
175 }
176