[DEV-162] Fix ticket comment issues (#1081)

* Fix bug in UI

* Fix bug in backend

* Fix some issues

* Improve coding

* Improve coding
This commit is contained in:
LautaroCesso 2021-11-26 17:37:33 -03:00 committed by GitHub
parent f5e9b2602c
commit 402af565a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 42 deletions

View File

@ -31,7 +31,9 @@ class TicketEvent extends React.Component {
private: React.PropTypes.string,
edited: React.PropTypes.bool,
edit: React.PropTypes.bool,
onToggleEdit: React.PropTypes.func
onToggleEdit: React.PropTypes.func,
isLastComment: React.PropTypes.bool,
isTicketClosed: React.PropTypes.bool
};
state = {
@ -92,12 +94,7 @@ class TicketEvent extends React.Component {
}
renderComment() {
const {
author,
date,
edit,
file
} = this.props;
const { author, date, edit, file } = this.props;
const customFields = (author && author.customfields) || [];
return (
@ -142,10 +139,13 @@ class TicketEvent extends React.Component {
}
renderContent() {
const { content, author, userId, userStaff, isLastComment, isTicketClosed } = this.props;
const { id, staff } = author;
return (
<div className="ticket-event__comment-content ql-editor">
<div dangerouslySetInnerHTML={{__html: this.props.content}}></div>
{((this.props.author.id == this.props.userId && this.props.author.staff == this.props.userStaff) || this.props.userStaff) ? this.renderEditIcon() : null}
<div className="ticket-event__comment-content ql-editor">
<div dangerouslySetInnerHTML={{__html: content}}></div>
{(id == userId && staff == userStaff && isLastComment && !isTicketClosed) ? this.renderEditIcon() : null }
</div>
)
}

View File

@ -83,30 +83,46 @@ class TicketViewer extends React.Component {
render() {
const { ticket, userStaff, userId, editable, allowAttachments, assignmentAllowed } = this.props;
const showResponseField = (!ticket.closed && (editable || !assignmentAllowed));
const { editTitle, loading, edit, editId } = this.state;
const { closed, author, content, date, edited, file, events} = ticket;
const showResponseField = (!closed && (editable || !assignmentAllowed));
const lastCommentIndex = events.map(
(event, index) => {
return {...event, index}}
).filter(
(event) => event.type === "COMMENT"
).at(-1).index;
const eventsWithModifiedComments = events.map(
(event, index) => {
return {...event, isLastComment: index === lastCommentIndex && event.type === "COMMENT"};
}
);
return (
<div className="ticket-viewer">
{this.state.editTitle ? this.renderEditableTitle() : this.renderTitleHeader()}
{editTitle ? this.renderEditableTitle() : this.renderTitleHeader()}
{editable ? this.renderEditableHeaders() : this.renderHeaders()}
<div className="ticket-viewer__content">
<TicketEvent
loading={this.state.loading}
loading={loading}
type="COMMENT"
author={ticket.author}
content={userStaff ? MentionsParser.parse(ticket.content) : ticket.content}
isLastComment={!events.filter(event => event.type === "COMMENT").length}
author={author}
isTicketClosed={closed}
content={userStaff ? MentionsParser.parse(content) : content}
userStaff={userStaff}
userId={userId}
date={ticket.date}
date={date}
onEdit={this.onEdit.bind(this,0)}
edited={ticket.edited}
file={ticket.file}
edit={this.state.edit && this.state.editId == 0}
edited={edited}
file={file}
edit={edit && editId == 0}
onToggleEdit={this.onToggleEdit.bind(this, 0)}
allowAttachments={allowAttachments} />
</div>
<div className="ticket-viewer__comments">
{ticket.events && ticket.events.map(this.renderTicketEvent.bind(this))}
{eventsWithModifiedComments && eventsWithModifiedComments.map(this.renderTicketEvent.bind(this, closed))}
</div>
{showResponseField ? this.renderResponseField() : this.renderReopenCloseButtons()}
</div>
@ -405,22 +421,26 @@ class TicketViewer extends React.Component {
return <Button type='link' size="medium" onClick={() => this.setState({["edit"+option]: false})}>{i18n('CLOSE')}</Button>
}
renderTicketEvent(options, index) {
renderTicketEvent(isTicketClosed, ticketEventObject, index) {
const { userStaff, ticket, userId, allowAttachments } = this.props;
const { edit, editId } = this.state;
const { content, author, id} = ticketEventObject;
if(userStaff && typeof options.content === 'string') {
options.content = MentionsParser.parse(options.content);
if(userStaff && typeof content === 'string') {
ticketEventObject.content = MentionsParser.parse(content);
}
return (
<TicketEvent
{...options}
author={(!_.isEmpty(options.author)) ? options.author : ticket.author}
{...ticketEventObject}
isLastComment={ticketEventObject.isLastComment}
author={(!_.isEmpty(author)) ? author : ticket.author}
userStaff={userStaff}
isTicketClosed={isTicketClosed}
userId={userId}
onEdit={this.onEdit.bind(this, options.id)}
edit={this.state.edit && this.state.editId == options.id}
onToggleEdit={this.onToggleEdit.bind(this, options.id)}
onEdit={this.onEdit.bind(this, id)}
edit={edit && editId == id}
onToggleEdit={this.onToggleEdit.bind(this, id)}
key={index}
allowAttachments={allowAttachments} />
);
@ -735,16 +755,16 @@ class TicketViewer extends React.Component {
})
}
onEdit(ticketeventid,{content}) {
onEdit(ticketeventid, {content}) {
this.setState({
loading: true
});
const data = {};
if(ticketeventid){
data.ticketEventId = ticketeventid
}else{
data.ticketNumber = this.props.ticket.ticketNumber
if(ticketeventid) {
data.ticketEventId = ticketeventid;
} else {
data.ticketNumber = this.props.ticket.ticketNumber;
}
API.call({

View File

@ -50,21 +50,29 @@ class EditCommentController extends Controller {
$user = Controller::getLoggedUser();
$newcontent = Controller::request('content', true);
$ticketNumberLog = null;
$ticketevent = Ticketevent::getTicketEvent(Controller::request('ticketEventId'));
$ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
if(!Controller::isStaffLogged() && ($user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId ) ){
if(!$ticketevent->isNull()) {
$ticket = Ticket::getDataStore($ticketevent->ticketId);
} else {
$ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
}
if(!Controller::isStaffLogged() && $user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId) {
throw new RequestException(ERRORS::NO_PERMISSION);
}
if(Controller::isStaffLogged()){
if(!$ticketevent->isNull()){
$ticket = $ticketevent->ticket;
}
if(Controller::isStaffLogged() && !$user->canManageTicket($ticket)) {
throw new RequestException(ERRORS::NO_PERMISSION);
}
if(!$user->canManageTicket($ticket)) {
throw new RequestException(ERRORS::NO_PERMISSION);
if(!$ticketevent->isNull()) {
if($ticketevent->type !== "COMMENT" || $ticket->closed || $ticket->getLatestEventOfType("COMMENT")['id'] !== $ticketevent->id) {
throw new RequestException(ERRORS::INVALID_TICKET_EVENT);
}
} else {
if(sizeof($ticket->getEventsOfType("COMMENT"))) {
throw new RequestException(ERRORS::INVALID_TICKET_EVENT);
}
}

View File

@ -233,4 +233,23 @@ class Ticket extends DataStore {
public function isOwner($user) {
return !$user->isNull() && $this->owner && $user->id == $this->owner->id && ($user instanceof Staff);
}
public function getEventsOfType($type) {
$ticketEvents = $this->eventsToArray();
$filteredEventsList = [];
foreach($ticketEvents as $ticketEvent) {
if($ticketEvent['type'] == $type) {
array_push($filteredEventsList, $ticketEvent);
}
}
return $filteredEventsList;
}
public function getLatestEventOfType($type) {
$filteredEventsList = $this->getEventsOfType($type);
return end($filteredEventsList);
}
}