PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.6.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.6.2
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / REST / Feedback.php

Feedback.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.6.2, at includes/REST/Feedback.php

248 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- live reaction analytics writes; cache would defeat the purpose.
3 namespace WPDeveloper\BetterDocs\REST;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 use WP_REST_Request;
10 use WPDeveloper\BetterDocs\Core\BaseAPI;
11
12 class Feedback extends BaseAPI {
13 /**
14 * @return mixed
15 */
16 public function register() {
17 $this->post(
18 '/feedback/(?P<id>\d+)',
19 [ $this, 'save' ],
20 [
21 'id' => [
22 'type' => 'integer',
23 'validate_callback' => function ( $param, $request, $key ) {
24 return ! empty( $param ) && is_numeric( $param ) && get_post( $param ) !== null;
25 },
26 'required' => false,
27 'default' => null
28 ],
29 'feelings' => [
30 'type' => 'string',
31 'validate_callback' => function ( $param, $request, $key ) {
32 $allowed_feelings = [ 'happy', 'sad', 'normal' ];
33 return in_array( $param, $allowed_feelings );
34 },
35 'required' => true
36 ]
37 ]
38 );
39
40 $this->register_field(
41 'docs',
42 'word_count',
43 [
44 'get_callback' => [ $this, 'get_word_count' ]
45 ]
46 );
47
48 $this->register_field(
49 'docs',
50 'total_views',
51 [
52 'get_callback' => [ $this, 'get_total_views' ]
53 ]
54 );
55
56 $this->register_field(
57 'docs',
58 'reactions',
59 [
60 'get_callback' => [ $this, 'get_reaction_count' ]
61 ]
62 );
63
64 $this->register_field(
65 'docs',
66 'author_info',
67 [
68 'get_callback' => [ $this, 'get_author_info' ]
69 ]
70 );
71
72 $this->register_field(
73 'docs',
74 'doc_category_info',
75 [
76 'get_callback' => [ $this, 'get_doc_category_info' ]
77 ]
78 );
79
80 $this->register_field(
81 'docs',
82 'doc_tag_info',
83 [
84 'get_callback' => [ $this, 'get_doc_tag_info' ]
85 ]
86 );
87
88 // $this->register_field( 'docs', 'author_list', [
89 // 'get_callback' => [$this, 'get_author_list']
90 // ] );
91 }
92
93 public function get_author_list( $object, $field_name, $request ) {
94 $args = [
95 'fields' => [
96 'ID',
97 'user_login',
98 'display_name'
99 ]
100 ];
101 $users = get_users( $args );
102 return $users;
103 }
104
105 public function analytics_by_post_id( $post_id ) {
106 global $wpdb;
107
108 return $wpdb->get_results(
109 $wpdb->prepare(
110 "SELECT
111 sum(impressions) as totalViews,
112 sum(unique_visit) as totalUniqueViews,
113 sum(happy + sad + normal) as totalReactions,
114 sum(happy) as totalHappy,
115 sum(normal) as totalNormal,
116 sum(sad) as totalSad
117 FROM {$wpdb->prefix}betterdocs_analytics
118 WHERE post_id = %d",
119 (int) $post_id
120 )
121 );
122 }
123
124 public function get_word_count( $object, $field_name, $request ) {
125 return str_word_count( trim( wp_strip_all_tags( get_post_field( 'post_content', $object['id'] ) ) ) );
126 }
127
128 public function get_total_views( $object, $field_name, $request ) {
129 $analytics = $this->analytics_by_post_id( $object['id'] );
130
131 if ( ! empty( $analytics ) ) {
132 return isset( $analytics[0]->totalViews ) ? $analytics[0]->totalViews : 0;
133 } else {
134 return 0;
135 }
136 }
137
138 public function get_reaction_count( $object, $field_name, $request ) {
139 $analytics = $this->analytics_by_post_id( $object['id'] );
140
141 if ( ! empty( $analytics ) ) {
142 return [
143 'happy' => isset( $analytics[0]->totalHappy ) ? $analytics[0]->totalHappy : 0,
144 'normal' => isset( $analytics[0]->totalNormal ) ? $analytics[0]->totalNormal : 0,
145 'sad' => isset( $analytics[0]->totalSad ) ? $analytics[0]->totalSad : 0
146 ];
147 } else {
148 return [
149 'happy' => 0,
150 'normal' => 0,
151 'sad' => 0
152 ];
153 }
154 }
155
156 public function save( WP_REST_Request $request ) {
157 global $wpdb;
158 $docs_id = isset( $request['id'] ) ? (int) $request['id'] : null;
159 $valid_feelings = [ 'happy', 'normal', 'sad' ];
160 $requested_feeling = isset( $request['feelings'] ) ? (string) $request['feelings'] : 'happy';
161 $feelings = in_array( $requested_feeling, $valid_feelings, true ) ? $requested_feeling : 'happy';
162 $analytics_table = $wpdb->prefix . 'betterdocs_analytics';
163 if ( $docs_id !== null && get_post( $docs_id ) && get_option( 'betterdocs_db_version' ) == true ) {
164 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- $analytics_table = $wpdb->prefix + literal; per-request reaction lookup, no cache layer applies.
165 $post_id = $wpdb->get_results(
166 $wpdb->prepare(
167 "SELECT * FROM {$analytics_table} WHERE created_at = %s AND post_id = %d",
168 gmdate( 'Y-m-d' ),
169 $docs_id
170 )
171 );
172 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
173
174 if ( ! empty( $post_id ) ) {
175 $feelings_increment = (int) $post_id[0]->{$feelings} + 1;
176 // $feelings is validated above against $valid_feelings allowlist — safe to interpolate as column identifier.
177 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- $analytics_table = $wpdb->prefix + literal; per-request reaction counter, no cache layer applies.
178 $insert = $wpdb->query(
179 $wpdb->prepare(
180 "UPDATE {$analytics_table} SET {$feelings} = %d WHERE created_at = %s AND post_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
181 $feelings_increment,
182 gmdate( 'Y-m-d' ),
183 $docs_id
184 )
185 );
186 } else {
187 // $feelings is validated above against $valid_feelings allowlist — safe to interpolate as column identifier.
188 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- $analytics_table = $wpdb->prefix + literal; per-request reaction counter, no cache layer applies.
189 $insert = $wpdb->query(
190 $wpdb->prepare(
191 "INSERT INTO {$analytics_table} ( post_id, {$feelings}, created_at ) VALUES ( %d, %d, %s )", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
192 $docs_id,
193 1,
194 gmdate( 'Y-m-d' )
195 )
196 );
197 }
198
199 if ( $insert == true ) {
200 return true;
201 }
202 }
203 return false;
204 }
205
206 public function get_author_info( $object, $field_name, $request ) {
207 $author_id = isset( $object['author'] ) ? $object['author'] : '';
208 if ( ! empty( $author_id ) ) {
209 return [
210 'name' => get_the_author_meta( 'display_name', $author_id ),
211 'author_nicename' => get_the_author_meta( 'nicename', $author_id ),
212 'author_url' => get_author_posts_url( $author_id )
213 ];
214 }
215 return [];
216 }
217
218 public function get_doc_category_info( $object, $field_name, $request ) {
219 $category_term_names = [];
220 $doc_categories = ! empty( $object['doc_category'] ) ? $object['doc_category'] : [];
221 foreach ( $doc_categories as $doc_category_id ) {
222 array_push(
223 $category_term_names,
224 [
225 'term_name' => get_term( $doc_category_id )->name,
226 'term_url' => get_term_link( $doc_category_id )
227 ]
228 );
229 }
230 return $category_term_names;
231 }
232
233 public function get_doc_tag_info( $object, $field_name, $request ) {
234 $doc_tag_term_names = [];
235 $doc_tags = ! empty( $object['doc_tag'] ) ? $object['doc_tag'] : [];
236 foreach ( $doc_tags as $tag_id ) {
237 array_push(
238 $doc_tag_term_names,
239 [
240 'term_name' => get_term( $tag_id )->name,
241 'term_url' => get_term_link( $tag_id )
242 ]
243 );
244 }
245 return $doc_tag_term_names;
246 }
247 }
248