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