Skip to content

Commit

Permalink
[issue_tracker] Add attachments to issues (#5394)
Browse files Browse the repository at this point in the history
This adds the ability to add attachments to issues in the issue tracker.
  • Loading branch information
maltheism authored Feb 27, 2020
1 parent 3c256b6 commit 6a7b0a0
Show file tree
Hide file tree
Showing 18 changed files with 1,213 additions and 37 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ documentation for file permissions has been added to the README.md file (PR #532

#### Modules

##### Issue Tracker
- The issue_tracker module now has the feature of uploading attachments to new or existing issues.

##### Battery Manager
- New module created to manage the entries in the test_battery table of the database.
This allows projects to modify their instrument battery without requiring backend access.
Expand Down
14 changes: 14 additions & 0 deletions SQL/0000-00-00-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2095,3 +2095,17 @@ CREATE TABLE `publication_users_edit_perm_rel` (
CONSTRAINT `FK_publication_users_edit_perm_rel_PublicationID` FOREIGN KEY (`PublicationID`) REFERENCES `publication` (`PublicationID`),
CONSTRAINT `FK_publication_users_edit_perm_rel_UserID` FOREIGN KEY (`UserID`) REFERENCES `users` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET='utf8';

CREATE TABLE `issues_attachments` (
`ID` int NOT NULL AUTO_INCREMENT,
`issueID` int(11) NOT NULL,
`file_hash` varchar(64) NOT NULL,
`date_added` timestamp NOT NULL DEFAULT current_timestamp(),
`file_name` varchar(255) NOT NULL DEFAULT '',
`deleted` tinyint(1) NOT NULL DEFAULT 0,
`user` varchar(255) NOT NULL DEFAULT '',
`description` text DEFAULT NULL,
`file_size` int(20) DEFAULT NULL,
`mime_type` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`ID`)
) DEFAULT CHARSET=utf8mb4;
8 changes: 6 additions & 2 deletions SQL/0000-00-03-ConfigTables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ CREATE TABLE `Config` (
KEY `fk_Config_1_idx` (`ConfigID`),
CONSTRAINT `fk_Config_1`
FOREIGN KEY (`ConfigID`)
REFERENCES `ConfigSettings` (`ID`)
ON DELETE CASCADE
REFERENCES `ConfigSettings` (`ID`)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Expand Down Expand Up @@ -126,6 +126,9 @@ INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType,
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'reCAPTCHAPrivate', 'Private Key for Google reCAPTCHA', 1, 0, 'text', ID, 'reCAPTCHA Private Key', 2 FROM ConfigSettings WHERE Name="APIKeys";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'reCAPTCHAPublic', 'Public Key for Google reCaptcha', 1, 0, 'text', ID, 'reCAPTCHA Public Key', 3 FROM ConfigSettings WHERE Name="APIKeys";

-- Issue_Tracker attachments for issues.
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) VALUES ('IssueTrackerDataPath', 'Path to Issue Tracker data files', 1, 0, 'web_path', 26, 'Issue Tracker Data Path', 8);

-- Loris-MRI/Imaging Pipeline options from the $profile (commonly "prod") file
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, Label, OrderNumber) VALUES ('imaging_pipeline', 'Imaging Pipeline settings', 1, 0, 'Imaging Pipeline', 12);
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'dataDirBasepath', 'Base Path to the data directory of Loris-MRI', 1, 0, 'text', ID, 'Loris-MRI Data Directory', 1 FROM ConfigSettings WHERE Name="imaging_pipeline";
Expand Down Expand Up @@ -258,3 +261,4 @@ INSERT INTO Config (ConfigID, Value) SELECT ID, 't2' FROM ConfigSettings WHER
INSERT INTO Config (ConfigID, Value) SELECT ID, 'pd' FROM ConfigSettings WHERE Name="modalities_to_deface";
INSERT INTO Config (ConfigID, Value) SELECT ID, 'false' FROM ConfigSettings WHERE Name="usePwnedPasswordsAPI";
INSERT INTO Config (ConfigID, Value) SELECT ID, 'Y-m-d H:i:s' FROM ConfigSettings WHERE Name="dateDisplayFormat";
INSERT INTO Config (ConfigID, Value) SELECT ID, '/data/issue_tracker/' FROM ConfigSettings WHERE Name="IssueTrackerDataPath";
25 changes: 25 additions & 0 deletions SQL/New_patches/2019-10-29_adding_issues_attachments_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
CREATE TABLE `issues_attachments` (
`ID` int NOT NULL AUTO_INCREMENT,
`issueID` int(11) NOT NULL,
`file_hash` varchar(64) NOT NULL,
`date_added` timestamp NOT NULL DEFAULT current_timestamp(),
`file_name` varchar(255) NOT NULL DEFAULT '',
`deleted` tinyint(1) NOT NULL DEFAULT 0,
`user` varchar(255) NOT NULL DEFAULT '',
`description` text DEFAULT NULL,
`file_size` int(20) DEFAULT NULL,
`mime_type` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`ID`)
) DEFAULT CHARSET=utf8mb4;

INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber)
VALUES('IssueTrackerDataPath', 'Path to Issue Tracker data files', 1, 0, 'web_path', 26, 'Issue Tracker Data Path', 8);

INSERT INTO Config (ConfigID, Value)
SELECT
ID,
'/data/issue_tracker/'
FROM
ConfigSettings
WHERE
Name = "IssueTrackerDataPath";
13 changes: 9 additions & 4 deletions modules/issue_tracker/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Issue Tracker

## Purpose
The Issues Module allows users to track issues they have with data, or with their LORIS instance itself. A form with pre-defined fields is provided for users to submit issues, and a filter-form gives a sortable and filterable table view of issues viewable by the user.
The Issues Module allows users to track issues they have with data, or with their LORIS instance itself. A form with pre-defined fields is provided for users to submit issues, upload attachments and a filter-form gives a sortable and filterable table view of issues viewable by the user.

## Permissions
- `issue_tracker_reporter` permission allows adding an issue, editing issues created by the user, and commenting on all issues for the site.
- `issue_tracker_developer` permission allows to do the same, as well as closing an issue or editing any field of a submitted issue for the site.
- `view_all_sites` permission allows a user to view issues relevant to data from other sites.
- `issue_tracker_reporter` permission allows adding an issue, editing issues created by the user, download issue attachments and commenting on all issues for the site.
- `issue_tracker_developer` permission allows to do the same, as well as closing an issue or editing any field of a submitted issue for the site.
- Permissions are in accordance with the data permissions granted to that user - if a user can not see data outside of their site, they will only be able to view issues relevant to their site.
- If a user has DCC permission they will be able to see issues relevant to all sites.
- Additionally, users can be designated reporters or developers.
- Reporters can add issues, edit their own issues, and comment on all issues.
- Developers can additionally edit all issues, and mark them as resolved.
- Most users of Loris should be designated as reporters.

Most of the permissions are controlled in `IssueForm.js`, dependent on values returned in `editIssue.php`.

Expand Down
26 changes: 25 additions & 1 deletion modules/issue_tracker/ajax/EditIssue.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
*/
require_once "Email.class.inc";

use LORIS\issue_tracker\Provisioners\AttachmentProvisioner;

//TODO: or split it into two files... :P
if ($_SERVER['REQUEST_METHOD'] === "GET") {
echo json_encode(getIssueFields());
Expand Down Expand Up @@ -112,6 +114,19 @@ function editIssue()
updateHistory($historyValues, $issueID);
updateComments($_POST['comment'], $issueID);

// Attachment for new issue.
if (isset($_FILES['file'])) {
$attachment = new \LORIS\issue_tracker\UploadHelper();
$attachment->setupUploading(
$user,
$_FILES,
array(
'fileDescription' => '',
'issueID' => $issueID,
)
);
}

// Adding new assignee to watching
if (isset($issueValues['assignee'])) {
$nowWatching = array(
Expand Down Expand Up @@ -662,12 +677,18 @@ function getIssueFields()
$issueID = $_GET['issueID'];
$issueData = getIssueData($issueID);

$desc = $db->pselect(
$desc = $db->pselect(
"SELECT issueComment
FROM issues_comments WHERE issueID=:i
ORDER BY dateAdded LIMIT 1",
array('i' => $issueID)
);

$provisioner = (new AttachmentProvisioner($issueID));
$attachments = (new \LORIS\Data\Table())
->withDataFrom($provisioner)
->toArray($user);

$isWatching = $db->pselectOne(
"SELECT userID, issueID FROM issues_watching
WHERE issueID=:issueID AND userID=:userID",
Expand All @@ -681,7 +702,10 @@ function getIssueFields()
} else {
$issueData['watching'] = "Yes";
}
$username = $user->getUsername();
$issueData['commentHistory'] = getComments($issueID);
$issueData['attachments'] = $attachments;
$issueData['whoami'] = $username;
$issueData['othersWatching'] = getWatching($issueID);
$issueData['desc'] = $desc[0]['issueComment'] ?? '';
}
Expand Down
4 changes: 2 additions & 2 deletions modules/issue_tracker/help/issue_tracker.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Issue Tracker

This module allows you to submit, edit, view, and track issues.
This module allows you to submit, edit, view, upload and track issues.

Use the *Selection Filter* section to search for specific issues, if desired. In the data table, you can navigate between different tabs—**All Issues**, **Active Issues**, **Closed Issues**, and **My Issues**—to further narrow your results.

In the table, in the *Title* column, you can click on any issue name to edit its details.

Click **New Issue** if you wish to add a new issue. Then, populate the fields and click **Submit Issue**.
Click **New Issue** if you wish to add a new issue. Then, populate the fields and click **Submit Issue**. You may also upload an attachment for the issue.

Note that you can add users to the "Watching" list - this will send email notifications to the selected users when the issue is created and updated. If you are the author of an issue, and you add yourself as Watching, you won't be sent notifications about your own updates.
19 changes: 3 additions & 16 deletions modules/issue_tracker/jsx/CommentList.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ class CommentList extends Component {
}

render() {
const btnCommentsLabel = (this.state.collapsed ?
'Show Comment History' :
'Hide Comment History');

const changes = this.props.commentHistory.reduce(function(carry, item) {
let label = item.dateAdded.concat(' - ', item.addedBy);
if (!carry[label]) {
Expand Down Expand Up @@ -59,18 +55,9 @@ class CommentList extends Component {
}, this);

return (
<div>
<div className='btn btn-primary'
onClick={this.toggleCollapsed}
data-toggle='collapse'
data-target='#comment-history'
style={{margin: '10px 0'}}
>
{btnCommentsLabel}
</div>
<div id='comment-history' className='collapse'>
{history}
</div>
<div id='comment-history'>
<h3>Comment History</h3>
{history}
</div>
);
}
Expand Down
63 changes: 63 additions & 0 deletions modules/issue_tracker/jsx/IssueForm.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import Loader from 'Loader';
import Modal from 'jsx/Modal';
import CommentList from './CommentList';
import IssueUploadAttachmentForm from './attachments/uploadForm';
import AttachmentsList from './attachments/attachmentsList';

/**
* Issue add/edit form
Expand All @@ -25,6 +28,7 @@ class IssueForm extends Component {
isLoaded: false,
isNewIssue: false,
issueID: 0,
showAttachmentUploadModal: false,
};

// Bind component instance to custom methods
Expand All @@ -33,12 +37,30 @@ class IssueForm extends Component {
this.setFormData = this.setFormData.bind(this);
this.isValidForm = this.isValidForm.bind(this);
this.showAlertMessage = this.showAlertMessage.bind(this);
this.closeAttachmentUploadModal = this.closeAttachmentUploadModal.bind(this);
this.openAttachmentUploadModal = this.openAttachmentUploadModal.bind(this);
}

componentDidMount() {
this.getFormData();
}

openAttachmentUploadModal(e) {
e.preventDefault();
this.setState({showAttachmentUploadModal: true});
}
closeAttachmentUploadModal() {
this.setState({
upload: {
formData: {
fileType: '',
fileDescription: '',
},
},
showAttachmentUploadModal: false,
});
}

render() {
// If error occurs, return a message.
// XXX: Replace this with a UI component for 500 errors.
Expand All @@ -64,6 +86,8 @@ class IssueForm extends Component {
let submitButtonValue;
let commentLabel;
let isWatching = this.state.issueData.watching;
let attachmentUploadBtn = null;
let attachmentFileElement = null;

if (this.state.isNewIssue) {
headerText = 'Create New Issue';
Expand All @@ -72,15 +96,38 @@ class IssueForm extends Component {
dateCreated = 'Sometime Soon!';
submitButtonValue = 'Submit Issue';
commentLabel = 'Description';
attachmentFileElement = (
<FileElement
name='file'
label='Attachment for issue'
onUserInput={this.setFormData}
errorMessage={this.state.errorMessage}
value={this.state.formData.file}
/>
);
} else {
headerText = 'Edit Issue #' + this.state.issueData.issueID;
lastUpdateValue = this.state.issueData.lastUpdate;
lastUpdatedByValue = this.state.issueData.lastUpdatedBy;
dateCreated = this.state.issueData.dateCreated;
submitButtonValue = 'Update Issue';
commentLabel = 'New Comment';
attachmentUploadBtn = (
<ButtonElement
onUserInput={this.openAttachmentUploadModal}
label={'Add Attachment'}
/>
);
}

const fileCollection = this.state.isNewIssue || (
<AttachmentsList issue={this.props.issue}
baseURL={this.props.baseURL}
attachments={this.state.issueData['attachments']}
userHasPermission={this.props.userHasPermission}
/>
);

const commentHistory = this.state.isNewIssue || (
<CommentList commentHistory={this.state.issueData.commentHistory} />
);
Expand Down Expand Up @@ -137,6 +184,16 @@ class IssueForm extends Component {

return (
<div>
<Modal
title='Attachment for Issue'
onClose={this.closeAttachmentUploadModal}
show={this.state.showAttachmentUploadModal}
>
<IssueUploadAttachmentForm
issue={this.props.issue}
baseURL={this.props.baseURL}
/>
</Modal>
<FormElement
name='issueEdit'
onSubmit={this.handleSubmit}
Expand Down Expand Up @@ -248,8 +305,11 @@ class IssueForm extends Component {
onUserInput={this.setFormData}
value={this.state.formData.comment}
/>
{attachmentFileElement}
<ButtonElement label={submitButtonValue}/>
{attachmentUploadBtn}
</FormElement>
{fileCollection}
{commentHistory}
</div>
);
Expand Down Expand Up @@ -438,7 +498,10 @@ class IssueForm extends Component {

IssueForm.propTypes = {
DataURL: PropTypes.string.isRequired,
baseURL: PropTypes.string.isRequired,
action: PropTypes.string.isRequired,
issue: PropTypes.string.isRequired,
whoami: PropTypes.string.isRequired,
};

export default IssueForm;
Expand Down
Loading

0 comments on commit 6a7b0a0

Please sign in to comment.