View source
<?php
namespace Drupal\auditfiles;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Database\Connection;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Session\AccountProxy;
use Drupal\Core\ProxyClass\File\MimeType\MimeTypeGuesser;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
class ServiceAuditFilesNotInDatabase {
use MessengerTrait;
protected $stringTranslation;
protected $configFactory;
protected $connection;
protected $streamWrapperManager;
protected $fileSystem;
protected $currentUser;
protected $fileMimeTypeGuesser;
protected $time;
protected $uuidService;
protected $dateFormatter;
protected $entityTypeManager;
public function __construct(TranslationInterface $translation, ConfigFactoryInterface $config_factory, Connection $connection, StreamWrapperManager $stream_wrapper_manager, FileSystemInterface $file_system, AccountProxy $current_user, MimeTypeGuesser $file_mime_type_guesser, TimeInterface $time, UuidInterface $uuid, DateFormatter $date_formatter, EntityTypeManagerInterface $entity_type_manager) {
$this->stringTranslation = $translation;
$this->configFactory = $config_factory;
$this->connection = $connection;
$this->streamWrapperManager = $stream_wrapper_manager;
$this->fileSystem = $file_system;
$this->currentUser = $current_user;
$this->fileMimeTypeGuesser = $file_mime_type_guesser;
$this->time = $time;
$this->uuidService = $uuid;
$this->dateFormatter = $date_formatter;
$this->entityTypeManager = $entity_type_manager;
}
public function auditfilesNotInDatabaseGetReportsFiles() {
$config = $this->configFactory
->get('auditfiles.settings');
$exclusions = $this
->auditFilesGetExclusions();
$report_files = [];
$reported_files = [];
$this
->auditfilesNotInDatabaseGetFilesForReport('', $report_files, $exclusions);
if (!empty($report_files)) {
$file_system_stream = $config
->get('auditfiles_file_system_path');
$real_files_path = $this->fileSystem
->realpath($file_system_stream . '://');
$date_format = $config
->get('auditfiles_report_options_date_format') ? $config
->get('auditfiles_report_options_date_format') : 'long';
foreach ($report_files as $report_file) {
if (empty($report_file['path_from_files_root'])) {
$file_to_check = $report_file['file_name'];
}
else {
$file_to_check = $report_file['path_from_files_root'] . DIRECTORY_SEPARATOR . $report_file['file_name'];
}
if (!$this
->auditfilesNotInDatabaseIsFileInDatabase($file_to_check)) {
$reported_files += $this
->auditfilesNotInDatabaseFormatRowData($report_file, $real_files_path, $date_format);
}
}
}
return $reported_files;
}
public function auditfilesNotInDatabaseGetFilesForReport($path, array &$report_files, $exclusions) {
$config = $this->configFactory
->get('auditfiles.settings');
$file_system_stream = $config
->get('auditfiles_file_system_path');
$real_files_path = $real_files_path = $this->fileSystem
->realpath($file_system_stream . '://');
$maximum_records = $config
->get('auditfiles_report_options_maximum_records');
if ($maximum_records > 0 && count($report_files) < $maximum_records) {
$new_files = $this
->auditfilesNotInDatabaseGetFiles($path, $exclusions);
if (!empty($new_files)) {
foreach ($new_files as $file) {
if (empty($file['path_from_files_root'])) {
$item_path_check = $real_files_path . DIRECTORY_SEPARATOR . $file['file_name'];
}
else {
$item_path_check = $real_files_path . DIRECTORY_SEPARATOR . $file['path_from_files_root'] . DIRECTORY_SEPARATOR . $file['file_name'];
}
if (is_dir($item_path_check)) {
if (empty($path)) {
$file_path = $file['file_name'];
}
else {
$file_path = $path . DIRECTORY_SEPARATOR . $file['file_name'];
}
$this
->auditfilesNotInDatabaseGetFilesForReport($file_path, $report_files, $exclusions);
}
else {
$file['path_from_files_root'] = $this
->auditfilesNotInDatabaseFixPathSeparators($file['path_from_files_root']);
$report_files[] = $file;
}
}
}
}
}
public function auditfilesNotInDatabaseIsFileInDatabase($filepathname) {
$file_uri = $this
->auditfilesBuildUri($filepathname);
$connection = $this->connection;
$fid = $connection
->select('file_managed', 'fm')
->condition('fm.uri', $file_uri)
->fields('fm', [
'fid',
])
->execute()
->fetchField();
return empty($fid) ? FALSE : TRUE;
}
public function auditfilesNotInDatabaseFormatRowData($file, $real_path, $date_format) {
if (empty($file['path_from_files_root'])) {
$filepathname = $file['file_name'];
}
else {
$filepathname = $file['path_from_files_root'] . DIRECTORY_SEPARATOR . $file['file_name'];
}
$real_filepathname = $real_path . DIRECTORY_SEPARATOR . $filepathname;
$filemime = $this->fileMimeTypeGuesser
->guess($real_filepathname);
$filesize = number_format(filesize($real_filepathname));
if (!empty($date_format)) {
$filemodtime = $this->dateFormatter
->format(filemtime($real_filepathname), $date_format);
}
$row_data[$filepathname] = [
'filepathname' => empty($filepathname) ? '' : $filepathname,
'filemime' => empty($filemime) ? '' : $filemime,
'filesize' => !isset($filesize) ? '' : $filesize,
'filemodtime' => empty($filemodtime) ? '' : $filemodtime,
'filename' => empty($file['file_name']) ? '' : $file['file_name'],
];
return $row_data;
}
public function auditfilesNotInDatabaseGetFiles($path, $exclusions) {
$config = $this->configFactory
->get('auditfiles.settings');
$file_system_stream = $config
->get('auditfiles_file_system_path');
$real_files_path = $real_files_path = $this->fileSystem
->realpath($file_system_stream . '://');
$file_list = [];
$scan_path = empty($path) ? $real_files_path : $real_files_path . DIRECTORY_SEPARATOR . $path;
$files = array_diff(scandir($scan_path), [
'..',
'.',
]);
foreach ($files as $file) {
if ($this
->auditfilesNotInDatabaseIncludeFile($real_files_path . DIRECTORY_SEPARATOR . $path, $file, $exclusions)) {
$file_list[] = [
'file_name' => $file,
'path_from_files_root' => $path,
];
}
}
return $file_list;
}
public function auditfilesNotInDatabaseFixPathSeparators($path) {
$path = preg_replace('@\\/\\/@', DIRECTORY_SEPARATOR, $path);
$path = preg_replace('@\\\\@', DIRECTORY_SEPARATOR, $path);
return $path;
}
public function auditfilesGetExclusions() {
$config = $this->configFactory
->get('auditfiles.settings');
$exclusions_array = [];
$files = $config
->get('auditfiles_exclude_files');
if ($files) {
$exclude_files = explode(';', $files);
array_walk($exclude_files, '\\Drupal\\auditfiles\\AuditFilesBatchProcess::auditfilesMakePreg', FALSE);
$exclusions_array = array_merge($exclusions_array, $exclude_files);
}
$paths = $config
->get('auditfiles_exclude_paths');
if ($paths) {
$exclude_paths = explode(';', $paths);
array_walk($exclude_paths, '\\Drupal\\auditfiles\\AuditFilesBatchProcess::auditfilesMakePreg', TRUE);
$exclusions_array = array_merge($exclusions_array, $exclude_paths);
}
$exclude_streams = [];
$auditfiles_file_system_path = $config
->get('auditfiles_file_system_path');
$file_system_paths = $this->streamWrapperManager
->getWrappers(StreamWrapperInterface::LOCAL);
foreach ($file_system_paths as $file_system_path_id => $file_system_path) {
if ($file_system_path_id != $auditfiles_file_system_path) {
$wrapper = $this->streamWrapperManager
->getViaUri($file_system_path_id . '://');
if ($wrapper && $wrapper
->realpath()) {
$exclude_streams[] = $wrapper
->realpath();
}
}
}
array_walk($exclude_streams, '\\Drupal\\auditfiles\\AuditFilesBatchProcess::auditfilesMakePreg', FALSE);
$exclusions_array = array_merge($exclusions_array, $exclude_streams);
$extensions = $config
->get('auditfiles_exclude_extensions');
if ($extensions) {
$exclude_extensions = explode(';', $extensions);
array_walk($exclude_extensions, '\\Drupal\\auditfiles\\AuditFilesBatchProcess::auditfilesMakePreg', FALSE);
$extensions = implode('|', $exclude_extensions);
$extensions = '(' . $extensions . ')$';
$exclusions_array[] = $extensions;
}
$exclusions = implode('|', $exclusions_array);
return $exclusions;
}
public function auditfilesNotInDatabaseIncludeFile($path, $file, $exclusions) {
if (empty($exclusions)) {
return TRUE;
}
elseif (!preg_match('@' . $exclusions . '@', $file) && !preg_match('@' . $exclusions . '@', $path . DIRECTORY_SEPARATOR . $file)) {
return TRUE;
}
return FALSE;
}
public function auditfilesNotInDatabaseGetHeader() {
return [
'filepathname' => [
'data' => $this->stringTranslation
->translate('File pathname'),
],
'filemime' => [
'data' => $this->stringTranslation
->translate('MIME'),
],
'filesize' => [
'data' => $this->stringTranslation
->translate('Size (in bytes)'),
],
'filemodtime' => [
'data' => $this->stringTranslation
->translate('Last modified'),
],
];
}
public function auditfilesNotInDatabaseBatchAddCreateBatch(array $fileids) {
$batch['title'] = $this->stringTranslation
->translate('Adding files to Drupal file management');
$batch['error_message'] = $this->stringTranslation
->translate('One or more errors were encountered processing the files.');
$batch['finished'] = "\\Drupal\\auditfiles\\AuditFilesBatchProcess::auditfilesNotInDatabaseBatchFinishBatch";
$batch['progress_message'] = $this->stringTranslation
->translate('Completed @current of @total operations.');
$operations = [];
$file_ids = [];
foreach ($fileids as $file_id) {
if (!empty($file_id)) {
$file_ids[] = $file_id;
}
}
foreach ($file_ids as $file_id) {
$operations[] = [
"\\Drupal\\auditfiles\\AuditFilesBatchProcess::auditfilesNotInDatabaseBatchAddProcessBatch",
[
$file_id,
],
];
}
$batch['operations'] = $operations;
return $batch;
}
public function auditfilesNotInDatabaseBatchAddProcessFile($filepathname) {
$user = $this->entityTypeManager
->getStorage('user')
->load($this->currentUser
->id());
$file = new \StdClass();
$file->uid = $user
->get('uid')->value;
$file->filename = trim(basename($filepathname));
$file->uri = $this
->auditfilesBuildUri($filepathname);
$real_filenamepath = $this->fileSystem
->realpath($file->uri);
$file->filemime = $this->fileMimeTypeGuesser
->guess($real_filenamepath);
$file->filesize = filesize($real_filenamepath);
$file->status = FILE_STATUS_PERMANENT;
$file->timestamp = $this->time
->getCurrentTime();
$uuid = $this->uuidService
->generate();
$connection = $this->connection;
$query = $connection
->select('file_managed', 'fm');
$query
->condition('fm.uri', $file->uri);
$query
->fields('fm', [
'fid',
]);
$existing_file = $query
->execute()
->fetchField();
if (empty($existing_file)) {
$results = $this->connection
->merge('file_managed')
->key([
'fid' => NULL,
])
->fields([
'fid' => NULL,
'uuid' => $uuid,
'langcode' => 'en',
'uid' => $file->uid,
'filename' => $file->filename,
'uri' => $file->uri,
'filemime' => $file->filemime,
'filesize' => $file->filesize,
'status' => $file->status,
'created' => $file->timestamp,
'changed' => $file->timestamp,
])
->execute();
if (empty($results)) {
$this
->messenger()
->addStatus($this->stringTranslation
->translate('Failed to add %file to the database.', [
'%file' => $filepathname,
]));
}
else {
$this
->messenger()
->addStatus($this->stringTranslation
->translate('Sucessfully added %file to the database.', [
'%file' => $filepathname,
]));
}
}
else {
$this
->messenger()
->addStatus($this->stringTranslation
->translate('The file %file is already in the database.', [
'%file' => $filepathname,
]));
}
}
public function auditfilesNotInDatabaseBatchDeleteCreateBatch(array $file_names) {
$batch['title'] = $this->stringTranslation
->translate('Adding files to Drupal file management');
$batch['error_message'] = $this->stringTranslation
->translate('One or more errors were encountered processing the files.');
$batch['finished'] = '\\Drupal\\auditfiles\\AuditFilesBatchProcess::auditfilesNotInDatabaseBatchFinishBatch';
$batch['progress_message'] = $this->stringTranslation
->translate('Completed @current of @total operations.');
$batch['title'] = $this->stringTranslation
->translate('Deleting files from the server');
$operations = [];
$filenames = [];
foreach ($file_names as $file_name) {
if (!empty($file_name)) {
$filenames[] = $file_name;
}
}
foreach ($filenames as $filename) {
$operations[] = [
'\\Drupal\\auditfiles\\AuditFilesBatchProcess::auditfilesNotInDatabaseBatchDeleteProcessBatch',
[
$filename,
],
];
}
$batch['operations'] = $operations;
return $batch;
}
public function auditfilesNotInDatabaseBatchDeleteProcessFile($filename) {
$config = $this->configFactory
->get('auditfiles.settings');
$file_system_stream = $config
->get('auditfiles_file_system_path');
$real_files_path = $this->fileSystem
->realpath($file_system_stream . '://');
if ($this->fileSystem
->delete($real_files_path . DIRECTORY_SEPARATOR . $filename)) {
$this
->messenger()
->addStatus($this->stringTranslation
->translate('Sucessfully deleted %file from the server.', [
'%file' => $filename,
]));
}
else {
$this
->messenger()
->addStatus($this->stringTranslation
->translate('Failed to delete %file from the server.', [
'%file' => $filename,
]));
}
}
public function auditfilesBuildUri($file_pathname) {
$config = $this->configFactory
->get('auditfiles.settings');
$file_system_stream = $config
->get('auditfiles_file_system_path');
return "{$file_system_stream}://{$file_pathname}";
}
}