Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[data_release] Preventing Duplication of File Names #6461

Merged
merged 9 commits into from
May 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ the list of modules.(PR #5824)
exception). It is recommended to run this tool for existing projects (PR #5270)
- New tool for automatically adding modules to the modules table. This tool should
be used by projects having custom modules not in LORIS. (PR #5913)
- Duplicate filenames in the data release module will cause an error when downloading. Make sure to remove all filename duplications before upgrading to this version. (PR #6461)

### Notes For Developers
- The tool `phpstan` has been added to our automated test suite. (PR #4928)
Expand Down
118 changes: 79 additions & 39 deletions modules/data_release/ajax/FileUpload.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
if ($_GET['action'] == 'upload') {
$fileName = $_FILES["file"]["name"];
$version = !empty($_POST['version']) ? $_POST['version'] : null;
$overwrite = $_GET['overwrite'] ?? false;
$upload_date = date('Y-m-d');

$settings = $factory->settings();
Expand All @@ -40,51 +41,90 @@
error_log('ERROR: ' . $msg);
header("HTTP/1.1 500 Internal Server Error");
} else {
// Check if file is duplicate
$duplicateFile = $DB->pselectrow(
"SELECT id, file_name FROM data_release WHERE file_name=:f",
['f' => $fileName]
);

// If file is duplicate and overwrite not declared, exit.
if (isset($duplicateFile) && !$overwrite) {
header("Location: {$baseURL}/data_release/?duplicate=true");
exit;
} else if (isset($duplicateFile) && $overwrite) {
// check if user has permission to file to be overwrittern.
$userPermission = $DB->pselectrow(
"SELECT userid FROM data_release_permissions
WHERE userid=:u AND data_release_id=:d",
['u' => $user->getID(), 'd' => $duplicateFile['id']]
);
if (!isset($userPermission) && !$user->hasPermission('superuser')) {
$msg = "File overwrite failed. Current user does not have
permission for file.";
error_log('ERROR: ' . $msg);
header("HTTP/1.1 403 Forbidden");
exit;
}
}

$target_path = $path . $fileName;
if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_path)) {
// make version lower case.
if (!is_null($version)) {
$version = strtolower($version);
}
// insert the file into the data_release table
$DB->insert(
'data_release',
array(
'file_name' => $fileName,
'version' => $version,
'upload_date' => $upload_date,
)
);
// get the ID of the user who uploaded the file
$user_ID = $DB->pselectOne(
"SELECT ID FROM users WHERE userid=:UserID",
array('UserID' => $user->getUsername())
);
// get the ID of the file inserted in the data_release table
$version_where = $version ? "version=:version" : "version IS :version";
$ID = $DB->pselectOne(
"SELECT
id
FROM
data_release
WHERE
file_name=:file_name
AND $version_where
AND upload_date=:upload_date",
array(
'file_name' => $fileName,
'version' => $version,
'upload_date' => $upload_date,
)
);
// add permission to the user for the uploaded data_release file
$DB->insert(
'data_release_permissions',
array(
'userid' => $user_ID,
'data_release_id' => $ID,
)
);
if (isset($duplicateFile)) {
// update file in data_release table.
$DB->update(
'data_release',
[
'version' => $version,
'upload_date' => $upload_date,
],
['file_name' => $fileName]
);
} else {
// insert the file into the data_release table
$DB->insert(
'data_release',
array(
'file_name' => $fileName,
'version' => $version,
'upload_date' => $upload_date,
)
);
// get the ID of the user who uploaded the file
$user_ID = $DB->pselectOne(
"SELECT ID FROM users WHERE userid=:UserID",
array('UserID' => $user->getUsername())
);
// get the ID of the file inserted in the data_release table
$version_where = $version ? "version=:version"
: "version IS :version";
$ID = $DB->pselectOne(
"SELECT
id
FROM
data_release
WHERE
file_name=:file_name
AND $version_where
AND upload_date=:upload_date",
array(
'file_name' => $fileName,
'version' => $version,
'upload_date' => $upload_date,
)
);
// add permission to the user for the uploaded data_release file
$DB->insert(
'data_release_permissions',
array(
'userid' => $user_ID,
'data_release_id' => $ID,
)
);
}
}
header("Location: {$baseURL}/data_release/?uploadSuccess=true");
header("HTTP/1.1 201 Created");
Expand Down
88 changes: 42 additions & 46 deletions modules/data_release/jsx/uploadFileForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ class UploadFileForm extends Component {
* Validate and submit the upload
*/
validateAndSubmit() {
let files = this.state.data.files ? this.state.data.files : [];
let formData = this.state.formData;

let errorMessage = {
Expand Down Expand Up @@ -143,36 +142,15 @@ class UploadFileForm extends Component {
return;
}

// Grep the uploaded file name
let fileName = formData.file ? formData.file.name.replace(/\s+/g, '_') : null;

// Check for duplicate file names
let isDuplicate = files.indexOf(fileName);
if (isDuplicate >= 0) {
swal({
title: 'Are you sure?',
text: 'A file with this name already exists!\n Would you like to override existing file?',
type: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, I am sure!',
cancelButtonText: 'No, cancel it!',
closeOnConfirm: false,
}, function(isConfirm) {
if (isConfirm) {
this.uploadFile();
} else {
swal('Cancelled', 'Your file is safe :)', 'error');
}
}.bind(this));
} else {
this.uploadFile();
}
this.uploadFile();
}

/**
* Upload the file to the server
*
* @param {boolean} overwrite
*/
uploadFile() {
uploadFile(overwrite) {
let formData = this.state.formData;
let formObj = new FormData();
for (let key in formData) {
Expand All @@ -182,7 +160,9 @@ class UploadFileForm extends Component {
}

// fetch API to upload the file
fetch(this.props.action, {
const url = overwrite ? this.props.action + '&overwrite=true'
: this.props.action;
fetch(url, {
method: 'post',
body: formObj,
cache: 'no-cache',
Expand All @@ -196,25 +176,41 @@ class UploadFileForm extends Component {
swal(msg, '', 'error');
console.error(msg);
} else {
// Add file to the list of existing files
let files = JSON.parse(JSON.stringify(this.state.data.files));
files.push(formData.file.name);
// Trigger an update event to update all observers (i.e. DataTable)
let event = new CustomEvent('update-datatable');
window.dispatchEvent(event);
this.setState({
files: files,
formData: {}, // reset form data after successful file upload
uploadProgress: -1,
});
swal({
text: 'Upload Successful!',
title: '',
type: 'success',
}, function() {
window.location.assign('/data_release');
});
this.props.fetchData();
const responseUrl = new URL(response.url);
if (responseUrl.searchParams.has('duplicate')) {
swal({
title: 'Are you sure?',
text: 'A file with this name already exists!\n Would you like to overwrite existing file?\n Note that the version associated with the file will also be overwritten.',
type: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, I am sure!',
cancelButtonText: 'No, cancel it!',
}, (isConfirm) => {
if (isConfirm) {
this.uploadFile(true);
}
});
} else {
// Add file to the list of existing files
let files = JSON.parse(JSON.stringify(this.state.data.files));
files.push(formData.file.name);
// Trigger an update event to update all observers (i.e. DataTable)
let event = new CustomEvent('update-datatable');
window.dispatchEvent(event);
this.setState({
files: files,
formData: {}, // reset form data after successful file upload
uploadProgress: -1,
});
swal({
text: 'Upload Successful!',
title: '',
type: 'success',
}, function() {
window.location.assign('/data_release');
});
this.props.fetchData();
}
}
}).catch( (error) => {
let msg = error.message ? error.message : 'Upload error!';
Expand Down