Skip to content

Commit 168663a

Browse files
authored
Merge pull request #3615 from ExpressionEngine/feature/7.x/cli-sync-upload-directory
Enabled synchronizing upload directory via CLI; #3044
2 parents 20f5460 + 45f2d77 commit 168663a

File tree

6 files changed

+586
-197
lines changed

6 files changed

+586
-197
lines changed

system/ee/ExpressionEngine/Cli/Cli.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class Cli
142142
// Sync
143143
'sync:conditional-fields' => Commands\CommandSyncConditionalFieldLogic::class,
144144
'sync:file-usage' => Commands\CommandSyncFileUsage::class,
145+
'sync:upload-directory' => Commands\CommandSyncUploadDirectory::class,
145146

146147
// Update
147148
'update' => Commands\CommandUpdate::class,
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
/**
3+
* This source file is part of the open source project
4+
* ExpressionEngine (https://expressionengine.com)
5+
*
6+
* @link https://expressionengine.com/
7+
* @copyright Copyright (c) 2003-2023, Packet Tide, LLC (https://www.packettide.com)
8+
* @license https://expressionengine.com/license Licensed under Apache License, Version 2.0
9+
*/
10+
11+
namespace ExpressionEngine\Cli\Commands;
12+
13+
use ExpressionEngine\Cli\Cli;
14+
15+
/**
16+
* Sync upload directory
17+
*/
18+
class CommandSyncUploadDirectory extends Cli
19+
{
20+
/**
21+
* name of command
22+
* @var string
23+
*/
24+
public $name = 'Sync Upload Directory';
25+
26+
/**
27+
* signature of command
28+
* @var string
29+
*/
30+
public $signature = 'sync:upload-directory';
31+
32+
/**
33+
* description of command
34+
* @var string
35+
*/
36+
public $description = 'command_sync_upload_directory_description';
37+
38+
/**
39+
* summary of command
40+
* @var string
41+
*/
42+
public $summary = 'command_sync_upload_directory_summary';
43+
44+
/**
45+
* How to use command
46+
* @var string
47+
*/
48+
public $usage = 'php eecli.php sync:upload-directory';
49+
50+
/**
51+
* options available for use in command
52+
* @var array
53+
*/
54+
public $commandOptions = [
55+
'upload-id,u:' => 'command_sync_upload_directory_option_id',
56+
'manipulations,m:' => 'command_sync_upload_directory_option_regenerate_manipulations',
57+
];
58+
/**
59+
* Run the command
60+
* @return mixed
61+
*/
62+
public function handle()
63+
{
64+
ee()->lang->loadfile('utilities');
65+
ee()->lang->loadfile('filemanager');
66+
ee()->load->library('session');
67+
68+
$this->info('command_sync_upload_directory_description');
69+
70+
// Is this MSM installation?
71+
$siteLabels = [];
72+
$sites = ee('Model')->get('Site')->all();
73+
foreach ($sites as $site) {
74+
$siteLabels[$site->getId()] = '';
75+
if (count($sites) > 1) {
76+
$siteLabels[$site->getId()] = ' [' . $site->site_label . ']';
77+
}
78+
}
79+
$directories = ee('Model')->get('UploadDestination')->order('site_id', 'ASC')->order('id', 'ASC')->all();
80+
$directoriesList = "\n";
81+
foreach ($directories as $directory) {
82+
$directoriesList .= $directory->getId() . ' - ' . $directory->name . $siteLabels[$directory->site_id] . "\n";
83+
}
84+
$upload_location_id = $this->getOptionOrAsk('--upload-id', lang('command_sync_upload_directory_ask_id') . $directoriesList, '', true);
85+
86+
$uploadLocation = ee('Model')->get('UploadDestination', $upload_location_id)->first();
87+
if (empty($uploadLocation)) {
88+
$this->fail('invalid_upload_destination');
89+
}
90+
91+
$fileDimensions = ee('Model')->get('FileDimension')->filter('upload_location_id', $upload_location_id)->all();
92+
$manipulations = '';
93+
if (!empty($fileDimensions)) {
94+
$manipulationsList = "\n";
95+
foreach ($fileDimensions as $fileDimension) {
96+
$manipulationsList .= $fileDimension->getId() . ' - ' . $fileDimension->short_name . ' [' . lang($fileDimension->resize_type) . ', ' . $fileDimension->width . 'px ' . lang('by') . ' ' . $fileDimension->height . 'px' . "]\n";
97+
}
98+
$manipulations = $this->getOptionOrAsk('--manipulations', lang('command_sync_upload_directory_ask_regenerate_manipulations') . $manipulationsList, '');
99+
}
100+
101+
$replaceSizeIds = [];
102+
if (trim($manipulations) == 'all') {
103+
$replaceSizeIds = $fileDimensions->pluck('id');
104+
} else {
105+
$replaceSizeIds = array_map('trim', explode(',', $manipulations));
106+
}
107+
108+
// Get a listing of raw files in the directory
109+
$filesChunks = array_chunk($uploadLocation->getAllFileNames(), 5);
110+
111+
$allSizes = array();
112+
foreach ($fileDimensions as $size) {
113+
$allSizes[$size->upload_location_id][$size->id] = array(
114+
'short_name' => $size->short_name,
115+
'resize_type' => $size->resize_type,
116+
'width' => $size->width,
117+
'height' => $size->height,
118+
'quality' => $size->quality,
119+
'watermark_id' => $size->watermark_id
120+
);
121+
}
122+
123+
$errors = [];
124+
foreach ($filesChunks as $files) {
125+
$synced = $uploadLocation->syncFiles($files, $allSizes, $replaceSizeIds);
126+
if ($synced !== true) {
127+
$errors = array_merge($errors, $synced);
128+
} else {
129+
echo '.';
130+
}
131+
}
132+
133+
$this->write('');
134+
135+
if (!empty($errors)) {
136+
$this->info('directory_sync_warning');
137+
$this->write('');
138+
foreach ($errors as $file => $error) {
139+
$this->write($file . ': ' . $error);
140+
}
141+
$this->fail();
142+
}
143+
144+
$this->info('directory_synced_desc');
145+
}
146+
}

system/ee/ExpressionEngine/Controller/Files/Uploads.php

Lines changed: 9 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -776,19 +776,6 @@ private function validateUploadPreferences($upload_destination)
776776
return empty($this->upload_errors);
777777
}
778778

779-
private function flattenDirectoryMap(&$flatMap = [], $nestedMap = [], $keyPrefix = '/')
780-
{
781-
foreach ($nestedMap as $key => $val) {
782-
$flatKey = rtrim($keyPrefix . $key, '/');
783-
if (! isset($flatMap[$flatKey])) {
784-
$flatMap[$flatKey] = $flatKey;
785-
}
786-
if (is_array($val)) {
787-
$flatMap[$flatKey] = $this->flattenDirectoryMap($flatMap, $val, $flatKey . '/');
788-
}
789-
}
790-
}
791-
792779
/**
793780
* Sync upload directory
794781
*
@@ -822,10 +809,7 @@ public function sync($upload_id = null)
822809
}
823810

824811
// Get a listing of raw files in the directory
825-
$directoryMap = $upload_destination->getDirectoryMap();
826-
$flatDirectoryMap = [];
827-
$this->flattenDirectoryMap($flatDirectoryMap, $directoryMap);
828-
$files = array_keys($flatDirectoryMap);
812+
$files = $upload_destination->getAllFileNames();
829813

830814
$files_count = count($files, COUNT_RECURSIVE);
831815

@@ -927,8 +911,6 @@ public function sync($upload_id = null)
927911
*/
928912
public function doSyncFiles()
929913
{
930-
ee()->load->library('filemanager');
931-
932914
$type = 'insert';
933915
$errors = array();
934916
$file_data = array();
@@ -979,191 +961,22 @@ public function doSyncFiles()
979961
return;
980962
}
981963

982-
/*ee()->filemanager->xss_clean_off();
983-
$dir_data['dimensions'] = (is_array($sizes[$id])) ? $sizes[$id] : array();
984-
ee()->filemanager->set_upload_dir_prefs($id, $dir_data);*/
985-
986-
// Now for everything NOT forcably replaced
987-
988-
$missing_only_sizes = (is_array($sizes[$id])) ? $sizes[$id] : array();
989-
990-
// Check for resize_ids
991964
$resize_ids = ee()->input->post('resize_ids');
992-
993-
if (is_array($resize_ids)) {
994-
foreach ($resize_ids as $resize_id) {
995-
if (!empty($resize_id)) {
996-
$replace_sizes[$resize_id] = $sizes[$id][$resize_id];
997-
unset($missing_only_sizes[$resize_id]);
998-
}
999-
}
1000-
}
1001-
1002-
$mimes = ee()->config->loadFile('mimes');
1003-
$fileTypes = array_filter(array_keys($mimes), 'is_string');
1004-
1005-
$filesystem = $uploadDestination->getFilesystem();
1006-
1007-
foreach ($current_files as $filePath) {
1008-
$fileInfo = $filesystem->getWithMetadata($filePath);
1009-
if (!isset($fileInfo['basename'])) {
1010-
$fileInfo['basename'] = basename($fileInfo['path']);
1011-
}
1012-
$mime = ($fileInfo['type'] != 'dir') ? $filesystem->getMimetype($filePath) : 'directory';
1013-
1014-
if ($mime == 'directory' && (!$uploadDestination->allow_subfolders || bool_config_item('file_manager_compatibility_mode'))) {
1015-
//silently continue on subfolders if those are not allowed
1016-
continue;
1017-
}
1018-
1019-
if (empty($mime)) {
1020-
$errors[$fileInfo['basename']] = lang('invalid_mime');
1021-
1022-
continue;
1023-
}
1024-
1025-
$file = $uploadDestination->getFileByPath($filePath);
1026-
1027-
// Clean filename
1028-
$clean_filename = ee()->filemanager->clean_subdir_and_filename($fileInfo['path'], $id, array(
1029-
'convert_spaces' => false,
1030-
'ignore_dupes' => true
1031-
));
1032-
1033-
if ($fileInfo['path'] != $clean_filename) {
1034-
// Make sure clean filename is unique
1035-
$clean_filename = ee()->filemanager->clean_subdir_and_filename($clean_filename, $id, array(
1036-
'convert_spaces' => false,
1037-
'ignore_dupes' => false
1038-
));
1039-
// Rename the file
1040-
if (! $filesystem->rename($fileInfo['path'], $clean_filename)) {
1041-
$errors[$fileInfo['path']] = lang('invalid_filename');
1042-
continue;
1043-
}
1044-
1045-
$filesystem->delete($fileInfo['path']);
1046-
$fileInfo['basename'] = $filesystem->basename($clean_filename);
1047-
}
1048-
1049-
if (! empty($file)) {
1050-
// It exists, but do we need to change sizes or add a missing thumb?
1051-
1052-
if (! $file->isEditableImage()) {
1053-
continue;
1054-
}
1055-
1056-
// Note 'Regular' batch needs to check if file exists- and then do something if so
1057-
if (! empty($replace_sizes)) {
1058-
$thumb_created = ee()->filemanager->create_thumb(
1059-
$file->getAbsolutePath(),
1060-
array(
1061-
'directory' => $uploadDestination,
1062-
'server_path' => $uploadDestination->server_path,
1063-
'file_name' => $fileInfo['basename'],
1064-
'dimensions' => $replace_sizes,
1065-
'mime_type' => $mime
1066-
),
1067-
true, // Create thumb
1068-
false // Overwrite existing thumbs
1069-
);
1070-
1071-
if (! $thumb_created) {
1072-
$errors[$fileInfo['basename']] = lang('thumb_not_created');
1073-
}
1074-
}
1075-
1076-
// Now for anything that wasn't forcably replaced- we make sure an image exists
1077-
$thumb_created = ee()->filemanager->create_thumb(
1078-
$file->getAbsolutePath(),
1079-
array(
1080-
'directory' => $uploadDestination,
1081-
'server_path' => $uploadDestination->server_path,
1082-
'file_name' => $fileInfo['basename'],
1083-
'dimensions' => $missing_only_sizes,
1084-
'mime_type' => $mime
1085-
),
1086-
true, // Create thumb
1087-
true // Don't overwrite existing thumbs
1088-
);
1089-
1090-
// Update dimensions
1091-
$image_dimensions = $file->actLocally(function($path) {
1092-
return ee()->filemanager->get_image_dimensions($path);
1093-
});
1094-
$file->setRawProperty('file_hw_original', $image_dimensions['height'] . ' ' . $image_dimensions['width']);
1095-
$file->file_size = $fileInfo['size'];
1096-
if ($file->file_type === null) {
1097-
$file->setProperty('file_type', 'other'); // default
1098-
foreach ($fileTypes as $fileType) {
1099-
if (in_array($file->getProperty('mime_type'), $mimes[$fileType])) {
1100-
$file->setProperty('file_type', $fileType);
1101-
break;
1102-
}
1103-
}
1104-
}
1105-
$file->save();
1106-
1107-
continue;
1108-
}
1109-
1110-
$file = ee('Model')->make('FileSystemEntity');
1111-
$file_data = [
1112-
'upload_location_id' => $uploadDestination->getId(),
1113-
'site_id' => ee()->config->item('site_id'),
1114-
'model_type' => ($mime == 'directory') ? 'Directory' : 'File',
1115-
'mime_type' => $mime,
1116-
'file_name' => $fileInfo['basename'],
1117-
'file_size' => isset($fileInfo['size']) ? $fileInfo['size'] : 0,
1118-
'uploaded_by_member_id' => ee()->session->userdata('member_id'),
1119-
'modified_by_member_id' => ee()->session->userdata('member_id'),
1120-
'upload_date' => $fileInfo['timestamp'],
1121-
'modified_date' => $fileInfo['timestamp']
1122-
];
1123-
$pathInfo = explode('/', trim(str_replace(DIRECTORY_SEPARATOR, '/', $filePath), '/'));
1124-
//get the subfolder info, but at the same time, skip if no subfolder are allowed
1125-
if (count($pathInfo) > 1) {
1126-
if (!$uploadDestination->allow_subfolders || bool_config_item('file_manager_compatibility_mode')) {
1127-
continue;
1128-
}
1129-
array_pop($pathInfo);
1130-
$directory = $uploadDestination->getFileByPath(implode('/', $pathInfo));
1131-
$file_data['directory_id'] = $directory->getId();
1132-
}
1133-
$file->set($file_data);
1134-
if ($file->isEditableImage()) {
1135-
$image_dimensions = $file->actLocally(function ($path) {
1136-
return ee()->filemanager->get_image_dimensions($path);
1137-
});
1138-
$file_data['file_hw_original'] = $image_dimensions['height'] . ' ' . $image_dimensions['width'];
1139-
$file->setRawProperty('file_hw_original', $file_data['file_hw_original']);
1140-
}
1141-
//$file->save(); need to fallback to old saving because of the checks
1142-
1143-
$saved = ee()->filemanager
1144-
->save_file(
1145-
$file->getAbsolutePath(),
1146-
$id,
1147-
$file_data,
1148-
false
1149-
);
1150-
1151-
if (! $saved['status']) {
1152-
$errors[$fileInfo['basename']] = $saved['message'];
1153-
}
1154-
}
965+
$synced = $uploadDestination->syncFiles($current_files, $sizes, $resize_ids);
1155966

1156967
if (AJAX_REQUEST) {
1157-
if (count($errors)) {
968+
if ($synced === true) {
969+
return ee()->output->send_ajax_response(array(
970+
'message_type' => 'success'
971+
));
972+
} else {
1158973
return ee()->output->send_ajax_response(array(
1159974
'message_type' => 'failure',
1160-
'errors' => $errors
975+
'errors' => $synced
1161976
));
1162977
}
1163978

1164-
return ee()->output->send_ajax_response(array(
1165-
'message_type' => 'success'
1166-
));
979+
1167980
}
1168981
}
1169982
}

0 commit comments

Comments
 (0)