PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.2.90
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.2.90
3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / modules / files.php
ai-engine / classes / modules Last commit date
chatbot.php 2 years ago discussions.php 2 years ago files.php 2 years ago security.php 2 years ago tasks.php 2 years ago utilities.php 2 years ago wand.php 2 years ago
files.php
661 lines
1 <?php
2
3 class Meow_MWAI_Modules_Files {
4 private $core = null;
5 private $wpdb = null;
6 private $namespace = 'mwai-ui/v1';
7 private $db_check = false;
8 private $table_files = null;
9 private $table_filemeta = null;
10
11 public function __construct( $core ) {
12 global $wpdb;
13 $this->core = $core;
14 $this->wpdb = $wpdb;
15 $this->table_files = $this->wpdb->prefix . 'mwai_files';
16 $this->table_filemeta = $this->wpdb->prefix . 'mwai_filemeta';
17 add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
18 if ( !wp_next_scheduled( 'mwai_files_cleanup' ) ) {
19 wp_schedule_event( time(), 'hourly', 'mwai_files_cleanup' );
20 }
21 add_action( 'mwai_files_cleanup', [ $this, 'cleanup_expired_files' ] );
22 }
23
24 public function cleanup_expired_files() {
25 if ( $this->check_db() ) {
26 $current_time = current_time( 'mysql' );
27 $expired_files = $this->wpdb->get_results(
28 "SELECT * FROM $this->table_files WHERE expires IS NOT NULL AND expires < '{$current_time}'"
29 );
30 }
31 $expired_posts = get_posts( [
32 'post_type' => 'attachment',
33 'meta_key' => '_mwai_file_expires',
34 'meta_value' => $current_time,
35 'meta_compare' => '<'
36 ] );
37 $fileRefs = [];
38 foreach ( $expired_files as $file ) {
39 $fileRefs[] = $file->refId;
40 }
41 foreach ( $expired_posts as $post ) {
42 $fileRefs[] = get_post_meta( $post->ID, '_mwai_file_id', true );
43 }
44 $this->delete_expired_files( $fileRefs );
45 }
46
47 public function delete_expired_files( $fileRefs ) {
48
49 // Give a chance to other process to delete the files (for example, in the case of files hosted by Assistants)
50 $fileRefs = apply_filters( 'mwai_files_delete', $fileRefs );
51
52 if ( !is_array( $fileRefs ) ) {
53 $fileRefs = [ $fileRefs ];
54 }
55 foreach ( $fileRefs as $refId ) {
56 $file = null;
57 if ( $this->check_db() ) {
58 $file = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT *
59 FROM $this->table_files
60 WHERE refId = %s", $refId
61 ) );
62 }
63 if ( $file ) {
64 $this->wpdb->delete( $this->table_files, [ 'refId' => $refId ] );
65 $this->wpdb->delete( $this->table_filemeta, [ 'file_id' => $file->id ] );
66 if ( file_exists( $file->path ) ) {
67 unlink( $file->path );
68 }
69 }
70 else {
71 $posts = get_posts( [ 'post_type' => 'attachment', 'meta_key' => '_mwai_file_id', 'meta_value' => $refId ] );
72 if ( $posts ) {
73 foreach ( $posts as $post ) {
74 wp_delete_attachment( $post->ID, true );
75 }
76 }
77 }
78 }
79 }
80
81 public function get_path( $refId ) {
82 $file = null;
83 if ( $this->check_db() ) {
84 $file = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT *
85 FROM $this->table_files
86 WHERE refId = %s", $refId
87 ) );
88 }
89 if ( $file ) {
90 return $file->path;
91 }
92 else {
93 $posts = get_posts( [ 'post_type' => 'attachment', 'meta_key' => '_mwai_file_id', 'meta_value' => $refId ] );
94 if ( $posts ) {
95 foreach ( $posts as $post ) {
96 return get_attached_file( $post->ID );
97 }
98 }
99 }
100 return null;
101 }
102
103 public function get_base64_data( $refId ) {
104 $path = $this->get_path( $refId );
105 if ( $path ) {
106 $content = file_get_contents( $path );
107 $data = base64_encode( $content );
108 return $data;
109 }
110 return null;
111 }
112
113 public function get_mime_type( $refId ) {
114 $path = $this->get_path( $refId );
115 if ( $path ) {
116 return $this->core->get_mime_type( $path );
117 }
118 $url = $this->get_url( $refId );
119 if ( $url ) {
120 return $this->core->get_mime_type( $url );
121 }
122 return null;
123 }
124
125 public function get_data( $refId ) {
126 $path = $this->get_path( $refId );
127 if ( $path ) {
128 $content = file_get_contents( $path );
129 return $content;
130 }
131 return null;
132 }
133
134 public function get_url( $refId ) {
135 $file = null;
136 if ( $this->check_db() ) {
137 $file = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT *
138 FROM $this->table_files
139 WHERE refId = %s", $refId
140 ) );
141 }
142 if ( $file ) {
143 return $file->url;
144 }
145 else {
146 $posts = get_posts( [ 'post_type' => 'attachment', 'meta_key' => '_mwai_file_id', 'meta_value' => $refId ] );
147 if ( $posts ) {
148 foreach ( $posts as $post ) {
149 return wp_get_attachment_url( $post->ID );
150 }
151 }
152 }
153 return null;
154 }
155
156 #region REST endpoints
157
158 public function rest_api_init() {
159 register_rest_route( $this->namespace, '/files/upload', array(
160 'methods' => 'POST',
161 'callback' => array( $this, 'rest_upload' ),
162 'permission_callback' => array( $this->core, 'check_rest_nonce' )
163 ) );
164 register_rest_route( $this->namespace, '/files/list', array(
165 'methods' => 'POST',
166 'callback' => array( $this, 'rest_list' ),
167 'permission_callback' => array( $this->core, 'check_rest_nonce' )
168 ) );
169 register_rest_route( $this->namespace, '/files/delete', array(
170 'methods' => 'POST',
171 'callback' => array( $this, 'rest_delete' ),
172 'permission_callback' => array( $this->core, 'check_rest_nonce' )
173 ) );
174 }
175
176
177 /*
178 * Record a new file in the Files database.
179 * This doesn't handle the upload or anything.
180 */
181 public function commit_file( $fileInfo ) {
182 if ( !$this->check_db() ) {
183 throw new Exception( 'Could not create database table.' );
184 }
185 $now = date( 'Y-m-d H:i:s' );
186 if ( empty( $fileInfo['refId'] ) ) {
187 if ( !empty( $fileInfo['url'] ) ) {
188 $fileInfo['redId'] = $this->generate_refId( $fileInfo['url'] );
189 }
190 else {
191 throw new Exception( 'File ID (or URL) is required.' );
192 }
193 }
194 $success = $this->wpdb->insert( $this->table_files, [
195 'refId' => $fileInfo['refId'],
196 'envId' => empty( $fileInfo['envId'] ) ? null : $fileInfo['envId'],
197 'userId' => empty( $fileInfo['userId'] ) ? $this->core->get_user_id() : $fileInfo['userId'],
198 'purpose' => empty( $fileInfo['purpose'] ) ? null : $fileInfo['purpose'],
199 'type' => empty( $fileInfo['type'] ) ? null : $fileInfo['type'],
200 'status' => empty( $fileInfo['status'] ) ? null : $fileInfo['status'],
201 'created' => empty( $fileInfo['created'] ) ? $now : $fileInfo['created'],
202 'updated' => empty( $fileInfo['updated'] ) ? $now : $fileInfo['updated'],
203 'expires' => empty( $fileInfo['expires'] ) ? null : $fileInfo['expires'],
204 'path' => empty( $fileInfo['path'] ) ? null : $fileInfo['path'],
205 'url' => empty( $fileInfo['url'] ) ? null : $fileInfo['url']
206 ] );
207 // check for error
208 if ( !$success ) {
209 throw new Exception( 'Error while adding file in the DB (' . $this->wpdb->last_error . ')' );
210 }
211 return $this->wpdb->insert_id;
212 }
213
214 // Generate a refId from a URL or random, and make sure it's unique
215 public function generate_refId( $url = null ) {
216 $refId = null;
217 if ( $url ) {
218 $refId = md5( $url );
219 $file = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT *
220 FROM $this->table_files
221 WHERE refId = %s", $refId
222 ) );
223 if ( $file ) {
224 $refId = md5( $url . date( 'Y-m-d H:i:s' ) );
225 }
226 }
227 else {
228 $refId = md5( date( 'Y-m-d H:i:s' ) );
229 }
230 return $refId;
231 }
232
233 public function upload_file( $path, $filename = null, $purpose = null,
234 $metadata = null, $envId = null, $target = null, $expiry = null ) {
235 require_once( ABSPATH . 'wp-admin/includes/image.php' );
236 require_once( ABSPATH . 'wp-admin/includes/file.php' );
237 require_once( ABSPATH . 'wp-admin/includes/media.php' );
238
239 $target = empty( $target ) ? $this->core->get_option( 'image_local_upload' ) : $target;
240 $expiry = empty( $expiry ) ? $this->core->get_option( 'image_expires' ) : $expiry;
241 if ( $purpose === 'assistant-in' || $purpose === 'assistant-out' ) {
242 // If it's an upload for an assistant, it's better to avoid having the file in the Media Library
243 // (and therefore, to only have it in the uploads folder) and to have it to never expire.
244 $target = 'uploads';
245 $expiry = null;
246 }
247
248 $expires = ( $expiry === 'never' || empty( $expiry ) ) ? null : date( 'Y-m-d H:i:s', time() + intval( $expiry ) );
249 $refId = null;
250 $url = null;
251 if ( empty( $filename ) ) {
252 $parsed_url = parse_url( $path, PHP_URL_PATH );
253 $filename = basename( $parsed_url );
254 $extension = pathinfo( $filename, PATHINFO_EXTENSION );
255 $filename = md5( $filename . date( 'Y-m-d-H-i-s' ) ) . '.' . $extension;
256 }
257 else {
258 $filename = basename( $filename );
259 }
260 $unique_filename = wp_unique_filename( wp_upload_dir()['path'], $filename );
261 $destination = wp_upload_dir()['path'] . '/' . $unique_filename;
262
263 if ( $target === 'uploads' ) {
264 if ( !$this->check_db() ) {
265 throw new Exception( 'Could not create database table.' );
266 }
267 if ( !copy( $path, $destination ) ) {
268 throw new Exception( 'Could not move the file.' );
269 }
270 $url = wp_upload_dir()['url'] . '/' . $unique_filename;
271 $refId = $this->generate_refId( $url );
272 $now = date( 'Y-m-d H:i:s' );
273 $fileId = $this->commit_file( [
274 'refId' => $refId,
275 'envId' => $envId,
276 'purpose' => $purpose,
277 'type' => 'image',
278 'status' => 'uploaded',
279 'created' => $now,
280 'updated' => $now,
281 'expires' => $expires,
282 'path' => $destination,
283 'url' => $url
284 ] );
285 if ( $metadata && is_array( $metadata ) ) {
286 foreach ( $metadata as $metaKey => $metaValue ) {
287 $this->add_metadata( $fileId, $metaKey, $metaValue );
288 }
289 }
290
291 }
292 else if ( $target === 'library' ) {
293 if ( filter_var( $path, FILTER_VALIDATE_URL ) ) {
294 $tmp = download_url( $path );
295 if ( is_wp_error( $tmp ) ) {
296 throw new Exception( $tmp->get_error_message() );
297 }
298 $file_array = [ 'name' => $unique_filename, 'tmp_name' => $tmp ];
299 }
300 else {
301 $file_array = [ 'name' => $unique_filename, 'tmp_name' => $path ];
302 }
303 $id = media_handle_sideload( $file_array, 0 );
304 if ( is_wp_error( $id ) ) {
305 throw new Exception( $id->get_error_message() );
306 }
307 $url = wp_get_attachment_url( $id );
308 $refId = md5( $url );
309 update_post_meta( $id, '_mwai_file_id', $refId );
310 update_post_meta( $id, '_mwai_file_expires', $expires );
311 }
312
313 return $refId;
314 }
315
316 public function add_metadata( $fileId, $metaKey, $metaValue ) {
317 $data = [
318 'file_id' => $fileId,
319 'meta_key' => $metaKey,
320 'meta_value' => $metaValue
321 ];
322 $res = $this->wpdb->insert( $this->table_filemeta, $data );
323 if ( $res === false ) {
324 error_log( "AI Engine: Error while writing files metadata (" . $this->wpdb->last_error . ")" );
325 return false;
326 }
327 return $this->wpdb->insert_id;
328 }
329
330 public function update_refId( $fileId, $refId ) {
331 if ( $this->check_db() ) {
332 $this->wpdb->update( $this->table_files, [ 'refId' => $refId ], [ 'id' => $fileId ] );
333 }
334 }
335
336 public function update_envId( $fileId, $envId ) {
337 if ( $this->check_db() ) {
338 $this->wpdb->update( $this->table_files, [ 'envId' => $envId ], [ 'id' => $fileId ] );
339 }
340 }
341
342 public function get_metadata( $refId, $fileId = null ) {
343 if ( !$fileId ) {
344 $fileId = $this->get_id_from_refId( $refId );
345 }
346 if ( $fileId ) {
347 $sql = $this->wpdb->prepare( "SELECT * FROM $this->table_filemeta WHERE file_id = %d", $fileId );
348 $metadata = $this->wpdb->get_results( $sql, ARRAY_A );
349 $meta = [];
350 foreach ( $metadata as $metaItem ) {
351 $meta[$metaItem['meta_key']] = $metaItem['meta_value'];
352 }
353 return $meta;
354 }
355 return null;
356 }
357
358 public function search( $userId = null, $purpose = null, $metadata = [], $envId = null ) {
359 list( $sql, $params ) = $this->_buildQuery( $userId, $purpose, $metadata, $envId, true );
360 $finalQuery = $this->wpdb->prepare( $sql, $params );
361 $files = $this->wpdb->get_results( $finalQuery, ARRAY_A );
362 foreach ( $files as &$file ) {
363 $file['metadata'] = $this->get_metadata( $file['refId'] );
364 }
365 return $files;
366 }
367
368 public function list( $userId = null, $purpose = null, $metadata = [],
369 $envId = null, $limit = 10, $offset = 0 )
370 {
371 list( $countSql, $countParams ) = $this->_buildQuery( $userId, $purpose, $metadata, $envId, false );
372 $total = $this->wpdb->get_var( $this->wpdb->prepare( $countSql, $countParams ) );
373
374 list( $fileSql, $fileParams ) = $this->_buildQuery( $userId, $purpose, $metadata, $envId, true );
375 if ( $limit ) {
376 $fileSql .= " LIMIT %d";
377 $fileParams[] = $limit;
378 }
379 if ( $offset ) {
380 $fileSql .= " OFFSET %d";
381 $fileParams[] = $offset;
382 }
383 $files = $this->wpdb->get_results( $this->wpdb->prepare( $fileSql, $fileParams ), ARRAY_A );
384 foreach ( $files as &$file ) {
385 $file['metadata'] = $this->get_metadata( $file['refId'] );
386 }
387 return [ 'files' => $files, 'total' => $total ];
388 }
389
390 private function _buildQuery( $userId, $purpose, $metadata, $envId, $selectStar ) {
391 $sql = $selectStar ? "SELECT * FROM $this->table_files WHERE 1=1" : "SELECT COUNT(*) FROM $this->table_files WHERE 1=1";
392 $params = [];
393
394 // Based on the old "search" function
395 $actualUserId = $this->core->get_user_id();
396 $canAdmin = $this->core->can_access_settings();
397 if ( $userId !== $actualUserId ) {
398 if ( !$canAdmin ) {
399 throw new Exception( 'You are not allowed to access files from another user.' );
400 }
401 }
402 if ( $userId ) {
403 $sql .= " AND userId = %d";
404 $params[] = $userId;
405 }
406 if ( $purpose ) {
407 if ( is_array( $purpose ) ) {
408 $sql .= " AND (";
409 foreach ( $purpose as $p ) {
410 $sql .= " purpose = %s OR";
411 $params[] = $p;
412 }
413 $sql = rtrim( $sql, 'OR' );
414 $sql .= ")";
415 }
416 else {
417 $sql .= " AND purpose = %s";
418 $params[] = $purpose;
419 }
420 }
421 if ( $metadata ) {
422 foreach ( $metadata as $metaKey => $metaValue ) {
423 $sql .= " AND EXISTS ( SELECT * FROM $this->table_filemeta
424 WHERE file_id = $this->table_files.id AND meta_key = %s AND meta_value = %s )";
425 $params[] = $metaKey;
426 $params[] = $metaValue;
427 }
428 }
429 if ( $envId ) {
430 $sql .= " AND envId = %s";
431 $params[] = $envId;
432 }
433 $sql .= " ORDER BY updated DESC";
434 return [ $sql, $params ];
435 }
436
437 // public function search( $userId = null, $purpose = null, $metadata = [], $limit = 10, $offset = 0 ) {
438 // $sql = "SELECT * FROM $this->table_files WHERE 1=1";
439 // $actualUserId = $this->core->get_user_id();
440 // $canAdmin = $this->core->can_access_settings();
441 // if ( $userId !== $actualUserId ) {
442 // if ( !$canAdmin ) {
443 // throw new Exception( 'You are not allowed to access files from another user.' );
444 // }
445 // }
446 // if ( $userId ) {
447 // $sql .= $this->wpdb->prepare( " AND userId = %d", $userId );
448 // }
449 // if ( $purpose ) {
450 // if ( is_array( $purpose ) ) {
451 // $sql .= " AND (";
452 // foreach ( $purpose as $p ) {
453 // $sql .= $this->wpdb->prepare( " purpose = %s OR", $p );
454 // }
455 // $sql = rtrim( $sql, 'OR' );
456 // $sql .= ")";
457 // }
458 // else {
459 // $sql .= $this->wpdb->prepare( " AND purpose = %s", $purpose );
460 // }
461 // }
462 // if ( $metadata ) {
463 // foreach ( $metadata as $metaKey => $metaValue ) {
464 // $sql .= $this->wpdb->prepare( " AND EXISTS ( SELECT * FROM $this->table_filemeta
465 // WHERE file_id = $this->table_files.id AND meta_key = %s AND meta_value = %s )",
466 // $metaKey, $metaValue
467 // );
468 // }
469 // }
470 // $sql .= " ORDER BY updated DESC";
471 // if ( $limit ) {
472 // $sql .= $this->wpdb->prepare( " LIMIT %d", $limit );
473 // }
474 // if ( $offset ) {
475 // $sql .= $this->wpdb->prepare( " OFFSET %d", $offset );
476 // }
477 // $files = $this->wpdb->get_results( $sql, ARRAY_A );
478
479 // // Add metadata
480 // foreach ( $files as &$file ) {
481 // $file['metadata'] = $this->get_metadata( $file['refId'] );
482 // }
483
484 // return $files;
485 // }
486
487 public function get_id_from_refId( $refId ) {
488 $file = null;
489 if ( $this->check_db() ) {
490 $file = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT *
491 FROM $this->table_files
492 WHERE refId = %s", $refId
493 ) );
494 }
495 if ( $file ) {
496 return $file->id;
497 }
498 return null;
499 }
500
501 public function add_metadata_from_refId( $refId, $metaKey, $metaValue ) {
502 $fileId = $this->get_id_from_refId( $refId );
503 if ( $fileId ) {
504 return $this->add_metadata( $fileId, $metaKey, $metaValue );
505 }
506 return false;
507 }
508
509 public function rest_list( $request ) {
510 $params = $request->get_json_params();
511 $userId = empty( $params['userId'] ) ? null : $params['userId'];
512 $envId = empty( $params['envId'] ) ? null : $params['envId'];
513 $purpose = empty( $params['purpose'] ) ? null : $params['purpose'];
514 $metadata = empty( $params['metadata'] ) ? null : json_decode( $params['metadata'], true );
515 $limit = empty( $params['limit'] ) ? 10 : intval( $params['limit'] );
516 $offset = empty( $params['page'] ) ? 0 : ( intval( $params['page'] ) - 1) * $limit;
517 $files = $this->list( $userId, $purpose, $metadata, $envId, $limit, $offset );
518 return new WP_REST_Response( [ 'success' => true, 'data' => $files ], 200 );
519 }
520
521 public function rest_delete( $request ) {
522 $params = $request->get_json_params();
523 $fileIds = empty( $params['files'] ) ? [] : $params['files'];
524 $this->delete_files( $fileIds );
525 return new WP_REST_Response( [ 'success' => true ], 200 );
526 }
527
528 public function delete_files( $fileIds ) {
529 $query = "SELECT refId, path FROM $this->table_files WHERE id IN (";
530 $params = [];
531 foreach ( $fileIds as $fileId ) {
532 $query .= "%s,";
533 $params[] = $fileId;
534 }
535 $query = rtrim( $query, ',' );
536 $query .= ")";
537 $files = $this->wpdb->get_results( $this->wpdb->prepare( $query, $params ), ARRAY_A );
538 $refIds = apply_filters( 'mwai_files_delete', array_column( $files, 'refId' ) );
539 foreach ( $files as $file ) {
540 if ( in_array( $file['refId'], $refIds ) ) {
541 $this->wpdb->delete( $this->table_files, [ 'refId' => $file['refId'] ] );
542 if ( file_exists( $file['path'] ) ) {
543 unlink( $file['path'] );
544 }
545 }
546 }
547 }
548
549 public function rest_upload() {
550 if ( empty( $_FILES['file'] ) ) {
551 return new WP_REST_Response( [ 'success' => false, 'message' => 'No file provided.' ], 400 );
552 }
553 $file = $_FILES['file'];
554 $purpose = empty( $_POST['purpose'] ) ? null : $_POST['purpose'];
555 $metadata = empty( $_POST['metadata'] ) ? null : json_decode( $_POST['metadata'], true );
556 $envId = empty( $_POST['envId'] ) ? null : $_POST['envId'];
557 if ( !$purpose ) {
558 return new WP_REST_Response( [ 'success' => false, 'message' => 'Purpose is required.' ], 400 );
559 }
560 $fileTypeCheck = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'] );
561 if ( !$fileTypeCheck['type'] ) {
562 return new WP_REST_Response( [ 'success' => false, 'message' => 'Invalid file type.' ], 400 );
563 }
564
565 try {
566 $refId = $this->upload_file( $file['tmp_name'], $file['name'], $purpose, $metadata, $envId );
567 $url = $this->get_url( $refId );
568 return new WP_REST_Response( [
569 'success' => true,
570 'data' => [ 'id' => $refId, 'url' => $url ]
571 ], 200 );
572 }
573 catch ( Exception $e ) {
574 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
575 }
576 }
577
578 #endregion
579
580 #region Database functions
581
582 function create_db() {
583 $charset_collate = $this->wpdb->get_charset_collate();
584 $sql = "CREATE TABLE $this->table_files (
585 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
586 refId VARCHAR(64) NOT NULL,
587 envId VARCHAR(128) NULL,
588 userId BIGINT(20) UNSIGNED NULL,
589 type VARCHAR(32) NULL,
590 status VARCHAR(32) NULL,
591 purpose VARCHAR(32) NULL,
592 created DATETIME NOT NULL,
593 updated DATETIME NOT NULL,
594 expires DATETIME NULL,
595 path TEXT NULL,
596 url TEXT NULL,
597 PRIMARY KEY (id),
598 UNIQUE KEY unique_file_id (refId)
599 ) $charset_collate;";
600
601 $sqlFileMeta = "CREATE TABLE $this->table_filemeta (
602 meta_id BIGINT(20) NOT NULL AUTO_INCREMENT,
603 file_id BIGINT(20) NOT NULL,
604 meta_key varchar(255) NULL,
605 meta_value longtext NULL,
606 PRIMARY KEY (meta_id)
607 ) $charset_collate;";
608
609 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
610 dbDelta( $sql );
611 dbDelta( $sqlFileMeta );
612 }
613
614 function check_db() {
615 if ( $this->db_check ) {
616 return true;
617 }
618
619 // Check if table_files exists
620 $sql = $this->wpdb->prepare( "SHOW TABLES LIKE %s", $this->table_files );
621 $table_files_exists = strtolower( $this->wpdb->get_var( $sql )) === strtolower( $this->table_files );
622
623 // Check if table_filemeta exists
624 $sqlMeta = $this->wpdb->prepare( "SHOW TABLES LIKE %s", $this->table_filemeta );
625 $table_filemeta_exists = strtolower( $this->wpdb->get_var( $sqlMeta )) === strtolower( $this->table_filemeta );
626
627 // If either table does not exist, create them
628 if ( !$table_files_exists || !$table_filemeta_exists ) {
629 $this->create_db();
630 }
631
632 // Update db_check for both tables
633 $this->db_check = $table_files_exists && $table_filemeta_exists;
634
635 // LATER: REMOVE THIS AFTER MARCH 2024
636 $this->db_check = $this->db_check && $this->wpdb->get_var( "SHOW COLUMNS FROM $this->table_files LIKE 'userId'" );
637 if ( !$this->db_check ) {
638 $this->wpdb->query( "ALTER TABLE $this->table_files ADD COLUMN userId BIGINT(20) UNSIGNED NULL" );
639 $this->wpdb->query( "ALTER TABLE $this->table_files ADD COLUMN purpose VARCHAR(32) NULL" );
640 $this->wpdb->query( "ALTER TABLE $this->table_files MODIFY COLUMN path TEXT NULL" );
641 $this->wpdb->query( "ALTER TABLE $this->table_files DROP COLUMN metadata" );
642 $this->db_check = true;
643 }
644 // LATER: REMOVE THIS AFTER MARCH 2024
645 $this->db_check = $this->db_check && !$this->wpdb->get_var( "SHOW COLUMNS FROM $this->table_files LIKE 'fileId'" );
646 if ( !$this->db_check ) {
647 $this->wpdb->query( "ALTER TABLE $this->table_files ADD COLUMN refId VARCHAR(64) NOT NULL" );
648 $this->wpdb->query( "ALTER TABLE $this->table_files DROP COLUMN fileId" );
649 $this->db_check = true;
650 }
651 // LATER: REMOVE THIS AFTER MARCH 2024
652 $this->db_check = $this->db_check && $this->wpdb->get_var( "SHOW COLUMNS FROM $this->table_files LIKE 'envId'" );
653 if ( !$this->db_check ) {
654 $this->wpdb->query( "ALTER TABLE $this->table_files ADD COLUMN envId VARCHAR(128) NULL" );
655 $this->db_check = true;
656 }
657 return $this->db_check;
658 }
659
660 #endregion
661 }