| @@ -7,8 +7,10 @@ | ||
| 7 | 7 | * @author Tobias Bäthge |
| 8 | 8 | * @since 1.0.0 |
| 9 | 9 | */ |
| 10 | 10 | |
| 11 | +declare(strict_types=1); | |
| 12 | + | |
| 11 | 13 | use TablePress\Import\File; |
| 12 | 14 | |
| 13 | 15 | // Prohibit direct script loading. |
| 14 | 16 | defined( 'ABSPATH' ) || die( 'No direct script access allowed!' ); |
| @@ -25,14 +27,14 @@ | ||
| 25 | 27 | */ |
| 26 | 28 | class TablePress_Import { |
| 27 | 29 | |
| 28 | 30 | /** |
| 29 | - * Instance of the TablePress Legacy Importer. | |
| 31 | + * Instance of the TablePress Legacy or PHPSpreadsheet Importer. | |
| 30 | 32 | * |
| 31 | 33 | * @since 1.0.0 |
| 32 | - * @var TablePress_Import_Legacy | |
| 34 | + * @var TablePress_Import_Legacy|TablePress_Import_PHPSpreadsheet | |
| 33 | 35 | */ |
| 34 | - protected $importer; | |
| 36 | + protected object $importer; | |
| 35 | 37 | |
| 36 | 38 | /** |
| 37 | 39 | * Import configuration (mainly the data from the Import form). |
| 38 | 40 | * |
| @@ -38,9 +40,9 @@ | ||
| 38 | 40 | * |
| 39 | 41 | * @since 2.0.0 |
| 40 | 42 | * @var array<string, mixed> |
| 41 | 43 | */ |
| 42 | - protected $import_config = array(); | |
| 44 | + protected array $import_config = array(); | |
| 43 | 45 | |
| 44 | 46 | /** |
| 45 | 47 | * Whether ZIP archive support is available (which it always is, as PclZip is used as a fallback). |
| 46 | 48 | * |
| @@ -45,11 +47,10 @@ | ||
| 45 | 47 | * Whether ZIP archive support is available (which it always is, as PclZip is used as a fallback). |
| 46 | 48 | * |
| 47 | 49 | * @since 1.0.0 |
| 48 | 50 | * @deprecated 2.3.0 ZIP support is now always available, either through `ZipArchive` or through `PclZip`. |
| 49 | - * @var bool | |
| 50 | 51 | */ |
| 51 | - public $zip_support_available = true; | |
| 52 | + public bool $zip_support_available = true; | |
| 52 | 53 | |
| 53 | 54 | /** |
| 54 | 55 | * List of table names/IDs for use when replacing/appending existing tables (except for the JSON format). |
| 55 | 56 | * |
| @@ -55,9 +56,9 @@ | ||
| 55 | 56 | * |
| 56 | 57 | * @since 2.0.0 |
| 57 | 58 | * @var array<string, string[]> |
| 58 | 59 | */ |
| 59 | - protected $table_names_ids = array(); | |
| 60 | + protected array $table_names_ids = array(); | |
| 60 | 61 | |
| 61 | 62 | /** |
| 62 | 63 | * Runs the import process for a given import configuration. |
| 63 | 64 | * |
| @@ -131,8 +132,11 @@ | ||
| 131 | 132 | if ( in_array( $ip, $blocked_ips, true ) ) { |
| 132 | 133 | return new WP_Error( 'table_import_url_host_blocked', '', array( 'url' => $this->import_config['url'], 'ip' => $ip ) ); |
| 133 | 134 | } |
| 134 | 135 | |
| 136 | + // Automatically adjust URLs of common services to point to a direct download URL. | |
| 137 | + $this->import_config['url'] = $this->fix_common_url_mistakes( $this->import_config['url'] ); | |
| 138 | + | |
| 135 | 139 | /** |
| 136 | 140 | * Load WP file functions to be sure that `download_url()` exists, in particular during Cron requests. |
| 137 | 141 | */ |
| 138 | 142 | require_once ABSPATH . 'wp-admin/includes/file.php'; |
| @@ -185,8 +189,45 @@ | ||
| 185 | 189 | return $import_files; |
| 186 | 190 | } |
| 187 | 191 | |
| 188 | 192 | /** |
| 193 | + * Fixes common mistakes in URLs from popular services to point to a direct download URL. | |
| 194 | + * | |
| 195 | + * Currently supports Google Sheets, Microsoft OneDrive, and Dropbox. | |
| 196 | + * See https://tablepress.org/tutorials/ for more specific instructions on how to get the correct URL. | |
| 197 | + * | |
| 198 | + * @since 3.2.4 | |
| 199 | + * | |
| 200 | + * @param string $url URL that shall be fixed. | |
| 201 | + * @return string Fixed URL. | |
| 202 | + */ | |
| 203 | + protected function fix_common_url_mistakes( string $url ): string { | |
| 204 | + /** | |
| 205 | + * Filters whether common URL mistakes shall be fixed automatically. | |
| 206 | + * | |
| 207 | + * @since 3.2.4 | |
| 208 | + * | |
| 209 | + * @param bool $fix_common_url_mistakes Whether to fix common URL mistakes. Default true. | |
| 210 | + */ | |
| 211 | + if ( ! apply_filters( 'tablepress_import_fix_common_url_mistakes', true ) ) { | |
| 212 | + return $url; | |
| 213 | + } | |
| 214 | + | |
| 215 | + if ( str_starts_with( $url, 'https://docs.google.com/spreadsheets/' ) && str_ends_with( $url, '/edit?usp=sharing' ) ) { | |
| 216 | + // Google Sheets "Sharing URL" to direct download URL. | |
| 217 | + $url = str_replace( '/edit?usp=sharing', '/export?format=csv', $url ); | |
| 218 | + } elseif ( str_starts_with( $url, 'https://1drv.ms/' ) && ! str_ends_with( $url, '&download=1' ) ) { | |
| 219 | + // OneDrive shared link to direct download link. | |
| 220 | + $url .= '&download=1'; | |
| 221 | + } elseif ( str_starts_with( $url, 'https://www.dropbox.com/' ) && str_ends_with( $url, '&dl=0' ) ) { | |
| 222 | + // Dropbox shared link to direct download link. | |
| 223 | + $url = str_replace( '&dl=0', '&dl=1', $url ); | |
| 224 | + } | |
| 225 | + | |
| 226 | + return $url; | |
| 227 | + } | |
| 228 | + | |
| 229 | + /** | |
| 189 | 230 | * Replaces ZIP archives in the import files with a list of their contents. |
| 190 | 231 | * |
| 191 | 232 | * ZIP files are removed from the list and their contents are added to the end of the list. |
| 192 | 233 | * |
| @@ -204,8 +245,12 @@ | ||
| 204 | 245 | continue; |
| 205 | 246 | } |
| 206 | 247 | |
| 207 | 248 | $file->extension = strtolower( pathinfo( $file->name, PATHINFO_EXTENSION ) ); |
| 249 | + if ( '' === $file->extension ) { | |
| 250 | + // If the file name has no extension, try to get it from the location (as WordPress tries adding an extension to that based on the MIME type, e.g. when downloading files). | |
| 251 | + $file->extension = strtolower( pathinfo( $file->location, PATHINFO_EXTENSION ) ); | |
| 252 | + } | |
| 208 | 253 | |
| 209 | 254 | if ( function_exists( 'mime_content_type' ) ) { |
| 210 | 255 | $mime_type = mime_content_type( $file->location ); |
| 211 | 256 | if ( false !== $mime_type ) { |
| @@ -286,16 +331,16 @@ | ||
| 286 | 331 | * @return File[]|WP_Error List of files to import that were extracted from the ZIP file or WP_Error on failure. |
| 287 | 332 | */ |
| 288 | 333 | protected function extract_zip_file_ziparchive( File $zip_file ) /* : array|WP_Error */ { |
| 289 | 334 | $archive = new ZipArchive(); |
| 290 | - $archive_opened = $archive->open( $zip_file->location, ZIPARCHIVE::CHECKCONS ); | |
| 335 | + $archive_opened = $archive->open( $zip_file->location, ZipArchive::CHECKCONS ); | |
| 291 | 336 | |
| 292 | - // If the ZIP file can't be opened with ZIPARCHIVE::CHECKCONS, try again without. | |
| 337 | + // If the ZIP file can't be opened with ZipArchive::CHECKCONS, try again without. | |
| 293 | 338 | if ( true !== $archive_opened ) { |
| 294 | 339 | $archive_opened = $archive->open( $zip_file->location ); |
| 295 | 340 | } |
| 296 | 341 | |
| 297 | - // If the ZIP file can't even be opened without ZIPARCHIVE::CHECKCONS, bail. | |
| 342 | + // If the ZIP file can't even be opened without ZipArchive::CHECKCONS, bail. | |
| 298 | 343 | if ( true !== $archive_opened ) { |
| 299 | 344 | return new WP_Error( 'table_import_error_zip_open', '', array( 'ziparchive_error' => $archive_opened ) ); |
| 300 | 345 | } |
| 301 | 346 | |
| @@ -373,9 +418,9 @@ | ||
| 373 | 418 | |
| 374 | 419 | require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; |
| 375 | 420 | |
| 376 | 421 | $archive = new PclZip( $zip_file->location ); |
| 377 | - $archive_files = $archive->extract( PCLZIP_OPT_EXTRACT_AS_STRING ); // @phpstan-ignore-line PclZip::extract() uses `func_get_args()` to handle optional arguments. | |
| 422 | + $archive_files = $archive->extract( PCLZIP_OPT_EXTRACT_AS_STRING ); // @phpstan-ignore arguments.count (PclZip::extract() uses `func_get_args()` to handle optional arguments.) | |
| 378 | 423 | |
| 379 | 424 | reset_mbstring_encoding(); |
| 380 | 425 | |
| 381 | 426 | // If the ZIP file can't be opened, bail. |
| @@ -510,10 +555,12 @@ | ||
| 510 | 555 | TablePress::load_file( 'class-import-base.php', 'classes' ); |
| 511 | 556 | |
| 512 | 557 | // Choose the Table Import library based on the PHP version and the filter hook value. |
| 513 | 558 | if ( $use_legacy_import_class ) { |
| 559 | + // @phpstan-ignore assign.propertyType (The `load_class()` method returns `object` and not a specific type.) | |
| 514 | 560 | $this->importer = TablePress::load_class( 'TablePress_Import_Legacy', 'class-import-legacy.php', 'classes' ); |
| 515 | 561 | } else { |
| 562 | + // @phpstan-ignore assign.propertyType (The `load_class()` method returns `object` and not a specific type.) | |
| 516 | 563 | $this->importer = TablePress::load_class( 'TablePress_Import_PHPSpreadsheet', 'class-import-phpspreadsheet.php', 'classes' ); |
| 517 | 564 | } |
| 518 | 565 | |
| 519 | 566 | // If there is more than one valid import file, ignore the chosen existing table for replacing/appending. |
| @@ -633,9 +680,9 @@ | ||
| 633 | 680 | if ( '' === $format ) { |
| 634 | 681 | $format = 'csv'; |
| 635 | 682 | } |
| 636 | 683 | |
| 637 | - if ( ! isset( $this->importer->import_formats[ $format ] ) ) { | |
| 684 | + if ( ! in_array( $format, $this->importer->import_formats, true ) ) { // @phpstan-ignore property.notFound (`$this->importer` is an instance of `TablePress_Import_Legacy` which has the property `import_formats`.) | |
| 638 | 685 | return new WP_Error( 'table_import_legacy_unknown_format', '', $file->name ); |
| 639 | 686 | } |
| 640 | 687 | |
| 641 | 688 | $table = $this->importer->import_table( $format, $data ); |
| @@ -656,9 +703,9 @@ | ||
| 656 | 703 | * @return array<string, mixed>|WP_Error Loaded table on success (either with all properties or just 'data'), WP_Error on failure. |
| 657 | 704 | */ |
| 658 | 705 | protected function load_table_from_file_phpspreadsheet( File $file ) /* : array|WP_Error */ { |
| 659 | 706 | // Convert File object to array, as those are not yet used outside of this class. |
| 660 | - return $this->importer->import_table( $file ); // @phpstan-ignore-line | |
| 707 | + return $this->importer->import_table( $file ); // @phpstan-ignore return.type (This is an instance of TablePress_Import_PHPSpreadsheet which does not return false.) | |
| 661 | 708 | } |
| 662 | 709 | |
| 663 | 710 | /** |
| 664 | 711 | * Imports a loaded table into TablePress. |