wip
This commit is contained in:
parent
08a6f7b8d0
commit
58c6f2e63f
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
|
import randomString from 'random-string';
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
import API from 'lib-app/api-call';
|
import API from 'lib-app/api-call';
|
||||||
|
@ -55,6 +56,7 @@ class AdminPanelEmailSettings extends React.Component {
|
||||||
['imap-host']: '',
|
['imap-host']: '',
|
||||||
['imap-user']: '',
|
['imap-user']: '',
|
||||||
['imap-pass']: 'HIDDEN',
|
['imap-pass']: 'HIDDEN',
|
||||||
|
['imap-token']: '',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,7 +137,7 @@ class AdminPanelEmailSettings extends React.Component {
|
||||||
<SubmitButton className="admin-panel-email-settings__submit" type="secondary"
|
<SubmitButton className="admin-panel-email-settings__submit" type="secondary"
|
||||||
size="small">{i18n('SAVE')}</SubmitButton>
|
size="small">{i18n('SAVE')}</SubmitButton>
|
||||||
<SubmitButton type="tertiary" size="small" onClick={this.testSMTP.bind(this)}>
|
<SubmitButton type="tertiary" size="small" onClick={this.testSMTP.bind(this)}>
|
||||||
Test
|
{i18n('TEST')}
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -148,11 +150,12 @@ class AdminPanelEmailSettings extends React.Component {
|
||||||
<FormField name="imap-host" label={i18n('IMAP_SERVER')} fieldProps={{size: 'large'}}/>
|
<FormField name="imap-host" label={i18n('IMAP_SERVER')} fieldProps={{size: 'large'}}/>
|
||||||
<FormField name="imap-user" label={i18n('IMAP_USER')} fieldProps={{size: 'large'}}/>
|
<FormField name="imap-user" label={i18n('IMAP_USER')} fieldProps={{size: 'large'}}/>
|
||||||
<FormField name="imap-pass" label={i18n('IMAP_PASSWORD')} fieldProps={{size: 'large'}}/>
|
<FormField name="imap-pass" label={i18n('IMAP_PASSWORD')} fieldProps={{size: 'large'}}/>
|
||||||
|
<FormField name="imap-token" label={i18n('IMAP_TOKEN')} infoMessage={i18n('IMAP_TOKEN_DESCRIPTION')} fieldProps={{size: 'large', icon: 'refresh', onIconClick: this.generateImapToken.bind(this)}}/>
|
||||||
<div className="admin-panel-email-settings__server-form-buttons">
|
<div className="admin-panel-email-settings__server-form-buttons">
|
||||||
<SubmitButton className="admin-panel-email-settings__submit" type="secondary"
|
<SubmitButton className="admin-panel-email-settings__submit" type="secondary"
|
||||||
size="small">{i18n('SAVE')}</SubmitButton>
|
size="small">{i18n('SAVE')}</SubmitButton>
|
||||||
<SubmitButton type="tertiary" size="small" onClick={this.testIMAP.bind(this)}>
|
<SubmitButton type="tertiary" size="small" onClick={this.testIMAP.bind(this)}>
|
||||||
Test
|
{i18n('TEST')}
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -334,6 +337,15 @@ class AdminPanelEmailSettings extends React.Component {
|
||||||
AreYouSure.openModal(i18n('WILL_RECOVER_EMAIL_TEMPLATE'), this.recoverEmailTemplate.bind(this));
|
AreYouSure.openModal(i18n('WILL_RECOVER_EMAIL_TEMPLATE'), this.recoverEmailTemplate.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateImapToken() {
|
||||||
|
this.setState({
|
||||||
|
imapForm: {
|
||||||
|
...this.state.imapForm,
|
||||||
|
['imap-token']: randomString({length: 20}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
submitEmailAddress(form) {
|
submitEmailAddress(form) {
|
||||||
this.editSettings(form, 'EMAIL_SUCCESS');
|
this.editSettings(form, 'EMAIL_SUCCESS');
|
||||||
}
|
}
|
||||||
|
@ -481,6 +493,7 @@ class AdminPanelEmailSettings extends React.Component {
|
||||||
['imap-host']: result.data['imap-host'],
|
['imap-host']: result.data['imap-host'],
|
||||||
['imap-user']: result.data['imap-user'],
|
['imap-user']: result.data['imap-user'],
|
||||||
['imap-pass']: 'HIDDEN',
|
['imap-pass']: 'HIDDEN',
|
||||||
|
['imap-token']: result.data['imap-token'],
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ class Input extends React.Component {
|
||||||
password: React.PropTypes.bool,
|
password: React.PropTypes.bool,
|
||||||
required: React.PropTypes.bool,
|
required: React.PropTypes.bool,
|
||||||
icon: React.PropTypes.string,
|
icon: React.PropTypes.string,
|
||||||
error: React.PropTypes.string
|
error: React.PropTypes.string,
|
||||||
|
onIconClick: React.PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -38,7 +39,7 @@ class Input extends React.Component {
|
||||||
let icon = null;
|
let icon = null;
|
||||||
|
|
||||||
if (this.props.icon) {
|
if (this.props.icon) {
|
||||||
icon = <span className="input__icon"><Icon name={this.props.icon} /></span>
|
icon = <span className="input__icon" onClick={this.onIconClick.bind(this)}><Icon name={this.props.icon} /></span>
|
||||||
}
|
}
|
||||||
|
|
||||||
return icon;
|
return icon;
|
||||||
|
@ -66,6 +67,7 @@ class Input extends React.Component {
|
||||||
'input': true,
|
'input': true,
|
||||||
'input_with-icon': (this.props.icon),
|
'input_with-icon': (this.props.icon),
|
||||||
'input_errored': (this.props.errored),
|
'input_errored': (this.props.errored),
|
||||||
|
'input_icon-clickable': (this.props.onIconClick),
|
||||||
['input_' + this.props.size]: true,
|
['input_' + this.props.size]: true,
|
||||||
|
|
||||||
[this.props.className]: (this.props.className)
|
[this.props.className]: (this.props.className)
|
||||||
|
@ -74,6 +76,14 @@ class Input extends React.Component {
|
||||||
return classNames(classes);
|
return classNames(classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onIconClick(event) {
|
||||||
|
if(this.props.onIconClick) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.focus();
|
||||||
|
this.props.onIconClick(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
if (this.refs.nativeInput) {
|
if (this.refs.nativeInput) {
|
||||||
this.refs.nativeInput.focus();
|
this.refs.nativeInput.focus();
|
||||||
|
|
|
@ -58,4 +58,11 @@
|
||||||
border: 1px solid $primary-red;
|
border: 1px solid $primary-red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&_icon-clickable {
|
||||||
|
|
||||||
|
.input__icon {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -144,6 +144,8 @@ export default {
|
||||||
'IMAP_USER': 'IMAP User',
|
'IMAP_USER': 'IMAP User',
|
||||||
'IMAP_SERVER': 'IMAP Server',
|
'IMAP_SERVER': 'IMAP Server',
|
||||||
'IMAP_PASSWORD': 'IMAP Password',
|
'IMAP_PASSWORD': 'IMAP Password',
|
||||||
|
'IMAP_TOKEN': 'IMAP Token',
|
||||||
|
'IMAP_TOKEN_DESCRIPTION': 'Use this token to authenticate the polling request.',
|
||||||
'PORT': 'Port',
|
'PORT': 'Port',
|
||||||
'RECAPTCHA_PUBLIC_KEY': 'Recaptcha Public Key',
|
'RECAPTCHA_PUBLIC_KEY': 'Recaptcha Public Key',
|
||||||
'RECAPTCHA_PRIVATE_KEY': 'Recaptcha Private Key',
|
'RECAPTCHA_PRIVATE_KEY': 'Recaptcha Private Key',
|
||||||
|
@ -382,6 +384,7 @@ export default {
|
||||||
'LAST_90_DAYS': 'Last 90 days',
|
'LAST_90_DAYS': 'Last 90 days',
|
||||||
'LAST_365_DAYS': 'Last 365 days',
|
'LAST_365_DAYS': 'Last 365 days',
|
||||||
|
|
||||||
|
'TEST': 'Test',
|
||||||
'ACTIVITY_COMMENT_THIS': 'commented this ticket',
|
'ACTIVITY_COMMENT_THIS': 'commented this ticket',
|
||||||
'ACTIVITY_ASSIGN_THIS': 'assigned this ticket to',
|
'ACTIVITY_ASSIGN_THIS': 'assigned this ticket to',
|
||||||
'ACTIVITY_UN_ASSIGN_THIS': 'unassigned this ticket to',
|
'ACTIVITY_UN_ASSIGN_THIS': 'unassigned this ticket to',
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
"ifsnop/mysqldump-php": "2.*",
|
"ifsnop/mysqldump-php": "2.*",
|
||||||
"ezyang/htmlpurifier": "^4.8",
|
"ezyang/htmlpurifier": "^4.8",
|
||||||
"codeguy/upload": "^1.3",
|
"codeguy/upload": "^1.3",
|
||||||
"php-imap/php-imap": "^3.0"
|
"php-imap/php-imap": "^3.0",
|
||||||
|
"willdurand/email-reply-parser": "^2.8",
|
||||||
|
"ext-fileinfo": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^5.7"
|
"phpunit/phpunit": "^5.7"
|
||||||
|
|
|
@ -31,5 +31,6 @@ $systemControllerGroup->addController(new DisableUserSystemController);
|
||||||
$systemControllerGroup->addController(new EnableUserSystemController);
|
$systemControllerGroup->addController(new EnableUserSystemController);
|
||||||
$systemControllerGroup->addController(new TestSMTPController);
|
$systemControllerGroup->addController(new TestSMTPController);
|
||||||
$systemControllerGroup->addController(new TestIMAPController);
|
$systemControllerGroup->addController(new TestIMAPController);
|
||||||
|
$systemControllerGroup->addController(new EmailPollingController);
|
||||||
|
|
||||||
$systemControllerGroup->finalize();
|
$systemControllerGroup->finalize();
|
||||||
|
|
|
@ -42,6 +42,7 @@ class EditSettingsController extends Controller {
|
||||||
'imap-host',
|
'imap-host',
|
||||||
'imap-user',
|
'imap-user',
|
||||||
'imap-pass',
|
'imap-pass',
|
||||||
|
'imap-token',
|
||||||
'smtp-host',
|
'smtp-host',
|
||||||
'smtp-user',
|
'smtp-user',
|
||||||
'smtp-pass',
|
'smtp-pass',
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
use Respect\Validation\Validator as DataValidator;
|
||||||
|
|
||||||
class EmailPolling extends Controller {
|
class EmailPollingController extends Controller {
|
||||||
const PATH = '/email-polling';
|
const PATH = '/email-polling';
|
||||||
const METHOD = 'POST';
|
const METHOD = 'POST';
|
||||||
|
|
||||||
|
@ -9,7 +10,12 @@ class EmailPolling extends Controller {
|
||||||
public function validations() {
|
public function validations() {
|
||||||
return [
|
return [
|
||||||
'permission' => 'any',
|
'permission' => 'any',
|
||||||
'requestData' => []
|
'requestData' => [
|
||||||
|
'token' => [
|
||||||
|
'validation' => DataValidator::length(1, 200),
|
||||||
|
'error' => ERRORS::INVALID_TOKEN
|
||||||
|
]
|
||||||
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +25,10 @@ class EmailPolling extends Controller {
|
||||||
$defaultLanguage = Setting::getSetting('language')->getValue();
|
$defaultLanguage = Setting::getSetting('language')->getValue();
|
||||||
$defaultDepartmentId = Department::getAll()->first()->id;
|
$defaultDepartmentId = Department::getAll()->first()->id;
|
||||||
|
|
||||||
|
|
||||||
|
if(Controller::request('token') !== Setting::getSetting('imap-token')->getValue())
|
||||||
|
throw new RequestException(ERRORS::INVALID_TOKEN);
|
||||||
|
|
||||||
if(Controller::isUserSystemEnabled())
|
if(Controller::isUserSystemEnabled())
|
||||||
throw new RequestException(ERRORS::USER_SYSTEM);
|
throw new RequestException(ERRORS::USER_SYSTEM);
|
||||||
|
|
||||||
|
@ -26,12 +36,12 @@ class EmailPolling extends Controller {
|
||||||
Setting::getSetting('imap-host')->getValue(),
|
Setting::getSetting('imap-host')->getValue(),
|
||||||
Setting::getSetting('imap-user')->getValue(),
|
Setting::getSetting('imap-user')->getValue(),
|
||||||
Setting::getSetting('imap-pass')->getValue(),
|
Setting::getSetting('imap-pass')->getValue(),
|
||||||
__DIR__
|
'files/'
|
||||||
);
|
);
|
||||||
|
|
||||||
$errors = [];
|
$errors = [];
|
||||||
$emails = $this->getLastEmails();
|
$emails = $this->getLastEmails();
|
||||||
|
/*
|
||||||
$session = Session::getInstance();
|
$session = Session::getInstance();
|
||||||
$oldSession = [
|
$oldSession = [
|
||||||
'userId' => $session->getUserId(),
|
'userId' => $session->getUserId(),
|
||||||
|
@ -61,6 +71,17 @@ class EmailPolling extends Controller {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if($email->getAttachement()) {
|
||||||
|
$attachment = $email->getAttachement();
|
||||||
|
$_FILES['file'] = [
|
||||||
|
'name' => $attachment->name,
|
||||||
|
'type' => mime_content_type($attachment->filePath),
|
||||||
|
'tmp_name' => $attachment->filePath,
|
||||||
|
'error' => UPLOAD_ERR_OK,
|
||||||
|
'size' => filesize($attachment->filePath),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if($email->isReply()) {
|
if($email->isReply()) {
|
||||||
if($email->getTicket()->authorToArray()['email'] === $email->getSender()) {
|
if($email->getTicket()->authorToArray()['email'] === $email->getSender()) {
|
||||||
|
@ -79,6 +100,8 @@ class EmailPolling extends Controller {
|
||||||
'error' => $e->__toString(),
|
'error' => $e->__toString(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unset($_FILES['file']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$session->clearSessionData();
|
$session->clearSessionData();
|
||||||
|
@ -90,7 +113,7 @@ class EmailPolling extends Controller {
|
||||||
Response::respondError(ERRORS::EMAIL_POLLING, null, $errors);
|
Response::respondError(ERRORS::EMAIL_POLLING, null, $errors);
|
||||||
} else {
|
} else {
|
||||||
Response::respondSuccess();
|
Response::respondSuccess();
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastEmails() {
|
public function getLastEmails() {
|
||||||
|
@ -101,12 +124,14 @@ class EmailPolling extends Controller {
|
||||||
foreach($mailsIds as $mailId) {
|
foreach($mailsIds as $mailId) {
|
||||||
$mail = $this->mailbox->getMail($mailId);
|
$mail = $this->mailbox->getMail($mailId);
|
||||||
$mailHeader = $this->mailbox->getMailHeader($mailId);
|
$mailHeader = $this->mailbox->getMailHeader($mailId);
|
||||||
|
$mailAttachment = count($mail->getAttachments()) ? $mail->getAttachments()[0] : null;
|
||||||
|
|
||||||
$emails[] = new Email([
|
$emails[] = new Email([
|
||||||
'fromAddress' => $mailHeader->fromAddress,
|
'fromAddress' => $mailHeader->fromAddress,
|
||||||
'fromName' => $mailHeader->fromName,
|
'fromName' => $mailHeader->fromName,
|
||||||
'subject' => $mailHeader->subject,
|
'subject' => $mailHeader->subject,
|
||||||
'content' => $mail->textPlain,
|
'content' => $mail->textPlain,
|
||||||
'file' => null,
|
'file' => $mailAttachment,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ class GetSettingsController extends Controller {
|
||||||
'smtp-user' => Setting::getSetting('smtp-user')->getValue(),
|
'smtp-user' => Setting::getSetting('smtp-user')->getValue(),
|
||||||
'imap-host' => Setting::getSetting('imap-host')->getValue(),
|
'imap-host' => Setting::getSetting('imap-host')->getValue(),
|
||||||
'imap-user' => Setting::getSetting('imap-user')->getValue(),
|
'imap-user' => Setting::getSetting('imap-user')->getValue(),
|
||||||
|
'imap-token' => Setting::getSetting('imap-token')->getValue(),
|
||||||
'registration' => Setting::getSetting('registration')->getValue(),
|
'registration' => Setting::getSetting('registration')->getValue(),
|
||||||
'departments' => Department::getAllDepartmentNames(),
|
'departments' => Department::getAllDepartmentNames(),
|
||||||
'supportedLanguages' => Language::getSupportedLanguages(),
|
'supportedLanguages' => Language::getSupportedLanguages(),
|
||||||
|
|
|
@ -88,7 +88,8 @@ class InitSettingsController extends Controller {
|
||||||
'ticket-gap' => Hashing::generateRandomPrime(100000, 999999),
|
'ticket-gap' => Hashing::generateRandomPrime(100000, 999999),
|
||||||
'ticket-first-number' => Hashing::generateRandomNumber(100000, 999999),
|
'ticket-first-number' => Hashing::generateRandomNumber(100000, 999999),
|
||||||
'session-prefix' => 'opensupports-'.Hashing::generateRandomToken().'_',
|
'session-prefix' => 'opensupports-'.Hashing::generateRandomToken().'_',
|
||||||
'mail-template-header-image' => 'https://s3.amazonaws.com/opensupports/logo.png'
|
'mail-template-header-image' => 'https://s3.amazonaws.com/opensupports/logo.png',
|
||||||
|
'imap-token' => '',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ class CommentController extends Controller {
|
||||||
public function handler() {
|
public function handler() {
|
||||||
$this->requestData();
|
$this->requestData();
|
||||||
$ticketAuthor = $this->ticket->authorToArray();
|
$ticketAuthor = $this->ticket->authorToArray();
|
||||||
$isAuthor = $this->ticket->isAuthor(Controller::getLoggedUser());
|
$isAuthor = $this->ticket->isAuthor(Controller::getLoggedUser()) || Session::getInstance()->isTicketSession();
|
||||||
$isOwner = $this->ticket->isOwner(Controller::getLoggedUser());
|
$isOwner = $this->ticket->isOwner(Controller::getLoggedUser());
|
||||||
|
|
||||||
if((Controller::isUserSystemEnabled() || Controller::isStaffLogged()) && !$isOwner && !$isAuthor) {
|
if((Controller::isUserSystemEnabled() || Controller::isStaffLogged()) && !$isOwner && !$isAuthor) {
|
||||||
|
@ -89,13 +89,13 @@ class CommentController extends Controller {
|
||||||
$this->storeComment();
|
$this->storeComment();
|
||||||
|
|
||||||
if($isAuthor && $this->ticket->owner) {
|
if($isAuthor && $this->ticket->owner) {
|
||||||
$this->sendMail([
|
$this->sendMail([
|
||||||
'email' => $this->ticket->owner->email,
|
'email' => $this->ticket->owner->email,
|
||||||
'name' => $this->ticket->owner->name,
|
'name' => $this->ticket->owner->name,
|
||||||
'staff' => true
|
'staff' => true
|
||||||
]);
|
]);
|
||||||
} else {
|
} else if($isOwner) {
|
||||||
$this->sendMail($ticketAuthor);
|
$this->sendMail($ticketAuthor);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::createLog('COMMENT', $this->ticket->ticketNumber);
|
Log::createLog('COMMENT', $this->ticket->ticketNumber);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
use EmailReplyParser\Parser\EmailParser;
|
||||||
|
|
||||||
class Email {
|
class Email {
|
||||||
private $sender;
|
private $sender;
|
||||||
|
@ -49,7 +50,9 @@ class Email {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseContent($data) {
|
private function parseContent($data) {
|
||||||
return $data['content'];
|
$emailParser = new EmailParser();
|
||||||
|
|
||||||
|
return $emailParser->parse($data['content'])->getVisibleText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseAttachment($data) {
|
private function parseAttachment($data) {
|
||||||
|
|
|
@ -41,6 +41,10 @@ class Session {
|
||||||
$this->store('token', Hashing::generateRandomToken());
|
$this->store('token', Hashing::generateRandomToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isTicketSession() {
|
||||||
|
return $this->getStoredData('ticketNumber') && $this->getStoredData('token');
|
||||||
|
}
|
||||||
|
|
||||||
public function getTicketNumber() {
|
public function getTicketNumber() {
|
||||||
return $this->getStoredData('ticketNumber');
|
return $this->getStoredData('ticketNumber');
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue