diff --git a/client/src/actions/session-actions.js b/client/src/actions/session-actions.js index 2d1b7995..4a329e90 100644 --- a/client/src/actions/session-actions.js +++ b/client/src/actions/session-actions.js @@ -71,6 +71,13 @@ export default { } }, + verify(value) { + return { + type: 'VERIFY', + payload: value + }; + }, + initSession() { return { type: 'CHECK_SESSION', diff --git a/client/src/app-components/language-selector.js b/client/src/app-components/language-selector.js index cacb0d8e..741ce176 100644 --- a/client/src/app-components/language-selector.js +++ b/client/src/app-components/language-selector.js @@ -82,7 +82,7 @@ class LanguageSelector extends React.Component { case 'supported': return this.props.supportedLanguages; case 'allowed': - return this.props.supportedLanguages; + return this.props.allowedLanguages; case 'custom': return this.props.customList; } diff --git a/client/src/app/App.js b/client/src/app/App.js index 23da32fe..d52ea248 100644 --- a/client/src/app/App.js +++ b/client/src/app/App.js @@ -60,11 +60,11 @@ class App extends React.Component { loggedOutStaff: _.includes(props.location.pathname, '/admin/panel') && !props.session.logged }; - if(props.config['maintenance-mode'] && !_.includes(props.location.pathname, '/admin') && !_.includes(props.location.pathname, '/maintenance')) { + if(props.config['maintenance-mode'] === '1' && !_.includes(props.location.pathname, '/admin') && !_.includes(props.location.pathname, '/maintenance')) { browserHistory.push('/maintenance'); } - if(!props.config['maintenance-mode'] && _.includes(props.location.pathname, '/maintenance')) { + if(props.config['maintenance-mode'] === '0' && _.includes(props.location.pathname, '/maintenance')) { browserHistory.push('/'); } diff --git a/client/src/app/Routes.js b/client/src/app/Routes.js index bd97b39b..2a137332 100644 --- a/client/src/app/Routes.js +++ b/client/src/app/Routes.js @@ -10,6 +10,7 @@ import DemoPage from 'app/demo/components-demo-page'; import MainLayout from 'app/main/main-layout'; import MainHomePage from 'app/main/main-home/main-home-page'; import MainSignUpPage from 'app/main/main-signup/main-signup-page'; +import MainVerifyTokenPage from 'app/main/main-verify-token-page'; import MainRecoverPasswordPage from 'app/main/main-recover-password/main-recover-password-page'; import MainMaintenancePage from 'app/main/main-maintenance-page'; @@ -59,6 +60,7 @@ export default ( + diff --git a/client/src/app/admin/panel/dashboard/activity-list.js b/client/src/app/admin/panel/dashboard/activity-list.js deleted file mode 100644 index 9a125793..00000000 --- a/client/src/app/admin/panel/dashboard/activity-list.js +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; - -import API from 'lib-app/api-call'; -import i18n from 'lib-app/i18n'; - -import ActivityRow from 'app-components/activity-row'; -import SubmitButton from 'core-components/submit-button'; - -class ActivityList extends React.Component { - - static propTypes = { - mode: React.PropTypes.oneOf(['staff', 'system']) - }; - - static childContextTypes = { - loading: React.PropTypes.bool - }; - - getChildContext() { - return { - loading: this.state.loading - }; - } - - state = { - activities: [], - page: 1, - limit: false, - loading: false - }; - - componentDidMount() { - this.retrieveNextPage(); - } - - render() { - if (this.props.mode === 'staff') { - return ( -
- {this.state.activities.map(this.renderRow.bind(this))} - {(!this.state.limit) ? this.renderButton() : null} -
- ); - } - else { - return ( -
- {this.state.activities.map(this.renderRow.bind(this))} - {(!this.state.limit) ? this.renderButton() : null} -
- ); - } - } - - renderButton() { - return ( - - {i18n('LOAD_MORE')} - - ); - } - - renderRow(row) { - return ( - - ); - } - - retrieveNextPage() { - this.setState({loading: true}); - - API.call({ - path: '/staff/last-events', - data: { - page: this.state.page - } - }).then(this.onRetrieveSuccess.bind(this)); - } - - onRetrieveSuccess(result) { - this.setState({ - activities: this.state.activities.concat(result.data), - page: this.state.page + 1, - limit: (result.data.length !== 10), - loading: false - }); - } -} - -export default ActivityList; \ No newline at end of file diff --git a/client/src/app/admin/panel/dashboard/activity-list.scss b/client/src/app/admin/panel/dashboard/activity-list.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/client/src/app/admin/panel/dashboard/admin-panel-activity.js b/client/src/app/admin/panel/dashboard/admin-panel-activity.js index 4b6539e6..251462fb 100644 --- a/client/src/app/admin/panel/dashboard/admin-panel-activity.js +++ b/client/src/app/admin/panel/dashboard/admin-panel-activity.js @@ -86,7 +86,7 @@ class AdminPanelActivity extends React.Component { onMenuItemClick(index) { this.setState({ - page: 0, + page: 1, mode: (index === 0) ? 'staff' : 'system', activities: [] }, this.retrieveNextPage.bind(this)); diff --git a/client/src/app/admin/panel/settings/admin-panel-system-preferences.js b/client/src/app/admin/panel/settings/admin-panel-system-preferences.js index dac728dc..21c7d44b 100644 --- a/client/src/app/admin/panel/settings/admin-panel-system-preferences.js +++ b/client/src/app/admin/panel/settings/admin-panel-system-preferences.js @@ -1,6 +1,8 @@ import React from 'react'; import _ from 'lodash'; +import store from 'app/store'; +import ConfigActions from 'actions/config-actions'; import API from 'lib-app/api-call'; import i18n from 'lib-app/i18n'; import LanguageSelector from 'app-components/language-selector'; @@ -171,7 +173,7 @@ class AdminPanelSystemPreferences extends React.Component { 'smtp-host': form['smtp-host'], 'smtp-port': form['smtp-port'], 'smtp-user': form['smtp-user'], - 'smtp-pass': form['smtp-password'], + 'smtp-pass': form['smtp-pass'], 'maintenance-mode': form['maintenance-mode'], 'allow-attachments': form['allow-attachments'], 'max-size': form['max-size'], @@ -225,6 +227,8 @@ class AdminPanelSystemPreferences extends React.Component { 'supportedLanguages': result.data.supportedLanguages.map(lang => (_.indexOf(languageKeys, lang))) } }); + + store.dispatch(ConfigActions.updateData()); } onRecoverSettingsFail() { diff --git a/client/src/app/admin/panel/users/admin-panel-view-user.js b/client/src/app/admin/panel/users/admin-panel-view-user.js index b18060a2..f0be1ac4 100644 --- a/client/src/app/admin/panel/users/admin-panel-view-user.js +++ b/client/src/app/admin/panel/users/admin-panel-view-user.js @@ -11,12 +11,14 @@ import AreYouSure from 'app-components/are-you-sure'; import Header from 'core-components/header'; import Button from 'core-components/button'; import Message from 'core-components/message'; +import InfoTooltip from 'core-components/info-tooltip'; class AdminPanelViewUser extends React.Component { state = { name: '', email: '', + verified: true, tickets: [], invalid: false, loading: true @@ -64,6 +66,7 @@ class AdminPanelViewUser extends React.Component { {i18n('EMAIL')}
{this.state.email} + {(!this.state.verified) ? this.renderNotVerified() : null}
@@ -79,6 +82,12 @@ class AdminPanelViewUser extends React.Component { ); } + renderNotVerified() { + return ( + + ); + } + getTicketListProps() { return { type: 'secondary', @@ -93,6 +102,7 @@ class AdminPanelViewUser extends React.Component { this.setState({ name: result.data.name, email: result.data.email, + verified: result.data.verified, tickets: result.data.tickets, loading: false }); diff --git a/client/src/app/admin/panel/users/admin-panel-view-user.scss b/client/src/app/admin/panel/users/admin-panel-view-user.scss index 98b3f25a..8d289ba2 100644 --- a/client/src/app/admin/panel/users/admin-panel-view-user.scss +++ b/client/src/app/admin/panel/users/admin-panel-view-user.scss @@ -29,4 +29,8 @@ margin-bottom: 20px; text-align: left; } + + &__unverified { + margin-left: 15px; + } } \ No newline at end of file diff --git a/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js b/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js index 84eb980b..83b012bc 100644 --- a/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js +++ b/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js @@ -33,6 +33,7 @@ class CreateTicketForm extends React.Component { form: { title: '', content: RichTextEditor.createEmptyValue(), + departmentIndex: 0, language: 'en' } }; diff --git a/client/src/app/main/main-home/__tests__/main-home-page-login-widget-test.js b/client/src/app/main/main-home/__tests__/main-home-page-login-widget-test.js index 56089230..ae22410a 100644 --- a/client/src/app/main/main-home/__tests__/main-home-page-login-widget-test.js +++ b/client/src/app/main/main-home/__tests__/main-home-page-login-widget-test.js @@ -91,7 +91,8 @@ describe('Login/Recover Widget', function () { renderComponent({ session: { pending: false, - failed: true + failed: true, + failMessage: 'INVALID_CREDENTIALS' } }); expect(loginForm.props.errors).to.deep.equal({password: 'Invalid password'}); diff --git a/client/src/app/main/main-home/main-home-page-login-widget.js b/client/src/app/main/main-home/main-home-page-login-widget.js index 0e99b8b9..c4da1867 100644 --- a/client/src/app/main/main-home/main-home-page-login-widget.js +++ b/client/src/app/main/main-home/main-home-page-login-widget.js @@ -19,18 +19,14 @@ import Message from 'core-components/message'; class MainHomePageLoginWidget extends React.Component { - constructor(props) { - super(props); - - this.state = { - sideToShow: 'front', - loginFormErrors: {}, - recoverFormErrors: {}, - recoverSent: false, - loadingLogin: false, - loadingRecover: false - }; - } + state = { + sideToShow: 'front', + loginFormErrors: {}, + recoverFormErrors: {}, + recoverSent: false, + loadingLogin: false, + loadingRecover: false + }; componentDidUpdate(prevProps) { if (!prevProps.session.failed && this.props.session.failed) { @@ -126,7 +122,11 @@ class MainHomePageLoginWidget extends React.Component { let errors = _.extend({}, this.state.loginFormErrors); if (this.props.session.failed) { - errors.password = i18n('ERROR_PASSWORD'); + if (this.props.session.failMessage === 'INVALID_CREDENTIALS') { + errors.password = i18n('ERROR_PASSWORD'); + } else if (this.props.session.failMessage === 'UNVERIFIED_USER') { + errors.email = i18n('UNVERIFIED_EMAIL'); + } } return errors; diff --git a/client/src/app/main/main-home/main-home-page.js b/client/src/app/main/main-home/main-home-page.js index c691a717..5b258354 100644 --- a/client/src/app/main/main-home/main-home-page.js +++ b/client/src/app/main/main-home/main-home-page.js @@ -1,13 +1,18 @@ import React from 'react'; +import {connect} from 'react-redux' +import i18n from 'lib-app/i18n'; + import MainHomePageLoginWidget from 'app/main/main-home/main-home-page-login-widget'; import MainHomePagePortal from 'app/main/main-home/main-home-page-portal'; +import Message from 'core-components/message'; class MainHomePage extends React.Component { render() { return (
+ {this.renderMessage()}
@@ -17,6 +22,37 @@ class MainHomePage extends React.Component {
); } + + renderMessage() { + switch (this.props.session.verify) { + case 'success': + return this.renderSuccess(); + case 'failed': + return this.renderFailed(); + default: + return null; + } + } + + renderSuccess() { + return ( + + {i18n('VERIFY_SUCCESS_DESCRIPTION')} + + ); + } + + renderFailed() { + return ( + + {i18n('VERIFY_FAILED_DESCRIPTION')} + + ); + } } -export default MainHomePage; \ No newline at end of file +export default connect((store) => { + return { + session: store.session + }; +})(MainHomePage); \ No newline at end of file diff --git a/client/src/app/main/main-home/main-home-page.scss b/client/src/app/main/main-home/main-home-page.scss index 2e494f51..b21ec3d0 100644 --- a/client/src/app/main/main-home/main-home-page.scss +++ b/client/src/app/main/main-home/main-home-page.scss @@ -1,3 +1,8 @@ .main-home-page { + &__message { + margin-bottom: 20px; + margin-left: 20px; + margin-right: 20px; + } } \ No newline at end of file diff --git a/client/src/app/main/main-layout-header.js b/client/src/app/main/main-layout-header.js index 3744ab66..dd031535 100644 --- a/client/src/app/main/main-layout-header.js +++ b/client/src/app/main/main-layout-header.js @@ -44,6 +44,7 @@ class MainLayoutHeader extends React.Component { return { className: 'main-layout-header__languages', value: this.props.config.language, + type: 'allowed', onChange: this.changeLanguage.bind(this) }; } diff --git a/client/src/app/main/main-verify-token-page.js b/client/src/app/main/main-verify-token-page.js new file mode 100644 index 00000000..8218414d --- /dev/null +++ b/client/src/app/main/main-verify-token-page.js @@ -0,0 +1,38 @@ +import React from 'react'; +import {connect} from 'react-redux' +import {browserHistory} from 'react-router'; + +import SessionActions from 'actions/session-actions' +import API from 'lib-app/api-call'; + +import Message from 'core-components/message'; + +class MainVerifyTokenPage extends React.Component { + + componentDidMount() { + API.call({ + path: '/user/verify', + data: { + token: this.props.params.token, + email: this.props.params.email + } + }).then(() => { + this.props.dispatch(SessionActions.verify(true)); + browserHistory.push('/'); + }).catch(() => { + this.props.dispatch(SessionActions.verify(false)); + browserHistory.push('/'); + }); + } + + render() { + return null; + } +} + + +export default connect((store) => { + return { + session: store.session + }; +})(MainVerifyTokenPage); \ No newline at end of file diff --git a/client/src/core-components/form-field.js b/client/src/core-components/form-field.js index cbb8f091..c913e959 100644 --- a/client/src/core-components/form-field.js +++ b/client/src/core-components/form-field.js @@ -149,7 +149,7 @@ class FormField extends React.Component { onChange(nativeEvent) { let event = nativeEvent; - if (this.props.field === 'checkbox') { + if (this.props.field === 'checkbox' && !this.props.decorator) { event = { target: { value: event.target.checked @@ -157,7 +157,7 @@ class FormField extends React.Component { }; } - if (this.props.field === 'select') { + if (this.props.field === 'select' && !this.props.decorator) { event = { target: { value: event.index diff --git a/client/src/data/fixtures/user-fixtures.js b/client/src/data/fixtures/user-fixtures.js index bf795b56..484d7c95 100644 --- a/client/src/data/fixtures/user-fixtures.js +++ b/client/src/data/fixtures/user-fixtures.js @@ -21,7 +21,7 @@ module.exports = [ } else { response = { status: 'fail', - message: 'Invalid Credientals' + message: 'INVALID_CREDENTIALS' }; } @@ -88,6 +88,16 @@ module.exports = [ } } }, + { + path: '/user/verify-token', + time: 200, + response: function () { + return { + status: 'success', + data: {} + }; + } + }, { path: '/user/signup', time: 1000, @@ -145,6 +155,7 @@ module.exports = [ data: { name: 'Kurt Gödel', email: 'kurt@currycurrylady.hs', + verified: false, tickets: _.times(13).map(() => { return { ticketNumber: '118551', diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index 98b136a9..61e8ec37 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -150,6 +150,8 @@ export default { 'LOAD_MORE': 'Load More', 'MY_NOTIFICATIONS': 'My notifications', 'ALL_NOTIFICATIONS': 'All notifications', + 'VERIFY_SUCCESS': 'User verified', + 'VERIFY_FAILED': 'Could not verify', 'TICKET_ACTIVITY': 'Ticket Activity', //ACTIVITIES @@ -210,6 +212,8 @@ export default { 'MAINTENANCE_MODE_DESCRIPTION': 'The support system is in maintenance mode, thus unavailable at the moment. We will come back as soon as possible.', 'EMAIL_TEMPLATES_DESCRIPTION': 'Here you can edit the templates of the emails that will be sent to users. Remember that the double brackets curly braces indicate a variable value. For example, \'name\' represents the user\'s name.', 'SYSTEM_PREFERENCES_DESCRIPTION': 'Here you can edit the preferences of the system.', + 'VERIFY_SUCCESS_DESCRIPTION': 'You user has been verified correctly. You can log in now.', + 'VERIFY_FAILED_DESCRIPTION': 'The verification could not be done.', //ERRORS 'EMAIL_OR_PASSWORD': 'Email or password invalid', @@ -233,6 +237,8 @@ export default { 'ERROR_RETRIEVING_ARTICLES': 'An error occurred while trying to retrieve articles.', 'ERROR_LIST': 'Select at least one', 'ERROR_URL': 'Invalid URL', + 'UNVERIFIED_EMAIL': 'Email is not verified yet', + 'ERROR_UPDATING_SETTINGS': 'An error occurred while trying to update settings', //MESSAGES 'SIGNUP_SUCCESS': 'You have registered successfully in our support system.', diff --git a/client/src/reducers/session-reducer.js b/client/src/reducers/session-reducer.js index 92251dce..be4323cc 100644 --- a/client/src/reducers/session-reducer.js +++ b/client/src/reducers/session-reducer.js @@ -9,7 +9,8 @@ class SessionReducer extends Reducer { initDone: false, logged: false, pending: false, - failed: false + failed: false, + verify: null }; } @@ -19,6 +20,7 @@ class SessionReducer extends Reducer { 'LOGIN_FULFILLED': this.onLoginCompleted.bind(this), 'LOGIN_REJECTED': this.onLoginFailed, 'LOGOUT_FULFILLED': this.onLogout, + 'VERIFY': this.onVerify, 'USER_DATA_FULFILLED': this.onUserDataRetrieved, 'CHECK_SESSION_REJECTED': (state) => { return _.extend({}, state, {initDone: true})}, 'SESSION_CHECKED': this.onSessionChecked, @@ -46,8 +48,9 @@ class SessionReducer extends Reducer { }); } - onLoginFailed(state) { + onLoginFailed(state, payload) { return _.extend({}, state, { + failMessage: payload.message, logged: false, pending: false, failed: true @@ -127,6 +130,12 @@ class SessionReducer extends Reducer { userTickets: userData.tickets }); } + + onVerify(state, payload) { + return _.extend({}, state, { + verify: (payload) ? 'success' : 'failed' + }); + } } export default SessionReducer.getInstance(); \ No newline at end of file diff --git a/server/controllers/staff/last-events.php b/server/controllers/staff/last-events.php index 6950bf47..c6de992f 100644 --- a/server/controllers/staff/last-events.php +++ b/server/controllers/staff/last-events.php @@ -27,7 +27,7 @@ class LastEventsStaffController extends Controller { $query = substr($query,0,-3); $query .= ') ORDER BY id desc LIMIT ? OFFSET ?' ; - $eventList = Ticketevent::find($query, [10, 10*($page-1)+1]); + $eventList = Ticketevent::find($query, [10, 10*($page-1)]); Response::respondSuccess($eventList->toArray()); } diff --git a/server/controllers/system.php b/server/controllers/system.php index 2cde4fe2..8bc393cb 100644 --- a/server/controllers/system.php +++ b/server/controllers/system.php @@ -9,6 +9,9 @@ require_once 'system/get-logs.php'; require_once 'system/get-mail-templates.php'; require_once 'system/edit-mail-template.php'; require_once 'system/recover-mail-template.php'; +require_once 'system/get-stats.php'; +require_once 'system/disable-registration.php'; +require_once 'system/enable-registration.php'; $systemControllerGroup = new ControllerGroup(); $systemControllerGroup->setGroupPath('/system'); @@ -23,5 +26,8 @@ $systemControllerGroup->addController(new GetLogsController); $systemControllerGroup->addController(new GetMailTemplatesController); $systemControllerGroup->addController(new EditMailTemplateController); $systemControllerGroup->addController(new RecoverMailTemplateController); +$systemControllerGroup->addController(new DisableRegistrationController); +$systemControllerGroup->addController(new EnableRegistrationController); +$systemControllerGroup->addController(new GetStatsController); $systemControllerGroup->finalize(); \ No newline at end of file diff --git a/server/controllers/system/disable-registration.php b/server/controllers/system/disable-registration.php new file mode 100644 index 00000000..4b9ab053 --- /dev/null +++ b/server/controllers/system/disable-registration.php @@ -0,0 +1,28 @@ + 'staff_3', + 'requestData' => [] + ]; + } + + public function handler() { + $password = Controller::request('password'); + + if(!Hashing::verifyPassword($password, Controller::getLoggedUser()->password)) { + Response::respondError(ERRORS::INVALID_PASSWORD); + return; + } + + $registrationRow = Setting::getSetting('registration'); + + $registrationRow->value = false; + $registrationRow->store(); + + Response::respondSuccess(); + } +} \ No newline at end of file diff --git a/server/controllers/system/edit-settings.php b/server/controllers/system/edit-settings.php index c78f1f97..22454e62 100644 --- a/server/controllers/system/edit-settings.php +++ b/server/controllers/system/edit-settings.php @@ -1,5 +1,4 @@ 'staff_3', + 'requestData' => [] + ]; + } + + public function handler() { + $password = Controller::request('password'); + + if(!Hashing::verifyPassword($password,Controller::getLoggedUser()->password)) { + Response::respondError(ERRORS::INVALID_PASSWORD); + return; + } + + $registrationRow = Setting::getSetting('registration'); + + $registrationRow->value = true; + $registrationRow->store(); + + Response::respondSuccess(); + } +} \ No newline at end of file diff --git a/server/controllers/system/get-logs.php b/server/controllers/system/get-logs.php index 90ffd6b9..99217560 100644 --- a/server/controllers/system/get-logs.php +++ b/server/controllers/system/get-logs.php @@ -18,7 +18,7 @@ class GetLogsController extends Controller { public function handler() { $page = Controller::request('page'); - $logList = Log::find('LIMIT ? OFFSET ?', [10, 10*($page-1)+1]); + $logList = Log::find('ORDER BY id desc LIMIT ? OFFSET ?', [10, 10*($page-1)]); Response::respondSuccess($logList->toArray()); } diff --git a/server/controllers/system/get-settings.php b/server/controllers/system/get-settings.php index 566110f3..9cddb302 100644 --- a/server/controllers/system/get-settings.php +++ b/server/controllers/system/get-settings.php @@ -26,8 +26,9 @@ class GetSettingsController extends Controller { 'title' => Setting::getSetting('title')->getValue(), 'no-reply-email' => Setting::getSetting('no-reply-email')->getValue(), 'smtp-port' => Setting::getSetting('smtp-port')->getValue(), - 'smtp-host' => Setting::getSetting('smtp-port')->getValue(), - 'smtp-user' => Setting::getSetting('smtp-port')->getValue(), + 'smtp-host' => Setting::getSetting('smtp-host')->getValue(), + 'smtp-user' => Setting::getSetting('smtp-user')->getValue(), + 'registration' => Setting::getSetting('registration')->getValue(), 'departments' => Department::getDepartmentNames(), 'supportedLanguages' => Language::getSupportedLanguages(), 'allowedLanguages' => Language::getAllowedLanguages() @@ -42,6 +43,7 @@ class GetSettingsController extends Controller { 'allow-attachments' => Setting::getSetting('allow-attachments')->getValue(), 'max-size' => Setting::getSetting('max-size')->getValue(), 'title' => Setting::getSetting('title')->getValue(), + 'registration' => Setting::getSetting('registration')->getValue(), 'departments' => Department::getDepartmentNames(), 'supportedLanguages' => Language::getSupportedLanguages(), 'allowedLanguages' => Language::getAllowedLanguages() diff --git a/server/controllers/system/get-stats.php b/server/controllers/system/get-stats.php new file mode 100644 index 00000000..ff1fdf3a --- /dev/null +++ b/server/controllers/system/get-stats.php @@ -0,0 +1,141 @@ + 'staff_1', + 'requestData' => [ + 'period' => [ + 'validation' => DataValidator::in(['week', 'month', 'quarter', 'year']), + 'error' => ERRORS::INVALID_PERIOD + ] + ] + ]; + } + + public function handler() { + $this->generationNewStats(); + + $staffId = Controller::request('staffId'); + + if($staffId) { + if($staffId !== Controller::getLoggedUser()->id && !Controller::isStaffLogged(3)) { + Response::respondError(ERRORS::NO_PERMISSION); + return; + } + + $this->getStaffStat(); + } else { + $this->getGeneralStat(); + } + } + + public function generationNewStats() { + $lastStatDay = Setting::getSetting('last-stat-day'); + $previousCurrentDate = floor(Date::getPreviousDate() / 10000); + $currentDate = floor(Date::getCurrentDate() / 10000); + + if($lastStatDay->value !== $previousCurrentDate) { + + $begin = new DateTime($lastStatDay->value); + $end = new DateTime($currentDate); + + $interval = new DateInterval('P1D'); + $dateRange = new DatePeriod($begin, $interval ,$end); + + $staffList = Staff::getAll(); + + foreach($dateRange as $date) { + $this->generateGeneralStat('CREATE_TICKET', $date); + $this->generateGeneralStat('CLOSE', $date); + $this->generateGeneralStat('SIGNUP', $date); + $this->generateGeneralStat('COMMENT', $date); + + foreach($staffList as $staff) { + $assignments = Ticketevent::count('type=? AND author_staff_id=? AND date LIKE ?',['ASSIGN',$staff->id, $date->format('Ymd') . '%']); + $closed = Ticketevent::count('type=? AND author_staff_id=? AND date LIKE ?',['CLOSE',$staff->id, $date->format('Ymd') . '%']); + + $statAssign = new Stat(); + $statAssign->setProperties([ + 'date' => $date->format('Ymd'), + 'type' => 'ASSIGN', + 'general' => 0, + 'value' => $assignments, + ]); + + $statClose = new Stat(); + $statClose->setProperties([ + 'date' => $date->format('Ymd'), + 'type' => 'CLOSE', + 'general' => 0, + 'value' => $closed, + ]); + + $staff->ownStatList->add($statAssign); + $staff->ownStatList->add($statClose); + + $staff->store(); + } + } + + $lastStatDay->value = $currentDate; + $lastStatDay->store(); + } + } + + public function generateGeneralStat($type, $date) { + $value = Log::count('type=? AND date LIKE ?',[$type, $date->format('Ymd') . '%']); + $stat = new Stat(); + + $stat->setProperties([ + 'date' => $date->format('Ymd'), + 'type' => $type, + 'general' => 1, + 'value' => $value, + ]); + + $stat->store(); + } + + public function getGeneralStat() { + $daysToRetrieve = $this->getDaysToRetrieve(); + + $statList = Stat::find('general=\'1\' ORDER BY id desc LIMIT ? ', [4 * $daysToRetrieve]); + + Response::respondSuccess($statList->toArray()); + } + + public function getStaffStat() { + $staffId = Controller::request('staffId'); + $daysToRetrieve = $this->getDaysToRetrieve(); + + $statList = Stat::find('general=\'0\' AND staff_id=? ORDER BY id desc LIMIT ? ', [$staffId, 4 * $daysToRetrieve]); + + Response::respondSuccess($statList->toArray()); + } + + public function getDaysToRetrieve() { + $period = Controller::request('period'); + $daysToRetrieve = 0; + + switch ($period) { + case 'week': + $daysToRetrieve = 7; + break; + case 'month': + $daysToRetrieve = 30; + break; + case 'quarter': + $daysToRetrieve = 90; + break; + case 'year': + $daysToRetrieve = 365; + break; + } + + return $daysToRetrieve; + } +} \ No newline at end of file diff --git a/server/controllers/system/init-settings.php b/server/controllers/system/init-settings.php index 98fce211..e31046f4 100644 --- a/server/controllers/system/init-settings.php +++ b/server/controllers/system/init-settings.php @@ -40,7 +40,9 @@ class InitSettingsController extends Controller { 'allow-attachments' => 0, 'max-size' => 0, 'title' => 'Support Center', - 'url' => 'http://www.opensupports.com/support' + 'url' => 'http://www.opensupports.com/support', + 'registration' => true, + 'last-stat-day' => '20170101' //TODO: get current date ]); } diff --git a/server/controllers/user/get-users.php b/server/controllers/user/get-users.php index 2743cdde..eadd6922 100644 --- a/server/controllers/user/get-users.php +++ b/server/controllers/user/get-users.php @@ -28,6 +28,7 @@ class GetUsersController extends Controller { $userListArray[] = [ 'id' => $user->id, 'name' => $user->name, + 'verified' => !$user->verificationToken, 'tickets' => $user->tickets, 'email' => $user->email, 'signupDate' => $user->signupDate diff --git a/server/controllers/user/get.php b/server/controllers/user/get.php index 23aa331e..43f189dd 100644 --- a/server/controllers/user/get.php +++ b/server/controllers/user/get.php @@ -29,6 +29,7 @@ class GetUserController extends Controller { Response::respondSuccess([ 'name' => $user->name, 'email' => $user->email, + 'verified' => !$user->verificationToken, 'tickets' => $parsedTicketList ]); } diff --git a/server/controllers/user/signup.php b/server/controllers/user/signup.php index b6ec4d23..48e6e1c6 100644 --- a/server/controllers/user/signup.php +++ b/server/controllers/user/signup.php @@ -51,6 +51,11 @@ class SignUpController extends Controller { return; } + if (!Setting::getSetting('registration')->value) { + Response::respondError(ERRORS::NO_PERMISSION); + return; + } + $userId = $this->createNewUserAndRetrieveId(); $this->sendRegistrationMail(); diff --git a/server/data/ERRORS.php b/server/data/ERRORS.php index e0052543..4b7507cf 100644 --- a/server/data/ERRORS.php +++ b/server/data/ERRORS.php @@ -35,4 +35,5 @@ class ERRORS { const INVALID_TEMPLATE = 'INVALID_TEMPLATE'; const INVALID_SUBJECT = 'INVALID_SUBJECT'; const INVALID_BODY = 'INVALID_BODY'; + const INVALID_PERIOD = 'INVALID_PERIOD'; } diff --git a/server/data/mail-templates/user-signup-en.html b/server/data/mail-templates/user-signup-en.html index 66a0219a..d5844f64 100644 --- a/server/data/mail-templates/user-signup-en.html +++ b/server/data/mail-templates/user-signup-en.html @@ -1,5 +1,6 @@
Welcome, {{name}} to our support center, your email is {{to}}, - your token is {{verificationToken}} + you can verify your user using this link + http://dev3.opensupports.com/verify-token/{{to}}/{{verificationToken}}
diff --git a/server/data/mail-templates/user-signup-es.html b/server/data/mail-templates/user-signup-es.html index da8e2c84..7590cafd 100644 --- a/server/data/mail-templates/user-signup-es.html +++ b/server/data/mail-templates/user-signup-es.html @@ -1,5 +1,6 @@
Bienvenido, {{name}} a nuestro centro de soporte, tu email es {{to}}, - tu codigo de verificacion es {{verificationToken}} + podes verificar tu usuatio entrando en este link + http://dev3.opensupports.com/verify-token/{{to}}/{{verificationToken}}
\ No newline at end of file diff --git a/server/libs/Date.php b/server/libs/Date.php index c3e8ba0c..6921789f 100644 --- a/server/libs/Date.php +++ b/server/libs/Date.php @@ -3,4 +3,8 @@ class Date { public static function getCurrentDate() { return date('YmdHi'); } + + public static function getPreviousDate() { + return date('YmdHi', strtotime(' -1 day ')); + } } diff --git a/server/libs/validations/validLanguage.php b/server/libs/validations/validLanguage.php index 4578f830..c31058d8 100644 --- a/server/libs/validations/validLanguage.php +++ b/server/libs/validations/validLanguage.php @@ -6,14 +6,7 @@ use Respect\Validation\Rules\AbstractRule; class ValidLanguage extends AbstractRule { - //TODO: Use a list from database instead - private $languages = [ - 'en', - 'es', - 'de' - ]; - public function validate($ticketNumber) { - return in_array($ticketNumber, $this->languages); + return in_array($ticketNumber, \Language::LANGUAGES); } } \ No newline at end of file diff --git a/server/models/Language.php b/server/models/Language.php index c070e5ab..7a44db84 100644 --- a/server/models/Language.php +++ b/server/models/Language.php @@ -8,7 +8,7 @@ class Language extends DataStore { 'es', 'de', 'fr', - 'pr', + 'pt', 'jp', 'ru', 'cn', diff --git a/server/models/Log.php b/server/models/Log.php index ebe24469..966f3b3a 100644 --- a/server/models/Log.php +++ b/server/models/Log.php @@ -9,7 +9,8 @@ class Log extends DataStore { 'type', 'authorUser', 'authorStaff', - 'to' + 'to', + 'date' ]; } @@ -22,7 +23,8 @@ class Log extends DataStore { $log->setProperties(array( 'type' => $type, - 'to' => $to + 'to' => $to, + 'date' => Date::getCurrentDate() )); if($author instanceof User) { @@ -44,7 +46,8 @@ class Log extends DataStore { 'name' => $author->name, 'id' => $author->id, 'staff' => $author instanceof Staff - ] + ], + 'date' => $this->date ]; } } \ No newline at end of file diff --git a/server/models/Staff.php b/server/models/Staff.php index cca0bc4c..13c3d7a8 100644 --- a/server/models/Staff.php +++ b/server/models/Staff.php @@ -18,13 +18,15 @@ class Staff extends DataStore { 'level', 'sharedDepartmentList', 'sharedTicketList', - 'lastLogin' + 'lastLogin', + 'ownStatList' ]; } public function getDefaultProps() { return [ - 'level' => 1 + 'level' => 1, + 'ownStatList' => new DataStoreList() ]; } diff --git a/server/models/Stat.php b/server/models/Stat.php new file mode 100644 index 00000000..97f85649 --- /dev/null +++ b/server/models/Stat.php @@ -0,0 +1,25 @@ + $this->date, + 'type' => $this->type, + 'general' => $this->general, + 'value' => $this->value + ]; + } +} \ No newline at end of file diff --git a/server/models/User.php b/server/models/User.php index 16ceb81a..3207356e 100644 --- a/server/models/User.php +++ b/server/models/User.php @@ -34,7 +34,8 @@ class User extends DataStore { return [ 'email' => $this->email, 'id' => $this->id, - 'name' => $this->name + 'name' => $this->name, + 'verified' => !$this->verificationToken ]; } } diff --git a/tests/init.rb b/tests/init.rb index 3eb51877..cae53d63 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -5,6 +5,7 @@ require 'uri' require 'mysql' require 'json' require 'mechanize' +require 'date' require './libs.rb' require './scripts.rb' @@ -51,3 +52,6 @@ require './staff/last-events.rb' require './system/get-mail-templates.rb' require './system/edit-mail-template.rb' require './system/recover-mail-template.rb' +require './system/disable-registration.rb' +require './system/enable-registration.rb' +require './system/get-stats.rb' diff --git a/tests/libs.rb b/tests/libs.rb index f5496d48..8b832429 100644 --- a/tests/libs.rb +++ b/tests/libs.rb @@ -29,6 +29,10 @@ class Database return queryResponse.fetch_hash end + + def query(query_string) + return @connection.query(query_string); + end end $database = Database.new diff --git a/tests/system/disable-registration.rb b/tests/system/disable-registration.rb new file mode 100644 index 00000000..13e4c06c --- /dev/null +++ b/tests/system/disable-registration.rb @@ -0,0 +1,43 @@ +describe'/system/disable-registration' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + + it 'should not disable registration if password is not correct' do + result= request('/system/disable-registration', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + password: 'hello' + }) + + (result['status']).should.equal('fail') + + row = $database.getRow('setting', 'registration', 'name') + + (row['value']).should.equal('1') + end + + it 'should disable registration' do + result= request('/system/disable-registration', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + password: $staff[:password] + }) + + (result['status']).should.equal('success') + + row = $database.getRow('setting', 'registration', 'name') + + (row['value']).should.equal('0') + end + + it 'should not create user in database if registration is false' do + response = request('/user/signup', { + :name => 'ponzio', + :email => 'jc@ponziolandia.com', + :password => 'tequila' + }) + + (response['status']).should.equal('fail') + + end +end diff --git a/tests/system/edit-settings.rb b/tests/system/edit-settings.rb index e5df7e69..0b2b77aa 100644 --- a/tests/system/edit-settings.rb +++ b/tests/system/edit-settings.rb @@ -6,19 +6,19 @@ describe'system/edit-settings' do result= request('/system/edit-settings', { "csrf_userid" => $csrf_userid, "csrf_token" => $csrf_token, - "maintenance-mode" => 1, + "maintenance-mode" => false, "time-zone" => -3, "layout" => 'full-width', "allow-attachments" => 1, "max-size" => 2, - "language" => 'es', + "language" => 'en', "no-reply-email" => 'testemail@hotmail.com' }) (result['status']).should.equal('success') row = $database.getRow('setting', 'maintenance-mode', 'name') - (row['value']).should.equal('1') + (row['value']).should.equal('0') row = $database.getRow('setting', 'time-zone', 'name') (row['value']).should.equal('-3') @@ -30,7 +30,7 @@ describe'system/edit-settings' do (row['value']).should.equal('2') row = $database.getRow('setting', 'language', 'name') - (row['value']).should.equal('es') + (row['value']).should.equal('en') row = $database.getRow('setting', 'no-reply-email', 'name') (row['value']).should.equal('testemail@hotmail.com') @@ -44,8 +44,8 @@ describe'system/edit-settings' do result= request('/system/edit-settings', { "csrf_userid" => $csrf_userid, "csrf_token" => $csrf_token, - "supportedLanguages" => '["en", "pr", "jp", "ru"]', - "allowedLanguages" => '["en","pr", "jp", "ru", "de"]' + "supportedLanguages" => '["en", "pt", "jp", "ru"]', + "allowedLanguages" => '["en","pt", "jp", "ru", "de"]' }) (result['status']).should.equal('success') @@ -53,7 +53,7 @@ describe'system/edit-settings' do row = $database.getRow('language', 'en', 'code') (row['supported']).should.equal('1') - row = $database.getRow('language', 'pr', 'code') + row = $database.getRow('language', 'pt', 'code') (row['supported']).should.equal('1') row = $database.getRow('language', 'jp', 'code') @@ -65,7 +65,7 @@ describe'system/edit-settings' do row = $database.getRow('language', 'en', 'code') (row['allowed']).should.equal('1') - row = $database.getRow('language', 'pr', 'code') + row = $database.getRow('language', 'pt', 'code') (row['allowed']).should.equal('1') row = $database.getRow('language', 'jp', 'code') diff --git a/tests/system/enable-registration.rb b/tests/system/enable-registration.rb new file mode 100644 index 00000000..6c87768f --- /dev/null +++ b/tests/system/enable-registration.rb @@ -0,0 +1,33 @@ +describe'/system/enable-registration' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + + it 'should not enable registration if password is not correct' do + result= request('/system/enable-registration', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + password: 'hello' + }) + + (result['status']).should.equal('fail') + + row = $database.getRow('setting', 'registration', 'name') + + (row['value']).should.equal('0') + end + + it 'should enable registration' do + result= request('/system/enable-registration', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + password: $staff[:password] + }) + + (result['status']).should.equal('success') + + row = $database.getRow('setting', 'registration', 'name') + + (row['value']).should.equal('1') + end + +end diff --git a/tests/system/get-settings.rb b/tests/system/get-settings.rb index 4a4b6a0a..65a89840 100644 --- a/tests/system/get-settings.rb +++ b/tests/system/get-settings.rb @@ -11,7 +11,7 @@ describe '/system/get-settings' do (result['data']['allowedLanguages'][1]).should.equal('es') (result['data']['allowedLanguages'][2]).should.equal('de') (result['data']['allowedLanguages'][3]).should.equal('fr') - (result['data']['allowedLanguages'][4]).should.equal('pr') + (result['data']['allowedLanguages'][4]).should.equal('pt') (result['data']['allowedLanguages'][5]).should.equal('jp') (result['data']['allowedLanguages'][6]).should.equal('ru') (result['data']['allowedLanguages'][7]).should.equal('cn') diff --git a/tests/system/get-stats.rb b/tests/system/get-stats.rb new file mode 100644 index 00000000..406c4516 --- /dev/null +++ b/tests/system/get-stats.rb @@ -0,0 +1,160 @@ +describe'/system/get-stats' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + + it 'should get stats' do + + d = Date.today.prev_day + yesterday = d.strftime("%Y%m%d%H%M") + d = Date.today.prev_day.prev_day + yesterday2 = d.strftime("%Y%m%d%H%M") + d = Date.today.prev_day.prev_day.prev_day + yesterday3 = d.strftime("%Y%m%d%H%M") + + #day 1 + for i in 0..5 + $database.query("INSERT INTO log VALUES('', 'SIGNUP', NULL, " + yesterday3 + ", NULL, NULL);") + end + for i in 0..0 + $database.query("INSERT INTO log VALUES('', 'CREATE_TICKET', NULL, " + yesterday3 + ", NULL, NULL);") + end + for i in 0..1 + $database.query("INSERT INTO log VALUES('', 'CLOSE', NULL, " + yesterday3 + ", NULL, NULL);") + end + for i in 0..2 + $database.query("INSERT INTO log VALUES('', 'COMMENT', NULL, " + yesterday3 + ", NULL, NULL);") + end + for i in 0..8 + $database.query("INSERT INTO ticketevent VALUES('', 'CLOSE', NULL, " + yesterday3 + ", NULL, NULL, 1);") + end + for i in 0..4 + $database.query("INSERT INTO ticketevent VALUES('', 'ASSIGN', NULL, " + yesterday3 + ", NULL, NULL, 1);") + end + + #day 2 + for i in 0..7 + $database.query("INSERT INTO log VALUES('', 'SIGNUP', NULL, " + yesterday2 + ", NULL, NULL);") + end + for i in 0..2 + $database.query("INSERT INTO log VALUES('', 'CREATE_TICKET', NULL, " + yesterday2 + ", NULL, NULL);") + end + for i in 0..9 + $database.query("INSERT INTO log VALUES('', 'CLOSE', NULL, " + yesterday2 + ", NULL, NULL);") + end + for i in 0..2 + $database.query("INSERT INTO log VALUES('', 'COMMENT', NULL, " + yesterday2 + ", NULL, NULL);") + end + for i in 0..10 + $database.query("INSERT INTO ticketevent VALUES('', 'CLOSE', NULL, " + yesterday2 + ", NULL, NULL, 1);") + end + for i in 0..2 + $database.query("INSERT INTO ticketevent VALUES('', 'ASSIGN', NULL, " + yesterday2 + ", NULL, NULL, 1);") + end + + #day 3 + for i in 0..0 + $database.query("INSERT INTO log VALUES('', 'SIGNUP', NULL, " + yesterday + ", NULL, NULL);") + end + for i in 0..1 + $database.query("INSERT INTO log VALUES('', 'CREATE_TICKET', NULL, " + yesterday + ", NULL, NULL);") + end + for i in 0..4 + $database.query("INSERT INTO log VALUES('', 'CLOSE', NULL, " + yesterday + ", NULL, NULL);") + end + for i in 0..7 + $database.query("INSERT INTO log VALUES('', 'COMMENT', NULL, " + yesterday + ", NULL, NULL);") + end + for i in 0..3 + $database.query("INSERT INTO ticketevent VALUES('', 'CLOSE', NULL, " + yesterday + ", NULL, NULL, 1);") + end + for i in 0..7 + $database.query("INSERT INTO ticketevent VALUES('', 'ASSIGN', NULL, " + yesterday + ", NULL, NULL, 1);") + end + + @result = request('/system/get-stats', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + period: 'week' + }) + + def assertData(position, date, type, value) + (@result['data'][position]['date']).should.equal(date) + (@result['data'][position]['type']).should.equal(type) + (@result['data'][position]['value']).should.equal(value) + end + + d = Date.today.prev_day + yesterday = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day + yesterday2 = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day.prev_day + yesterday3 = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day.prev_day.prev_day + yesterday4 = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day.prev_day.prev_day.prev_day + yesterday5 = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day + yesterday6 = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day + yesterday7 = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day + yesterday8 = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day + yesterday9 = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day + yesterday10 = d.strftime("%Y%m%d") + d = Date.today.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day.prev_day + yesterday11 = d.strftime("%Y%m%d") + + assertData(11, yesterday3, 'CREATE_TICKET', '1') + assertData(10, yesterday3, 'CLOSE', '2') + assertData(9, yesterday3, 'SIGNUP', '6') + assertData(8, yesterday3, 'COMMENT', '3') + + + assertData(7, yesterday2, 'CREATE_TICKET', '3') + assertData(6, yesterday2, 'CLOSE', '10') + assertData(5, yesterday2, 'SIGNUP', '8') + assertData(4, yesterday2, 'COMMENT', '3') + + assertData(3, yesterday, 'CREATE_TICKET', '2') + assertData(2, yesterday, 'CLOSE', '5') + assertData(1, yesterday, 'SIGNUP', '1') + assertData(0, yesterday, 'COMMENT', '8') + + + @result = request('/system/get-stats', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + period: 'week', + staffId: '1' + }) + assertData(0, yesterday, 'CLOSE', '4') + assertData(1, yesterday, 'ASSIGN', '8') + assertData(2, yesterday2, 'CLOSE', '11') + assertData(3, yesterday2, 'ASSIGN', '3') + + assertData(4, yesterday3, 'CLOSE', '9') + assertData(5, yesterday3, 'ASSIGN', '5') + assertData(6, yesterday4, 'CLOSE', '0') + assertData(7, yesterday4, 'ASSIGN', '0') + + assertData(8, yesterday5, 'CLOSE', '0') + assertData(9, yesterday5, 'ASSIGN', '0') + assertData(10, yesterday6, 'CLOSE', '0') + assertData(11, yesterday6, 'ASSIGN', '0') + + assertData(12, yesterday7, 'CLOSE', '0') + assertData(13, yesterday7, 'ASSIGN', '0') + assertData(14, yesterday8, 'CLOSE', '0') + assertData(15, yesterday8, 'ASSIGN', '0') + + assertData(16, yesterday9, 'CLOSE', '0') + assertData(17, yesterday9, 'ASSIGN', '0') + assertData(18, yesterday10, 'CLOSE', '0') + assertData(19, yesterday10, 'ASSIGN', '0') + + assertData(20, yesterday11, 'CLOSE', '0') + assertData(21, yesterday11, 'ASSIGN', '0') + end +end diff --git a/tests/user/get.rb b/tests/user/get.rb index aec21e2b..96030a4d 100644 --- a/tests/user/get.rb +++ b/tests/user/get.rb @@ -8,7 +8,6 @@ describe '/user/get' do content: 'A Lannister always pays his debts.', departmentId: 1, language: 'en', - language: 'en', csrf_userid: $csrf_userid, csrf_token: $csrf_token })