Changeset 3448768
- Timestamp:
- 01/28/2026 02:14:29 PM (6 weeks ago)
- Location:
- integrate-dropbox
- Files:
-
- 2 edited
-
tags/1.3.4/models/Files.php (modified) (50 diffs)
-
trunk/models/Files.php (modified) (50 diffs)
Legend:
- Unmodified
- Added
- Removed
-
integrate-dropbox/tags/1.3.4/models/Files.php
r3448719 r3448768 9 9 use CodeConfig\IDB\Utils\MimeTypeManager; 10 10 use CodeConfig\IDB\Utils\Singleton; 11 use WP_Error; 12 11 13 use function count; 12 14 use function in_array; 13 15 use function intval; 14 16 use function is_array; 15 use WP_Error; 16 class Files extends BaseModel { 17 18 class Files extends BaseModel 19 { 17 20 use Singleton; 18 21 public const TABLE_NAME = 'ccpidb_files'; … … 56 59 public const META_DATA = ['attachmentId', 'mediaInfo', 'childCount']; 57 60 58 public function __construct() { 59 parent::__construct( self::TABLE_NAME ); 61 public function __construct() 62 { 63 parent::__construct(self::TABLE_NAME); 60 64 // $this->getChildPathsWithThumbnails(['7618d90767fefcb7d21109071e707ae1'], 'id:XR5DPLkvVHQAAAAAAAADAA'); 61 65 // $this->getChildPathsWithThumbnails(['7618d90767fefcb7d21109071e707ae1'], 'id:XR5DPLkvVHQAAAAAAAADAA'); … … 71 75 * @return array|null|WP_Error An array of processed file data from the specified folder. 72 76 */ 73 public function getFolder( $folderKey, $config = [] ) { 77 public function getFolder($folderKey, $config = []) 78 { 74 79 global $wpdb; 75 80 $allowedOrderBy = [ … … 79 84 'size' 80 85 ]; 81 $order = $this->sanitizeOrder( $config['order'] ?? 'DESC');82 $orderBy = $this->sanitizeOrderBy( $config['orderBy'] ?? 'createdAt', $allowedOrderBy);83 $page = ( isset( $config['page'] ) ? (int) $config['page'] : 1);84 $perPage = ( isset( $config['perPage'] ) ? (int) $config['perPage'] : 20);85 $pagination = $this->sanitizePagination( $page, $perPage);86 if ( $folderKey !== '/') {87 $file = $this->getFile( $folderKey);88 if ( $file instanceof File === false || is_wp_error( $file )) {89 return new WP_Error(404, __( 'Folder not found.', 'integrate-dropbox'));86 $order = $this->sanitizeOrder($config['order'] ?? 'DESC'); 87 $orderBy = $this->sanitizeOrderBy($config['orderBy'] ?? 'createdAt', $allowedOrderBy); 88 $page = (isset($config['page']) ? (int) $config['page'] : 1); 89 $perPage = (isset($config['perPage']) ? (int) $config['perPage'] : 20); 90 $pagination = $this->sanitizePagination($page, $perPage); 91 if ($folderKey !== '/') { 92 $file = $this->getFile($folderKey); 93 if ($file instanceof File === false || is_wp_error($file)) { 94 return new WP_Error(404, __('Folder not found.', 'integrate-dropbox')); 90 95 } 91 96 $accountId = $file->getAccountId(); … … 93 98 } else { 94 99 $account = Accounts::getInstance()->getAccount(); 95 if ( $account instanceof Account === false || is_wp_error( $account )) {96 return new WP_Error(404, __( 'Account not found.', 'integrate-dropbox'));100 if ($account instanceof Account === false || is_wp_error($account)) { 101 return new WP_Error(404, __('Account not found.', 'integrate-dropbox')); 97 102 } 98 103 $accountId = $account->getId(); 99 104 $path = '/'; 100 105 } 101 if ( !current_user_can( 'manage_options' ) && !wp_doing_cron()) {102 if ( !is_user_logged_in()) {103 return new WP_Error('unauthorized', __( 'You must be logged in to access this folder.', 'integrate-dropbox'));106 if (!current_user_can('manage_options') && !wp_doing_cron()) { 107 if (!is_user_logged_in()) { 108 return new WP_Error('unauthorized', __('You must be logged in to access this folder.', 'integrate-dropbox')); 104 109 } 105 110 $user = wp_get_current_user(); 106 if ( !$user instanceof \WP_User) {107 return new WP_Error('unauthorized', __( 'You must be logged in to access this folder.', 'integrate-dropbox'));111 if (!$user instanceof \WP_User) { 112 return new WP_Error('unauthorized', __('You must be logged in to access this folder.', 'integrate-dropbox')); 108 113 } 109 114 $userName = $user->user_login; 110 115 $roles = $user->roles; 111 $accessSettings = UserAccess::getInstance()->getAccessData( $userName, $roles);112 if ( empty( $accessSettings )) {113 if ( !current_user_can( 'manage_options' )) {114 return new WP_Error('forbidden', __( 'You do not have permission to access this folder.', 'integrate-dropbox'));116 $accessSettings = UserAccess::getInstance()->getAccessData($userName, $roles); 117 if (empty($accessSettings)) { 118 if (!current_user_can('manage_options')) { 119 return new WP_Error('forbidden', __('You do not have permission to access this folder.', 'integrate-dropbox')); 115 120 } 116 121 } else { 117 122 $accessSettingsFolders = $accessSettings['folders'] ?? []; 118 if ( empty( $accessSettingsFolders ) || !is_array( $accessSettingsFolders )) {119 return new WP_Error('forbidden', __( 'You do not have permission to access this folder.', 'integrate-dropbox'));123 if (empty($accessSettingsFolders) || !is_array($accessSettingsFolders)) { 124 return new WP_Error('forbidden', __('You do not have permission to access this folder.', 'integrate-dropbox')); 120 125 } 121 if ( $folderKey === '/') {122 $folder = $this->getFilesByKeys( $accessSettingsFolders, [126 if ($folderKey === '/') { 127 $folder = $this->getFilesByKeys($accessSettingsFolders, [ 123 128 'returnType' => 'array', 124 129 'perPage' => $config['perPage'], … … 127 132 'order' => $config['order'], 128 133 'recursive' => false, 129 ] );134 ]); 130 135 return $folder; 131 136 } 132 if ( !Helpers::validateFileKey( $folderKey, $accessSettingsFolders )) {133 return new WP_Error('forbidden', __( 'You do not have permission to access this folder.', 'integrate-dropbox'));137 if (!Helpers::validateFileKey($folderKey, $accessSettingsFolders)) { 138 return new WP_Error('forbidden', __('You do not have permission to access this folder.', 'integrate-dropbox')); 134 139 } 135 140 } 136 141 } 137 $sql = $wpdb->prepare( "SELECT * FROM %i WHERE accountId = %s", $this->tableName, $accountId);138 $totalSql = $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE accountId = %s", $this->tableName, $accountId);139 if ( !empty( $config['search'] )) {140 $searchPattern = '%' . $wpdb->esc_like( $config['search'] ?? '') . '%';141 $searchScope = ( in_array( $config['searchScope'] ?? 'folder', ['folder', 'global'] ) ? $config['searchScope'] : 'folder');142 $sql .= $wpdb->prepare( " AND name LIKE %s", $searchPattern);143 $totalSql .= $wpdb->prepare( " AND name LIKE %s", $searchPattern);144 if ( $searchScope === 'folder') {145 $sql .= $wpdb->prepare( " AND (path LIKE %s OR parent LIKE %s)", "{$path}%", "{$path}%");146 $totalSql .= $wpdb->prepare( " AND (path LIKE %s OR parent LIKE %s)", "{$path}%", "{$path}%");142 $sql = $wpdb->prepare("SELECT * FROM %i WHERE accountId = %s", $this->tableName, $accountId); 143 $totalSql = $wpdb->prepare("SELECT COUNT(*) FROM %i WHERE accountId = %s", $this->tableName, $accountId); 144 if (!empty($config['search'])) { 145 $searchPattern = '%' . $wpdb->esc_like($config['search'] ?? '') . '%'; 146 $searchScope = (in_array($config['searchScope'] ?? 'folder', ['folder', 'global']) ? $config['searchScope'] : 'folder'); 147 $sql .= $wpdb->prepare(" AND name LIKE %s", $searchPattern); 148 $totalSql .= $wpdb->prepare(" AND name LIKE %s", $searchPattern); 149 if ($searchScope === 'folder') { 150 $sql .= $wpdb->prepare(" AND (path LIKE %s OR parent LIKE %s)", "{$path}%", "{$path}%"); 151 $totalSql .= $wpdb->prepare(" AND (path LIKE %s OR parent LIKE %s)", "{$path}%", "{$path}%"); 147 152 } 148 153 } else { 149 if ( !empty( $config['recursive'] ) && $config['recursive'] === true) {150 $sql .= $wpdb->prepare( " AND parent LIKE %s", "{$path}%");151 $totalSql .= $wpdb->prepare( " AND parent LIKE %s", "{$path}%");154 if (!empty($config['recursive']) && $config['recursive'] === true) { 155 $sql .= $wpdb->prepare(" AND parent LIKE %s", "{$path}%"); 156 $totalSql .= $wpdb->prepare(" AND parent LIKE %s", "{$path}%"); 152 157 } else { 153 $sql .= $wpdb->prepare( " AND parent = %s", $path);154 $totalSql .= $wpdb->prepare( " AND parent = %s", $path);155 } 156 } 157 if ( !empty( $config['types'] ) && is_array( $config['types'] )) {158 $sql .= $wpdb->prepare(" AND parent = %s", $path); 159 $totalSql .= $wpdb->prepare(" AND parent = %s", $path); 160 } 161 } 162 if (!empty($config['types']) && is_array($config['types'])) { 158 163 $types = $config['types']; 159 $extensions = MimeTypeManager::getExtensionsByCategory( $types);160 $placeholders = implode( ',', array_fill( 0, count( $extensions ), '%s' ));161 $sql .= $wpdb->prepare( " AND `extension` IN ({$placeholders})", $extensions);162 $totalSql .= $wpdb->prepare( " AND `extension` IN ({$placeholders})", $extensions);164 $extensions = MimeTypeManager::getExtensionsByCategory($types); 165 $placeholders = implode(',', array_fill(0, count($extensions), '%s')); 166 $sql .= $wpdb->prepare(" AND `extension` IN ({$placeholders})", $extensions); 167 $totalSql .= $wpdb->prepare(" AND `extension` IN ({$placeholders})", $extensions); 163 168 } 164 169 $sql .= $wpdb->prepare( … … 168 173 $pagination['offset'] 169 174 ); 170 $cache_key = "ccpidb_folder_" . md5( $sql);171 $cache_group = "ccpidb_files_" . md5( "{$path}_{$accountId}");172 $files = wp_cache_get( $cache_key, $cache_group);173 if ( $files !== false) {175 $cache_key = "ccpidb_folder_" . md5($sql); 176 $cache_group = "ccpidb_files_" . md5("{$path}_{$accountId}"); 177 $files = wp_cache_get($cache_key, $cache_group); 178 if ($files !== false) { 174 179 return $files; 175 180 } 176 181 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching WordPress.DB.PreparedSQL.NotPrepared 177 $files = $wpdb->get_results( $sql);182 $files = $wpdb->get_results($sql); 178 183 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching WordPress.DB.PreparedSQL.NotPrepared 179 $total = $wpdb->get_var( $totalSql);180 if ( empty( $files ) || is_wp_error( $files )) {184 $total = $wpdb->get_var($totalSql); 185 if (empty($files) || is_wp_error($files)) { 181 186 $files = []; 182 187 } 183 $processedFiles = $this->processFiles( $files);184 $totalPages = ceil( $total / $perPage);188 $processedFiles = $this->processFiles($files); 189 $totalPages = ceil($total / $perPage); 185 190 $hasMore = $totalPages > $page; 186 191 $response = [ 187 'breadcrumb' => array_reverse( $this->getBreadcrumbByKey( $folderKey )),188 'totalPage' => ( $totalPages < 1 ? 1 : $totalPages),192 'breadcrumb' => array_reverse($this->getBreadcrumbByKey($folderKey)), 193 'totalPage' => ($totalPages < 1 ? 1 : $totalPages), 189 194 'hasMore' => $hasMore, 190 195 'currentPage' => $page, 191 196 'files' => $processedFiles, 192 'totalFiles' => intval( $total),193 ]; 194 if ( $hasMore) {197 'totalFiles' => intval($total), 198 ]; 199 if ($hasMore) { 195 200 $response['nextPage'] = $page + 1; 196 201 } 197 if ( !empty( $processedFiles )) {202 if (!empty($processedFiles)) { 198 203 wp_cache_set( 199 204 $cache_key, … … 219 224 * @return \CodeConfig\IDB\App\File|array|false The processed file data if found, otherwise null. 220 225 */ 221 public function getFile( $fileKey, $returnType = 'object' ) { 226 public function getFile($fileKey, $returnType = 'object') 227 { 222 228 global $wpdb; 223 229 // if ($this->isValidAccount($accountId) === false) { … … 226 232 // $file = $this->fetch("SELECT * FROM {$this->tableName} WHERE id = %s AND accountId = %s", [$id, $accountId]); 227 233 $cache_key = "ccpidb_file_{$fileKey}_{$returnType}"; 228 $file = wp_cache_get( $cache_key, 'ccpidb_files');229 if ( $file !== false) {234 $file = wp_cache_get($cache_key, 'ccpidb_files'); 235 if ($file !== false) { 230 236 return $file; 231 237 } 232 238 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 233 $file = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ));234 if ( empty( $file ) || is_wp_error( $file )) {235 return false; 236 } 237 $processedFile = $this->processFile( $file, $returnType);238 if ( !empty( $processedFile )) {239 wp_cache_set( $cache_key, $processedFile, 'ccpidb_files');239 $file = $wpdb->get_row($wpdb->prepare("SELECT * FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 240 if (empty($file) || is_wp_error($file)) { 241 return false; 242 } 243 $processedFile = $this->processFile($file, $returnType); 244 if (!empty($processedFile)) { 245 wp_cache_set($cache_key, $processedFile, 'ccpidb_files'); 240 246 } 241 247 return $processedFile; 242 248 } 243 249 244 private function getFileByPath( $path, $accountId, $returnType = 'object' ) { 250 private function getFileByPath($path, $accountId, $returnType = 'object') 251 { 245 252 global $wpdb; 246 253 $cache_key = "ccpidb_file_{$path}_{$accountId}"; 247 $file = wp_cache_get( $cache_key, 'ccpidb_files');248 if ( $file !== false) {254 $file = wp_cache_get($cache_key, 'ccpidb_files'); 255 if ($file !== false) { 249 256 return $file; 250 257 } 251 $file = $wpdb->get_row( $wpdb->prepare(258 $file = $wpdb->get_row($wpdb->prepare( 252 259 "SELECT * FROM %i WHERE path = %s AND accountId = %s", 253 260 $this->tableName, 254 261 $path, 255 262 $accountId 256 ) );257 $processedFile = $this->processFile( $file, $returnType);258 if ( !empty( $processedFile )) {259 wp_cache_set( $cache_key, $processedFile, 'ccpidb_files');263 )); 264 $processedFile = $this->processFile($file, $returnType); 265 if (!empty($processedFile)) { 266 wp_cache_set($cache_key, $processedFile, 'ccpidb_files'); 260 267 } 261 268 return $processedFile; 262 269 } 263 270 264 public function getFilesByKeys( array $keys, array $args = [] ) { 265 if ( empty( $keys ) ) { 271 public function getFilesByKeys(array $keys, array $args = []) 272 { 273 if (empty($keys)) { 266 274 return []; 267 275 } … … 279 287 'accountId' => '', 280 288 ]; 281 $args = wp_parse_args( $args, $defaults);289 $args = wp_parse_args($args, $defaults); 282 290 $recursive = $args['recursive']; 283 291 $moduleType = $args['moduleType'] ?? ''; 284 292 $accountId = $args['accountId'] ?? null; 285 if ( 'search-box' === $moduleType && empty( $args['search'] ) && $recursive && ($args['fileKey'] ?? '') !== '/') {293 if ('search-box' === $moduleType && empty($args['search']) && $recursive && ($args['fileKey'] ?? '') !== '/') { 286 294 $moduleType = 'file-browser'; 287 295 } … … 296 304 $applyNamesFilter = $args['applyNameFilter'] ?? []; 297 305 $types = $args['types'] ?? []; 298 $extensions = ccpidbGetAllowedModuleExtensions( $moduleType ); 299 $allowedExtensions = $this->processExtensions( $extensions, $additionalExtensions, $extensionsFilterType ); 300 ob_start(); 301 echo '<pre>'; 302 var_dump( $accountId ); 303 echo '</pre>'; 304 error_log( ob_get_clean() ); 305 $filesData = $this->getFileAttributesByKeys( $keys, [ 306 $extensions = ccpidbGetAllowedModuleExtensions($moduleType); 307 $allowedExtensions = $this->processExtensions($extensions, $additionalExtensions, $extensionsFilterType); 308 309 $filesData = $this->getFileAttributesByKeys($keys, [ 306 310 'id', 307 311 'path', … … 309 313 'name', 310 314 'isDir' 311 ], $accountId );312 if ( is_wp_error( $filesData ) || empty( $filesData )) {313 return ( $filesData ?: []);314 } 315 if ( empty( $filesData )) {315 ], $accountId); 316 if (is_wp_error($filesData) || empty($filesData)) { 317 return ($filesData ?: []); 318 } 319 if (empty($filesData)) { 316 320 return []; 317 321 } 318 $paths = array_filter( array_map( fn( $file ) => $file['path'] ?? null, $filesData ));319 if ( empty( $paths )) {322 $paths = array_filter(array_map(fn ($file) => $file['path'] ?? null, $filesData)); 323 if (empty($paths)) { 320 324 return []; 321 325 } 322 326 global $wpdb; 323 $sql = $wpdb->prepare( "SELECT * FROM %i WHERE 1 = 1", $this->tableName);324 $totalSql = $wpdb->prepare( "SELECT COUNT(*) as count FROM %i WHERE 1 = 1", $this->tableName);325 if ( !empty( $search )) {327 $sql = $wpdb->prepare("SELECT * FROM %i WHERE 1 = 1", $this->tableName); 328 $totalSql = $wpdb->prepare("SELECT COUNT(*) as count FROM %i WHERE 1 = 1", $this->tableName); 329 if (!empty($search)) { 326 330 $searchPath = []; 327 if ( $searchScope === 'global') {328 foreach ( $filesData as $file) {329 $searchPath[] = $this->getSuccessors( $file['path'], $file['accountId']);331 if ($searchScope === 'global') { 332 foreach ($filesData as $file) { 333 $searchPath[] = $this->getSuccessors($file['path'], $file['accountId']); 330 334 } 331 $paths = array_merge( ...$searchPath);332 } 333 if ( empty( $paths )) {335 $paths = array_merge(...$searchPath); 336 } 337 if (empty($paths)) { 334 338 return []; 335 339 } 336 $placeholders = implode( ',', array_fill( 0, count( $paths ), '%s' ));337 if ( $searchScope === 'global') {338 $sql .= $wpdb->prepare( " AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge( $paths, $paths, ["%{$search}%"] ));339 $totalSql .= $wpdb->prepare( " AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge( $paths, $paths, ["%{$search}%"] ));340 $placeholders = implode(',', array_fill(0, count($paths), '%s')); 341 if ($searchScope === 'global') { 342 $sql .= $wpdb->prepare(" AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge($paths, $paths, ["%{$search}%"])); 343 $totalSql .= $wpdb->prepare(" AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge($paths, $paths, ["%{$search}%"])); 340 344 } else { 341 $sql .= $wpdb->prepare( " AND (`parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge( $paths, ["%{$search}%"] ));342 $totalSql .= $wpdb->prepare( " AND (`parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge( $paths, ["%{$search}%"] ));343 } 344 } elseif ( $recursive) {345 $placeholders = implode( ',', array_fill( 0, count( $paths ), '%s' ));346 if ( $moduleType === 'file-browser') {347 $sql .= $wpdb->prepare( " AND `parent` IN ({$placeholders})", $paths);348 $totalSql .= $wpdb->prepare( " AND `parent` IN ({$placeholders})", $paths);349 } elseif ( $moduleType === 'file-uploader') {350 $uploadKeys = json_decode( sanitize_text_field( wp_unslash( $_COOKIE["ccpidb_file_uploader_files_{$shortcodeId}"] ?? '' ) ), true);351 if ( empty( $uploadKeys ) || !is_array( $uploadKeys )) {345 $sql .= $wpdb->prepare(" AND (`parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge($paths, ["%{$search}%"])); 346 $totalSql .= $wpdb->prepare(" AND (`parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge($paths, ["%{$search}%"])); 347 } 348 } elseif ($recursive) { 349 $placeholders = implode(',', array_fill(0, count($paths), '%s')); 350 if ($moduleType === 'file-browser') { 351 $sql .= $wpdb->prepare(" AND `parent` IN ({$placeholders})", $paths); 352 $totalSql .= $wpdb->prepare(" AND `parent` IN ({$placeholders})", $paths); 353 } elseif ($moduleType === 'file-uploader') { 354 $uploadKeys = json_decode(sanitize_text_field(wp_unslash($_COOKIE["ccpidb_file_uploader_files_{$shortcodeId}"] ?? '')), true); 355 if (empty($uploadKeys) || !is_array($uploadKeys)) { 352 356 return []; 353 357 } 354 $uploadKeysPlaceholders = implode( ',', array_fill( 0, count( $uploadKeys ), '%s' ));355 $sql .= $wpdb->prepare( " AND `parent` IN ({$placeholders}) AND `fileKey` IN ({$uploadKeysPlaceholders})", array_merge( $paths, $uploadKeys ));356 $totalSql .= $wpdb->prepare( " AND `parent` IN ({$placeholders}) AND `fileKey` IN ({$uploadKeysPlaceholders})", array_merge( $paths, $uploadKeys ));358 $uploadKeysPlaceholders = implode(',', array_fill(0, count($uploadKeys), '%s')); 359 $sql .= $wpdb->prepare(" AND `parent` IN ({$placeholders}) AND `fileKey` IN ({$uploadKeysPlaceholders})", array_merge($paths, $uploadKeys)); 360 $totalSql .= $wpdb->prepare(" AND `parent` IN ({$placeholders}) AND `fileKey` IN ({$uploadKeysPlaceholders})", array_merge($paths, $uploadKeys)); 357 361 } else { 358 $sql .= $wpdb->prepare( " AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders}))", ...$paths, ...$paths);359 $totalSql .= $wpdb->prepare( " AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders}))", ...$paths, ...$paths);362 $sql .= $wpdb->prepare(" AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders}))", ...$paths, ...$paths); 363 $totalSql .= $wpdb->prepare(" AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders}))", ...$paths, ...$paths); 360 364 // foreach ($paths as $path) { 361 365 // $sql .= $wpdb->prepare(" AND (`path` LIKE %s OR `parent` LIKE %s)", "$path%", "$path%"); … … 364 368 } 365 369 } else { 366 if ( !empty( $allowedExtensions ) && !in_array( 'folder', $allowedExtensions )) {370 if (!empty($allowedExtensions) && !in_array('folder', $allowedExtensions)) { 367 371 $allowedExtensions[] = 'folder'; 368 372 } 369 $placeholders = implode( ',', array_fill( 0, count( $paths ), '%s' ));370 $sql .= $wpdb->prepare( " AND `path` IN ({$placeholders})", $paths);371 $totalSql .= $wpdb->prepare( " AND `path` IN ({$placeholders})", $paths);372 } 373 if ( !empty( $types ) && is_array( $types )) {374 $extensions = MimeTypeManager::getExtensionsByCategory( $types);375 $extPlaceholders = implode( ',', array_fill( 0, count( $extensions ), '%s' ));376 $sql .= $wpdb->prepare( " AND `extension` IN ({$extPlaceholders})", $extensions);377 $totalSql .= $wpdb->prepare( " AND `extension` IN ({$extPlaceholders})", $extensions);378 } 379 if ( !empty( $allowedExtensions )) {380 $extPlaceholders = implode( ',', array_fill( 0, count( $allowedExtensions ), '%s' ));381 $sql .= $wpdb->prepare( " AND `extension` IN ({$extPlaceholders})", $allowedExtensions);382 $totalSql .= $wpdb->prepare( " AND `extension` IN ({$extPlaceholders})", $allowedExtensions);383 } 384 if ( !empty( $args['orderBy'] ) && !empty( $args['order'] )) {373 $placeholders = implode(',', array_fill(0, count($paths), '%s')); 374 $sql .= $wpdb->prepare(" AND `path` IN ({$placeholders})", $paths); 375 $totalSql .= $wpdb->prepare(" AND `path` IN ({$placeholders})", $paths); 376 } 377 if (!empty($types) && is_array($types)) { 378 $extensions = MimeTypeManager::getExtensionsByCategory($types); 379 $extPlaceholders = implode(',', array_fill(0, count($extensions), '%s')); 380 $sql .= $wpdb->prepare(" AND `extension` IN ({$extPlaceholders})", $extensions); 381 $totalSql .= $wpdb->prepare(" AND `extension` IN ({$extPlaceholders})", $extensions); 382 } 383 if (!empty($allowedExtensions)) { 384 $extPlaceholders = implode(',', array_fill(0, count($allowedExtensions), '%s')); 385 $sql .= $wpdb->prepare(" AND `extension` IN ({$extPlaceholders})", $allowedExtensions); 386 $totalSql .= $wpdb->prepare(" AND `extension` IN ({$extPlaceholders})", $allowedExtensions); 387 } 388 if (!empty($args['orderBy']) && !empty($args['order'])) { 385 389 $allowedOrderBy = [ 386 390 'id', … … 390 394 'updatedAt' 391 395 ]; 392 $orderBy = $this->sanitizeOrderBy( $args['orderBy'], $allowedOrderBy);393 $order = $this->sanitizeOrder( $args['order']);394 $offset = $this->sanitizePagination( $args['page'], $args['perPage']);395 $sql .= $wpdb->prepare( " ORDER BY (CASE WHEN extension = 'folder' THEN 0 ELSE 1 END), `{$orderBy}` {$order} LIMIT %d OFFSET %d", $offset['perPage'], $offset['offset']);396 $orderBy = $this->sanitizeOrderBy($args['orderBy'], $allowedOrderBy); 397 $order = $this->sanitizeOrder($args['order']); 398 $offset = $this->sanitizePagination($args['page'], $args['perPage']); 399 $sql .= $wpdb->prepare(" ORDER BY (CASE WHEN extension = 'folder' THEN 0 ELSE 1 END), `{$orderBy}` {$order} LIMIT %d OFFSET %d", $offset['perPage'], $offset['offset']); 396 400 } 397 401 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching WordPress.DB.PreparedSQL.NotPrepared 398 $files = $wpdb->get_results( $sql);402 $files = $wpdb->get_results($sql); 399 403 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching WordPress.DB.PreparedSQL.NotPrepared 400 $totalCount = $wpdb->get_row( $totalSql);401 if ( empty( $files ) || is_wp_error( $files ) || is_wp_error( $totalCount )) {404 $totalCount = $wpdb->get_row($totalSql); 405 if (empty($files) || is_wp_error($files) || is_wp_error($totalCount)) { 402 406 return []; 403 407 } 404 $files = $this->processFiles( $files, $returnType);405 $totalFiles = ( isset( $totalCount->count ) ? (int) $totalCount->count : count( $files ));408 $files = $this->processFiles($files, $returnType); 409 $totalFiles = (isset($totalCount->count) ? (int) $totalCount->count : count($files)); 406 410 $page = $offset['page'] ?? 1; 407 $totalPage = ceil( $totalFiles / ($offset['perPage'] ?? 1));411 $totalPage = ceil($totalFiles / ($offset['perPage'] ?? 1)); 408 412 $response = [ 409 413 'breadcrumb' => [[ 410 414 'fileKey' => '/', 411 'name' => __( 'Home', 'integrate-dropbox'),415 'name' => __('Home', 'integrate-dropbox'), 412 416 ]], 413 'totalPage' => ( $totalPage < 1 ? 1 : $totalPage),417 'totalPage' => ($totalPage < 1 ? 1 : $totalPage), 414 418 'hasMore' => $totalPage > $page, 415 419 'currentPage' => $page, … … 417 421 'totalFiles' => $totalFiles, 418 422 ]; 419 if ( $response['hasMore']) {423 if ($response['hasMore']) { 420 424 $response['nextPage'] = $page + 1; 421 425 } … … 423 427 } 424 428 425 public function getFolderTree( $accountId, ... $rootPaths ) { 429 public function getFolderTree($accountId, ... $rootPaths) 430 { 426 431 global $wpdb; 427 432 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 428 $sql = $wpdb->prepare( "SELECT fileKey, name, parent, path FROM %i WHERE `extension` = 'folder' AND accountId = %s", $this->tableName, $accountId);429 if ( !empty( $rootPaths ) && !in_array( '/', $rootPaths, true )) {433 $sql = $wpdb->prepare("SELECT fileKey, name, parent, path FROM %i WHERE `extension` = 'folder' AND accountId = %s", $this->tableName, $accountId); 434 if (!empty($rootPaths) && !in_array('/', $rootPaths, true)) { 430 435 $sql .= " AND ( 0=1 "; 431 foreach ( $rootPaths as $rootPath) {432 $sql .= $wpdb->prepare( " OR path LIKE %s OR path LIKE %s", "{$rootPath}/%", "{$rootPath}");436 foreach ($rootPaths as $rootPath) { 437 $sql .= $wpdb->prepare(" OR path LIKE %s OR path LIKE %s", "{$rootPath}/%", "{$rootPath}"); 433 438 } 434 439 $sql .= " ) "; 435 440 } 436 $files = $wpdb->get_results( $sql, ARRAY_A);437 if ( empty( $files ) || is_wp_error( $files )) {441 $files = $wpdb->get_results($sql, ARRAY_A); 442 if (empty($files) || is_wp_error($files)) { 438 443 return []; 439 444 } 440 return $this->processFolderTree( $files, $rootPaths ); 441 } 442 443 public function addFile( array $data ) { 445 return $this->processFolderTree($files, $rootPaths); 446 } 447 448 public function addFile(array $data) 449 { 444 450 global $wpdb; 445 451 $file = [ … … 448 454 'path' => $data['path'] ?? '', 449 455 'name' => $data['name'] ?? null, 450 'size' => intval( $data['size'] ?? 0),456 'size' => intval($data['size'] ?? 0), 451 457 'parent' => $data['parent'] ?? null, 452 458 'accountId' => $data['accountId'] ?? '', … … 457 463 'sharedLink' => $data['sharedLink'] ?? null, 458 464 'isDir' => $data['isDir'] ?? null, 459 'permissions' => maybe_serialize( $data['permissions'] ?? []),465 'permissions' => maybe_serialize($data['permissions'] ?? []), 460 466 'hasOwnThumbnail' => $data['hasOwnThumbnail'] ?? null, 461 467 'icon' => $data['icon'] ?? null, 462 'additionalData' => maybe_serialize( $data['additionalData'] ?? []),463 'createdAt' => current_time( 'mysql'),464 'updatedAt' => current_time( 'mysql'),465 ]; 466 if ( empty( $file['fileId'] ) || empty( $file['accountId'] )) {467 return new WP_Error(404, __( 'Missing file ID or account ID.', 'integrate-dropbox'));468 'additionalData' => maybe_serialize($data['additionalData'] ?? []), 469 'createdAt' => current_time('mysql'), 470 'updatedAt' => current_time('mysql'), 471 ]; 472 if (empty($file['fileId']) || empty($file['accountId'])) { 473 return new WP_Error(404, __('Missing file ID or account ID.', 'integrate-dropbox')); 468 474 } 469 475 $format = [ … … 506 512 '%s', 507 513 ]; 508 if ( !empty( $data['thumbnailRatio'] )) {514 if (!empty($data['thumbnailRatio'])) { 509 515 $file['thumbnailRatio'] = $data['thumbnailRatio']; 510 516 $format[] = '%s'; 511 517 // thumbnailRatio 512 518 } 513 if ( $this->isCachedFile( $file["fileKey"] )) {519 if ($this->isCachedFile($file["fileKey"])) { 514 520 unset($file["id"]); 515 521 unset($file["fileId"]); … … 549 555 '%s', 550 556 ]; 551 if ( !empty( $data['thumbnailRatio'] )) {557 if (!empty($data['thumbnailRatio'])) { 552 558 $updateFormat[] = '%s'; 553 559 // thumbnailRatio … … 563 569 ); 564 570 } 565 return $wpdb->insert( $this->tableName, $file, $format ); 566 } 567 568 public function deleteFiles( $fileKeys ) : int { 569 global $wpdb; 570 if ( empty( $fileKeys ) ) { 571 return $wpdb->insert($this->tableName, $file, $format); 572 } 573 574 public function deleteFiles($fileKeys): int 575 { 576 global $wpdb; 577 if (empty($fileKeys)) { 571 578 return 0; 572 579 } 573 580 $fileKeys = (array) $fileKeys; 574 $files = $this->getFileAttributesByKeys( $fileKeys, ['accountId', 'path', 'isDir']);575 if ( empty( $files )) {581 $files = $this->getFileAttributesByKeys($fileKeys, ['accountId', 'path', 'isDir']); 582 if (empty($files)) { 576 583 return 0; 577 584 } 578 585 $filePaths = []; 579 586 $folderRefs = []; 580 foreach ( $files as $file) {581 if ( empty( $file['path'] ) || empty( $file['accountId'] )) {587 foreach ($files as $file) { 588 if (empty($file['path']) || empty($file['accountId'])) { 582 589 continue; 583 590 } 584 if ( !empty( $file['isDir'] )) {591 if (!empty($file['isDir'])) { 585 592 $folderRefs[] = [$file['path'], $file['accountId']]; 586 593 } else { … … 588 595 } 589 596 } 590 foreach ( $folderRefs as [$path, $accountId]) {591 $filePaths = array_merge( $filePaths, (array) $this->getSuccessors( $path, $accountId ));592 } 593 if ( empty( $filePaths )) {597 foreach ($folderRefs as [$path, $accountId]) { 598 $filePaths = array_merge($filePaths, (array) $this->getSuccessors($path, $accountId)); 599 } 600 if (empty($filePaths)) { 594 601 return 0; 595 602 } 596 $placeholders = implode( ',', array_fill( 0, count( $filePaths ), '%s' ) ); 597 return (int) $wpdb->query( $wpdb->prepare( "DELETE FROM %i WHERE path IN ({$placeholders}) OR parent IN ({$placeholders})", array_merge( [$this->tableName], $filePaths, $filePaths ) ) ); 598 } 599 600 public function deleteFilesByAccount( $accountId ) { 601 if ( empty( $accountId ) ) { 603 $placeholders = implode(',', array_fill(0, count($filePaths), '%s')); 604 return (int) $wpdb->query($wpdb->prepare("DELETE FROM %i WHERE path IN ({$placeholders}) OR parent IN ({$placeholders})", array_merge([$this->tableName], $filePaths, $filePaths))); 605 } 606 607 public function deleteFilesByAccount($accountId) 608 { 609 if (empty($accountId)) { 602 610 return 0; 603 611 } 604 return $this->deleteRecords( [612 return $this->deleteRecords([ 605 613 'accountId' => $accountId, 606 ] ); 607 } 608 609 public function isCachedFile( $fileKey ) { 610 global $wpdb; 611 $folder = $wpdb->get_row( $wpdb->prepare( "SELECT fileKey FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 612 return !empty( $folder ); 613 } 614 615 public function getPathById( $fileId ) { 616 global $wpdb; 617 $file = $wpdb->get_row( $wpdb->prepare( "SELECT path FROM %i WHERE fileId = %s", $this->tableName, $fileId ) ); 618 if ( empty( $file ) || is_wp_error( $file ) ) { 614 ]); 615 } 616 617 public function isCachedFile($fileKey) 618 { 619 global $wpdb; 620 $folder = $wpdb->get_row($wpdb->prepare("SELECT fileKey FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 621 return !empty($folder); 622 } 623 624 public function getPathById($fileId) 625 { 626 global $wpdb; 627 $file = $wpdb->get_row($wpdb->prepare("SELECT path FROM %i WHERE fileId = %s", $this->tableName, $fileId)); 628 if (empty($file) || is_wp_error($file)) { 619 629 return false; 620 630 } … … 622 632 } 623 633 624 public function getPathsByKeys( $fileKeys ) { 625 global $wpdb; 626 $isString = \is_string( $fileKeys ); 627 if ( $isString ) { 634 public function getPathsByKeys($fileKeys) 635 { 636 global $wpdb; 637 $isString = \is_string($fileKeys); 638 if ($isString) { 628 639 $fileKeys = [$fileKeys]; 629 640 } 630 $placeholders = implode( ',', array_fill( 0, count( $fileKeys ), '%s' ));631 $preparedQuery = $wpdb->prepare( "SELECT path FROM %i WHERE fileKey IN ({$placeholders})", array_merge( [$this->tableName], $fileKeys ));632 $results = $wpdb->get_results( $preparedQuery);633 if ( $isString && \count( $results ) === 1) {641 $placeholders = implode(',', array_fill(0, count($fileKeys), '%s')); 642 $preparedQuery = $wpdb->prepare("SELECT path FROM %i WHERE fileKey IN ({$placeholders})", array_merge([$this->tableName], $fileKeys)); 643 $results = $wpdb->get_results($preparedQuery); 644 if ($isString && \count($results) === 1) { 634 645 return $results[0]->path; 635 646 } 636 647 $paths = []; 637 foreach ( $results as $row) {648 foreach ($results as $row) { 638 649 $paths[] = $row->path; 639 650 } … … 650 661 global $wpdb; 651 662 // Validate inputs 652 if ( empty( $columns ) || empty( $fileKeys )) {663 if (empty($columns) || empty($fileKeys)) { 653 664 return []; 654 665 } 655 666 // Filter and sanitize columns 656 $sanitizedColumns = array_filter( $columns, fn( $col ) => in_array( $col, self::COLUMNS, true ));657 if ( empty( $sanitizedColumns )) {667 $sanitizedColumns = array_filter($columns, fn ($col) => in_array($col, self::COLUMNS, true)); 668 if (empty($sanitizedColumns)) { 658 669 return []; 659 670 } 660 671 // Validate WHERE clause columns 661 if ( !empty( $where )) {662 foreach ( array_keys( $where ) as $whereColumn) {663 if ( !in_array( $whereColumn, self::COLUMNS, true )) {672 if (!empty($where)) { 673 foreach (array_keys($where) as $whereColumn) { 674 if (!in_array($whereColumn, self::COLUMNS, true)) { 664 675 return []; 665 676 } … … 667 678 } 668 679 // Prepare column identifiers using %i placeholders for security 669 $columnPlaceholders = implode( ',', array_fill( 0, count( $sanitizedColumns ), '%i' ));670 $keyPlaceholders = implode( ',', array_fill( 0, count( $fileKeys ), '%s' ));680 $columnPlaceholders = implode(',', array_fill(0, count($sanitizedColumns), '%i')); 681 $keyPlaceholders = implode(',', array_fill(0, count($fileKeys), '%s')); 671 682 // Build the base query with proper placeholders 672 683 $sql = "SELECT {$columnPlaceholders} FROM %i WHERE fileKey IN ({$keyPlaceholders})"; 673 $params = array_merge( $sanitizedColumns, [$this->tableName], $fileKeys);684 $params = array_merge($sanitizedColumns, [$this->tableName], $fileKeys); 674 685 // Add additional WHERE conditions with proper format specifiers 675 if ( !empty( $where )) {686 if (!empty($where)) { 676 687 $whereConditions = []; 677 688 $whereIndex = 0; 678 foreach ( $where as $column => $value) {689 foreach ($where as $column => $value) { 679 690 // Use provided format or default to %s 680 $format = ( !empty( $where_format[$whereIndex] ) ? $where_format[$whereIndex] : '%s');691 $format = (!empty($where_format[$whereIndex]) ? $where_format[$whereIndex] : '%s'); 681 692 $whereConditions[] = "%i = {$format}"; 682 693 $params[] = $column; … … 684 695 $whereIndex++; 685 696 } 686 $sql .= " AND " . implode( ' AND ', $whereConditions);687 } 688 $preparedQuery = $wpdb->prepare( $sql, $params);689 $results = $wpdb->get_results( $preparedQuery, $returnType);690 if ( empty( $results ) || is_wp_error( $results )) {697 $sql .= " AND " . implode(' AND ', $whereConditions); 698 } 699 $preparedQuery = $wpdb->prepare($sql, $params); 700 $results = $wpdb->get_results($preparedQuery, $returnType); 701 if (empty($results) || is_wp_error($results)) { 691 702 return []; 692 703 } … … 694 705 } 695 706 696 public function getAccountIdByFileKey( $fileKey ) { 697 global $wpdb; 698 $file = $wpdb->get_row( $wpdb->prepare( "SELECT accountId FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 699 if ( empty( $file ) || is_wp_error( $file ) ) { 707 public function getAccountIdByFileKey($fileKey) 708 { 709 global $wpdb; 710 $file = $wpdb->get_row($wpdb->prepare("SELECT accountId FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 711 if (empty($file) || is_wp_error($file)) { 700 712 return false; 701 713 } … … 703 715 } 704 716 705 public function updateThumbnail( $fileKey, $url ) { 717 public function updateThumbnail($fileKey, $url) 718 { 706 719 global $wpdb; 707 720 return $wpdb->update( … … 740 753 $clean = false 741 754 ) { 742 if ( $path === '' || $path === '/' || empty( $accountId )) {743 return new WP_Error(400, __( 'Invalid path or account ID.', 'integrate-dropbox'));744 } 745 global $wpdb; 746 if ( $clean === false) {747 $file = $this->getFileByPath( $path, $accountId, 'array');748 if ( is_wp_error( $file )) {755 if ($path === '' || $path === '/' || empty($accountId)) { 756 return new WP_Error(400, __('Invalid path or account ID.', 'integrate-dropbox')); 757 } 758 global $wpdb; 759 if ($clean === false) { 760 $file = $this->getFileByPath($path, $accountId, 'array'); 761 if (is_wp_error($file)) { 749 762 return $file; 750 763 } 751 764 $existingData = $file['metaData'] ?? []; 752 if ( is_array( $existingData )) {753 $metaData = array_merge( $existingData, $metaData);765 if (is_array($existingData)) { 766 $metaData = array_merge($existingData, $metaData); 754 767 } 755 768 } 756 769 $filteredMetaData = []; 757 foreach ( self::META_DATA as $key) {758 if ( isset( $metaData[$key] )) {770 foreach (self::META_DATA as $key) { 771 if (isset($metaData[$key])) { 759 772 $filteredMetaData[$key] = $metaData[$key]; 760 773 } … … 763 776 $this->tableName, 764 777 [ 765 'metaData' => maybe_serialize( $filteredMetaData),778 'metaData' => maybe_serialize($filteredMetaData), 766 779 ], 767 780 [ … … 774 787 } 775 788 776 public function getBreadcrumbByKey( $key, $args = [] ) { 789 public function getBreadcrumbByKey($key, $args = []) 790 { 777 791 $defaults = [ 778 792 'rootFileKey' => null, 779 793 'rootFolderKey' => '/', 780 794 ]; 781 $args = wp_parse_args( $args, $defaults);795 $args = wp_parse_args($args, $defaults); 782 796 $rootFileKey = $args['rootFileKey']; 783 797 $rootFolderKey = $args['rootFolderKey']; 784 798 $home = [[ 785 799 'fileKey' => '/', 786 'name' => __( 'Home', 'integrate-dropbox'),800 'name' => __('Home', 'integrate-dropbox'), 787 801 ]]; 788 802 // Empty or root key 789 if ( empty( $key ) || $key === '/' || $key === $rootFolderKey) {803 if (empty($key) || $key === '/' || $key === $rootFolderKey) { 790 804 return $home; 791 805 } 792 806 // Resolve root folder key if rootFileKey is provided 793 if ( $rootFileKey !== null && $rootFolderKey === '/') {794 $rootFile = $this->getFile( $rootFileKey, 'array');795 if ( is_wp_error( $rootFile )) {807 if ($rootFileKey !== null && $rootFolderKey === '/') { 808 $rootFile = $this->getFile($rootFileKey, 'array'); 809 if (is_wp_error($rootFile)) { 796 810 return $home; 797 811 } 798 812 $parentPath = $rootFile['parent'] ?? null; 799 if ( $parentPath) {800 $rootFolder = $this->getFileByPath( $parentPath, $rootFile['accountId'] ?? '', 'array');801 if ( !is_wp_error( $rootFolder )) {813 if ($parentPath) { 814 $rootFolder = $this->getFileByPath($parentPath, $rootFile['accountId'] ?? '', 'array'); 815 if (!is_wp_error($rootFolder)) { 802 816 $rootFolderKey = $rootFolder['fileKey'] ?? '/'; 803 817 } 804 818 } 805 819 } 806 $file = $this->getFile( $key, 'array');807 if ( is_wp_error( $file )) {820 $file = $this->getFile($key, 'array'); 821 if (is_wp_error($file)) { 808 822 return $home; 809 823 } 810 824 $fileId = $file['fileId'] ?? null; 811 825 $accountId = $file['accountId'] ?? null; 812 if ( !$fileId || !$accountId) {826 if (!$fileId || !$accountId) { 813 827 return $home; 814 828 } … … 816 830 $breadcrumb = [[ 817 831 'fileKey' => $key, 818 'name' => $file['name'] ?? __( 'Unknown Folder', 'integrate-dropbox'),832 'name' => $file['name'] ?? __('Unknown Folder', 'integrate-dropbox'), 819 833 ]]; 820 834 $parentPath = $file['parent'] ?? null; 821 835 // Stop if no parent 822 if ( empty( $parentPath )) {836 if (empty($parentPath)) { 823 837 return $breadcrumb; 824 838 } 825 839 // Resolve parent folder key 826 840 $parentFolderKey = '/'; 827 if ( $parentPath !== '/') {828 $parentFolder = $this->getFileByPath( $parentPath, $accountId, 'array');829 if ( is_wp_error( $parentFolder )) {841 if ($parentPath !== '/') { 842 $parentFolder = $this->getFileByPath($parentPath, $accountId, 'array'); 843 if (is_wp_error($parentFolder)) { 830 844 return $home; 831 845 } … … 833 847 } 834 848 // Stop at root folder 835 if ( $parentFolderKey === '/' || $parentFolderKey === $rootFolderKey) {836 return array_merge( $breadcrumb, $home);849 if ($parentFolderKey === '/' || $parentFolderKey === $rootFolderKey) { 850 return array_merge($breadcrumb, $home); 837 851 } 838 852 // Recursive parent breadcrumb 839 $parentBreadcrumb = $this->getBreadcrumbByKey( $parentFolderKey, [853 $parentBreadcrumb = $this->getBreadcrumbByKey($parentFolderKey, [ 840 854 'rootFolderKey' => $rootFolderKey, 841 ] );842 if ( is_wp_error( $parentBreadcrumb )) {855 ]); 856 if (is_wp_error($parentBreadcrumb)) { 843 857 return $parentBreadcrumb; 844 858 } 845 return array_merge( $breadcrumb, $parentBreadcrumb ); 846 } 847 848 public function getFileAttributesByKeys( array $keys, array $attributes = ['id'], $accountId = null ) { 849 if ( empty( $keys ) ) { 859 return array_merge($breadcrumb, $parentBreadcrumb); 860 } 861 862 public function getFileAttributesByKeys(array $keys, array $attributes = ['id'], $accountId = null) 863 { 864 if (empty($keys)) { 850 865 return []; 851 866 } 852 867 global $wpdb; 853 $placeholders = implode( ',', array_fill( 0, count( $keys ), '%s' ));854 $preparedQuery = $wpdb->prepare( "SELECT * FROM %i WHERE `fileKey` IN ({$placeholders})", array_merge( [$this->tableName], $keys ));855 if ( !empty( $accountId )) {856 $preparedQuery .= $wpdb->prepare( " AND `accountId` = %s", $accountId);857 } 858 $cacheKey = "ccpidb_file_attributes_" . md5( $preparedQuery);859 if ( false !== ($cached = wp_cache_get( $cacheKey, 'ccpidb_files' ))) {868 $placeholders = implode(',', array_fill(0, count($keys), '%s')); 869 $preparedQuery = $wpdb->prepare("SELECT * FROM %i WHERE `fileKey` IN ({$placeholders})", array_merge([$this->tableName], $keys)); 870 if (!empty($accountId)) { 871 $preparedQuery .= $wpdb->prepare(" AND `accountId` = %s", $accountId); 872 } 873 $cacheKey = "ccpidb_file_attributes_" . md5($preparedQuery); 874 if (false !== ($cached = wp_cache_get($cacheKey, 'ccpidb_files'))) { 860 875 return $cached; 861 876 } 862 $files = $wpdb->get_results( $preparedQuery);863 if ( empty( $files )) {877 $files = $wpdb->get_results($preparedQuery); 878 if (empty($files)) { 864 879 return []; 865 880 } 866 $processedFiles = $this->processFiles( $files, 'object');867 if ( count( $attributes ) === 1) {881 $processedFiles = $this->processFiles($files, 'object'); 882 if (count($attributes) === 1) { 868 883 $attr = $attributes[0]; 869 884 $result = []; 870 foreach ( $processedFiles as $file) {885 foreach ($processedFiles as $file) { 871 886 $result[] = $file->{$attr} ?? null; 872 887 } 873 wp_cache_set( $cacheKey, $result, 'ccpidb_files');888 wp_cache_set($cacheKey, $result, 'ccpidb_files'); 874 889 return $result; 875 890 } 876 891 $result = []; 877 foreach ( $processedFiles as $file) {892 foreach ($processedFiles as $file) { 878 893 $fileData = []; 879 foreach ( $attributes as $attr) {894 foreach ($attributes as $attr) { 880 895 $fileData[$attr] = $file->{$attr} ?? null; 881 896 } 882 897 $result[] = $fileData; 883 898 } 884 wp_cache_set( $cacheKey, $result, 'ccpidb_files');899 wp_cache_set($cacheKey, $result, 'ccpidb_files'); 885 900 return $result; 886 901 } 887 902 888 public function getSuccessors( $parentPath, $accountId ) { 903 public function getSuccessors($parentPath, $accountId) 904 { 889 905 $successor = []; 890 $folders = $this->getChildFolderIds( $parentPath, $accountId);891 foreach ( $folders as $folderRow) {906 $folders = $this->getChildFolderIds($parentPath, $accountId); 907 foreach ($folders as $folderRow) { 892 908 $folderPath = $folderRow['path']; 893 909 $successor[] = $folderPath; 894 $childFolders = $this->getChildFolderIds( $folderPath, $accountId);895 if ( !empty( $childFolders )) {896 $successor = array_merge( $successor, $this->getSuccessors( $folderPath, $accountId ));910 $childFolders = $this->getChildFolderIds($folderPath, $accountId); 911 if (!empty($childFolders)) { 912 $successor = array_merge($successor, $this->getSuccessors($folderPath, $accountId)); 897 913 } 898 914 } 899 915 $successor[] = $parentPath; 900 return array_unique( $successor ); 901 } 902 903 public function getAllPhotos( $args = [] ) { 916 return array_unique($successor); 917 } 918 919 public function getAllPhotos($args = []) 920 { 904 921 $defaults = [ 905 922 'perPage' => 40, … … 908 925 'order' => 'desc', 909 926 ]; 910 $args = wp_parse_args( $args, $defaults);927 $args = wp_parse_args($args, $defaults); 911 928 $perPage = (int) $args['perPage']; 912 929 $page = (int) $args['page']; 913 $orderBy = $this->sanitizeOrderBy( $args['orderBy'], [930 $orderBy = $this->sanitizeOrderBy($args['orderBy'], [ 914 931 'name', 915 932 'createdAt', 916 933 'updatedAt', 917 934 'size' 918 ] );919 $order = $this->sanitizeOrder( $args['order']);935 ]); 936 $order = $this->sanitizeOrder($args['order']); 920 937 $offset = ($page - 1) * $perPage; 921 938 global $wpdb; 922 939 $cache_key = "ccpidb_all_photos_{$perPage}_{$page}_{$orderBy}_{$order}"; 923 $photos = wp_cache_get( $cache_key, 'ccpidb_files');924 if ( !empty( $photos )) {940 $photos = wp_cache_get($cache_key, 'ccpidb_files'); 941 if (!empty($photos)) { 925 942 return $photos; 926 943 } 927 $photos = $wpdb->get_results( $wpdb->prepare(944 $photos = $wpdb->get_results($wpdb->prepare( 928 945 "SELECT * FROM %i WHERE mimeType LIKE %s ORDER BY %i {$order} LIMIT %d OFFSET %d", 929 946 $this->tableName, … … 932 949 $perPage, 933 950 $offset 934 ) );935 if ( is_wp_error( $photos )) {951 )); 952 if (is_wp_error($photos)) { 936 953 return $photos; 937 954 } 938 $total = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE mimeType LIKE %s", $this->tableName, 'image/%' ));939 if ( is_wp_error( $total )) {955 $total = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM %i WHERE mimeType LIKE %s", $this->tableName, 'image/%')); 956 if (is_wp_error($total)) { 940 957 return $total; 941 958 } 942 $totalPages = ceil( $total / $perPage);959 $totalPages = ceil($total / $perPage); 943 960 $hasMore = $page < $totalPages; 944 961 $response = [ … … 948 965 'totalPages' => (int) $totalPages, 949 966 'hasMore' => $hasMore, 950 'photos' => $this->processFiles( $photos),951 ]; 952 if ( $hasMore) {967 'photos' => $this->processFiles($photos), 968 ]; 969 if ($hasMore) { 953 970 $response['nextPage'] = $page + 1; 954 971 } 955 if ( !empty( $photos )) {956 wp_cache_set( $cache_key, $response, 'ccpidb_files');972 if (!empty($photos)) { 973 wp_cache_set($cache_key, $response, 'ccpidb_files'); 957 974 } 958 975 return $response; 959 976 } 960 977 961 public function searchFiles( $query, $options = [] ) { 962 global $wpdb; 963 if ( empty( $query ) || empty( $options['accountId'] ) ) { 964 return new WP_Error('invalid_query', __( 'Search query and account ID are required.', 'integrate-dropbox' )); 978 public function searchFiles($query, $options = []) 979 { 980 global $wpdb; 981 if (empty($query) || empty($options['accountId'])) { 982 return new WP_Error('invalid_query', __('Search query and account ID are required.', 'integrate-dropbox')); 965 983 } 966 984 $defaults = [ … … 972 990 'order' => 'ASC', 973 991 ]; 974 $options = wp_parse_args( $options, $defaults);975 $order = $this->sanitizeOrder( $options['order']);976 $orderBy = $this->sanitizeOrderBy( $options['orderBy'], [992 $options = wp_parse_args($options, $defaults); 993 $order = $this->sanitizeOrder($options['order']); 994 $orderBy = $this->sanitizeOrderBy($options['orderBy'], [ 977 995 'name', 978 996 'createdAt', 979 997 'updatedAt', 980 998 'size' 981 ] );982 $pagination = $this->sanitizePagination( $options['page'], $options['perPage']);999 ]); 1000 $pagination = $this->sanitizePagination($options['page'], $options['perPage']); 983 1001 $perPage = $pagination['perPage']; 984 1002 $offset = $pagination['offset']; 985 1003 $page = $pagination['page']; 986 $searchPattern = '%' . $wpdb->esc_like( $query) . '%';1004 $searchPattern = '%' . $wpdb->esc_like($query) . '%'; 987 1005 $sql = $wpdb->prepare( 988 1006 "SELECT * FROM %i WHERE accountId = %s AND name LIKE %s", … … 997 1015 $searchPattern 998 1016 ); 999 if ( $options['scope'] === 'folder' && !empty( $options['path'] )) {1000 $sql .= $wpdb->prepare( " AND parent = %s", $options['path']);1001 $totalSql .= $wpdb->prepare( " AND parent = %s", $options['path']);1002 } 1003 if ( !empty( $options['types'] ) && is_array( $options['types'] )) {1004 $extensions = MimeTypeManager::getExtensionsByCategory( $options['types']);1005 if ( !empty( $extensions )) {1006 $extPlaceholders = implode( ',', array_fill( 0, count( $extensions ), '%s' ));1007 $sql .= $wpdb->prepare( " AND extension IN ({$extPlaceholders})", $extensions);1008 $totalSql .= $wpdb->prepare( " AND extension IN ({$extPlaceholders})", $extensions);1017 if ($options['scope'] === 'folder' && !empty($options['path'])) { 1018 $sql .= $wpdb->prepare(" AND parent = %s", $options['path']); 1019 $totalSql .= $wpdb->prepare(" AND parent = %s", $options['path']); 1020 } 1021 if (!empty($options['types']) && is_array($options['types'])) { 1022 $extensions = MimeTypeManager::getExtensionsByCategory($options['types']); 1023 if (!empty($extensions)) { 1024 $extPlaceholders = implode(',', array_fill(0, count($extensions), '%s')); 1025 $sql .= $wpdb->prepare(" AND extension IN ({$extPlaceholders})", $extensions); 1026 $totalSql .= $wpdb->prepare(" AND extension IN ({$extPlaceholders})", $extensions); 1009 1027 } 1010 1028 } … … 1015 1033 $offset 1016 1034 ); 1017 $cache_key = "ccpidb_search_{$options['accountId']}_" . md5( $query . serialize( $options ));1018 $cached = wp_cache_get( $cache_key, 'ccpidb_files');1019 if ( !empty( $cached )) {1035 $cache_key = "ccpidb_search_{$options['accountId']}_" . md5($query . serialize($options)); 1036 $cached = wp_cache_get($cache_key, 'ccpidb_files'); 1037 if (!empty($cached)) { 1020 1038 return $cached; 1021 1039 } 1022 $results = $wpdb->get_results( $sql);1023 $totalFile = $wpdb->get_var( $totalSql);1024 if ( is_wp_error( $results ) || is_wp_error( $totalFile )) {1040 $results = $wpdb->get_results($sql); 1041 $totalFile = $wpdb->get_var($totalSql); 1042 if (is_wp_error($results) || is_wp_error($totalFile)) { 1025 1043 return $results; 1026 1044 } 1027 if ( empty( $results ) || $totalFile == 0) {1045 if (empty($results) || $totalFile == 0) { 1028 1046 return []; 1029 1047 } 1030 1048 $response = [ 1031 1049 'total' => (int) $totalFile, 1032 'files' => $this->processFiles( $results),1050 'files' => $this->processFiles($results), 1033 1051 'perPage' => $perPage, 1034 'totalPages' => ceil( $totalFile / $perPage),1052 'totalPages' => ceil($totalFile / $perPage), 1035 1053 'currentPage' => $page, 1036 1054 'hasMore' => $page * $perPage < $totalFile, 1037 1055 ]; 1038 if ( $response['hasMore']) {1056 if ($response['hasMore']) { 1039 1057 $response['nextPage'] = $page + 1; 1040 1058 } 1041 if ( !empty( $results )) {1042 wp_cache_set( $cache_key, $response, 'ccpidb_files');1059 if (!empty($results)) { 1060 wp_cache_set($cache_key, $response, 'ccpidb_files'); 1043 1061 } 1044 1062 return $response; 1045 1063 } 1046 1064 1047 public function getSharedKey( $fileKey, $options = [] ) { 1065 public function getSharedKey($fileKey, $options = []) 1066 { 1048 1067 $defaults = [ 1049 1068 'expireIn' => 3600, 1050 1069 'password' => null, 1051 1070 ]; 1052 $options = wp_parse_args( $options, $defaults);1053 $expireIn = intval( $options['expireIn']);1054 $password = sanitize_text_field( $options['password'] ?? null);1055 $expiry = ( $expireIn > 0 ? time() + $expireIn : 0);1056 $passwordHash = ( !empty( $password ) ? md5( $password ) : '');1057 $sharedData = $this->getSharedData( $fileKey);1058 $key = md5( "{$fileKey}|{$expiry}|{$passwordHash}");1059 if ( !empty( $sharedData[$key] ) && $sharedData[$key]['expiry'] >= time()) {1071 $options = wp_parse_args($options, $defaults); 1072 $expireIn = intval($options['expireIn']); 1073 $password = sanitize_text_field($options['password'] ?? null); 1074 $expiry = ($expireIn > 0 ? time() + $expireIn : 0); 1075 $passwordHash = (!empty($password) ? md5($password) : ''); 1076 $sharedData = $this->getSharedData($fileKey); 1077 $key = md5("{$fileKey}|{$expiry}|{$passwordHash}"); 1078 if (!empty($sharedData[$key]) && $sharedData[$key]['expiry'] >= time()) { 1060 1079 return "{$fileKey}-{$key}"; 1061 1080 } … … 1067 1086 ]; 1068 1087 // Save entire sharedData list 1069 $this->saveSharedData( $fileKey, $sharedData);1088 $this->saveSharedData($fileKey, $sharedData); 1070 1089 return "{$fileKey}-{$key}"; 1071 1090 } 1072 1091 1073 public function getDownloadKey( $fileKey, $options = [] ) { 1092 public function getDownloadKey($fileKey, $options = []) 1093 { 1074 1094 $defaults = [ 1075 1095 'expireIn' => 3600, … … 1077 1097 'limit' => 0, 1078 1098 ]; 1079 $options = wp_parse_args( $options, $defaults);1080 $expireIn = intval( $options['expireIn']);1081 $password = sanitize_text_field( $options['password'] ?? null);1082 $limit = intval( $options['limit']);1083 $expiry = ( $expireIn > 0 ? time() + $expireIn : 0);1084 $passwordHash = ( !empty( $password ) ? md5( $password ) : '');1085 $downloadData = $this->getDownloadData( $fileKey);1086 $key = md5( "{$fileKey}|{$expiry}|{$passwordHash}|{$limit}");1087 if ( !empty( $downloadData[$key] ) && $downloadData[$key]['expiry'] >= time()) {1099 $options = wp_parse_args($options, $defaults); 1100 $expireIn = intval($options['expireIn']); 1101 $password = sanitize_text_field($options['password'] ?? null); 1102 $limit = intval($options['limit']); 1103 $expiry = ($expireIn > 0 ? time() + $expireIn : 0); 1104 $passwordHash = (!empty($password) ? md5($password) : ''); 1105 $downloadData = $this->getDownloadData($fileKey); 1106 $key = md5("{$fileKey}|{$expiry}|{$passwordHash}|{$limit}"); 1107 if (!empty($downloadData[$key]) && $downloadData[$key]['expiry'] >= time()) { 1088 1108 return "{$fileKey}-{$key}"; 1089 1109 } … … 1096 1116 ]; 1097 1117 // Save entire sharedData list 1098 $this->saveDownloadData( $fileKey, $downloadData);1118 $this->saveDownloadData($fileKey, $downloadData); 1099 1119 return "{$fileKey}-{$key}"; 1100 1120 } 1101 1121 1102 public function validateSharedLink( $combinedKey, $password = '' ) { 1103 [$fileKey, $linkKey] = $this->parseCombinedKey( $combinedKey ); 1104 if ( !$fileKey || !$linkKey ) { 1105 return false; 1106 } 1107 $sharedData = $this->getSharedData( $fileKey ); 1108 if ( empty( $sharedData[$linkKey] ) ) { 1122 public function validateSharedLink($combinedKey, $password = '') 1123 { 1124 [$fileKey, $linkKey] = $this->parseCombinedKey($combinedKey); 1125 if (!$fileKey || !$linkKey) { 1126 return false; 1127 } 1128 $sharedData = $this->getSharedData($fileKey); 1129 if (empty($sharedData[$linkKey])) { 1109 1130 return false; 1110 1131 } 1111 1132 $shareInfo = $sharedData[$linkKey]; 1112 if ( $shareInfo['expiry'] < time() && $shareInfo['expiry'] != 0) {1113 $this->deleteSharedEntry( $fileKey, $linkKey);1114 return false; 1115 } 1116 $hashedPassword = md5( sanitize_text_field( $password ));1117 if ( !empty( $shareInfo['password'] )) {1118 if ( empty( $password )) {1119 return new WP_Error('password_required', __( 'This shared link is protected by a password. Please provide the password to access the file.', 'integrate-dropbox'));1120 } 1121 if ( $shareInfo['password'] !== $hashedPassword) {1122 return new WP_Error('invalid_password', __( 'The provided password is incorrect.', 'integrate-dropbox'));1123 } 1124 } 1125 $shareInfo['viewCount'] = intval( $shareInfo['viewCount'] ?? 0) + 1;1126 $shareInfo['lastViewed'] = current_time( 'mysql');1127 $result = $this->updateSharedData( $combinedKey, $shareInfo);1128 if ( is_wp_error( $result )) {1133 if ($shareInfo['expiry'] < time() && $shareInfo['expiry'] != 0) { 1134 $this->deleteSharedEntry($fileKey, $linkKey); 1135 return false; 1136 } 1137 $hashedPassword = md5(sanitize_text_field($password)); 1138 if (!empty($shareInfo['password'])) { 1139 if (empty($password)) { 1140 return new WP_Error('password_required', __('This shared link is protected by a password. Please provide the password to access the file.', 'integrate-dropbox')); 1141 } 1142 if ($shareInfo['password'] !== $hashedPassword) { 1143 return new WP_Error('invalid_password', __('The provided password is incorrect.', 'integrate-dropbox')); 1144 } 1145 } 1146 $shareInfo['viewCount'] = intval($shareInfo['viewCount'] ?? 0) + 1; 1147 $shareInfo['lastViewed'] = current_time('mysql'); 1148 $result = $this->updateSharedData($combinedKey, $shareInfo); 1149 if (is_wp_error($result)) { 1129 1150 return false; 1130 1151 } … … 1132 1153 } 1133 1154 1134 public function validateDownloadLink( $combinedKey, $password = '' ) { 1135 [$fileKey, $linkKey] = $this->parseCombinedKey( $combinedKey ); 1136 if ( !$fileKey || !$linkKey ) { 1137 return false; 1138 } 1139 $downloadData = $this->getDownloadData( $fileKey ); 1140 if ( empty( $downloadData[$linkKey] ) ) { 1155 public function validateDownloadLink($combinedKey, $password = '') 1156 { 1157 [$fileKey, $linkKey] = $this->parseCombinedKey($combinedKey); 1158 if (!$fileKey || !$linkKey) { 1159 return false; 1160 } 1161 $downloadData = $this->getDownloadData($fileKey); 1162 if (empty($downloadData[$linkKey])) { 1141 1163 return false; 1142 1164 } 1143 1165 $downloadInfo = $downloadData[$linkKey]; 1144 if ( $downloadInfo['expiry'] < time() && $downloadInfo['expiry'] != 0) {1145 $this->deleteDownloadEntry( $fileKey, $linkKey);1146 return false; 1147 } 1148 $downloadLimit = intval( $downloadInfo['limit'] ?? 0);1149 if ( $downloadLimit > 0 && intval( $downloadInfo['downloadCount'] ?? 0 ) >= $downloadLimit) {1150 $this->deleteDownloadEntry( $fileKey, $linkKey);1151 return new WP_Error('download_limit_exceeded', __( 'The download limit for this link has been exceeded.', 'integrate-dropbox'));1152 } 1153 $hashedPassword = md5( sanitize_text_field( $password ));1154 if ( !empty( $downloadInfo['password'] )) {1155 if ( empty( $password )) {1156 return new WP_Error('password_required', __( 'This shared link is protected by a password. Please provide the password to access the file.', 'integrate-dropbox'));1157 } 1158 if ( $downloadInfo['password'] !== $hashedPassword) {1159 return new WP_Error('invalid_password', __( 'The provided password is incorrect.', 'integrate-dropbox'));1160 } 1161 } 1162 $downloadInfo['downloadCount'] = intval( $downloadInfo['downloadCount'] ?? 0) + 1;1163 $downloadInfo['lastViewed'] = current_time( 'mysql');1164 $result = $this->updateDownloadData( $combinedKey, $downloadInfo);1165 if ( is_wp_error( $result )) {1166 if ($downloadInfo['expiry'] < time() && $downloadInfo['expiry'] != 0) { 1167 $this->deleteDownloadEntry($fileKey, $linkKey); 1168 return false; 1169 } 1170 $downloadLimit = intval($downloadInfo['limit'] ?? 0); 1171 if ($downloadLimit > 0 && intval($downloadInfo['downloadCount'] ?? 0) >= $downloadLimit) { 1172 $this->deleteDownloadEntry($fileKey, $linkKey); 1173 return new WP_Error('download_limit_exceeded', __('The download limit for this link has been exceeded.', 'integrate-dropbox')); 1174 } 1175 $hashedPassword = md5(sanitize_text_field($password)); 1176 if (!empty($downloadInfo['password'])) { 1177 if (empty($password)) { 1178 return new WP_Error('password_required', __('This shared link is protected by a password. Please provide the password to access the file.', 'integrate-dropbox')); 1179 } 1180 if ($downloadInfo['password'] !== $hashedPassword) { 1181 return new WP_Error('invalid_password', __('The provided password is incorrect.', 'integrate-dropbox')); 1182 } 1183 } 1184 $downloadInfo['downloadCount'] = intval($downloadInfo['downloadCount'] ?? 0) + 1; 1185 $downloadInfo['lastViewed'] = current_time('mysql'); 1186 $result = $this->updateDownloadData($combinedKey, $downloadInfo); 1187 if (is_wp_error($result)) { 1166 1188 return false; 1167 1189 } … … 1170 1192 1171 1193 /* ================= PRIVATE ================= */ 1172 private function parseCombinedKey( $sharedKey ) { 1173 $parts = explode( '-', $sharedKey, 2 ); 1194 private function parseCombinedKey($sharedKey) 1195 { 1196 $parts = explode('-', $sharedKey, 2); 1174 1197 return [$parts[0] ?? '', $parts[1] ?? '']; 1175 1198 } 1176 1199 1177 private function getSharedData( $fileKey ) { 1178 global $wpdb; 1179 $file = $wpdb->get_row( $wpdb->prepare( "SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 1180 if ( !$file ) { 1200 private function getSharedData($fileKey) 1201 { 1202 global $wpdb; 1203 $file = $wpdb->get_row($wpdb->prepare("SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 1204 if (!$file) { 1181 1205 return []; 1182 1206 } 1183 $metaData = maybe_unserialize( $file->metaData);1207 $metaData = maybe_unserialize($file->metaData); 1184 1208 return $metaData['sharedData'] ?? []; 1185 1209 } 1186 1210 1187 private function getDownloadData( $fileKey ) { 1188 global $wpdb; 1189 $file = $wpdb->get_row( $wpdb->prepare( "SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 1190 if ( !$file ) { 1211 private function getDownloadData($fileKey) 1212 { 1213 global $wpdb; 1214 $file = $wpdb->get_row($wpdb->prepare("SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 1215 if (!$file) { 1191 1216 return []; 1192 1217 } 1193 $metaData = maybe_unserialize( $file->metaData);1218 $metaData = maybe_unserialize($file->metaData); 1194 1219 return $metaData['downloadData'] ?? []; 1195 1220 } 1196 1221 1197 private function saveSharedData( $fileKey, $sharedData ) { 1198 global $wpdb; 1199 $file = $wpdb->get_row( $wpdb->prepare( "SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 1200 if ( !$file ) { 1201 return false; 1202 } 1203 $metaData = maybe_unserialize( $file->metaData ) ?? []; 1222 private function saveSharedData($fileKey, $sharedData) 1223 { 1224 global $wpdb; 1225 $file = $wpdb->get_row($wpdb->prepare("SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 1226 if (!$file) { 1227 return false; 1228 } 1229 $metaData = maybe_unserialize($file->metaData) ?? []; 1204 1230 $metaData['sharedData'] = $sharedData; 1205 1231 return $wpdb->update( 1206 1232 $this->tableName, 1207 1233 [ 1208 'metaData' => maybe_serialize( $metaData),1234 'metaData' => maybe_serialize($metaData), 1209 1235 ], 1210 1236 [ … … 1216 1242 } 1217 1243 1218 private function saveDownloadData( $fileKey, $downloadData ) { 1219 global $wpdb; 1220 $file = $wpdb->get_row( $wpdb->prepare( "SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 1221 if ( !$file ) { 1222 return false; 1223 } 1224 $metaData = maybe_unserialize( $file->metaData ) ?? []; 1244 private function saveDownloadData($fileKey, $downloadData) 1245 { 1246 global $wpdb; 1247 $file = $wpdb->get_row($wpdb->prepare("SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 1248 if (!$file) { 1249 return false; 1250 } 1251 $metaData = maybe_unserialize($file->metaData) ?? []; 1225 1252 $metaData['downloadData'] = $downloadData; 1226 1253 return $wpdb->update( 1227 1254 $this->tableName, 1228 1255 [ 1229 'metaData' => maybe_serialize( $metaData),1256 'metaData' => maybe_serialize($metaData), 1230 1257 ], 1231 1258 [ … … 1237 1264 } 1238 1265 1239 public function updateSharedData( $combinedKey, $updates = [] ) { 1240 [$fileKey, $linkKey] = $this->parseCombinedKey( $combinedKey ); 1241 if ( !$fileKey || !$linkKey ) { 1242 return false; 1243 } 1244 $sharedData = $this->getSharedData( $fileKey ); 1245 if ( empty( $sharedData[$linkKey] ) ) { 1266 public function updateSharedData($combinedKey, $updates = []) 1267 { 1268 [$fileKey, $linkKey] = $this->parseCombinedKey($combinedKey); 1269 if (!$fileKey || !$linkKey) { 1270 return false; 1271 } 1272 $sharedData = $this->getSharedData($fileKey); 1273 if (empty($sharedData[$linkKey])) { 1246 1274 return false; 1247 1275 } 1248 1276 // Only update provided fields 1249 $sharedData[$linkKey] = array_merge( $sharedData[$linkKey], array_filter( $updates, function ( $v) {1277 $sharedData[$linkKey] = array_merge($sharedData[$linkKey], array_filter($updates, function ($v) { 1250 1278 return $v !== null; 1251 } ) ); 1252 return $this->saveSharedData( $fileKey, $sharedData ); 1253 } 1254 1255 public function updateDownloadData( $combinedKey, $updates = [] ) { 1256 [$fileKey, $linkKey] = $this->parseCombinedKey( $combinedKey ); 1257 if ( !$fileKey || !$linkKey ) { 1258 return false; 1259 } 1260 $downloadData = $this->getDownloadData( $fileKey ); 1261 if ( empty( $downloadData[$linkKey] ) ) { 1279 })); 1280 return $this->saveSharedData($fileKey, $sharedData); 1281 } 1282 1283 public function updateDownloadData($combinedKey, $updates = []) 1284 { 1285 [$fileKey, $linkKey] = $this->parseCombinedKey($combinedKey); 1286 if (!$fileKey || !$linkKey) { 1287 return false; 1288 } 1289 $downloadData = $this->getDownloadData($fileKey); 1290 if (empty($downloadData[$linkKey])) { 1262 1291 return false; 1263 1292 } 1264 1293 // Only update provided fields 1265 $downloadData[$linkKey] = array_merge( $downloadData[$linkKey], array_filter( $updates, function ( $v) {1294 $downloadData[$linkKey] = array_merge($downloadData[$linkKey], array_filter($updates, function ($v) { 1266 1295 return $v !== null; 1267 } ) ); 1268 return $this->saveDownloadData( $fileKey, $downloadData ); 1269 } 1270 1271 private function deleteSharedEntry( $fileKey, $linkKey ) { 1272 $sharedData = $this->getSharedData( $fileKey ); 1273 if ( isset( $sharedData[$linkKey] ) ) { 1296 })); 1297 return $this->saveDownloadData($fileKey, $downloadData); 1298 } 1299 1300 private function deleteSharedEntry($fileKey, $linkKey) 1301 { 1302 $sharedData = $this->getSharedData($fileKey); 1303 if (isset($sharedData[$linkKey])) { 1274 1304 unset($sharedData[$linkKey]); 1275 return $this->saveSharedData( $fileKey, $sharedData);1305 return $this->saveSharedData($fileKey, $sharedData); 1276 1306 } 1277 1307 return false; 1278 1308 } 1279 1309 1280 private function deleteDownloadEntry( $fileKey, $linkKey ) { 1281 $downloadData = $this->getDownloadData( $fileKey ); 1282 if ( isset( $downloadData[$linkKey] ) ) { 1310 private function deleteDownloadEntry($fileKey, $linkKey) 1311 { 1312 $downloadData = $this->getDownloadData($fileKey); 1313 if (isset($downloadData[$linkKey])) { 1283 1314 unset($downloadData[$linkKey]); 1284 return $this->saveDownloadData( $fileKey, $downloadData);1315 return $this->saveDownloadData($fileKey, $downloadData); 1285 1316 } 1286 1317 return false; 1287 1318 } 1288 1319 1289 private function getChildFolderIds( $parentPath, $accountId ) { 1290 if ( empty( $parentPath ) || empty( $accountId ) ) { 1320 private function getChildFolderIds($parentPath, $accountId) 1321 { 1322 if (empty($parentPath) || empty($accountId)) { 1291 1323 return []; 1292 1324 } 1293 1325 global $wpdb; 1294 return $wpdb->get_results( $wpdb->prepare(1326 return $wpdb->get_results($wpdb->prepare( 1295 1327 "SELECT path FROM %i WHERE parent = %s AND accountId = %s AND extension = 'folder'", 1296 1328 $this->tableName, 1297 1329 $parentPath, 1298 1330 $accountId 1299 ), ARRAY_A ); 1300 } 1301 1302 private function processFiles( $files, $returnType = 'array', $filter = null ) { 1331 ), ARRAY_A); 1332 } 1333 1334 private function processFiles($files, $returnType = 'array', $filter = null) 1335 { 1303 1336 $processedFiles = []; 1304 foreach ( $files as $file) {1305 $processedFiles[] = $this->processFile( $file, $returnType, $filter);1337 foreach ($files as $file) { 1338 $processedFiles[] = $this->processFile($file, $returnType, $filter); 1306 1339 } 1307 1340 return $processedFiles; 1308 1341 } 1309 1342 1310 private function processFile( $file, $returnType = 'object', $filter = null ) { 1311 if ( empty( $file ) ) { 1343 private function processFile($file, $returnType = 'object', $filter = null) 1344 { 1345 if (empty($file)) { 1312 1346 return []; 1313 1347 } … … 1327 1361 'sharedLink' => $file->sharedLink, 1328 1362 'isDir' => $file->isDir, 1329 'permissions' => maybe_unserialize( $file->permissions),1363 'permissions' => maybe_unserialize($file->permissions), 1330 1364 'hasOwnThumbnail' => $file->hasOwnThumbnail, 1331 1365 'icon' => $file->icon, 1332 'additionalData' => maybe_unserialize( $file->additionalData),1333 'metaData' => maybe_unserialize( $file->metaData),1366 'additionalData' => maybe_unserialize($file->additionalData), 1367 'metaData' => maybe_unserialize($file->metaData), 1334 1368 'createdAt' => $file->createdAt, 1335 1369 'updatedAt' => $file->updatedAt, 1336 1370 ]; 1337 if ( $returnType === 'object') {1371 if ($returnType === 'object') { 1338 1372 return new File($fileData); 1339 } elseif ( $returnType === 'array') {1373 } elseif ($returnType === 'array') { 1340 1374 return $fileData; 1341 1375 } … … 1346 1380 'mimeType' => $file->mimeType, 1347 1381 'size' => $file->size, 1348 'thumbnails' => maybe_unserialize( $file->thumbnails ), 1349 ]; 1350 } 1351 1352 private function processExtensions( array $extensions, array $additionalExtensions, string $filterType ) : array { 1353 if ( empty( $additionalExtensions ) ) { 1382 'thumbnails' => maybe_unserialize($file->thumbnails), 1383 ]; 1384 } 1385 1386 private function processExtensions(array $extensions, array $additionalExtensions, string $filterType): array 1387 { 1388 if (empty($additionalExtensions)) { 1354 1389 return $extensions; 1355 1390 } 1356 if ( empty( $extensions )) {1391 if (empty($extensions)) { 1357 1392 return $additionalExtensions; 1358 1393 } 1359 if ( $filterType === 'include') {1360 $filterExtensions = array_filter( $extensions, function ( $ext ) use($additionalExtensions) {1361 return in_array( $ext, $additionalExtensions);1362 } );1363 return array_values( $filterExtensions);1364 } elseif ( $filterType === 'exclude') {1365 $filterExtensions = array_filter( $extensions, fn( $ext ) => !in_array( $ext, $additionalExtensions ));1366 return array_values( $filterExtensions);1394 if ($filterType === 'include') { 1395 $filterExtensions = array_filter($extensions, function ($ext) use ($additionalExtensions) { 1396 return in_array($ext, $additionalExtensions); 1397 }); 1398 return array_values($filterExtensions); 1399 } elseif ($filterType === 'exclude') { 1400 $filterExtensions = array_filter($extensions, fn ($ext) => !in_array($ext, $additionalExtensions)); 1401 return array_values($filterExtensions); 1367 1402 } 1368 1403 return $extensions; 1369 1404 } 1370 1405 1371 private function processFolderTree( array $items, array $rootPaths ) { 1406 private function processFolderTree(array $items, array $rootPaths) 1407 { 1372 1408 $tree = []; 1373 1409 $lookup = []; 1374 foreach ( $items as $item) {1375 $path = rtrim( $item['path'], '/');1410 foreach ($items as $item) { 1411 $path = rtrim($item['path'], '/'); 1376 1412 $lookup[$path] = [ 1377 1413 'name' => $item['name'], … … 1379 1415 'children' => [], 1380 1416 'path' => $path, 1381 'parent' => rtrim( $item['parent'], '/'),1417 'parent' => rtrim($item['parent'], '/'), 1382 1418 ]; 1383 1419 } 1384 foreach ( $lookup as $path => &$node) {1385 if ( in_array( $node['path'], $rootPaths, true ) || $node['parent'] === '' || $node['parent'] === '/') {1420 foreach ($lookup as $path => &$node) { 1421 if (in_array($node['path'], $rootPaths, true) || $node['parent'] === '' || $node['parent'] === '/') { 1386 1422 unset($node['parent']); 1387 $tree[] = & $node;1423 $tree[] = & $node; 1388 1424 } else { 1389 if ( isset( $lookup[$node['parent']] )) {1390 $lookup[$node['parent']]['children'][] = & $node;1425 if (isset($lookup[$node['parent']])) { 1426 $lookup[$node['parent']]['children'][] = & $node; 1391 1427 } 1392 1428 } … … 1402 1438 // } 1403 1439 // } 1404 $tree = array_map( function ( $item) {1405 if ( empty( $item['children'] )) {1440 $tree = array_map(function ($item) { 1441 if (empty($item['children'])) { 1406 1442 unset($item['children']); 1407 1443 } 1408 1444 return $item; 1409 }, $tree );1410 return array_values( $tree);1445 }, $tree); 1446 return array_values($tree); 1411 1447 } 1412 1448 -
integrate-dropbox/trunk/models/Files.php
r3448719 r3448768 9 9 use CodeConfig\IDB\Utils\MimeTypeManager; 10 10 use CodeConfig\IDB\Utils\Singleton; 11 use WP_Error; 12 11 13 use function count; 12 14 use function in_array; 13 15 use function intval; 14 16 use function is_array; 15 use WP_Error; 16 class Files extends BaseModel { 17 18 class Files extends BaseModel 19 { 17 20 use Singleton; 18 21 public const TABLE_NAME = 'ccpidb_files'; … … 56 59 public const META_DATA = ['attachmentId', 'mediaInfo', 'childCount']; 57 60 58 public function __construct() { 59 parent::__construct( self::TABLE_NAME ); 61 public function __construct() 62 { 63 parent::__construct(self::TABLE_NAME); 60 64 // $this->getChildPathsWithThumbnails(['7618d90767fefcb7d21109071e707ae1'], 'id:XR5DPLkvVHQAAAAAAAADAA'); 61 65 // $this->getChildPathsWithThumbnails(['7618d90767fefcb7d21109071e707ae1'], 'id:XR5DPLkvVHQAAAAAAAADAA'); … … 71 75 * @return array|null|WP_Error An array of processed file data from the specified folder. 72 76 */ 73 public function getFolder( $folderKey, $config = [] ) { 77 public function getFolder($folderKey, $config = []) 78 { 74 79 global $wpdb; 75 80 $allowedOrderBy = [ … … 79 84 'size' 80 85 ]; 81 $order = $this->sanitizeOrder( $config['order'] ?? 'DESC');82 $orderBy = $this->sanitizeOrderBy( $config['orderBy'] ?? 'createdAt', $allowedOrderBy);83 $page = ( isset( $config['page'] ) ? (int) $config['page'] : 1);84 $perPage = ( isset( $config['perPage'] ) ? (int) $config['perPage'] : 20);85 $pagination = $this->sanitizePagination( $page, $perPage);86 if ( $folderKey !== '/') {87 $file = $this->getFile( $folderKey);88 if ( $file instanceof File === false || is_wp_error( $file )) {89 return new WP_Error(404, __( 'Folder not found.', 'integrate-dropbox'));86 $order = $this->sanitizeOrder($config['order'] ?? 'DESC'); 87 $orderBy = $this->sanitizeOrderBy($config['orderBy'] ?? 'createdAt', $allowedOrderBy); 88 $page = (isset($config['page']) ? (int) $config['page'] : 1); 89 $perPage = (isset($config['perPage']) ? (int) $config['perPage'] : 20); 90 $pagination = $this->sanitizePagination($page, $perPage); 91 if ($folderKey !== '/') { 92 $file = $this->getFile($folderKey); 93 if ($file instanceof File === false || is_wp_error($file)) { 94 return new WP_Error(404, __('Folder not found.', 'integrate-dropbox')); 90 95 } 91 96 $accountId = $file->getAccountId(); … … 93 98 } else { 94 99 $account = Accounts::getInstance()->getAccount(); 95 if ( $account instanceof Account === false || is_wp_error( $account )) {96 return new WP_Error(404, __( 'Account not found.', 'integrate-dropbox'));100 if ($account instanceof Account === false || is_wp_error($account)) { 101 return new WP_Error(404, __('Account not found.', 'integrate-dropbox')); 97 102 } 98 103 $accountId = $account->getId(); 99 104 $path = '/'; 100 105 } 101 if ( !current_user_can( 'manage_options' ) && !wp_doing_cron()) {102 if ( !is_user_logged_in()) {103 return new WP_Error('unauthorized', __( 'You must be logged in to access this folder.', 'integrate-dropbox'));106 if (!current_user_can('manage_options') && !wp_doing_cron()) { 107 if (!is_user_logged_in()) { 108 return new WP_Error('unauthorized', __('You must be logged in to access this folder.', 'integrate-dropbox')); 104 109 } 105 110 $user = wp_get_current_user(); 106 if ( !$user instanceof \WP_User) {107 return new WP_Error('unauthorized', __( 'You must be logged in to access this folder.', 'integrate-dropbox'));111 if (!$user instanceof \WP_User) { 112 return new WP_Error('unauthorized', __('You must be logged in to access this folder.', 'integrate-dropbox')); 108 113 } 109 114 $userName = $user->user_login; 110 115 $roles = $user->roles; 111 $accessSettings = UserAccess::getInstance()->getAccessData( $userName, $roles);112 if ( empty( $accessSettings )) {113 if ( !current_user_can( 'manage_options' )) {114 return new WP_Error('forbidden', __( 'You do not have permission to access this folder.', 'integrate-dropbox'));116 $accessSettings = UserAccess::getInstance()->getAccessData($userName, $roles); 117 if (empty($accessSettings)) { 118 if (!current_user_can('manage_options')) { 119 return new WP_Error('forbidden', __('You do not have permission to access this folder.', 'integrate-dropbox')); 115 120 } 116 121 } else { 117 122 $accessSettingsFolders = $accessSettings['folders'] ?? []; 118 if ( empty( $accessSettingsFolders ) || !is_array( $accessSettingsFolders )) {119 return new WP_Error('forbidden', __( 'You do not have permission to access this folder.', 'integrate-dropbox'));123 if (empty($accessSettingsFolders) || !is_array($accessSettingsFolders)) { 124 return new WP_Error('forbidden', __('You do not have permission to access this folder.', 'integrate-dropbox')); 120 125 } 121 if ( $folderKey === '/') {122 $folder = $this->getFilesByKeys( $accessSettingsFolders, [126 if ($folderKey === '/') { 127 $folder = $this->getFilesByKeys($accessSettingsFolders, [ 123 128 'returnType' => 'array', 124 129 'perPage' => $config['perPage'], … … 127 132 'order' => $config['order'], 128 133 'recursive' => false, 129 ] );134 ]); 130 135 return $folder; 131 136 } 132 if ( !Helpers::validateFileKey( $folderKey, $accessSettingsFolders )) {133 return new WP_Error('forbidden', __( 'You do not have permission to access this folder.', 'integrate-dropbox'));137 if (!Helpers::validateFileKey($folderKey, $accessSettingsFolders)) { 138 return new WP_Error('forbidden', __('You do not have permission to access this folder.', 'integrate-dropbox')); 134 139 } 135 140 } 136 141 } 137 $sql = $wpdb->prepare( "SELECT * FROM %i WHERE accountId = %s", $this->tableName, $accountId);138 $totalSql = $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE accountId = %s", $this->tableName, $accountId);139 if ( !empty( $config['search'] )) {140 $searchPattern = '%' . $wpdb->esc_like( $config['search'] ?? '') . '%';141 $searchScope = ( in_array( $config['searchScope'] ?? 'folder', ['folder', 'global'] ) ? $config['searchScope'] : 'folder');142 $sql .= $wpdb->prepare( " AND name LIKE %s", $searchPattern);143 $totalSql .= $wpdb->prepare( " AND name LIKE %s", $searchPattern);144 if ( $searchScope === 'folder') {145 $sql .= $wpdb->prepare( " AND (path LIKE %s OR parent LIKE %s)", "{$path}%", "{$path}%");146 $totalSql .= $wpdb->prepare( " AND (path LIKE %s OR parent LIKE %s)", "{$path}%", "{$path}%");142 $sql = $wpdb->prepare("SELECT * FROM %i WHERE accountId = %s", $this->tableName, $accountId); 143 $totalSql = $wpdb->prepare("SELECT COUNT(*) FROM %i WHERE accountId = %s", $this->tableName, $accountId); 144 if (!empty($config['search'])) { 145 $searchPattern = '%' . $wpdb->esc_like($config['search'] ?? '') . '%'; 146 $searchScope = (in_array($config['searchScope'] ?? 'folder', ['folder', 'global']) ? $config['searchScope'] : 'folder'); 147 $sql .= $wpdb->prepare(" AND name LIKE %s", $searchPattern); 148 $totalSql .= $wpdb->prepare(" AND name LIKE %s", $searchPattern); 149 if ($searchScope === 'folder') { 150 $sql .= $wpdb->prepare(" AND (path LIKE %s OR parent LIKE %s)", "{$path}%", "{$path}%"); 151 $totalSql .= $wpdb->prepare(" AND (path LIKE %s OR parent LIKE %s)", "{$path}%", "{$path}%"); 147 152 } 148 153 } else { 149 if ( !empty( $config['recursive'] ) && $config['recursive'] === true) {150 $sql .= $wpdb->prepare( " AND parent LIKE %s", "{$path}%");151 $totalSql .= $wpdb->prepare( " AND parent LIKE %s", "{$path}%");154 if (!empty($config['recursive']) && $config['recursive'] === true) { 155 $sql .= $wpdb->prepare(" AND parent LIKE %s", "{$path}%"); 156 $totalSql .= $wpdb->prepare(" AND parent LIKE %s", "{$path}%"); 152 157 } else { 153 $sql .= $wpdb->prepare( " AND parent = %s", $path);154 $totalSql .= $wpdb->prepare( " AND parent = %s", $path);155 } 156 } 157 if ( !empty( $config['types'] ) && is_array( $config['types'] )) {158 $sql .= $wpdb->prepare(" AND parent = %s", $path); 159 $totalSql .= $wpdb->prepare(" AND parent = %s", $path); 160 } 161 } 162 if (!empty($config['types']) && is_array($config['types'])) { 158 163 $types = $config['types']; 159 $extensions = MimeTypeManager::getExtensionsByCategory( $types);160 $placeholders = implode( ',', array_fill( 0, count( $extensions ), '%s' ));161 $sql .= $wpdb->prepare( " AND `extension` IN ({$placeholders})", $extensions);162 $totalSql .= $wpdb->prepare( " AND `extension` IN ({$placeholders})", $extensions);164 $extensions = MimeTypeManager::getExtensionsByCategory($types); 165 $placeholders = implode(',', array_fill(0, count($extensions), '%s')); 166 $sql .= $wpdb->prepare(" AND `extension` IN ({$placeholders})", $extensions); 167 $totalSql .= $wpdb->prepare(" AND `extension` IN ({$placeholders})", $extensions); 163 168 } 164 169 $sql .= $wpdb->prepare( … … 168 173 $pagination['offset'] 169 174 ); 170 $cache_key = "ccpidb_folder_" . md5( $sql);171 $cache_group = "ccpidb_files_" . md5( "{$path}_{$accountId}");172 $files = wp_cache_get( $cache_key, $cache_group);173 if ( $files !== false) {175 $cache_key = "ccpidb_folder_" . md5($sql); 176 $cache_group = "ccpidb_files_" . md5("{$path}_{$accountId}"); 177 $files = wp_cache_get($cache_key, $cache_group); 178 if ($files !== false) { 174 179 return $files; 175 180 } 176 181 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching WordPress.DB.PreparedSQL.NotPrepared 177 $files = $wpdb->get_results( $sql);182 $files = $wpdb->get_results($sql); 178 183 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching WordPress.DB.PreparedSQL.NotPrepared 179 $total = $wpdb->get_var( $totalSql);180 if ( empty( $files ) || is_wp_error( $files )) {184 $total = $wpdb->get_var($totalSql); 185 if (empty($files) || is_wp_error($files)) { 181 186 $files = []; 182 187 } 183 $processedFiles = $this->processFiles( $files);184 $totalPages = ceil( $total / $perPage);188 $processedFiles = $this->processFiles($files); 189 $totalPages = ceil($total / $perPage); 185 190 $hasMore = $totalPages > $page; 186 191 $response = [ 187 'breadcrumb' => array_reverse( $this->getBreadcrumbByKey( $folderKey )),188 'totalPage' => ( $totalPages < 1 ? 1 : $totalPages),192 'breadcrumb' => array_reverse($this->getBreadcrumbByKey($folderKey)), 193 'totalPage' => ($totalPages < 1 ? 1 : $totalPages), 189 194 'hasMore' => $hasMore, 190 195 'currentPage' => $page, 191 196 'files' => $processedFiles, 192 'totalFiles' => intval( $total),193 ]; 194 if ( $hasMore) {197 'totalFiles' => intval($total), 198 ]; 199 if ($hasMore) { 195 200 $response['nextPage'] = $page + 1; 196 201 } 197 if ( !empty( $processedFiles )) {202 if (!empty($processedFiles)) { 198 203 wp_cache_set( 199 204 $cache_key, … … 219 224 * @return \CodeConfig\IDB\App\File|array|false The processed file data if found, otherwise null. 220 225 */ 221 public function getFile( $fileKey, $returnType = 'object' ) { 226 public function getFile($fileKey, $returnType = 'object') 227 { 222 228 global $wpdb; 223 229 // if ($this->isValidAccount($accountId) === false) { … … 226 232 // $file = $this->fetch("SELECT * FROM {$this->tableName} WHERE id = %s AND accountId = %s", [$id, $accountId]); 227 233 $cache_key = "ccpidb_file_{$fileKey}_{$returnType}"; 228 $file = wp_cache_get( $cache_key, 'ccpidb_files');229 if ( $file !== false) {234 $file = wp_cache_get($cache_key, 'ccpidb_files'); 235 if ($file !== false) { 230 236 return $file; 231 237 } 232 238 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 233 $file = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ));234 if ( empty( $file ) || is_wp_error( $file )) {235 return false; 236 } 237 $processedFile = $this->processFile( $file, $returnType);238 if ( !empty( $processedFile )) {239 wp_cache_set( $cache_key, $processedFile, 'ccpidb_files');239 $file = $wpdb->get_row($wpdb->prepare("SELECT * FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 240 if (empty($file) || is_wp_error($file)) { 241 return false; 242 } 243 $processedFile = $this->processFile($file, $returnType); 244 if (!empty($processedFile)) { 245 wp_cache_set($cache_key, $processedFile, 'ccpidb_files'); 240 246 } 241 247 return $processedFile; 242 248 } 243 249 244 private function getFileByPath( $path, $accountId, $returnType = 'object' ) { 250 private function getFileByPath($path, $accountId, $returnType = 'object') 251 { 245 252 global $wpdb; 246 253 $cache_key = "ccpidb_file_{$path}_{$accountId}"; 247 $file = wp_cache_get( $cache_key, 'ccpidb_files');248 if ( $file !== false) {254 $file = wp_cache_get($cache_key, 'ccpidb_files'); 255 if ($file !== false) { 249 256 return $file; 250 257 } 251 $file = $wpdb->get_row( $wpdb->prepare(258 $file = $wpdb->get_row($wpdb->prepare( 252 259 "SELECT * FROM %i WHERE path = %s AND accountId = %s", 253 260 $this->tableName, 254 261 $path, 255 262 $accountId 256 ) );257 $processedFile = $this->processFile( $file, $returnType);258 if ( !empty( $processedFile )) {259 wp_cache_set( $cache_key, $processedFile, 'ccpidb_files');263 )); 264 $processedFile = $this->processFile($file, $returnType); 265 if (!empty($processedFile)) { 266 wp_cache_set($cache_key, $processedFile, 'ccpidb_files'); 260 267 } 261 268 return $processedFile; 262 269 } 263 270 264 public function getFilesByKeys( array $keys, array $args = [] ) { 265 if ( empty( $keys ) ) { 271 public function getFilesByKeys(array $keys, array $args = []) 272 { 273 if (empty($keys)) { 266 274 return []; 267 275 } … … 279 287 'accountId' => '', 280 288 ]; 281 $args = wp_parse_args( $args, $defaults);289 $args = wp_parse_args($args, $defaults); 282 290 $recursive = $args['recursive']; 283 291 $moduleType = $args['moduleType'] ?? ''; 284 292 $accountId = $args['accountId'] ?? null; 285 if ( 'search-box' === $moduleType && empty( $args['search'] ) && $recursive && ($args['fileKey'] ?? '') !== '/') {293 if ('search-box' === $moduleType && empty($args['search']) && $recursive && ($args['fileKey'] ?? '') !== '/') { 286 294 $moduleType = 'file-browser'; 287 295 } … … 296 304 $applyNamesFilter = $args['applyNameFilter'] ?? []; 297 305 $types = $args['types'] ?? []; 298 $extensions = ccpidbGetAllowedModuleExtensions( $moduleType ); 299 $allowedExtensions = $this->processExtensions( $extensions, $additionalExtensions, $extensionsFilterType ); 300 ob_start(); 301 echo '<pre>'; 302 var_dump( $accountId ); 303 echo '</pre>'; 304 error_log( ob_get_clean() ); 305 $filesData = $this->getFileAttributesByKeys( $keys, [ 306 $extensions = ccpidbGetAllowedModuleExtensions($moduleType); 307 $allowedExtensions = $this->processExtensions($extensions, $additionalExtensions, $extensionsFilterType); 308 309 $filesData = $this->getFileAttributesByKeys($keys, [ 306 310 'id', 307 311 'path', … … 309 313 'name', 310 314 'isDir' 311 ], $accountId );312 if ( is_wp_error( $filesData ) || empty( $filesData )) {313 return ( $filesData ?: []);314 } 315 if ( empty( $filesData )) {315 ], $accountId); 316 if (is_wp_error($filesData) || empty($filesData)) { 317 return ($filesData ?: []); 318 } 319 if (empty($filesData)) { 316 320 return []; 317 321 } 318 $paths = array_filter( array_map( fn( $file ) => $file['path'] ?? null, $filesData ));319 if ( empty( $paths )) {322 $paths = array_filter(array_map(fn ($file) => $file['path'] ?? null, $filesData)); 323 if (empty($paths)) { 320 324 return []; 321 325 } 322 326 global $wpdb; 323 $sql = $wpdb->prepare( "SELECT * FROM %i WHERE 1 = 1", $this->tableName);324 $totalSql = $wpdb->prepare( "SELECT COUNT(*) as count FROM %i WHERE 1 = 1", $this->tableName);325 if ( !empty( $search )) {327 $sql = $wpdb->prepare("SELECT * FROM %i WHERE 1 = 1", $this->tableName); 328 $totalSql = $wpdb->prepare("SELECT COUNT(*) as count FROM %i WHERE 1 = 1", $this->tableName); 329 if (!empty($search)) { 326 330 $searchPath = []; 327 if ( $searchScope === 'global') {328 foreach ( $filesData as $file) {329 $searchPath[] = $this->getSuccessors( $file['path'], $file['accountId']);331 if ($searchScope === 'global') { 332 foreach ($filesData as $file) { 333 $searchPath[] = $this->getSuccessors($file['path'], $file['accountId']); 330 334 } 331 $paths = array_merge( ...$searchPath);332 } 333 if ( empty( $paths )) {335 $paths = array_merge(...$searchPath); 336 } 337 if (empty($paths)) { 334 338 return []; 335 339 } 336 $placeholders = implode( ',', array_fill( 0, count( $paths ), '%s' ));337 if ( $searchScope === 'global') {338 $sql .= $wpdb->prepare( " AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge( $paths, $paths, ["%{$search}%"] ));339 $totalSql .= $wpdb->prepare( " AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge( $paths, $paths, ["%{$search}%"] ));340 $placeholders = implode(',', array_fill(0, count($paths), '%s')); 341 if ($searchScope === 'global') { 342 $sql .= $wpdb->prepare(" AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge($paths, $paths, ["%{$search}%"])); 343 $totalSql .= $wpdb->prepare(" AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge($paths, $paths, ["%{$search}%"])); 340 344 } else { 341 $sql .= $wpdb->prepare( " AND (`parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge( $paths, ["%{$search}%"] ));342 $totalSql .= $wpdb->prepare( " AND (`parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge( $paths, ["%{$search}%"] ));343 } 344 } elseif ( $recursive) {345 $placeholders = implode( ',', array_fill( 0, count( $paths ), '%s' ));346 if ( $moduleType === 'file-browser') {347 $sql .= $wpdb->prepare( " AND `parent` IN ({$placeholders})", $paths);348 $totalSql .= $wpdb->prepare( " AND `parent` IN ({$placeholders})", $paths);349 } elseif ( $moduleType === 'file-uploader') {350 $uploadKeys = json_decode( sanitize_text_field( wp_unslash( $_COOKIE["ccpidb_file_uploader_files_{$shortcodeId}"] ?? '' ) ), true);351 if ( empty( $uploadKeys ) || !is_array( $uploadKeys )) {345 $sql .= $wpdb->prepare(" AND (`parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge($paths, ["%{$search}%"])); 346 $totalSql .= $wpdb->prepare(" AND (`parent` IN ({$placeholders})) AND `name` LIKE %s", array_merge($paths, ["%{$search}%"])); 347 } 348 } elseif ($recursive) { 349 $placeholders = implode(',', array_fill(0, count($paths), '%s')); 350 if ($moduleType === 'file-browser') { 351 $sql .= $wpdb->prepare(" AND `parent` IN ({$placeholders})", $paths); 352 $totalSql .= $wpdb->prepare(" AND `parent` IN ({$placeholders})", $paths); 353 } elseif ($moduleType === 'file-uploader') { 354 $uploadKeys = json_decode(sanitize_text_field(wp_unslash($_COOKIE["ccpidb_file_uploader_files_{$shortcodeId}"] ?? '')), true); 355 if (empty($uploadKeys) || !is_array($uploadKeys)) { 352 356 return []; 353 357 } 354 $uploadKeysPlaceholders = implode( ',', array_fill( 0, count( $uploadKeys ), '%s' ));355 $sql .= $wpdb->prepare( " AND `parent` IN ({$placeholders}) AND `fileKey` IN ({$uploadKeysPlaceholders})", array_merge( $paths, $uploadKeys ));356 $totalSql .= $wpdb->prepare( " AND `parent` IN ({$placeholders}) AND `fileKey` IN ({$uploadKeysPlaceholders})", array_merge( $paths, $uploadKeys ));358 $uploadKeysPlaceholders = implode(',', array_fill(0, count($uploadKeys), '%s')); 359 $sql .= $wpdb->prepare(" AND `parent` IN ({$placeholders}) AND `fileKey` IN ({$uploadKeysPlaceholders})", array_merge($paths, $uploadKeys)); 360 $totalSql .= $wpdb->prepare(" AND `parent` IN ({$placeholders}) AND `fileKey` IN ({$uploadKeysPlaceholders})", array_merge($paths, $uploadKeys)); 357 361 } else { 358 $sql .= $wpdb->prepare( " AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders}))", ...$paths, ...$paths);359 $totalSql .= $wpdb->prepare( " AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders}))", ...$paths, ...$paths);362 $sql .= $wpdb->prepare(" AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders}))", ...$paths, ...$paths); 363 $totalSql .= $wpdb->prepare(" AND (`path` IN ({$placeholders}) OR `parent` IN ({$placeholders}))", ...$paths, ...$paths); 360 364 // foreach ($paths as $path) { 361 365 // $sql .= $wpdb->prepare(" AND (`path` LIKE %s OR `parent` LIKE %s)", "$path%", "$path%"); … … 364 368 } 365 369 } else { 366 if ( !empty( $allowedExtensions ) && !in_array( 'folder', $allowedExtensions )) {370 if (!empty($allowedExtensions) && !in_array('folder', $allowedExtensions)) { 367 371 $allowedExtensions[] = 'folder'; 368 372 } 369 $placeholders = implode( ',', array_fill( 0, count( $paths ), '%s' ));370 $sql .= $wpdb->prepare( " AND `path` IN ({$placeholders})", $paths);371 $totalSql .= $wpdb->prepare( " AND `path` IN ({$placeholders})", $paths);372 } 373 if ( !empty( $types ) && is_array( $types )) {374 $extensions = MimeTypeManager::getExtensionsByCategory( $types);375 $extPlaceholders = implode( ',', array_fill( 0, count( $extensions ), '%s' ));376 $sql .= $wpdb->prepare( " AND `extension` IN ({$extPlaceholders})", $extensions);377 $totalSql .= $wpdb->prepare( " AND `extension` IN ({$extPlaceholders})", $extensions);378 } 379 if ( !empty( $allowedExtensions )) {380 $extPlaceholders = implode( ',', array_fill( 0, count( $allowedExtensions ), '%s' ));381 $sql .= $wpdb->prepare( " AND `extension` IN ({$extPlaceholders})", $allowedExtensions);382 $totalSql .= $wpdb->prepare( " AND `extension` IN ({$extPlaceholders})", $allowedExtensions);383 } 384 if ( !empty( $args['orderBy'] ) && !empty( $args['order'] )) {373 $placeholders = implode(',', array_fill(0, count($paths), '%s')); 374 $sql .= $wpdb->prepare(" AND `path` IN ({$placeholders})", $paths); 375 $totalSql .= $wpdb->prepare(" AND `path` IN ({$placeholders})", $paths); 376 } 377 if (!empty($types) && is_array($types)) { 378 $extensions = MimeTypeManager::getExtensionsByCategory($types); 379 $extPlaceholders = implode(',', array_fill(0, count($extensions), '%s')); 380 $sql .= $wpdb->prepare(" AND `extension` IN ({$extPlaceholders})", $extensions); 381 $totalSql .= $wpdb->prepare(" AND `extension` IN ({$extPlaceholders})", $extensions); 382 } 383 if (!empty($allowedExtensions)) { 384 $extPlaceholders = implode(',', array_fill(0, count($allowedExtensions), '%s')); 385 $sql .= $wpdb->prepare(" AND `extension` IN ({$extPlaceholders})", $allowedExtensions); 386 $totalSql .= $wpdb->prepare(" AND `extension` IN ({$extPlaceholders})", $allowedExtensions); 387 } 388 if (!empty($args['orderBy']) && !empty($args['order'])) { 385 389 $allowedOrderBy = [ 386 390 'id', … … 390 394 'updatedAt' 391 395 ]; 392 $orderBy = $this->sanitizeOrderBy( $args['orderBy'], $allowedOrderBy);393 $order = $this->sanitizeOrder( $args['order']);394 $offset = $this->sanitizePagination( $args['page'], $args['perPage']);395 $sql .= $wpdb->prepare( " ORDER BY (CASE WHEN extension = 'folder' THEN 0 ELSE 1 END), `{$orderBy}` {$order} LIMIT %d OFFSET %d", $offset['perPage'], $offset['offset']);396 $orderBy = $this->sanitizeOrderBy($args['orderBy'], $allowedOrderBy); 397 $order = $this->sanitizeOrder($args['order']); 398 $offset = $this->sanitizePagination($args['page'], $args['perPage']); 399 $sql .= $wpdb->prepare(" ORDER BY (CASE WHEN extension = 'folder' THEN 0 ELSE 1 END), `{$orderBy}` {$order} LIMIT %d OFFSET %d", $offset['perPage'], $offset['offset']); 396 400 } 397 401 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching WordPress.DB.PreparedSQL.NotPrepared 398 $files = $wpdb->get_results( $sql);402 $files = $wpdb->get_results($sql); 399 403 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching WordPress.DB.PreparedSQL.NotPrepared 400 $totalCount = $wpdb->get_row( $totalSql);401 if ( empty( $files ) || is_wp_error( $files ) || is_wp_error( $totalCount )) {404 $totalCount = $wpdb->get_row($totalSql); 405 if (empty($files) || is_wp_error($files) || is_wp_error($totalCount)) { 402 406 return []; 403 407 } 404 $files = $this->processFiles( $files, $returnType);405 $totalFiles = ( isset( $totalCount->count ) ? (int) $totalCount->count : count( $files ));408 $files = $this->processFiles($files, $returnType); 409 $totalFiles = (isset($totalCount->count) ? (int) $totalCount->count : count($files)); 406 410 $page = $offset['page'] ?? 1; 407 $totalPage = ceil( $totalFiles / ($offset['perPage'] ?? 1));411 $totalPage = ceil($totalFiles / ($offset['perPage'] ?? 1)); 408 412 $response = [ 409 413 'breadcrumb' => [[ 410 414 'fileKey' => '/', 411 'name' => __( 'Home', 'integrate-dropbox'),415 'name' => __('Home', 'integrate-dropbox'), 412 416 ]], 413 'totalPage' => ( $totalPage < 1 ? 1 : $totalPage),417 'totalPage' => ($totalPage < 1 ? 1 : $totalPage), 414 418 'hasMore' => $totalPage > $page, 415 419 'currentPage' => $page, … … 417 421 'totalFiles' => $totalFiles, 418 422 ]; 419 if ( $response['hasMore']) {423 if ($response['hasMore']) { 420 424 $response['nextPage'] = $page + 1; 421 425 } … … 423 427 } 424 428 425 public function getFolderTree( $accountId, ... $rootPaths ) { 429 public function getFolderTree($accountId, ... $rootPaths) 430 { 426 431 global $wpdb; 427 432 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 428 $sql = $wpdb->prepare( "SELECT fileKey, name, parent, path FROM %i WHERE `extension` = 'folder' AND accountId = %s", $this->tableName, $accountId);429 if ( !empty( $rootPaths ) && !in_array( '/', $rootPaths, true )) {433 $sql = $wpdb->prepare("SELECT fileKey, name, parent, path FROM %i WHERE `extension` = 'folder' AND accountId = %s", $this->tableName, $accountId); 434 if (!empty($rootPaths) && !in_array('/', $rootPaths, true)) { 430 435 $sql .= " AND ( 0=1 "; 431 foreach ( $rootPaths as $rootPath) {432 $sql .= $wpdb->prepare( " OR path LIKE %s OR path LIKE %s", "{$rootPath}/%", "{$rootPath}");436 foreach ($rootPaths as $rootPath) { 437 $sql .= $wpdb->prepare(" OR path LIKE %s OR path LIKE %s", "{$rootPath}/%", "{$rootPath}"); 433 438 } 434 439 $sql .= " ) "; 435 440 } 436 $files = $wpdb->get_results( $sql, ARRAY_A);437 if ( empty( $files ) || is_wp_error( $files )) {441 $files = $wpdb->get_results($sql, ARRAY_A); 442 if (empty($files) || is_wp_error($files)) { 438 443 return []; 439 444 } 440 return $this->processFolderTree( $files, $rootPaths ); 441 } 442 443 public function addFile( array $data ) { 445 return $this->processFolderTree($files, $rootPaths); 446 } 447 448 public function addFile(array $data) 449 { 444 450 global $wpdb; 445 451 $file = [ … … 448 454 'path' => $data['path'] ?? '', 449 455 'name' => $data['name'] ?? null, 450 'size' => intval( $data['size'] ?? 0),456 'size' => intval($data['size'] ?? 0), 451 457 'parent' => $data['parent'] ?? null, 452 458 'accountId' => $data['accountId'] ?? '', … … 457 463 'sharedLink' => $data['sharedLink'] ?? null, 458 464 'isDir' => $data['isDir'] ?? null, 459 'permissions' => maybe_serialize( $data['permissions'] ?? []),465 'permissions' => maybe_serialize($data['permissions'] ?? []), 460 466 'hasOwnThumbnail' => $data['hasOwnThumbnail'] ?? null, 461 467 'icon' => $data['icon'] ?? null, 462 'additionalData' => maybe_serialize( $data['additionalData'] ?? []),463 'createdAt' => current_time( 'mysql'),464 'updatedAt' => current_time( 'mysql'),465 ]; 466 if ( empty( $file['fileId'] ) || empty( $file['accountId'] )) {467 return new WP_Error(404, __( 'Missing file ID or account ID.', 'integrate-dropbox'));468 'additionalData' => maybe_serialize($data['additionalData'] ?? []), 469 'createdAt' => current_time('mysql'), 470 'updatedAt' => current_time('mysql'), 471 ]; 472 if (empty($file['fileId']) || empty($file['accountId'])) { 473 return new WP_Error(404, __('Missing file ID or account ID.', 'integrate-dropbox')); 468 474 } 469 475 $format = [ … … 506 512 '%s', 507 513 ]; 508 if ( !empty( $data['thumbnailRatio'] )) {514 if (!empty($data['thumbnailRatio'])) { 509 515 $file['thumbnailRatio'] = $data['thumbnailRatio']; 510 516 $format[] = '%s'; 511 517 // thumbnailRatio 512 518 } 513 if ( $this->isCachedFile( $file["fileKey"] )) {519 if ($this->isCachedFile($file["fileKey"])) { 514 520 unset($file["id"]); 515 521 unset($file["fileId"]); … … 549 555 '%s', 550 556 ]; 551 if ( !empty( $data['thumbnailRatio'] )) {557 if (!empty($data['thumbnailRatio'])) { 552 558 $updateFormat[] = '%s'; 553 559 // thumbnailRatio … … 563 569 ); 564 570 } 565 return $wpdb->insert( $this->tableName, $file, $format ); 566 } 567 568 public function deleteFiles( $fileKeys ) : int { 569 global $wpdb; 570 if ( empty( $fileKeys ) ) { 571 return $wpdb->insert($this->tableName, $file, $format); 572 } 573 574 public function deleteFiles($fileKeys): int 575 { 576 global $wpdb; 577 if (empty($fileKeys)) { 571 578 return 0; 572 579 } 573 580 $fileKeys = (array) $fileKeys; 574 $files = $this->getFileAttributesByKeys( $fileKeys, ['accountId', 'path', 'isDir']);575 if ( empty( $files )) {581 $files = $this->getFileAttributesByKeys($fileKeys, ['accountId', 'path', 'isDir']); 582 if (empty($files)) { 576 583 return 0; 577 584 } 578 585 $filePaths = []; 579 586 $folderRefs = []; 580 foreach ( $files as $file) {581 if ( empty( $file['path'] ) || empty( $file['accountId'] )) {587 foreach ($files as $file) { 588 if (empty($file['path']) || empty($file['accountId'])) { 582 589 continue; 583 590 } 584 if ( !empty( $file['isDir'] )) {591 if (!empty($file['isDir'])) { 585 592 $folderRefs[] = [$file['path'], $file['accountId']]; 586 593 } else { … … 588 595 } 589 596 } 590 foreach ( $folderRefs as [$path, $accountId]) {591 $filePaths = array_merge( $filePaths, (array) $this->getSuccessors( $path, $accountId ));592 } 593 if ( empty( $filePaths )) {597 foreach ($folderRefs as [$path, $accountId]) { 598 $filePaths = array_merge($filePaths, (array) $this->getSuccessors($path, $accountId)); 599 } 600 if (empty($filePaths)) { 594 601 return 0; 595 602 } 596 $placeholders = implode( ',', array_fill( 0, count( $filePaths ), '%s' ) ); 597 return (int) $wpdb->query( $wpdb->prepare( "DELETE FROM %i WHERE path IN ({$placeholders}) OR parent IN ({$placeholders})", array_merge( [$this->tableName], $filePaths, $filePaths ) ) ); 598 } 599 600 public function deleteFilesByAccount( $accountId ) { 601 if ( empty( $accountId ) ) { 603 $placeholders = implode(',', array_fill(0, count($filePaths), '%s')); 604 return (int) $wpdb->query($wpdb->prepare("DELETE FROM %i WHERE path IN ({$placeholders}) OR parent IN ({$placeholders})", array_merge([$this->tableName], $filePaths, $filePaths))); 605 } 606 607 public function deleteFilesByAccount($accountId) 608 { 609 if (empty($accountId)) { 602 610 return 0; 603 611 } 604 return $this->deleteRecords( [612 return $this->deleteRecords([ 605 613 'accountId' => $accountId, 606 ] ); 607 } 608 609 public function isCachedFile( $fileKey ) { 610 global $wpdb; 611 $folder = $wpdb->get_row( $wpdb->prepare( "SELECT fileKey FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 612 return !empty( $folder ); 613 } 614 615 public function getPathById( $fileId ) { 616 global $wpdb; 617 $file = $wpdb->get_row( $wpdb->prepare( "SELECT path FROM %i WHERE fileId = %s", $this->tableName, $fileId ) ); 618 if ( empty( $file ) || is_wp_error( $file ) ) { 614 ]); 615 } 616 617 public function isCachedFile($fileKey) 618 { 619 global $wpdb; 620 $folder = $wpdb->get_row($wpdb->prepare("SELECT fileKey FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 621 return !empty($folder); 622 } 623 624 public function getPathById($fileId) 625 { 626 global $wpdb; 627 $file = $wpdb->get_row($wpdb->prepare("SELECT path FROM %i WHERE fileId = %s", $this->tableName, $fileId)); 628 if (empty($file) || is_wp_error($file)) { 619 629 return false; 620 630 } … … 622 632 } 623 633 624 public function getPathsByKeys( $fileKeys ) { 625 global $wpdb; 626 $isString = \is_string( $fileKeys ); 627 if ( $isString ) { 634 public function getPathsByKeys($fileKeys) 635 { 636 global $wpdb; 637 $isString = \is_string($fileKeys); 638 if ($isString) { 628 639 $fileKeys = [$fileKeys]; 629 640 } 630 $placeholders = implode( ',', array_fill( 0, count( $fileKeys ), '%s' ));631 $preparedQuery = $wpdb->prepare( "SELECT path FROM %i WHERE fileKey IN ({$placeholders})", array_merge( [$this->tableName], $fileKeys ));632 $results = $wpdb->get_results( $preparedQuery);633 if ( $isString && \count( $results ) === 1) {641 $placeholders = implode(',', array_fill(0, count($fileKeys), '%s')); 642 $preparedQuery = $wpdb->prepare("SELECT path FROM %i WHERE fileKey IN ({$placeholders})", array_merge([$this->tableName], $fileKeys)); 643 $results = $wpdb->get_results($preparedQuery); 644 if ($isString && \count($results) === 1) { 634 645 return $results[0]->path; 635 646 } 636 647 $paths = []; 637 foreach ( $results as $row) {648 foreach ($results as $row) { 638 649 $paths[] = $row->path; 639 650 } … … 650 661 global $wpdb; 651 662 // Validate inputs 652 if ( empty( $columns ) || empty( $fileKeys )) {663 if (empty($columns) || empty($fileKeys)) { 653 664 return []; 654 665 } 655 666 // Filter and sanitize columns 656 $sanitizedColumns = array_filter( $columns, fn( $col ) => in_array( $col, self::COLUMNS, true ));657 if ( empty( $sanitizedColumns )) {667 $sanitizedColumns = array_filter($columns, fn ($col) => in_array($col, self::COLUMNS, true)); 668 if (empty($sanitizedColumns)) { 658 669 return []; 659 670 } 660 671 // Validate WHERE clause columns 661 if ( !empty( $where )) {662 foreach ( array_keys( $where ) as $whereColumn) {663 if ( !in_array( $whereColumn, self::COLUMNS, true )) {672 if (!empty($where)) { 673 foreach (array_keys($where) as $whereColumn) { 674 if (!in_array($whereColumn, self::COLUMNS, true)) { 664 675 return []; 665 676 } … … 667 678 } 668 679 // Prepare column identifiers using %i placeholders for security 669 $columnPlaceholders = implode( ',', array_fill( 0, count( $sanitizedColumns ), '%i' ));670 $keyPlaceholders = implode( ',', array_fill( 0, count( $fileKeys ), '%s' ));680 $columnPlaceholders = implode(',', array_fill(0, count($sanitizedColumns), '%i')); 681 $keyPlaceholders = implode(',', array_fill(0, count($fileKeys), '%s')); 671 682 // Build the base query with proper placeholders 672 683 $sql = "SELECT {$columnPlaceholders} FROM %i WHERE fileKey IN ({$keyPlaceholders})"; 673 $params = array_merge( $sanitizedColumns, [$this->tableName], $fileKeys);684 $params = array_merge($sanitizedColumns, [$this->tableName], $fileKeys); 674 685 // Add additional WHERE conditions with proper format specifiers 675 if ( !empty( $where )) {686 if (!empty($where)) { 676 687 $whereConditions = []; 677 688 $whereIndex = 0; 678 foreach ( $where as $column => $value) {689 foreach ($where as $column => $value) { 679 690 // Use provided format or default to %s 680 $format = ( !empty( $where_format[$whereIndex] ) ? $where_format[$whereIndex] : '%s');691 $format = (!empty($where_format[$whereIndex]) ? $where_format[$whereIndex] : '%s'); 681 692 $whereConditions[] = "%i = {$format}"; 682 693 $params[] = $column; … … 684 695 $whereIndex++; 685 696 } 686 $sql .= " AND " . implode( ' AND ', $whereConditions);687 } 688 $preparedQuery = $wpdb->prepare( $sql, $params);689 $results = $wpdb->get_results( $preparedQuery, $returnType);690 if ( empty( $results ) || is_wp_error( $results )) {697 $sql .= " AND " . implode(' AND ', $whereConditions); 698 } 699 $preparedQuery = $wpdb->prepare($sql, $params); 700 $results = $wpdb->get_results($preparedQuery, $returnType); 701 if (empty($results) || is_wp_error($results)) { 691 702 return []; 692 703 } … … 694 705 } 695 706 696 public function getAccountIdByFileKey( $fileKey ) { 697 global $wpdb; 698 $file = $wpdb->get_row( $wpdb->prepare( "SELECT accountId FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 699 if ( empty( $file ) || is_wp_error( $file ) ) { 707 public function getAccountIdByFileKey($fileKey) 708 { 709 global $wpdb; 710 $file = $wpdb->get_row($wpdb->prepare("SELECT accountId FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 711 if (empty($file) || is_wp_error($file)) { 700 712 return false; 701 713 } … … 703 715 } 704 716 705 public function updateThumbnail( $fileKey, $url ) { 717 public function updateThumbnail($fileKey, $url) 718 { 706 719 global $wpdb; 707 720 return $wpdb->update( … … 740 753 $clean = false 741 754 ) { 742 if ( $path === '' || $path === '/' || empty( $accountId )) {743 return new WP_Error(400, __( 'Invalid path or account ID.', 'integrate-dropbox'));744 } 745 global $wpdb; 746 if ( $clean === false) {747 $file = $this->getFileByPath( $path, $accountId, 'array');748 if ( is_wp_error( $file )) {755 if ($path === '' || $path === '/' || empty($accountId)) { 756 return new WP_Error(400, __('Invalid path or account ID.', 'integrate-dropbox')); 757 } 758 global $wpdb; 759 if ($clean === false) { 760 $file = $this->getFileByPath($path, $accountId, 'array'); 761 if (is_wp_error($file)) { 749 762 return $file; 750 763 } 751 764 $existingData = $file['metaData'] ?? []; 752 if ( is_array( $existingData )) {753 $metaData = array_merge( $existingData, $metaData);765 if (is_array($existingData)) { 766 $metaData = array_merge($existingData, $metaData); 754 767 } 755 768 } 756 769 $filteredMetaData = []; 757 foreach ( self::META_DATA as $key) {758 if ( isset( $metaData[$key] )) {770 foreach (self::META_DATA as $key) { 771 if (isset($metaData[$key])) { 759 772 $filteredMetaData[$key] = $metaData[$key]; 760 773 } … … 763 776 $this->tableName, 764 777 [ 765 'metaData' => maybe_serialize( $filteredMetaData),778 'metaData' => maybe_serialize($filteredMetaData), 766 779 ], 767 780 [ … … 774 787 } 775 788 776 public function getBreadcrumbByKey( $key, $args = [] ) { 789 public function getBreadcrumbByKey($key, $args = []) 790 { 777 791 $defaults = [ 778 792 'rootFileKey' => null, 779 793 'rootFolderKey' => '/', 780 794 ]; 781 $args = wp_parse_args( $args, $defaults);795 $args = wp_parse_args($args, $defaults); 782 796 $rootFileKey = $args['rootFileKey']; 783 797 $rootFolderKey = $args['rootFolderKey']; 784 798 $home = [[ 785 799 'fileKey' => '/', 786 'name' => __( 'Home', 'integrate-dropbox'),800 'name' => __('Home', 'integrate-dropbox'), 787 801 ]]; 788 802 // Empty or root key 789 if ( empty( $key ) || $key === '/' || $key === $rootFolderKey) {803 if (empty($key) || $key === '/' || $key === $rootFolderKey) { 790 804 return $home; 791 805 } 792 806 // Resolve root folder key if rootFileKey is provided 793 if ( $rootFileKey !== null && $rootFolderKey === '/') {794 $rootFile = $this->getFile( $rootFileKey, 'array');795 if ( is_wp_error( $rootFile )) {807 if ($rootFileKey !== null && $rootFolderKey === '/') { 808 $rootFile = $this->getFile($rootFileKey, 'array'); 809 if (is_wp_error($rootFile)) { 796 810 return $home; 797 811 } 798 812 $parentPath = $rootFile['parent'] ?? null; 799 if ( $parentPath) {800 $rootFolder = $this->getFileByPath( $parentPath, $rootFile['accountId'] ?? '', 'array');801 if ( !is_wp_error( $rootFolder )) {813 if ($parentPath) { 814 $rootFolder = $this->getFileByPath($parentPath, $rootFile['accountId'] ?? '', 'array'); 815 if (!is_wp_error($rootFolder)) { 802 816 $rootFolderKey = $rootFolder['fileKey'] ?? '/'; 803 817 } 804 818 } 805 819 } 806 $file = $this->getFile( $key, 'array');807 if ( is_wp_error( $file )) {820 $file = $this->getFile($key, 'array'); 821 if (is_wp_error($file)) { 808 822 return $home; 809 823 } 810 824 $fileId = $file['fileId'] ?? null; 811 825 $accountId = $file['accountId'] ?? null; 812 if ( !$fileId || !$accountId) {826 if (!$fileId || !$accountId) { 813 827 return $home; 814 828 } … … 816 830 $breadcrumb = [[ 817 831 'fileKey' => $key, 818 'name' => $file['name'] ?? __( 'Unknown Folder', 'integrate-dropbox'),832 'name' => $file['name'] ?? __('Unknown Folder', 'integrate-dropbox'), 819 833 ]]; 820 834 $parentPath = $file['parent'] ?? null; 821 835 // Stop if no parent 822 if ( empty( $parentPath )) {836 if (empty($parentPath)) { 823 837 return $breadcrumb; 824 838 } 825 839 // Resolve parent folder key 826 840 $parentFolderKey = '/'; 827 if ( $parentPath !== '/') {828 $parentFolder = $this->getFileByPath( $parentPath, $accountId, 'array');829 if ( is_wp_error( $parentFolder )) {841 if ($parentPath !== '/') { 842 $parentFolder = $this->getFileByPath($parentPath, $accountId, 'array'); 843 if (is_wp_error($parentFolder)) { 830 844 return $home; 831 845 } … … 833 847 } 834 848 // Stop at root folder 835 if ( $parentFolderKey === '/' || $parentFolderKey === $rootFolderKey) {836 return array_merge( $breadcrumb, $home);849 if ($parentFolderKey === '/' || $parentFolderKey === $rootFolderKey) { 850 return array_merge($breadcrumb, $home); 837 851 } 838 852 // Recursive parent breadcrumb 839 $parentBreadcrumb = $this->getBreadcrumbByKey( $parentFolderKey, [853 $parentBreadcrumb = $this->getBreadcrumbByKey($parentFolderKey, [ 840 854 'rootFolderKey' => $rootFolderKey, 841 ] );842 if ( is_wp_error( $parentBreadcrumb )) {855 ]); 856 if (is_wp_error($parentBreadcrumb)) { 843 857 return $parentBreadcrumb; 844 858 } 845 return array_merge( $breadcrumb, $parentBreadcrumb ); 846 } 847 848 public function getFileAttributesByKeys( array $keys, array $attributes = ['id'], $accountId = null ) { 849 if ( empty( $keys ) ) { 859 return array_merge($breadcrumb, $parentBreadcrumb); 860 } 861 862 public function getFileAttributesByKeys(array $keys, array $attributes = ['id'], $accountId = null) 863 { 864 if (empty($keys)) { 850 865 return []; 851 866 } 852 867 global $wpdb; 853 $placeholders = implode( ',', array_fill( 0, count( $keys ), '%s' ));854 $preparedQuery = $wpdb->prepare( "SELECT * FROM %i WHERE `fileKey` IN ({$placeholders})", array_merge( [$this->tableName], $keys ));855 if ( !empty( $accountId )) {856 $preparedQuery .= $wpdb->prepare( " AND `accountId` = %s", $accountId);857 } 858 $cacheKey = "ccpidb_file_attributes_" . md5( $preparedQuery);859 if ( false !== ($cached = wp_cache_get( $cacheKey, 'ccpidb_files' ))) {868 $placeholders = implode(',', array_fill(0, count($keys), '%s')); 869 $preparedQuery = $wpdb->prepare("SELECT * FROM %i WHERE `fileKey` IN ({$placeholders})", array_merge([$this->tableName], $keys)); 870 if (!empty($accountId)) { 871 $preparedQuery .= $wpdb->prepare(" AND `accountId` = %s", $accountId); 872 } 873 $cacheKey = "ccpidb_file_attributes_" . md5($preparedQuery); 874 if (false !== ($cached = wp_cache_get($cacheKey, 'ccpidb_files'))) { 860 875 return $cached; 861 876 } 862 $files = $wpdb->get_results( $preparedQuery);863 if ( empty( $files )) {877 $files = $wpdb->get_results($preparedQuery); 878 if (empty($files)) { 864 879 return []; 865 880 } 866 $processedFiles = $this->processFiles( $files, 'object');867 if ( count( $attributes ) === 1) {881 $processedFiles = $this->processFiles($files, 'object'); 882 if (count($attributes) === 1) { 868 883 $attr = $attributes[0]; 869 884 $result = []; 870 foreach ( $processedFiles as $file) {885 foreach ($processedFiles as $file) { 871 886 $result[] = $file->{$attr} ?? null; 872 887 } 873 wp_cache_set( $cacheKey, $result, 'ccpidb_files');888 wp_cache_set($cacheKey, $result, 'ccpidb_files'); 874 889 return $result; 875 890 } 876 891 $result = []; 877 foreach ( $processedFiles as $file) {892 foreach ($processedFiles as $file) { 878 893 $fileData = []; 879 foreach ( $attributes as $attr) {894 foreach ($attributes as $attr) { 880 895 $fileData[$attr] = $file->{$attr} ?? null; 881 896 } 882 897 $result[] = $fileData; 883 898 } 884 wp_cache_set( $cacheKey, $result, 'ccpidb_files');899 wp_cache_set($cacheKey, $result, 'ccpidb_files'); 885 900 return $result; 886 901 } 887 902 888 public function getSuccessors( $parentPath, $accountId ) { 903 public function getSuccessors($parentPath, $accountId) 904 { 889 905 $successor = []; 890 $folders = $this->getChildFolderIds( $parentPath, $accountId);891 foreach ( $folders as $folderRow) {906 $folders = $this->getChildFolderIds($parentPath, $accountId); 907 foreach ($folders as $folderRow) { 892 908 $folderPath = $folderRow['path']; 893 909 $successor[] = $folderPath; 894 $childFolders = $this->getChildFolderIds( $folderPath, $accountId);895 if ( !empty( $childFolders )) {896 $successor = array_merge( $successor, $this->getSuccessors( $folderPath, $accountId ));910 $childFolders = $this->getChildFolderIds($folderPath, $accountId); 911 if (!empty($childFolders)) { 912 $successor = array_merge($successor, $this->getSuccessors($folderPath, $accountId)); 897 913 } 898 914 } 899 915 $successor[] = $parentPath; 900 return array_unique( $successor ); 901 } 902 903 public function getAllPhotos( $args = [] ) { 916 return array_unique($successor); 917 } 918 919 public function getAllPhotos($args = []) 920 { 904 921 $defaults = [ 905 922 'perPage' => 40, … … 908 925 'order' => 'desc', 909 926 ]; 910 $args = wp_parse_args( $args, $defaults);927 $args = wp_parse_args($args, $defaults); 911 928 $perPage = (int) $args['perPage']; 912 929 $page = (int) $args['page']; 913 $orderBy = $this->sanitizeOrderBy( $args['orderBy'], [930 $orderBy = $this->sanitizeOrderBy($args['orderBy'], [ 914 931 'name', 915 932 'createdAt', 916 933 'updatedAt', 917 934 'size' 918 ] );919 $order = $this->sanitizeOrder( $args['order']);935 ]); 936 $order = $this->sanitizeOrder($args['order']); 920 937 $offset = ($page - 1) * $perPage; 921 938 global $wpdb; 922 939 $cache_key = "ccpidb_all_photos_{$perPage}_{$page}_{$orderBy}_{$order}"; 923 $photos = wp_cache_get( $cache_key, 'ccpidb_files');924 if ( !empty( $photos )) {940 $photos = wp_cache_get($cache_key, 'ccpidb_files'); 941 if (!empty($photos)) { 925 942 return $photos; 926 943 } 927 $photos = $wpdb->get_results( $wpdb->prepare(944 $photos = $wpdb->get_results($wpdb->prepare( 928 945 "SELECT * FROM %i WHERE mimeType LIKE %s ORDER BY %i {$order} LIMIT %d OFFSET %d", 929 946 $this->tableName, … … 932 949 $perPage, 933 950 $offset 934 ) );935 if ( is_wp_error( $photos )) {951 )); 952 if (is_wp_error($photos)) { 936 953 return $photos; 937 954 } 938 $total = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE mimeType LIKE %s", $this->tableName, 'image/%' ));939 if ( is_wp_error( $total )) {955 $total = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM %i WHERE mimeType LIKE %s", $this->tableName, 'image/%')); 956 if (is_wp_error($total)) { 940 957 return $total; 941 958 } 942 $totalPages = ceil( $total / $perPage);959 $totalPages = ceil($total / $perPage); 943 960 $hasMore = $page < $totalPages; 944 961 $response = [ … … 948 965 'totalPages' => (int) $totalPages, 949 966 'hasMore' => $hasMore, 950 'photos' => $this->processFiles( $photos),951 ]; 952 if ( $hasMore) {967 'photos' => $this->processFiles($photos), 968 ]; 969 if ($hasMore) { 953 970 $response['nextPage'] = $page + 1; 954 971 } 955 if ( !empty( $photos )) {956 wp_cache_set( $cache_key, $response, 'ccpidb_files');972 if (!empty($photos)) { 973 wp_cache_set($cache_key, $response, 'ccpidb_files'); 957 974 } 958 975 return $response; 959 976 } 960 977 961 public function searchFiles( $query, $options = [] ) { 962 global $wpdb; 963 if ( empty( $query ) || empty( $options['accountId'] ) ) { 964 return new WP_Error('invalid_query', __( 'Search query and account ID are required.', 'integrate-dropbox' )); 978 public function searchFiles($query, $options = []) 979 { 980 global $wpdb; 981 if (empty($query) || empty($options['accountId'])) { 982 return new WP_Error('invalid_query', __('Search query and account ID are required.', 'integrate-dropbox')); 965 983 } 966 984 $defaults = [ … … 972 990 'order' => 'ASC', 973 991 ]; 974 $options = wp_parse_args( $options, $defaults);975 $order = $this->sanitizeOrder( $options['order']);976 $orderBy = $this->sanitizeOrderBy( $options['orderBy'], [992 $options = wp_parse_args($options, $defaults); 993 $order = $this->sanitizeOrder($options['order']); 994 $orderBy = $this->sanitizeOrderBy($options['orderBy'], [ 977 995 'name', 978 996 'createdAt', 979 997 'updatedAt', 980 998 'size' 981 ] );982 $pagination = $this->sanitizePagination( $options['page'], $options['perPage']);999 ]); 1000 $pagination = $this->sanitizePagination($options['page'], $options['perPage']); 983 1001 $perPage = $pagination['perPage']; 984 1002 $offset = $pagination['offset']; 985 1003 $page = $pagination['page']; 986 $searchPattern = '%' . $wpdb->esc_like( $query) . '%';1004 $searchPattern = '%' . $wpdb->esc_like($query) . '%'; 987 1005 $sql = $wpdb->prepare( 988 1006 "SELECT * FROM %i WHERE accountId = %s AND name LIKE %s", … … 997 1015 $searchPattern 998 1016 ); 999 if ( $options['scope'] === 'folder' && !empty( $options['path'] )) {1000 $sql .= $wpdb->prepare( " AND parent = %s", $options['path']);1001 $totalSql .= $wpdb->prepare( " AND parent = %s", $options['path']);1002 } 1003 if ( !empty( $options['types'] ) && is_array( $options['types'] )) {1004 $extensions = MimeTypeManager::getExtensionsByCategory( $options['types']);1005 if ( !empty( $extensions )) {1006 $extPlaceholders = implode( ',', array_fill( 0, count( $extensions ), '%s' ));1007 $sql .= $wpdb->prepare( " AND extension IN ({$extPlaceholders})", $extensions);1008 $totalSql .= $wpdb->prepare( " AND extension IN ({$extPlaceholders})", $extensions);1017 if ($options['scope'] === 'folder' && !empty($options['path'])) { 1018 $sql .= $wpdb->prepare(" AND parent = %s", $options['path']); 1019 $totalSql .= $wpdb->prepare(" AND parent = %s", $options['path']); 1020 } 1021 if (!empty($options['types']) && is_array($options['types'])) { 1022 $extensions = MimeTypeManager::getExtensionsByCategory($options['types']); 1023 if (!empty($extensions)) { 1024 $extPlaceholders = implode(',', array_fill(0, count($extensions), '%s')); 1025 $sql .= $wpdb->prepare(" AND extension IN ({$extPlaceholders})", $extensions); 1026 $totalSql .= $wpdb->prepare(" AND extension IN ({$extPlaceholders})", $extensions); 1009 1027 } 1010 1028 } … … 1015 1033 $offset 1016 1034 ); 1017 $cache_key = "ccpidb_search_{$options['accountId']}_" . md5( $query . serialize( $options ));1018 $cached = wp_cache_get( $cache_key, 'ccpidb_files');1019 if ( !empty( $cached )) {1035 $cache_key = "ccpidb_search_{$options['accountId']}_" . md5($query . serialize($options)); 1036 $cached = wp_cache_get($cache_key, 'ccpidb_files'); 1037 if (!empty($cached)) { 1020 1038 return $cached; 1021 1039 } 1022 $results = $wpdb->get_results( $sql);1023 $totalFile = $wpdb->get_var( $totalSql);1024 if ( is_wp_error( $results ) || is_wp_error( $totalFile )) {1040 $results = $wpdb->get_results($sql); 1041 $totalFile = $wpdb->get_var($totalSql); 1042 if (is_wp_error($results) || is_wp_error($totalFile)) { 1025 1043 return $results; 1026 1044 } 1027 if ( empty( $results ) || $totalFile == 0) {1045 if (empty($results) || $totalFile == 0) { 1028 1046 return []; 1029 1047 } 1030 1048 $response = [ 1031 1049 'total' => (int) $totalFile, 1032 'files' => $this->processFiles( $results),1050 'files' => $this->processFiles($results), 1033 1051 'perPage' => $perPage, 1034 'totalPages' => ceil( $totalFile / $perPage),1052 'totalPages' => ceil($totalFile / $perPage), 1035 1053 'currentPage' => $page, 1036 1054 'hasMore' => $page * $perPage < $totalFile, 1037 1055 ]; 1038 if ( $response['hasMore']) {1056 if ($response['hasMore']) { 1039 1057 $response['nextPage'] = $page + 1; 1040 1058 } 1041 if ( !empty( $results )) {1042 wp_cache_set( $cache_key, $response, 'ccpidb_files');1059 if (!empty($results)) { 1060 wp_cache_set($cache_key, $response, 'ccpidb_files'); 1043 1061 } 1044 1062 return $response; 1045 1063 } 1046 1064 1047 public function getSharedKey( $fileKey, $options = [] ) { 1065 public function getSharedKey($fileKey, $options = []) 1066 { 1048 1067 $defaults = [ 1049 1068 'expireIn' => 3600, 1050 1069 'password' => null, 1051 1070 ]; 1052 $options = wp_parse_args( $options, $defaults);1053 $expireIn = intval( $options['expireIn']);1054 $password = sanitize_text_field( $options['password'] ?? null);1055 $expiry = ( $expireIn > 0 ? time() + $expireIn : 0);1056 $passwordHash = ( !empty( $password ) ? md5( $password ) : '');1057 $sharedData = $this->getSharedData( $fileKey);1058 $key = md5( "{$fileKey}|{$expiry}|{$passwordHash}");1059 if ( !empty( $sharedData[$key] ) && $sharedData[$key]['expiry'] >= time()) {1071 $options = wp_parse_args($options, $defaults); 1072 $expireIn = intval($options['expireIn']); 1073 $password = sanitize_text_field($options['password'] ?? null); 1074 $expiry = ($expireIn > 0 ? time() + $expireIn : 0); 1075 $passwordHash = (!empty($password) ? md5($password) : ''); 1076 $sharedData = $this->getSharedData($fileKey); 1077 $key = md5("{$fileKey}|{$expiry}|{$passwordHash}"); 1078 if (!empty($sharedData[$key]) && $sharedData[$key]['expiry'] >= time()) { 1060 1079 return "{$fileKey}-{$key}"; 1061 1080 } … … 1067 1086 ]; 1068 1087 // Save entire sharedData list 1069 $this->saveSharedData( $fileKey, $sharedData);1088 $this->saveSharedData($fileKey, $sharedData); 1070 1089 return "{$fileKey}-{$key}"; 1071 1090 } 1072 1091 1073 public function getDownloadKey( $fileKey, $options = [] ) { 1092 public function getDownloadKey($fileKey, $options = []) 1093 { 1074 1094 $defaults = [ 1075 1095 'expireIn' => 3600, … … 1077 1097 'limit' => 0, 1078 1098 ]; 1079 $options = wp_parse_args( $options, $defaults);1080 $expireIn = intval( $options['expireIn']);1081 $password = sanitize_text_field( $options['password'] ?? null);1082 $limit = intval( $options['limit']);1083 $expiry = ( $expireIn > 0 ? time() + $expireIn : 0);1084 $passwordHash = ( !empty( $password ) ? md5( $password ) : '');1085 $downloadData = $this->getDownloadData( $fileKey);1086 $key = md5( "{$fileKey}|{$expiry}|{$passwordHash}|{$limit}");1087 if ( !empty( $downloadData[$key] ) && $downloadData[$key]['expiry'] >= time()) {1099 $options = wp_parse_args($options, $defaults); 1100 $expireIn = intval($options['expireIn']); 1101 $password = sanitize_text_field($options['password'] ?? null); 1102 $limit = intval($options['limit']); 1103 $expiry = ($expireIn > 0 ? time() + $expireIn : 0); 1104 $passwordHash = (!empty($password) ? md5($password) : ''); 1105 $downloadData = $this->getDownloadData($fileKey); 1106 $key = md5("{$fileKey}|{$expiry}|{$passwordHash}|{$limit}"); 1107 if (!empty($downloadData[$key]) && $downloadData[$key]['expiry'] >= time()) { 1088 1108 return "{$fileKey}-{$key}"; 1089 1109 } … … 1096 1116 ]; 1097 1117 // Save entire sharedData list 1098 $this->saveDownloadData( $fileKey, $downloadData);1118 $this->saveDownloadData($fileKey, $downloadData); 1099 1119 return "{$fileKey}-{$key}"; 1100 1120 } 1101 1121 1102 public function validateSharedLink( $combinedKey, $password = '' ) { 1103 [$fileKey, $linkKey] = $this->parseCombinedKey( $combinedKey ); 1104 if ( !$fileKey || !$linkKey ) { 1105 return false; 1106 } 1107 $sharedData = $this->getSharedData( $fileKey ); 1108 if ( empty( $sharedData[$linkKey] ) ) { 1122 public function validateSharedLink($combinedKey, $password = '') 1123 { 1124 [$fileKey, $linkKey] = $this->parseCombinedKey($combinedKey); 1125 if (!$fileKey || !$linkKey) { 1126 return false; 1127 } 1128 $sharedData = $this->getSharedData($fileKey); 1129 if (empty($sharedData[$linkKey])) { 1109 1130 return false; 1110 1131 } 1111 1132 $shareInfo = $sharedData[$linkKey]; 1112 if ( $shareInfo['expiry'] < time() && $shareInfo['expiry'] != 0) {1113 $this->deleteSharedEntry( $fileKey, $linkKey);1114 return false; 1115 } 1116 $hashedPassword = md5( sanitize_text_field( $password ));1117 if ( !empty( $shareInfo['password'] )) {1118 if ( empty( $password )) {1119 return new WP_Error('password_required', __( 'This shared link is protected by a password. Please provide the password to access the file.', 'integrate-dropbox'));1120 } 1121 if ( $shareInfo['password'] !== $hashedPassword) {1122 return new WP_Error('invalid_password', __( 'The provided password is incorrect.', 'integrate-dropbox'));1123 } 1124 } 1125 $shareInfo['viewCount'] = intval( $shareInfo['viewCount'] ?? 0) + 1;1126 $shareInfo['lastViewed'] = current_time( 'mysql');1127 $result = $this->updateSharedData( $combinedKey, $shareInfo);1128 if ( is_wp_error( $result )) {1133 if ($shareInfo['expiry'] < time() && $shareInfo['expiry'] != 0) { 1134 $this->deleteSharedEntry($fileKey, $linkKey); 1135 return false; 1136 } 1137 $hashedPassword = md5(sanitize_text_field($password)); 1138 if (!empty($shareInfo['password'])) { 1139 if (empty($password)) { 1140 return new WP_Error('password_required', __('This shared link is protected by a password. Please provide the password to access the file.', 'integrate-dropbox')); 1141 } 1142 if ($shareInfo['password'] !== $hashedPassword) { 1143 return new WP_Error('invalid_password', __('The provided password is incorrect.', 'integrate-dropbox')); 1144 } 1145 } 1146 $shareInfo['viewCount'] = intval($shareInfo['viewCount'] ?? 0) + 1; 1147 $shareInfo['lastViewed'] = current_time('mysql'); 1148 $result = $this->updateSharedData($combinedKey, $shareInfo); 1149 if (is_wp_error($result)) { 1129 1150 return false; 1130 1151 } … … 1132 1153 } 1133 1154 1134 public function validateDownloadLink( $combinedKey, $password = '' ) { 1135 [$fileKey, $linkKey] = $this->parseCombinedKey( $combinedKey ); 1136 if ( !$fileKey || !$linkKey ) { 1137 return false; 1138 } 1139 $downloadData = $this->getDownloadData( $fileKey ); 1140 if ( empty( $downloadData[$linkKey] ) ) { 1155 public function validateDownloadLink($combinedKey, $password = '') 1156 { 1157 [$fileKey, $linkKey] = $this->parseCombinedKey($combinedKey); 1158 if (!$fileKey || !$linkKey) { 1159 return false; 1160 } 1161 $downloadData = $this->getDownloadData($fileKey); 1162 if (empty($downloadData[$linkKey])) { 1141 1163 return false; 1142 1164 } 1143 1165 $downloadInfo = $downloadData[$linkKey]; 1144 if ( $downloadInfo['expiry'] < time() && $downloadInfo['expiry'] != 0) {1145 $this->deleteDownloadEntry( $fileKey, $linkKey);1146 return false; 1147 } 1148 $downloadLimit = intval( $downloadInfo['limit'] ?? 0);1149 if ( $downloadLimit > 0 && intval( $downloadInfo['downloadCount'] ?? 0 ) >= $downloadLimit) {1150 $this->deleteDownloadEntry( $fileKey, $linkKey);1151 return new WP_Error('download_limit_exceeded', __( 'The download limit for this link has been exceeded.', 'integrate-dropbox'));1152 } 1153 $hashedPassword = md5( sanitize_text_field( $password ));1154 if ( !empty( $downloadInfo['password'] )) {1155 if ( empty( $password )) {1156 return new WP_Error('password_required', __( 'This shared link is protected by a password. Please provide the password to access the file.', 'integrate-dropbox'));1157 } 1158 if ( $downloadInfo['password'] !== $hashedPassword) {1159 return new WP_Error('invalid_password', __( 'The provided password is incorrect.', 'integrate-dropbox'));1160 } 1161 } 1162 $downloadInfo['downloadCount'] = intval( $downloadInfo['downloadCount'] ?? 0) + 1;1163 $downloadInfo['lastViewed'] = current_time( 'mysql');1164 $result = $this->updateDownloadData( $combinedKey, $downloadInfo);1165 if ( is_wp_error( $result )) {1166 if ($downloadInfo['expiry'] < time() && $downloadInfo['expiry'] != 0) { 1167 $this->deleteDownloadEntry($fileKey, $linkKey); 1168 return false; 1169 } 1170 $downloadLimit = intval($downloadInfo['limit'] ?? 0); 1171 if ($downloadLimit > 0 && intval($downloadInfo['downloadCount'] ?? 0) >= $downloadLimit) { 1172 $this->deleteDownloadEntry($fileKey, $linkKey); 1173 return new WP_Error('download_limit_exceeded', __('The download limit for this link has been exceeded.', 'integrate-dropbox')); 1174 } 1175 $hashedPassword = md5(sanitize_text_field($password)); 1176 if (!empty($downloadInfo['password'])) { 1177 if (empty($password)) { 1178 return new WP_Error('password_required', __('This shared link is protected by a password. Please provide the password to access the file.', 'integrate-dropbox')); 1179 } 1180 if ($downloadInfo['password'] !== $hashedPassword) { 1181 return new WP_Error('invalid_password', __('The provided password is incorrect.', 'integrate-dropbox')); 1182 } 1183 } 1184 $downloadInfo['downloadCount'] = intval($downloadInfo['downloadCount'] ?? 0) + 1; 1185 $downloadInfo['lastViewed'] = current_time('mysql'); 1186 $result = $this->updateDownloadData($combinedKey, $downloadInfo); 1187 if (is_wp_error($result)) { 1166 1188 return false; 1167 1189 } … … 1170 1192 1171 1193 /* ================= PRIVATE ================= */ 1172 private function parseCombinedKey( $sharedKey ) { 1173 $parts = explode( '-', $sharedKey, 2 ); 1194 private function parseCombinedKey($sharedKey) 1195 { 1196 $parts = explode('-', $sharedKey, 2); 1174 1197 return [$parts[0] ?? '', $parts[1] ?? '']; 1175 1198 } 1176 1199 1177 private function getSharedData( $fileKey ) { 1178 global $wpdb; 1179 $file = $wpdb->get_row( $wpdb->prepare( "SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 1180 if ( !$file ) { 1200 private function getSharedData($fileKey) 1201 { 1202 global $wpdb; 1203 $file = $wpdb->get_row($wpdb->prepare("SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 1204 if (!$file) { 1181 1205 return []; 1182 1206 } 1183 $metaData = maybe_unserialize( $file->metaData);1207 $metaData = maybe_unserialize($file->metaData); 1184 1208 return $metaData['sharedData'] ?? []; 1185 1209 } 1186 1210 1187 private function getDownloadData( $fileKey ) { 1188 global $wpdb; 1189 $file = $wpdb->get_row( $wpdb->prepare( "SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 1190 if ( !$file ) { 1211 private function getDownloadData($fileKey) 1212 { 1213 global $wpdb; 1214 $file = $wpdb->get_row($wpdb->prepare("SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 1215 if (!$file) { 1191 1216 return []; 1192 1217 } 1193 $metaData = maybe_unserialize( $file->metaData);1218 $metaData = maybe_unserialize($file->metaData); 1194 1219 return $metaData['downloadData'] ?? []; 1195 1220 } 1196 1221 1197 private function saveSharedData( $fileKey, $sharedData ) { 1198 global $wpdb; 1199 $file = $wpdb->get_row( $wpdb->prepare( "SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 1200 if ( !$file ) { 1201 return false; 1202 } 1203 $metaData = maybe_unserialize( $file->metaData ) ?? []; 1222 private function saveSharedData($fileKey, $sharedData) 1223 { 1224 global $wpdb; 1225 $file = $wpdb->get_row($wpdb->prepare("SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 1226 if (!$file) { 1227 return false; 1228 } 1229 $metaData = maybe_unserialize($file->metaData) ?? []; 1204 1230 $metaData['sharedData'] = $sharedData; 1205 1231 return $wpdb->update( 1206 1232 $this->tableName, 1207 1233 [ 1208 'metaData' => maybe_serialize( $metaData),1234 'metaData' => maybe_serialize($metaData), 1209 1235 ], 1210 1236 [ … … 1216 1242 } 1217 1243 1218 private function saveDownloadData( $fileKey, $downloadData ) { 1219 global $wpdb; 1220 $file = $wpdb->get_row( $wpdb->prepare( "SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey ) ); 1221 if ( !$file ) { 1222 return false; 1223 } 1224 $metaData = maybe_unserialize( $file->metaData ) ?? []; 1244 private function saveDownloadData($fileKey, $downloadData) 1245 { 1246 global $wpdb; 1247 $file = $wpdb->get_row($wpdb->prepare("SELECT metaData FROM %i WHERE fileKey = %s", $this->tableName, $fileKey)); 1248 if (!$file) { 1249 return false; 1250 } 1251 $metaData = maybe_unserialize($file->metaData) ?? []; 1225 1252 $metaData['downloadData'] = $downloadData; 1226 1253 return $wpdb->update( 1227 1254 $this->tableName, 1228 1255 [ 1229 'metaData' => maybe_serialize( $metaData),1256 'metaData' => maybe_serialize($metaData), 1230 1257 ], 1231 1258 [ … … 1237 1264 } 1238 1265 1239 public function updateSharedData( $combinedKey, $updates = [] ) { 1240 [$fileKey, $linkKey] = $this->parseCombinedKey( $combinedKey ); 1241 if ( !$fileKey || !$linkKey ) { 1242 return false; 1243 } 1244 $sharedData = $this->getSharedData( $fileKey ); 1245 if ( empty( $sharedData[$linkKey] ) ) { 1266 public function updateSharedData($combinedKey, $updates = []) 1267 { 1268 [$fileKey, $linkKey] = $this->parseCombinedKey($combinedKey); 1269 if (!$fileKey || !$linkKey) { 1270 return false; 1271 } 1272 $sharedData = $this->getSharedData($fileKey); 1273 if (empty($sharedData[$linkKey])) { 1246 1274 return false; 1247 1275 } 1248 1276 // Only update provided fields 1249 $sharedData[$linkKey] = array_merge( $sharedData[$linkKey], array_filter( $updates, function ( $v) {1277 $sharedData[$linkKey] = array_merge($sharedData[$linkKey], array_filter($updates, function ($v) { 1250 1278 return $v !== null; 1251 } ) ); 1252 return $this->saveSharedData( $fileKey, $sharedData ); 1253 } 1254 1255 public function updateDownloadData( $combinedKey, $updates = [] ) { 1256 [$fileKey, $linkKey] = $this->parseCombinedKey( $combinedKey ); 1257 if ( !$fileKey || !$linkKey ) { 1258 return false; 1259 } 1260 $downloadData = $this->getDownloadData( $fileKey ); 1261 if ( empty( $downloadData[$linkKey] ) ) { 1279 })); 1280 return $this->saveSharedData($fileKey, $sharedData); 1281 } 1282 1283 public function updateDownloadData($combinedKey, $updates = []) 1284 { 1285 [$fileKey, $linkKey] = $this->parseCombinedKey($combinedKey); 1286 if (!$fileKey || !$linkKey) { 1287 return false; 1288 } 1289 $downloadData = $this->getDownloadData($fileKey); 1290 if (empty($downloadData[$linkKey])) { 1262 1291 return false; 1263 1292 } 1264 1293 // Only update provided fields 1265 $downloadData[$linkKey] = array_merge( $downloadData[$linkKey], array_filter( $updates, function ( $v) {1294 $downloadData[$linkKey] = array_merge($downloadData[$linkKey], array_filter($updates, function ($v) { 1266 1295 return $v !== null; 1267 } ) ); 1268 return $this->saveDownloadData( $fileKey, $downloadData ); 1269 } 1270 1271 private function deleteSharedEntry( $fileKey, $linkKey ) { 1272 $sharedData = $this->getSharedData( $fileKey ); 1273 if ( isset( $sharedData[$linkKey] ) ) { 1296 })); 1297 return $this->saveDownloadData($fileKey, $downloadData); 1298 } 1299 1300 private function deleteSharedEntry($fileKey, $linkKey) 1301 { 1302 $sharedData = $this->getSharedData($fileKey); 1303 if (isset($sharedData[$linkKey])) { 1274 1304 unset($sharedData[$linkKey]); 1275 return $this->saveSharedData( $fileKey, $sharedData);1305 return $this->saveSharedData($fileKey, $sharedData); 1276 1306 } 1277 1307 return false; 1278 1308 } 1279 1309 1280 private function deleteDownloadEntry( $fileKey, $linkKey ) { 1281 $downloadData = $this->getDownloadData( $fileKey ); 1282 if ( isset( $downloadData[$linkKey] ) ) { 1310 private function deleteDownloadEntry($fileKey, $linkKey) 1311 { 1312 $downloadData = $this->getDownloadData($fileKey); 1313 if (isset($downloadData[$linkKey])) { 1283 1314 unset($downloadData[$linkKey]); 1284 return $this->saveDownloadData( $fileKey, $downloadData);1315 return $this->saveDownloadData($fileKey, $downloadData); 1285 1316 } 1286 1317 return false; 1287 1318 } 1288 1319 1289 private function getChildFolderIds( $parentPath, $accountId ) { 1290 if ( empty( $parentPath ) || empty( $accountId ) ) { 1320 private function getChildFolderIds($parentPath, $accountId) 1321 { 1322 if (empty($parentPath) || empty($accountId)) { 1291 1323 return []; 1292 1324 } 1293 1325 global $wpdb; 1294 return $wpdb->get_results( $wpdb->prepare(1326 return $wpdb->get_results($wpdb->prepare( 1295 1327 "SELECT path FROM %i WHERE parent = %s AND accountId = %s AND extension = 'folder'", 1296 1328 $this->tableName, 1297 1329 $parentPath, 1298 1330 $accountId 1299 ), ARRAY_A ); 1300 } 1301 1302 private function processFiles( $files, $returnType = 'array', $filter = null ) { 1331 ), ARRAY_A); 1332 } 1333 1334 private function processFiles($files, $returnType = 'array', $filter = null) 1335 { 1303 1336 $processedFiles = []; 1304 foreach ( $files as $file) {1305 $processedFiles[] = $this->processFile( $file, $returnType, $filter);1337 foreach ($files as $file) { 1338 $processedFiles[] = $this->processFile($file, $returnType, $filter); 1306 1339 } 1307 1340 return $processedFiles; 1308 1341 } 1309 1342 1310 private function processFile( $file, $returnType = 'object', $filter = null ) { 1311 if ( empty( $file ) ) { 1343 private function processFile($file, $returnType = 'object', $filter = null) 1344 { 1345 if (empty($file)) { 1312 1346 return []; 1313 1347 } … … 1327 1361 'sharedLink' => $file->sharedLink, 1328 1362 'isDir' => $file->isDir, 1329 'permissions' => maybe_unserialize( $file->permissions),1363 'permissions' => maybe_unserialize($file->permissions), 1330 1364 'hasOwnThumbnail' => $file->hasOwnThumbnail, 1331 1365 'icon' => $file->icon, 1332 'additionalData' => maybe_unserialize( $file->additionalData),1333 'metaData' => maybe_unserialize( $file->metaData),1366 'additionalData' => maybe_unserialize($file->additionalData), 1367 'metaData' => maybe_unserialize($file->metaData), 1334 1368 'createdAt' => $file->createdAt, 1335 1369 'updatedAt' => $file->updatedAt, 1336 1370 ]; 1337 if ( $returnType === 'object') {1371 if ($returnType === 'object') { 1338 1372 return new File($fileData); 1339 } elseif ( $returnType === 'array') {1373 } elseif ($returnType === 'array') { 1340 1374 return $fileData; 1341 1375 } … … 1346 1380 'mimeType' => $file->mimeType, 1347 1381 'size' => $file->size, 1348 'thumbnails' => maybe_unserialize( $file->thumbnails ), 1349 ]; 1350 } 1351 1352 private function processExtensions( array $extensions, array $additionalExtensions, string $filterType ) : array { 1353 if ( empty( $additionalExtensions ) ) { 1382 'thumbnails' => maybe_unserialize($file->thumbnails), 1383 ]; 1384 } 1385 1386 private function processExtensions(array $extensions, array $additionalExtensions, string $filterType): array 1387 { 1388 if (empty($additionalExtensions)) { 1354 1389 return $extensions; 1355 1390 } 1356 if ( empty( $extensions )) {1391 if (empty($extensions)) { 1357 1392 return $additionalExtensions; 1358 1393 } 1359 if ( $filterType === 'include') {1360 $filterExtensions = array_filter( $extensions, function ( $ext ) use($additionalExtensions) {1361 return in_array( $ext, $additionalExtensions);1362 } );1363 return array_values( $filterExtensions);1364 } elseif ( $filterType === 'exclude') {1365 $filterExtensions = array_filter( $extensions, fn( $ext ) => !in_array( $ext, $additionalExtensions ));1366 return array_values( $filterExtensions);1394 if ($filterType === 'include') { 1395 $filterExtensions = array_filter($extensions, function ($ext) use ($additionalExtensions) { 1396 return in_array($ext, $additionalExtensions); 1397 }); 1398 return array_values($filterExtensions); 1399 } elseif ($filterType === 'exclude') { 1400 $filterExtensions = array_filter($extensions, fn ($ext) => !in_array($ext, $additionalExtensions)); 1401 return array_values($filterExtensions); 1367 1402 } 1368 1403 return $extensions; 1369 1404 } 1370 1405 1371 private function processFolderTree( array $items, array $rootPaths ) { 1406 private function processFolderTree(array $items, array $rootPaths) 1407 { 1372 1408 $tree = []; 1373 1409 $lookup = []; 1374 foreach ( $items as $item) {1375 $path = rtrim( $item['path'], '/');1410 foreach ($items as $item) { 1411 $path = rtrim($item['path'], '/'); 1376 1412 $lookup[$path] = [ 1377 1413 'name' => $item['name'], … … 1379 1415 'children' => [], 1380 1416 'path' => $path, 1381 'parent' => rtrim( $item['parent'], '/'),1417 'parent' => rtrim($item['parent'], '/'), 1382 1418 ]; 1383 1419 } 1384 foreach ( $lookup as $path => &$node) {1385 if ( in_array( $node['path'], $rootPaths, true ) || $node['parent'] === '' || $node['parent'] === '/') {1420 foreach ($lookup as $path => &$node) { 1421 if (in_array($node['path'], $rootPaths, true) || $node['parent'] === '' || $node['parent'] === '/') { 1386 1422 unset($node['parent']); 1387 $tree[] = & $node;1423 $tree[] = & $node; 1388 1424 } else { 1389 if ( isset( $lookup[$node['parent']] )) {1390 $lookup[$node['parent']]['children'][] = & $node;1425 if (isset($lookup[$node['parent']])) { 1426 $lookup[$node['parent']]['children'][] = & $node; 1391 1427 } 1392 1428 } … … 1402 1438 // } 1403 1439 // } 1404 $tree = array_map( function ( $item) {1405 if ( empty( $item['children'] )) {1440 $tree = array_map(function ($item) { 1441 if (empty($item['children'])) { 1406 1442 unset($item['children']); 1407 1443 } 1408 1444 return $item; 1409 }, $tree );1410 return array_values( $tree);1445 }, $tree); 1446 return array_values($tree); 1411 1447 } 1412 1448
Note: See TracChangeset
for help on using the changeset viewer.