PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.4.2
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.4.2
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
advisor.php 2 years ago 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
694 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 is_image( $refId ) {
114 $info = $this->get_info( $refId );
115 return $info['type'] === 'image';
116 }
117
118 public function get_mime_type( $refId ) {
119 $path = $this->get_path( $refId );
120 if ( $path ) {
121 return Meow_MWAI_Core::get_mime_type( $path );
122 }
123 $url = $this->get_url( $refId );
124 if ( $url ) {
125 return Meow_MWAI_Core::get_mime_type( $url );
126 }
127 return null;
128 }
129
130 public function get_data( $refId ) {
131 $path = $this->get_path( $refId );
132 if ( $path ) {
133 $content = file_get_contents( $path );
134 return $content;
135 }
136 return null;
137 }
138
139 public function get_info( $refId ) {
140 $info = null;
141 if ( $this->check_db() ) {
142 $info = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT *
143 FROM $this->table_files
144 WHERE refId = %s", $refId
145 ), ARRAY_A );
146 }
147 if ( !$info ) {
148 $posts = get_posts( [ 'post_type' => 'attachment', 'meta_key' => '_mwai_file_id', 'meta_value' => $refId ] );
149 if ( $posts ) {
150 $post = $posts[0];
151 $info = [
152 'refId' => $refId,
153 'url' => wp_get_attachment_url( $post->ID ),
154 'path' => get_attached_file( $post->ID )
155 ];
156 }
157 }
158 if ( $info ) {
159 $info['metadata'] = $this->get_metadata( $refId );
160 }
161 return $info;
162 }
163
164 public function get_url( $refId ) {
165 $file = null;
166 if ( $this->check_db() ) {
167 $file = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT *
168 FROM $this->table_files
169 WHERE refId = %s", $refId
170 ) );
171 }
172 if ( $file ) {
173 return $file->url;
174 }
175 else {
176 $posts = get_posts( [ 'post_type' => 'attachment', 'meta_key' => '_mwai_file_id', 'meta_value' => $refId ] );
177 if ( $posts ) {
178 foreach ( $posts as $post ) {
179 return wp_get_attachment_url( $post->ID );
180 }
181 }
182 }
183 return null;
184 }
185
186 #region REST endpoints
187
188 public function rest_api_init() {
189 register_rest_route( $this->namespace, '/files/upload', array(
190 'methods' => 'POST',
191 'callback' => array( $this, 'rest_upload' ),
192 'permission_callback' => array( $this->core, 'check_rest_nonce' )
193 ) );
194 register_rest_route( $this->namespace, '/files/list', array(
195 'methods' => 'POST',
196 'callback' => array( $this, 'rest_list' ),
197 'permission_callback' => array( $this->core, 'check_rest_nonce' )
198 ) );
199 register_rest_route( $this->namespace, '/files/delete', array(
200 'methods' => 'POST',
201 'callback' => array( $this, 'rest_delete' ),
202 'permission_callback' => array( $this->core, 'check_rest_nonce' )
203 ) );
204 }
205
206
207 /*
208 * Record a new file in the Files database.
209 * This doesn't handle the upload or anything.
210 */
211 public function commit_file( $fileInfo ) {
212 if ( !$this->check_db() ) {
213 throw new Exception( 'Could not create database table.' );
214 }
215 $now = date( 'Y-m-d H:i:s' );
216 if ( empty( $fileInfo['refId'] ) ) {
217 if ( !empty( $fileInfo['url'] ) ) {
218 $fileInfo['redId'] = $this->generate_refId( $fileInfo['url'] );
219 }
220 else {
221 throw new Exception( 'File ID (or URL) is required.' );
222 }
223 }
224 if ( empty( $fileInfo['type'] ) ) {
225 $fileInfo['type'] = Meow_MWAI_Core::is_image( $fileInfo['url'] ) ? 'image' : 'file';
226 }
227 $success = $this->wpdb->insert( $this->table_files, [
228 'refId' => $fileInfo['refId'],
229 'envId' => empty( $fileInfo['envId'] ) ? null : $fileInfo['envId'],
230 'userId' => empty( $fileInfo['userId'] ) ? $this->core->get_user_id() : $fileInfo['userId'],
231 'purpose' => empty( $fileInfo['purpose'] ) ? null : $fileInfo['purpose'],
232 'type' => empty( $fileInfo['type'] ) ? null : $fileInfo['type'],
233 'status' => empty( $fileInfo['status'] ) ? null : $fileInfo['status'],
234 'created' => empty( $fileInfo['created'] ) ? $now : $fileInfo['created'],
235 'updated' => empty( $fileInfo['updated'] ) ? $now : $fileInfo['updated'],
236 'expires' => empty( $fileInfo['expires'] ) ? null : $fileInfo['expires'],
237 'path' => empty( $fileInfo['path'] ) ? null : $fileInfo['path'],
238 'url' => empty( $fileInfo['url'] ) ? null : $fileInfo['url']
239 ] );
240 // check for error
241 if ( !$success ) {
242 throw new Exception( 'Error while adding file in the DB (' . $this->wpdb->last_error . ')' );
243 }
244 return $this->wpdb->insert_id;
245 }
246
247 // Generate a refId from a URL or random, and make sure it's unique
248 public function generate_refId( $url = null ) {
249 $refId = null;
250 if ( $url ) {
251 $refId = md5( $url );
252 $file = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT *
253 FROM $this->table_files
254 WHERE refId = %s", $refId
255 ) );
256 if ( $file ) {
257 $refId = md5( $url . date( 'Y-m-d H:i:s' ) );
258 }
259 }
260 else {
261 $refId = md5( date( 'Y-m-d H:i:s' ) );
262 }
263 return $refId;
264 }
265
266 public function upload_file( $path, $filename = null, $purpose = null,
267 $metadata = null, $envId = null, $target = null, $expiry = null ) {
268 require_once( ABSPATH . 'wp-admin/includes/image.php' );
269 require_once( ABSPATH . 'wp-admin/includes/file.php' );
270 require_once( ABSPATH . 'wp-admin/includes/media.php' );
271
272 $target = empty( $target ) ? $this->core->get_option( 'image_local_upload' ) : $target;
273 $expiry = empty( $expiry ) ? $this->core->get_option( 'image_expires' ) : $expiry;
274
275 $expires = ( $expiry === 'never' || empty( $expiry ) ) ? null : date( 'Y-m-d H:i:s', time() + intval( $expiry ) );
276 $refId = null;
277 $url = null;
278 if ( empty( $filename ) ) {
279 $parsed_url = parse_url( $path, PHP_URL_PATH );
280 $filename = basename( $parsed_url );
281 $extension = pathinfo( $filename, PATHINFO_EXTENSION );
282 $filename = md5( $filename . date( 'Y-m-d-H-i-s' ) ) . '.' . $extension;
283 }
284 else {
285 $filename = basename( $filename );
286 }
287 $unique_filename = wp_unique_filename( wp_upload_dir()['path'], $filename );
288 $destination = wp_upload_dir()['path'] . '/' . $unique_filename;
289
290 if ( $target === 'uploads' ) {
291 if ( !$this->check_db() ) {
292 throw new Exception( 'Could not create database table.' );
293 }
294 if ( !copy( $path, $destination ) ) {
295 throw new Exception( 'Could not move the file.' );
296 }
297 $url = wp_upload_dir()['url'] . '/' . $unique_filename;
298 $refId = $this->generate_refId( $url );
299 $now = date( 'Y-m-d H:i:s' );
300 $fileId = $this->commit_file( [
301 'refId' => $refId,
302 'envId' => $envId,
303 'purpose' => $purpose,
304 'type' => null,
305 'status' => 'uploaded',
306 'created' => $now,
307 'updated' => $now,
308 'expires' => $expires,
309 'path' => $destination,
310 'url' => $url
311 ] );
312 if ( $metadata && is_array( $metadata ) ) {
313 foreach ( $metadata as $metaKey => $metaValue ) {
314 $this->add_metadata( $fileId, $metaKey, $metaValue );
315 }
316 }
317
318 }
319 else if ( $target === 'library' ) {
320 if ( filter_var( $path, FILTER_VALIDATE_URL ) ) {
321 $tmp = download_url( $path );
322 if ( is_wp_error( $tmp ) ) {
323 throw new Exception( $tmp->get_error_message() );
324 }
325 $file_array = [ 'name' => $unique_filename, 'tmp_name' => $tmp ];
326 }
327 else {
328 $file_array = [ 'name' => $unique_filename, 'tmp_name' => $path ];
329 }
330 $id = media_handle_sideload( $file_array, 0 );
331 if ( is_wp_error( $id ) ) {
332 throw new Exception( $id->get_error_message() );
333 }
334 $url = wp_get_attachment_url( $id );
335 $refId = md5( $url );
336 update_post_meta( $id, '_mwai_file_id', $refId );
337 update_post_meta( $id, '_mwai_file_expires', $expires );
338 }
339
340 return $refId;
341 }
342
343 public function add_metadata( $fileId, $metaKey, $metaValue ) {
344 $data = [
345 'file_id' => $fileId,
346 'meta_key' => $metaKey,
347 'meta_value' => $metaValue
348 ];
349 $res = $this->wpdb->insert( $this->table_filemeta, $data );
350 if ( $res === false ) {
351 $this->core->log( "⚠️ (Files) Error while writing files metadata (" . $this->wpdb->last_error . ")" );
352 return false;
353 }
354 return $this->wpdb->insert_id;
355 }
356
357 public function update_refId( $fileId, $refId ) {
358 if ( $this->check_db() ) {
359 $this->wpdb->update( $this->table_files, [ 'refId' => $refId ], [ 'id' => $fileId ] );
360 }
361 }
362
363 public function update_purpose( $fileId, $purpose ) {
364 if ( $this->check_db() ) {
365 $this->wpdb->update( $this->table_files, [ 'purpose' => $purpose ], [ 'id' => $fileId ] );
366 }
367 }
368
369 public function update_envId( $fileId, $envId ) {
370 if ( $this->check_db() ) {
371 $this->wpdb->update( $this->table_files, [ 'envId' => $envId ], [ 'id' => $fileId ] );
372 }
373 }
374
375 public function get_metadata( $refId, $fileId = null ) {
376 if ( !$fileId ) {
377 $fileId = $this->get_id_from_refId( $refId );
378 }
379 if ( $fileId ) {
380 $sql = $this->wpdb->prepare( "SELECT * FROM $this->table_filemeta WHERE file_id = %d", $fileId );
381 $metadata = $this->wpdb->get_results( $sql, ARRAY_A );
382 $meta = [];
383 foreach ( $metadata as $metaItem ) {
384 $meta[$metaItem['meta_key']] = $metaItem['meta_value'];
385 }
386 return $meta;
387 }
388 return null;
389 }
390
391 public function search( $userId = null, $purpose = null, $metadata = [], $envId = null ) {
392 list( $sql, $params ) = $this->_buildQuery( $userId, $purpose, $metadata, $envId, true );
393 $finalQuery = $this->wpdb->prepare( $sql, $params );
394 $files = $this->wpdb->get_results( $finalQuery, ARRAY_A );
395 foreach ( $files as &$file ) {
396 $file['metadata'] = $this->get_metadata( $file['refId'] );
397 }
398 return $files;
399 }
400
401 public function list( $userId = null, $purpose = null, $metadata = [],
402 $envId = null, $limit = 10, $offset = 0 )
403 {
404 list( $countSql, $countParams ) = $this->_buildQuery( $userId, $purpose, $metadata, $envId, false );
405 $total = $this->wpdb->get_var( $this->wpdb->prepare( $countSql, $countParams ) );
406
407 list( $fileSql, $fileParams ) = $this->_buildQuery( $userId, $purpose, $metadata, $envId, true );
408 if ( $limit ) {
409 $fileSql .= " LIMIT %d";
410 $fileParams[] = $limit;
411 }
412 if ( $offset ) {
413 $fileSql .= " OFFSET %d";
414 $fileParams[] = $offset;
415 }
416 $files = $this->wpdb->get_results( $this->wpdb->prepare( $fileSql, $fileParams ), ARRAY_A );
417 foreach ( $files as &$file ) {
418 $file['metadata'] = $this->get_metadata( $file['refId'] );
419 }
420 return [ 'files' => $files, 'total' => $total ];
421 }
422
423 private function _buildQuery( $userId, $purpose, $metadata, $envId, $selectStar ) {
424 $sql = $selectStar ? "SELECT * FROM $this->table_files WHERE 1=1" : "SELECT COUNT(*) FROM $this->table_files WHERE 1=1";
425 $params = [];
426
427 // Based on the old "search" function
428 $actualUserId = $this->core->get_user_id();
429 $canAdmin = $this->core->can_access_settings();
430 if ( $userId !== $actualUserId ) {
431 if ( !$canAdmin ) {
432 throw new Exception( 'You are not allowed to access files from another user.' );
433 }
434 }
435 if ( $userId ) {
436 $sql .= " AND userId = %d";
437 $params[] = $userId;
438 }
439 if ( $purpose ) {
440 if ( is_array( $purpose ) ) {
441 $sql .= " AND (";
442 foreach ( $purpose as $p ) {
443 $sql .= " purpose = %s OR";
444 $params[] = $p;
445 }
446 $sql = rtrim( $sql, 'OR' );
447 $sql .= ")";
448 }
449 else {
450 $sql .= " AND purpose = %s";
451 $params[] = $purpose;
452 }
453 }
454 if ( $metadata ) {
455 foreach ( $metadata as $metaKey => $metaValue ) {
456 $sql .= " AND EXISTS ( SELECT * FROM $this->table_filemeta
457 WHERE file_id = $this->table_files.id AND meta_key = %s AND meta_value = %s )";
458 $params[] = $metaKey;
459 $params[] = $metaValue;
460 }
461 }
462 if ( $envId ) {
463 $sql .= " AND envId = %s";
464 $params[] = $envId;
465 }
466 $sql .= " ORDER BY updated DESC";
467 return [ $sql, $params ];
468 }
469
470 // public function search( $userId = null, $purpose = null, $metadata = [], $limit = 10, $offset = 0 ) {
471 // $sql = "SELECT * FROM $this->table_files WHERE 1=1";
472 // $actualUserId = $this->core->get_user_id();
473 // $canAdmin = $this->core->can_access_settings();
474 // if ( $userId !== $actualUserId ) {
475 // if ( !$canAdmin ) {
476 // throw new Exception( 'You are not allowed to access files from another user.' );
477 // }
478 // }
479 // if ( $userId ) {
480 // $sql .= $this->wpdb->prepare( " AND userId = %d", $userId );
481 // }
482 // if ( $purpose ) {
483 // if ( is_array( $purpose ) ) {
484 // $sql .= " AND (";
485 // foreach ( $purpose as $p ) {
486 // $sql .= $this->wpdb->prepare( " purpose = %s OR", $p );
487 // }
488 // $sql = rtrim( $sql, 'OR' );
489 // $sql .= ")";
490 // }
491 // else {
492 // $sql .= $this->wpdb->prepare( " AND purpose = %s", $purpose );
493 // }
494 // }
495 // if ( $metadata ) {
496 // foreach ( $metadata as $metaKey => $metaValue ) {
497 // $sql .= $this->wpdb->prepare( " AND EXISTS ( SELECT * FROM $this->table_filemeta
498 // WHERE file_id = $this->table_files.id AND meta_key = %s AND meta_value = %s )",
499 // $metaKey, $metaValue
500 // );
501 // }
502 // }
503 // $sql .= " ORDER BY updated DESC";
504 // if ( $limit ) {
505 // $sql .= $this->wpdb->prepare( " LIMIT %d", $limit );
506 // }
507 // if ( $offset ) {
508 // $sql .= $this->wpdb->prepare( " OFFSET %d", $offset );
509 // }
510 // $files = $this->wpdb->get_results( $sql, ARRAY_A );
511
512 // // Add metadata
513 // foreach ( $files as &$file ) {
514 // $file['metadata'] = $this->get_metadata( $file['refId'] );
515 // }
516
517 // return $files;
518 // }
519
520 public function get_id_from_refId( $refId ) {
521 $file = null;
522 if ( $this->check_db() ) {
523 $file = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT *
524 FROM $this->table_files
525 WHERE refId = %s", $refId
526 ) );
527 }
528 if ( $file ) {
529 return $file->id;
530 }
531 return null;
532 }
533
534 public function add_metadata_from_refId( $refId, $metaKey, $metaValue ) {
535 $fileId = $this->get_id_from_refId( $refId );
536 if ( $fileId ) {
537 return $this->add_metadata( $fileId, $metaKey, $metaValue );
538 }
539 return false;
540 }
541
542 public function rest_list( $request ) {
543 $params = $request->get_json_params();
544 $userId = empty( $params['userId'] ) ? null : $params['userId'];
545 $envId = empty( $params['envId'] ) ? null : $params['envId'];
546 $purpose = empty( $params['purpose'] ) ? null : $params['purpose'];
547 $metadata = empty( $params['metadata'] ) ? null : json_decode( $params['metadata'], true );
548 $limit = empty( $params['limit'] ) ? 10 : intval( $params['limit'] );
549 $offset = empty( $params['page'] ) ? 0 : ( intval( $params['page'] ) - 1) * $limit;
550 $files = $this->list( $userId, $purpose, $metadata, $envId, $limit, $offset );
551 return new WP_REST_Response( [ 'success' => true, 'data' => $files ], 200 );
552 }
553
554 public function rest_delete( $request ) {
555 $params = $request->get_json_params();
556 $fileIds = empty( $params['files'] ) ? [] : $params['files'];
557 $this->delete_files( $fileIds );
558 return new WP_REST_Response( [ 'success' => true ], 200 );
559 }
560
561 public function delete_files( $fileIds ) {
562 $query = "SELECT refId, path FROM $this->table_files WHERE id IN (";
563 $params = [];
564 foreach ( $fileIds as $fileId ) {
565 $query .= "%s,";
566 $params[] = $fileId;
567 }
568 $query = rtrim( $query, ',' );
569 $query .= ")";
570 $files = $this->wpdb->get_results( $this->wpdb->prepare( $query, $params ), ARRAY_A );
571 $refIds = apply_filters( 'mwai_files_delete', array_column( $files, 'refId' ) );
572 foreach ( $files as $file ) {
573 if ( in_array( $file['refId'], $refIds ) ) {
574 $this->wpdb->delete( $this->table_files, [ 'refId' => $file['refId'] ] );
575 if ( file_exists( $file['path'] ) ) {
576 unlink( $file['path'] );
577 }
578 }
579 }
580 }
581
582 public function rest_upload() {
583 if ( empty( $_FILES['file'] ) ) {
584 return new WP_REST_Response( [ 'success' => false, 'message' => 'No file provided.' ], 400 );
585 }
586 $file = $_FILES['file'];
587 $purpose = empty( $_POST['purpose'] ) ? null : $_POST['purpose'];
588 $metadata = empty( $_POST['metadata'] ) ? null : json_decode( $_POST['metadata'], true );
589 $envId = empty( $_POST['envId'] ) ? null : $_POST['envId'];
590 if ( !$purpose ) {
591 return new WP_REST_Response( [ 'success' => false, 'message' => 'Purpose is required.' ], 400 );
592 }
593 $fileTypeCheck = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'] );
594 if ( !$fileTypeCheck['type'] ) {
595 return new WP_REST_Response( [ 'success' => false, 'message' => 'Invalid file type.' ], 400 );
596 }
597
598 try {
599 $refId = $this->upload_file( $file['tmp_name'], $file['name'], $purpose, $metadata, $envId );
600 $url = $this->get_url( $refId );
601 return new WP_REST_Response( [
602 'success' => true,
603 'data' => [ 'id' => $refId, 'url' => $url ]
604 ], 200 );
605 }
606 catch ( Exception $e ) {
607 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
608 }
609 }
610
611 #endregion
612
613 #region Database functions
614
615 function create_db() {
616 $charset_collate = $this->wpdb->get_charset_collate();
617 $sql = "CREATE TABLE $this->table_files (
618 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
619 refId VARCHAR(64) NOT NULL,
620 envId VARCHAR(128) NULL,
621 userId BIGINT(20) UNSIGNED NULL,
622 type VARCHAR(32) NULL,
623 status VARCHAR(32) NULL,
624 purpose VARCHAR(32) NULL,
625 created DATETIME NOT NULL,
626 updated DATETIME NOT NULL,
627 expires DATETIME NULL,
628 path TEXT NULL,
629 url TEXT NULL,
630 PRIMARY KEY (id),
631 UNIQUE KEY unique_file_id (refId)
632 ) $charset_collate;";
633
634 $sqlFileMeta = "CREATE TABLE $this->table_filemeta (
635 meta_id BIGINT(20) NOT NULL AUTO_INCREMENT,
636 file_id BIGINT(20) NOT NULL,
637 meta_key varchar(255) NULL,
638 meta_value longtext NULL,
639 PRIMARY KEY (meta_id)
640 ) $charset_collate;";
641
642 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
643 dbDelta( $sql );
644 dbDelta( $sqlFileMeta );
645 }
646
647 function check_db() {
648 if ( $this->db_check ) {
649 return true;
650 }
651
652 // Check if table_files exists
653 $sql = $this->wpdb->prepare( "SHOW TABLES LIKE %s", $this->table_files );
654 $table_files_exists = strtolower( $this->wpdb->get_var( $sql )) === strtolower( $this->table_files );
655
656 // Check if table_filemeta exists
657 $sqlMeta = $this->wpdb->prepare( "SHOW TABLES LIKE %s", $this->table_filemeta );
658 $table_filemeta_exists = strtolower( $this->wpdb->get_var( $sqlMeta )) === strtolower( $this->table_filemeta );
659
660 // If either table does not exist, create them
661 if ( !$table_files_exists || !$table_filemeta_exists ) {
662 $this->create_db();
663 }
664
665 // Update db_check for both tables
666 $this->db_check = $table_files_exists && $table_filemeta_exists;
667
668 // LATER: REMOVE THIS AFTER MARCH 2024
669 $this->db_check = $this->db_check && $this->wpdb->get_var( "SHOW COLUMNS FROM $this->table_files LIKE 'userId'" );
670 if ( !$this->db_check ) {
671 $this->wpdb->query( "ALTER TABLE $this->table_files ADD COLUMN userId BIGINT(20) UNSIGNED NULL" );
672 $this->wpdb->query( "ALTER TABLE $this->table_files ADD COLUMN purpose VARCHAR(32) NULL" );
673 $this->wpdb->query( "ALTER TABLE $this->table_files MODIFY COLUMN path TEXT NULL" );
674 $this->wpdb->query( "ALTER TABLE $this->table_files DROP COLUMN metadata" );
675 $this->db_check = true;
676 }
677 // LATER: REMOVE THIS AFTER MARCH 2024
678 $this->db_check = $this->db_check && !$this->wpdb->get_var( "SHOW COLUMNS FROM $this->table_files LIKE 'fileId'" );
679 if ( !$this->db_check ) {
680 $this->wpdb->query( "ALTER TABLE $this->table_files ADD COLUMN refId VARCHAR(64) NOT NULL" );
681 $this->wpdb->query( "ALTER TABLE $this->table_files DROP COLUMN fileId" );
682 $this->db_check = true;
683 }
684 // LATER: REMOVE THIS AFTER MARCH 2024
685 $this->db_check = $this->db_check && $this->wpdb->get_var( "SHOW COLUMNS FROM $this->table_files LIKE 'envId'" );
686 if ( !$this->db_check ) {
687 $this->wpdb->query( "ALTER TABLE $this->table_files ADD COLUMN envId VARCHAR(128) NULL" );
688 $this->db_check = true;
689 }
690 return $this->db_check;
691 }
692
693 #endregion
694 }