Merge pull request #688 from guillegiu/master
Edit Ticket Title feature
This commit is contained in:
commit
880caa0388
|
@ -19,6 +19,7 @@ class ActivityRow extends React.Component {
|
|||
'RE_OPEN',
|
||||
'DEPARTMENT_CHANGED',
|
||||
'PRIORITY_CHANGED',
|
||||
'EDIT_TITLE',
|
||||
'EDIT_COMMENT',
|
||||
|
||||
'EDIT_SETTINGS',
|
||||
|
@ -60,6 +61,8 @@ class ActivityRow extends React.Component {
|
|||
'DEPARTMENT_CHANGED',
|
||||
'PRIORITY_CHANGED',
|
||||
'COMMENT_EDITED',
|
||||
'EDIT_TITLE',
|
||||
'EDIT_COMMENT',
|
||||
];
|
||||
|
||||
return (
|
||||
|
@ -113,6 +116,7 @@ class ActivityRow extends React.Component {
|
|||
'RE_OPEN': 'unlock-alt',
|
||||
'DEPARTMENT_CHANGED': 'exchange',
|
||||
'PRIORITY_CHANGED': 'exclamation',
|
||||
'EDIT_TITLE': 'edit',
|
||||
'EDIT_COMMENT': 'edit',
|
||||
|
||||
'EDIT_SETTINGS': 'wrench',
|
||||
|
|
|
@ -59,7 +59,10 @@ class TicketViewer extends React.Component {
|
|||
commentEdited: false,
|
||||
commentPrivate: false,
|
||||
edit: false,
|
||||
editId: 0
|
||||
editTitle: false,
|
||||
editId: 0,
|
||||
newTitle: this.props.ticket.title,
|
||||
editTitleError: false
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -72,13 +75,7 @@ class TicketViewer extends React.Component {
|
|||
const ticket = this.props.ticket;
|
||||
return (
|
||||
<div className="ticket-viewer">
|
||||
<div className="ticket-viewer__header row">
|
||||
<span className="ticket-viewer__number">#{ticket.ticketNumber}</span>
|
||||
<span className="ticket-viewer__title">{ticket.title}</span>
|
||||
<span className="ticket-viewer__flag">
|
||||
<Icon name={(ticket.language === 'en') ? 'us' : ticket.language}/>
|
||||
</span>
|
||||
</div>
|
||||
{this.state.editTitle ? this.renderEditableTitle() : this.renderTitleHeader()}
|
||||
{this.props.editable ? this.renderEditableHeaders() : this.renderHeaders()}
|
||||
<div className="ticket-viewer__content">
|
||||
<TicketEvent
|
||||
|
@ -105,6 +102,50 @@ class TicketViewer extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderTitleHeader() {
|
||||
const {ticket, userStaff, userId} = this.props;
|
||||
const {ticketNumber, title, author, editedTitle, language} = ticket;
|
||||
|
||||
return(
|
||||
<div className="ticket-viewer__header row">
|
||||
<span className="ticket-viewer__number">#{ticketNumber}</span>
|
||||
<span className="ticket-viewer__title">{title}</span>
|
||||
<span className="ticket-viewer__flag">
|
||||
<Icon name={(language === 'en') ? 'us' : language}/>
|
||||
</span>
|
||||
{((author.id == userId && author.staff == userStaff) || userStaff) ? this.renderEditTitleOption() : null}
|
||||
{editedTitle ? this.renderEditedTitleText() : null }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderEditedTitleText(){
|
||||
return(
|
||||
<div className="ticket-viewer__edited-title-text"> {i18n('TITLE_EDITED')} </div>
|
||||
)
|
||||
}
|
||||
|
||||
renderEditTitleOption() {
|
||||
return(
|
||||
<span className="ticket-viewer__edit-title-icon">
|
||||
<Icon name="pencil" onClick={() => this.setState({editTitle: true})} />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
renderEditableTitle(){
|
||||
return(
|
||||
<div className="ticket-viewer__header row">
|
||||
<div className="ticket-viewer__edit-title-box">
|
||||
<FormField className="ticket-viewer___input-edit-title" error={this.state.editTitleError} value={this.state.newTitle} field='input' onChange={(e) => this.setState({newTitle: e.target.value })} />
|
||||
</div>
|
||||
<Button type='secondary' size="extra-small" onClick={this.changeTitle.bind(this)}>
|
||||
{i18n('EDIT_TITLE')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderEditableHeaders() {
|
||||
const ticket = this.props.ticket;
|
||||
const departments = this.getDepartmentsForTransfer();
|
||||
|
@ -392,6 +433,26 @@ class TicketViewer extends React.Component {
|
|||
AreYouSure.openModal(null, this.deleteTicket.bind(this));
|
||||
}
|
||||
|
||||
changeTitle(){
|
||||
API.call({
|
||||
path: '/ticket/edit-title',
|
||||
data: {
|
||||
ticketNumber: this.props.ticket.ticketNumber,
|
||||
title: this.state.newTitle
|
||||
}
|
||||
}).then(() => {
|
||||
this.setState({
|
||||
editTitle: false,
|
||||
editTitleError: false
|
||||
});
|
||||
this.onTicketModification();
|
||||
}).catch((result) => {
|
||||
this.setState({
|
||||
editTitleError: i18n(result.message)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
reopenTicket() {
|
||||
API.call({
|
||||
path: '/ticket/re-open',
|
||||
|
@ -608,7 +669,6 @@ class TicketViewer extends React.Component {
|
|||
}
|
||||
|
||||
export default connect((store) => {
|
||||
|
||||
return {
|
||||
userId: store.session.userId,
|
||||
userStaff: store.session.staff,
|
||||
|
|
|
@ -9,6 +9,42 @@
|
|||
color: white;
|
||||
font-size: 16px;
|
||||
padding: 6px 0;
|
||||
display: flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
position: relative;
|
||||
&:hover {
|
||||
.ticket-viewer__edit-title-icon {
|
||||
color: $grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__edited-title-text {
|
||||
font-style: italic;
|
||||
font-size: 14px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&__edit-title-icon {
|
||||
position: absolute;
|
||||
color: #414A59;
|
||||
right: 12px;
|
||||
&:hover {
|
||||
cursor:pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&___input-edit-title {
|
||||
color: black;
|
||||
align-items:center;
|
||||
justify-content: center;
|
||||
margin-bottom: 6px;
|
||||
margin-right: 6px;
|
||||
|
||||
.input__text {
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
&__number {
|
||||
|
|
|
@ -91,6 +91,7 @@ export default {
|
|||
'BAN_EMAIL': 'Ban email',
|
||||
'EDIT_EMAIL': 'Edit email',
|
||||
'EDIT_PASSWORD': 'Edit password',
|
||||
'EDIT_TITLE': 'Edit title',
|
||||
'CHANGE_EMAIL': 'Change email',
|
||||
'CHANGE_PASSWORD': 'Change password',
|
||||
'NAME': 'Name',
|
||||
|
@ -233,7 +234,7 @@ export default {
|
|||
'ACTIVITY_DEPARTMENT_CHANGED': 'changed department of ticket',
|
||||
'ACTIVITY_PRIORITY_CHANGED': 'changed priority of ticket',
|
||||
'ACTIVITY_EDIT_COMMENT': 'edited a comment of ticket',
|
||||
|
||||
'ACTIVITY_EDIT_TITLE': 'edited title of ticket',
|
||||
'ACTIVITY_EDIT_SETTINGS': 'edited settings',
|
||||
'ACTIVITY_SIGNUP': 'signed up',
|
||||
'ACTIVITY_INVITE': 'invited user',
|
||||
|
@ -360,6 +361,7 @@ export default {
|
|||
'TICKET_COMMENT_ERROR': 'An error occurred while trying to add the comment.',
|
||||
'NO_PERMISSION': 'You\'ve no permission to access to this page.',
|
||||
'INVALID_USER': 'User id is invalid',
|
||||
'INVALID_TITLE': 'invalid title',
|
||||
'ERROR_RETRIEVING_TICKETS': 'An error occurred while trying to retrieve tickets.',
|
||||
'ERROR_RETRIEVING_USERS': 'An error occurred while trying to retrieve users.',
|
||||
'ERROR_RETRIEVING_BAN_LIST': 'An error occurred while trying to retrieve the list of banned emails.',
|
||||
|
@ -408,6 +410,7 @@ export default {
|
|||
'SERVER_CREDENTIALS_WORKING': 'Server credentials are working correctly',
|
||||
'DELETE_CUSTOM_FIELD_SURE': 'Some users may be using this field. Are you sure you want to delete it?',
|
||||
|
||||
'TITLE_EDITED': '(title edited)',
|
||||
'COMMENT_EDITED': '(comment edited)',
|
||||
'LAST_7_DAYS': 'Last 7 days',
|
||||
'LAST_30_DAYS': 'Last 30 days',
|
||||
|
|
|
@ -4,6 +4,7 @@ $ticketControllers->setGroupPath('/ticket');
|
|||
|
||||
$ticketControllers->addController(new CreateController);
|
||||
$ticketControllers->addController(new EditCommentController);
|
||||
$ticketControllers->addController(new EditTitleController);
|
||||
$ticketControllers->addController(new CommentController);
|
||||
$ticketControllers->addController(new TicketGetController);
|
||||
$ticketControllers->addController(new CheckTicketController);
|
||||
|
|
|
@ -20,6 +20,7 @@ DataValidator::with('CustomValidations', true);
|
|||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
* @apiUse INVALID_CONTENT
|
||||
* @apiUse INVALID_TOKEN
|
||||
*
|
||||
* @apiSuccess {Object} data Empty object
|
||||
*
|
||||
|
@ -30,15 +31,39 @@ class EditCommentController extends Controller {
|
|||
const METHOD = 'POST';
|
||||
|
||||
public function validations() {
|
||||
return [
|
||||
'permission' => 'user',
|
||||
'requestData' => [
|
||||
'content' => [
|
||||
'validation' => DataValidator::length(10, 5000),
|
||||
'error' => ERRORS::INVALID_CONTENT
|
||||
if(Controller::isUserSystemEnabled()){
|
||||
return [
|
||||
'permission' => 'user',
|
||||
'requestData' => [
|
||||
'content' => [
|
||||
'validation' => DataValidator::length(10, 5000),
|
||||
'error' => ERRORS::INVALID_CONTENT
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'permission' => 'any',
|
||||
'requestData' => [
|
||||
'content' => [
|
||||
'validation' => DataValidator::length(10, 5000),
|
||||
'error' => ERRORS::INVALID_CONTENT
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
],
|
||||
'csrf_token' => [
|
||||
'validation' => DataValidator::equals(Session::getInstance()->getToken()),
|
||||
'error' => ERRORS::INVALID_TOKEN
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
|
@ -49,7 +74,7 @@ class EditCommentController extends Controller {
|
|||
$ticketevent = Ticketevent::getTicketEvent(Controller::request('ticketEventId'));
|
||||
$ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
|
||||
|
||||
if(!Controller::isStaffLogged() && ($user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId ) ){
|
||||
if(Controller::isUserSystemEnabled() && !Controller::isStaffLogged() && ($user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId ) ){
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
use Respect\Validation\Validator as DataValidator;
|
||||
DataValidator::with('CustomValidations', true);
|
||||
|
||||
/**
|
||||
* @api {post} /ticket/edit-title Edit title of a ticket
|
||||
* @apiVersion 4.5.0
|
||||
*
|
||||
* @apiName Edit title
|
||||
*
|
||||
* @apiGroup Ticket
|
||||
*
|
||||
* @apiDescription This path edits the title of a ticket.
|
||||
*
|
||||
* @apiPermission user
|
||||
*
|
||||
* @apiParam {String} title The new title of the ticket.
|
||||
* @apiParam {Number} ticketNumber The number of the ticket.
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
* @apiUse INVALID_TITLE
|
||||
* @apiUse INVALID_TOKEN
|
||||
*
|
||||
* @apiSuccess {Object} data Empty object
|
||||
*
|
||||
*/
|
||||
|
||||
class EditTitleController extends Controller {
|
||||
const PATH = '/edit-title';
|
||||
const METHOD = 'POST';
|
||||
|
||||
public function validations() {
|
||||
if(Controller::isUserSystemEnabled()){
|
||||
return [
|
||||
'permission' => 'user',
|
||||
'requestData' => [
|
||||
'title' => [
|
||||
'validation' => DataValidator::length(1, 200),
|
||||
'error' => ERRORS::INVALID_TITLE
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::validTicketNumber(),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
]
|
||||
]
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'permission' => 'any',
|
||||
'requestData' => [
|
||||
'title' => [
|
||||
'validation' => DataValidator::length(1, 200),
|
||||
'error' => ERRORS::INVALID_TITLE
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::validTicketNumber(),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
],
|
||||
'csrf_token' => [
|
||||
'validation' => DataValidator::equals(Session::getInstance()->getToken()),
|
||||
'error' => ERRORS::INVALID_TOKEN
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
$user = Controller::getLoggedUser();
|
||||
$newtitle = Controller::request('title');
|
||||
$ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
|
||||
|
||||
if(Controller::isUserSystemEnabled() && !$user->canManageTicket($ticket)) {
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
$ticket->title = $newtitle;
|
||||
$ticket->editedTitle = true;
|
||||
$ticket->store();
|
||||
|
||||
$ticketNumber = $ticket->ticketNumber;
|
||||
Log::createLog('EDIT_TITLE', $ticketNumber);
|
||||
|
||||
Response::respondSuccess();
|
||||
}
|
||||
}
|
|
@ -51,7 +51,8 @@ class Ticket extends DataStore {
|
|||
'authorEmail',
|
||||
'authorName',
|
||||
'sharedTagList',
|
||||
'editedContent'
|
||||
'editedContent',
|
||||
'editedTitle'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -132,7 +133,8 @@ class Ticket extends DataStore {
|
|||
'owner' => $this->ownerToArray(),
|
||||
'events' => $minimized ? [] : $this->eventsToArray(),
|
||||
'tags' => $this->sharedTagList->toArray(true),
|
||||
'edited' => $this->editedContent
|
||||
'edited' => $this->editedContent,
|
||||
'editedTitle' => $this->editedTitle
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ require './ticket/delete-tag.rb'
|
|||
require './ticket/add-tag.rb'
|
||||
require './ticket/delete-tag.rb'
|
||||
require './ticket/edit-comment.rb'
|
||||
require './ticket/edit-title.rb'
|
||||
require './system/disable-user-system.rb'
|
||||
require './ticket/search.rb'
|
||||
# require './system/get-stats.rb'
|
||||
|
|
|
@ -19,7 +19,7 @@ describe'system/disable-user-system' do
|
|||
|
||||
numberOftickets = $database.query("SELECT * FROM ticket WHERE author_id IS NULL AND author_email IS NOT NULL AND author_name IS NOT NULL")
|
||||
|
||||
(numberOftickets.num_rows).should.equal(51)
|
||||
(numberOftickets.num_rows).should.equal(52)
|
||||
|
||||
request('/user/logout')
|
||||
|
||||
|
@ -122,7 +122,7 @@ describe'system/disable-user-system' do
|
|||
(result['status']).should.equal('success')
|
||||
(result['data'].size).should.equal(10)
|
||||
end
|
||||
|
||||
|
||||
it 'should be able to get system logs as admin' do
|
||||
result = request('/system/get-logs', {
|
||||
page: 1,
|
||||
|
@ -220,7 +220,7 @@ describe'system/disable-user-system' do
|
|||
|
||||
numberOftickets= $database.query("SELECT * FROM ticket WHERE author_email IS NULL AND author_name IS NULL AND author_id IS NOT NULL" )
|
||||
|
||||
(numberOftickets.num_rows).should.equal(54)
|
||||
(numberOftickets.num_rows).should.equal(55)
|
||||
end
|
||||
|
||||
it 'should not enable the user system' do
|
||||
|
|
|
@ -15,7 +15,7 @@ describe '/ticket/edit-comment' do
|
|||
})
|
||||
|
||||
ticket = $database.getRow('ticket', 'ticket made by an user', 'title')
|
||||
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
(ticket['content']).should.equal('content edited by the user')
|
||||
end
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
describe '/ticket/edit-title' do
|
||||
|
||||
request('/user/logout')
|
||||
Scripts.login();
|
||||
Scripts.createTicket('Valar Morghulis','content of the ticket made by an user')
|
||||
ticket = $database.getRow('ticket', 'Valar Morghulis', 'title')
|
||||
ticketNumber = ticket['ticket_number']
|
||||
|
||||
it 'should fail change title of the ticket if the title is invalid' do
|
||||
result = request('/ticket/edit-title', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
title: '',
|
||||
ticketNumber: ticket['ticket_number']
|
||||
})
|
||||
|
||||
ticket = $database.getRow('ticket', ticketNumber, 'ticket_number')
|
||||
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_TITLE')
|
||||
end
|
||||
|
||||
it 'should change title of the ticket if the author user tries it' do
|
||||
result = request('/ticket/edit-title', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
title: 'Valar dohaeris',
|
||||
ticketNumber: ticket['ticket_number']
|
||||
})
|
||||
|
||||
ticket = $database.getRow('ticket', ticketNumber, 'ticket_number')
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
(ticket['title']).should.equal('Valar dohaeris')
|
||||
(ticket['edited_title']).should.equal('1')
|
||||
end
|
||||
|
||||
it 'should change the title of the ticket if staff is logged' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
|
||||
result = request('/ticket/edit-title', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
title: 'Valar dohaeris by Staff',
|
||||
ticketNumber: ticket['ticket_number']
|
||||
})
|
||||
|
||||
ticket = $database.getRow('ticket', ticketNumber, 'ticket_number')
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
(ticket['title']).should.equal('Valar dohaeris by Staff')
|
||||
(ticket['edited_title']).should.equal('1')
|
||||
|
||||
end
|
||||
|
||||
it 'should not change the title if the user is not the author' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
Scripts.createTicket('Winterfell')
|
||||
ticket = $database.getRow('ticket', 'Winterfell', 'title')
|
||||
|
||||
request('/user/logout')
|
||||
Scripts.login()
|
||||
|
||||
result = request('/ticket/edit-title', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
title: 'Casterly Rock',
|
||||
ticketNumber: ticket['ticket_number']
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('NO_PERMISSION')
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue