summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author=Corey Hulen <corey@hulen.com>2015-09-10 14:56:37 -0700
committer=Corey Hulen <corey@hulen.com>2015-09-10 14:56:37 -0700
commit8bf35081c80a56051037d0bc374e9fec3fb9529e (patch)
treedf5bad1c0b9448ed02b475125ed618b3872a5078
parente54d0da392119e75788f3d5a431b85e931a7e824 (diff)
downloadchat-8bf35081c80a56051037d0bc374e9fec3fb9529e.tar.gz
chat-8bf35081c80a56051037d0bc374e9fec3fb9529e.tar.bz2
chat-8bf35081c80a56051037d0bc374e9fec3fb9529e.zip
PLT-12 UI framework for admin console
-rw-r--r--api/context.go3
-rw-r--r--api/user.go31
-rw-r--r--model/user_test.go4
-rw-r--r--web/react/components/admin_console/admin_controller.jsx57
-rw-r--r--web/react/components/admin_console/admin_sidebar.jsx164
-rw-r--r--web/react/components/admin_console/email_settings.jsx311
-rw-r--r--web/react/components/admin_console/jobs_settings.jsx183
-rw-r--r--web/react/components/admin_console/select_team_modal.jsx124
-rw-r--r--web/react/pages/admin_console.jsx11
-rw-r--r--web/templates/admin_console.html382
-rw-r--r--web/web.go22
11 files changed, 924 insertions, 368 deletions
diff --git a/api/context.go b/api/context.go
index 2beea2408..1852ed4d6 100644
--- a/api/context.go
+++ b/api/context.go
@@ -285,7 +285,8 @@ func (c *Context) HasPermissionsToChannel(sc store.StoreChannel, where string) b
}
func (c *Context) IsSystemAdmin() bool {
- if model.IsInRole(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) && IsPrivateIpAddress(c.IpAddress) {
+ // TODO XXX FIXME && IsPrivateIpAddress(c.IpAddress)
+ if model.IsInRole(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) {
return true
}
return false
diff --git a/api/user.go b/api/user.go
index 48f974dd5..0698ea2f0 100644
--- a/api/user.go
+++ b/api/user.go
@@ -985,22 +985,25 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) {
func UpdateRoles(c *Context, user *model.User, roles string) *model.User {
// make sure there is at least 1 other active admin
- if model.IsInRole(user.Roles, model.ROLE_ADMIN) && !model.IsInRole(roles, model.ROLE_ADMIN) {
- if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil {
- c.Err = result.Err
- return nil
- } else {
- activeAdmins := -1
- profileUsers := result.Data.(map[string]*model.User)
- for _, profileUser := range profileUsers {
- if profileUser.DeleteAt == 0 && model.IsInRole(profileUser.Roles, model.ROLE_ADMIN) {
- activeAdmins = activeAdmins + 1
- }
- }
- if activeAdmins <= 0 {
- c.Err = model.NewAppError("updateRoles", "There must be at least one active admin", "")
+ if !model.IsInRole(roles, model.ROLE_SYSTEM_ADMIN) {
+ if model.IsInRole(user.Roles, model.ROLE_ADMIN) && !model.IsInRole(roles, model.ROLE_ADMIN) {
+ if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil {
+ c.Err = result.Err
return nil
+ } else {
+ activeAdmins := -1
+ profileUsers := result.Data.(map[string]*model.User)
+ for _, profileUser := range profileUsers {
+ if profileUser.DeleteAt == 0 && model.IsInRole(profileUser.Roles, model.ROLE_ADMIN) {
+ activeAdmins = activeAdmins + 1
+ }
+ }
+
+ if activeAdmins <= 0 {
+ c.Err = model.NewAppError("updateRoles", "There must be at least one active admin", "")
+ return nil
+ }
}
}
}
diff --git a/model/user_test.go b/model/user_test.go
index 190e5826e..d9c1a00b6 100644
--- a/model/user_test.go
+++ b/model/user_test.go
@@ -210,4 +210,8 @@ func TestRoles(t *testing.T) {
if !IsInRole("system_admin junk", "system_admin") {
t.Fatal()
}
+
+ if IsInRole("admin", "system_admin") {
+ t.Fatal()
+ }
}
diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx
new file mode 100644
index 000000000..b069f6432
--- /dev/null
+++ b/web/react/components/admin_console/admin_controller.jsx
@@ -0,0 +1,57 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var AdminSidebar = require('./admin_sidebar.jsx');
+var EmailTab = require('./email_settings.jsx');
+var JobsTab = require('./jobs_settings.jsx');
+var Navbar = require('../../components/navbar.jsx');
+
+export default class AdminController extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.selectTab = this.selectTab.bind(this);
+
+ this.state = {
+ selected: 'email_settings'
+ };
+ }
+
+ selectTab(tab) {
+ this.setState({selected: tab});
+ }
+
+ render() {
+ var tab = '';
+
+ if (this.state.selected === 'email_settings') {
+ tab = <EmailTab />;
+ } else if (this.state.selected === 'job_settings') {
+ tab = <JobsTab />;
+ }
+
+ return (
+ <div>
+ <AdminSidebar
+ selected={this.state.selected}
+ selectTab={this.selectTab}
+ />
+ <div className='inner__wrap channel__wrap'>
+ <div className='row header'>
+ <Navbar teamDisplayName='Admin Console' />
+ </div>
+ <div className='row main'>
+ <div
+ id='app-content'
+ className='app__content admin'
+ >
+ <div className='wrapper--fixed'>
+ {tab}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx
new file mode 100644
index 000000000..64a99b1f4
--- /dev/null
+++ b/web/react/components/admin_console/admin_sidebar.jsx
@@ -0,0 +1,164 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var SidebarHeader = require('../sidebar_header.jsx');
+
+export default class AdminSidebar extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.isSelected = this.isSelected.bind(this);
+ this.handleClick = this.handleClick.bind(this);
+
+ this.state = {
+ };
+ }
+
+ handleClick(name) {
+ this.props.selectTab(name);
+ }
+
+ isSelected(name) {
+ if (this.props.selected === name) {
+ return 'active';
+ }
+
+ return '';
+ }
+
+ render() {
+ return (
+ <div className='sidebar--left sidebar--collapsable'>
+ <div>
+ <SidebarHeader
+ teamDisplayName='Admin Console'
+ teamType='I'
+ />
+ <ul className='nav nav-pills nav-stacked'>
+ <li>
+ <a href='#'
+ className='nav__menu-item active'
+ >
+ <span className='icon fa fa-gear'></span> <span>{'Basic Settings'}</span></a>
+ <ul className='nav nav__sub-menu'>
+ <li>
+ <a
+ href='#'
+ className={this.isSelected('email_settings')}
+ onClick={this.handleClick.bind(null, 'email_settings')}
+ >
+ {'Email Settings'}
+ </a>
+ </li>
+ <li><a href='#'>{'Other Settings'}</a></li>
+ </ul>
+ </li>
+ <li>
+ <a
+ href='#'
+ className='nav__menu-item'
+ >
+ <span className='icon fa fa-gear'></span> <span>{'Jobs'}</span>
+ </a>
+ <ul className='nav nav__sub-menu hide'>
+ <li>
+ <a
+ href='#'
+ className={this.isSelected('job_settings')}
+ onClick={this.handleClick.bind(null, 'job_settings')}
+ >
+ {'Job Settings'}
+ </a>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <a
+ href='#'
+ className='nav__menu-item'
+ >
+ <span className='icon fa fa-gear'></span>
+ <span>{'Team Settings (306)'}</span>
+ <span className='menu-icon--right'>
+ <i className='fa fa-plus'></i>
+ </span>
+ </a>
+ <ul className='nav nav__sub-menu hide'>
+ <li>
+ <a
+ href='#'
+ className='nav__sub-menu-item active'
+ >
+ {'Adal '}
+ <span className='menu-icon--right menu__close'>{'x'}</span>
+ </a>
+ </li>
+ <li>
+ <ul className='nav nav__inner-menu'>
+ <li>
+ <a
+ href='#'
+ className='active'
+ >
+ {'- Users'}
+ </a>
+ </li>
+ <li><a href='#'>{'- View Statistics'}</a></li>
+ <li>
+ <a href='#'>
+ {'- View Audit Log'}
+ <span className='badge pull-right small'>{'1'}</span>
+ </a>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <a
+ href='#'
+ className='nav__sub-menu-item'
+ >
+ {'Boole '}
+ <span className='menu-icon--right menu__close'>{'x'}</span>
+ </a>
+ </li>
+ <li>
+ <ul className='nav nav__inner-menu hide'>
+ <li>
+ <a
+ href='#'
+ className='active'
+ >
+ {'- Users'}
+ </a>
+ </li>
+ <li><a href='#'>{'- View Statistics'}</a></li>
+ <li>
+ <a href='#'>
+ {'- View Audit Log'}
+ <span className='badge pull-right small'>{'1'}</span>
+ </a>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <span
+ data-toggle='modal'
+ data-target='#select-team'
+ className='nav-more'
+ >
+ {'Select a team'}
+ </span>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ </div>
+ );
+ }
+}
+
+AdminSidebar.propTypes = {
+ selected: React.PropTypes.string,
+ selectTab: React.PropTypes.func
+}; \ No newline at end of file
diff --git a/web/react/components/admin_console/email_settings.jsx b/web/react/components/admin_console/email_settings.jsx
new file mode 100644
index 000000000..59475dc94
--- /dev/null
+++ b/web/react/components/admin_console/email_settings.jsx
@@ -0,0 +1,311 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+export default class EmailSettings extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ };
+ }
+
+ render() {
+ return (
+ <div>
+ <h3>{'Email Settings'}</h3>
+ <form
+ className='form-horizontal'
+ role='form'
+ >
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='email'
+ >
+ {'Bypass Email: '}
+ <a
+ href='#'
+ data-trigger='hover click'
+ data-toggle='popover'
+ data-position='bottom'
+ data-content={'Here\'s some more help text inside a popover for the Bypass Email field just to show how popovers look.'}
+ >
+ {'(?)'}
+ </a>
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='byPassEmail'
+ value='option1'
+ />
+ {'True'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='byPassEmail'
+ value='option2'
+ />
+ {'False'}
+ </label>
+ <p className='help-text'>{'This is some sample help text for the Bypass Email field'}</p>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='smtpUsername'
+ >
+ {'SMTP Username:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='email'
+ className='form-control'
+ id='smtpUsername'
+ placeholder='Enter your SMTP username'
+ value=''
+ />
+ <div className='help-text'>
+ <div className='alert alert-warning'><i className='fa fa-warning'></i>{' This is some error text for the Bypass Email field'}</div>
+ </div>
+ <p className='help-text'>{'This is some sample help text for the SMTP username field'}</p>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='smtpPassword'
+ >
+ {'SMTP Password:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='password'
+ className='form-control'
+ id='smtpPassword'
+ placeholder='Enter your SMTP password'
+ value=''
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='smtpServer'
+ >
+ {'SMTP Server:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='smtpServer'
+ placeholder='Enter your SMTP server'
+ value=''
+ />
+ <div className='help-text'>
+ <a
+ href='#'
+ className='help-link'
+ >
+ {'Test Connection'}
+ </a>
+ <div className='alert alert-success'><i className='fa fa-check'></i>{' Connection successful'}</div>
+ <div className='alert alert-warning hide'><i className='fa fa-warning'></i>{' Connection unsuccessful'}</div>
+ </div>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label className='control-label col-sm-4'>{'Use TLS:'}</label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='tls'
+ value='option1'
+ />
+ {'True'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='tls'
+ value='option2'
+ />
+ {'False'}
+ </label>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label className='control-label col-sm-4'>{'Use Start TLS:'}</label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='starttls'
+ value='option1'
+ />
+ {'True'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='starttls'
+ value='option2'
+ />
+ {'False'}
+ </label>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='feedbackEmail'
+ >
+ {'Feedback Email:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='feedbackEmail'
+ placeholder='Enter your feedback email'
+ value=''
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='feedbackUsername'
+ >
+ {'Feedback Username:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='feedbackUsername'
+ placeholder='Enter your feedback username'
+ value=''
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <div className='col-sm-offset-4 col-sm-8'>
+ <div className='checkbox'>
+ <label><input type='checkbox' />{'Remember me'}</label>
+ </div>
+ </div>
+ </div>
+
+ <div
+ className='panel-group'
+ id='accordion'
+ role='tablist'
+ aria-multiselectable='true'
+ >
+ <div className='panel panel-default'>
+ <div
+ className='panel-heading'
+ role='tab'
+ id='headingOne'
+ >
+ <h3 className='panel-title'>
+ <a
+ className='collapsed'
+ role='button'
+ data-toggle='collapse'
+ data-parent='#accordion'
+ href='#collapseOne'
+ aria-expanded='true'
+ aria-controls='collapseOne'
+ >
+ {'Advanced Settings '}
+ <i className='fa fa-plus'></i>
+ <i className='fa fa-minus'></i>
+ </a>
+ </h3>
+ </div>
+ <div
+ id='collapseOne'
+ className='panel-collapse collapse'
+ role='tabpanel'
+ aria-labelledby='headingOne'
+ >
+ <div className='panel-body'>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='feedbackUsername'
+ >
+ {'Apple push server:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='feedbackUsername'
+ placeholder='Enter your Apple push server'
+ value=''
+ />
+ <p className='help-text'>{'This is some sample help text for the Apple push server field'}</p>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='feedbackUsername'
+ >
+ {'Apple push certificate public:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='feedbackUsername'
+ placeholder='Enter your public apple push certificate'
+ value=''
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='feedbackUsername'
+ >
+ {'Apple push certificate private:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='feedbackUsername'
+ placeholder='Enter your private apple push certificate'
+ value=''
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <div className='col-sm-12'>
+ <button
+ type='submit'
+ className='btn btn-primary'
+ >
+ {'Submit'}
+ </button>
+ </div>
+ </div>
+ </form>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/web/react/components/admin_console/jobs_settings.jsx b/web/react/components/admin_console/jobs_settings.jsx
new file mode 100644
index 000000000..c36d20df7
--- /dev/null
+++ b/web/react/components/admin_console/jobs_settings.jsx
@@ -0,0 +1,183 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+export default class Jobs extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ };
+ }
+
+ render() {
+ return (
+ <div>
+ <h3>{' ************** JOB Settings'}</h3>
+ <form
+ className='form-horizontal'
+ role='form'
+ >
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='email'
+ >
+ {'Bypass Email: '}
+ <a
+ href='#'
+ data-trigger='hover click'
+ data-toggle='popover'
+ data-position='bottom'
+ data-content={'Here\'s some more help text inside a popover for the Bypass Email field just to show how popovers look.'}
+ >
+ {'(?)'}
+ </a>
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='byPassEmail'
+ value='option1'
+ />
+ {'True'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='byPassEmail'
+ value='option2'
+ />
+ {'False'}
+ </label>
+ <p className='help-text'>{'This is some sample help text for the Bypass Email field'}</p>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='smtpUsername'
+ >
+ {'SMTP Username:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='email'
+ className='form-control'
+ id='smtpUsername'
+ placeholder='Enter your SMTP username'
+ value=''
+ />
+ <div className='help-text'>
+ <div className='alert alert-warning'><i className='fa fa-warning'></i>{' This is some error text for the Bypass Email field'}</div>
+ </div>
+ <p className='help-text'>{'This is some sample help text for the SMTP username field'}</p>
+ </div>
+ </div>
+ <div
+ className='panel-group'
+ id='accordion'
+ role='tablist'
+ aria-multiselectable='true'
+ >
+ <div className='panel panel-default'>
+ <div
+ className='panel-heading'
+ role='tab'
+ id='headingOne'
+ >
+ <h3 className='panel-title'>
+ <a
+ className='collapsed'
+ role='button'
+ data-toggle='collapse'
+ data-parent='#accordion'
+ href='#collapseOne'
+ aria-expanded='true'
+ aria-controls='collapseOne'
+ >
+ {'Advanced Settings '}
+ <i className='fa fa-plus'></i>
+ <i className='fa fa-minus'></i>
+ </a>
+ </h3>
+ </div>
+ <div
+ id='collapseOne'
+ className='panel-collapse collapse'
+ role='tabpanel'
+ aria-labelledby='headingOne'
+ >
+ <div className='panel-body'>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='feedbackUsername'
+ >
+ {'Apple push server:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='feedbackUsername'
+ placeholder='Enter your Apple push server'
+ value=''
+ />
+ <p className='help-text'>{'This is some sample help text for the Apple push server field'}</p>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='feedbackUsername'
+ >
+ {'Apple push certificate public:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='feedbackUsername'
+ placeholder='Enter your public apple push certificate'
+ value=''
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='feedbackUsername'
+ >
+ {'Apple push certificate private:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='feedbackUsername'
+ placeholder='Enter your private apple push certificate'
+ value=''
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <div className='col-sm-12'>
+ <button
+ type='submit'
+ className='btn btn-primary'
+ >
+ {'Submit'}
+ </button>
+ </div>
+ </div>
+ </form>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/web/react/components/admin_console/select_team_modal.jsx b/web/react/components/admin_console/select_team_modal.jsx
new file mode 100644
index 000000000..fa30de7b2
--- /dev/null
+++ b/web/react/components/admin_console/select_team_modal.jsx
@@ -0,0 +1,124 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+export default class SelectTeam extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ };
+ }
+
+ render() {
+ return (
+ <div className='modal fade'
+ id='select-team'
+ tabIndex='-1'
+ role='dialog'
+ aria-labelledby='teamsModalLabel'
+ >
+ <div className='modal-dialog'
+ role='document'
+ >
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <button
+ type='button'
+ className='close'
+ data-dismiss='modal'
+ aria-label='Close'
+ >
+ <span aria-hidden='true'>&times;</span>
+ </button>
+ <h4
+ className='modal-title'
+ id='teamsModalLabel'
+ >
+ {'Select a team'}
+ </h4>
+ </div>
+ <div className='modal-body'>
+ <table className='more-channel-table table'>
+ <tbody>
+ <tr>
+ <td>
+ <p className='more-channel-name'>{'Descartes'}</p>
+ </td>
+ <td className='td--action'>
+ <button className='btn btn-primary'>{'Join'}</button>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p className='more-channel-name'>{'Grouping'}</p>
+ </td>
+ <td className='td--action'>
+ <button className='btn btn-primary'>{'Join'}</button>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p className='more-channel-name'>{'Adventure'}</p>
+ </td>
+ <td className='td--action'>
+ <button className='btn btn-primary'>{'Join'}</button>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p className='more-channel-name'>{'Crossroads'}</p>
+ </td>
+ <td className='td--action'>
+ <button className='btn btn-primary'>{'Join'}</button>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p className='more-channel-name'>{'Sky scraping'}</p>
+ </td>
+ <td className='td--action'>
+ <button className='btn btn-primary'>{'Join'}</button>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p className='more-channel-name'>{'Outdoors'}</p>
+ </td>
+ <td className='td--action'>
+ <button className='btn btn-primary'>{'Join'}</button>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p className='more-channel-name'>{'Microsoft'}</p>
+ </td>
+ <td className='td--action'>
+ <button className='btn btn-primary'>{'Join'}</button>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p className='more-channel-name'>{'Apple'}</p>
+ </td>
+ <td className='td--action'>
+ <button className='btn btn-primary'>{'Join'}</button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div className='modal-footer'>
+ <button
+ type='button'
+ className='btn btn-default'
+ data-dismiss='modal'
+ >
+ {'Close'}
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/web/react/pages/admin_console.jsx b/web/react/pages/admin_console.jsx
index 0e14d3ff8..7bffeab08 100644
--- a/web/react/pages/admin_console.jsx
+++ b/web/react/pages/admin_console.jsx
@@ -1,8 +1,19 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
+var SelectTeamModal = require('../components/admin_console/select_team_modal.jsx');
+var AdminController = require('../components/admin_console/admin_controller.jsx');
+
export function setupAdminConsolePage() {
+ React.render(
+ <AdminController />,
+ document.getElementById('admin_controller')
+ );
+ React.render(
+ <SelectTeamModal />,
+ document.getElementById('select_team_modal')
+ );
}
global.window.setup_admin_console_page = setupAdminConsolePage;
diff --git a/web/templates/admin_console.html b/web/templates/admin_console.html
index 9ee5b74ef..d7fd9217e 100644
--- a/web/templates/admin_console.html
+++ b/web/templates/admin_console.html
@@ -4,358 +4,52 @@
<html>
{{template "head" . }}
<body>
- <div id="error_bar"></div>
-
- <div class="container-fluid">
- <div class="modal fade" id="teamsModal" tabindex="-1" role="dialog" aria-labelledby="teamsModalLabel">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- <h4 class="modal-title" id="teamsModalLabel">Select a team</h4>
- </div>
- <div class="modal-body">
- <table class="more-channel-table table">
- <tbody>
- <tr>
- <td>
- <p class="more-channel-name">Descartes</p>
- </td>
- <td class="td--action">
- <button class="btn btn-primary">Join</button>
- </td>
- </tr>
- <tr>
- <td>
- <p class="more-channel-name">Grouping</p>
- </td>
- <td class="td--action">
- <button class="btn btn-primary">Join</button>
- </td>
- </tr>
- <tr>
- <td>
- <p class="more-channel-name">Adventure</p>
- </td>
- <td class="td--action">
- <button class="btn btn-primary">Join</button>
- </td>
- </tr>
- <tr>
- <td>
- <p class="more-channel-name">Crossroads</p>
- </td>
- <td class="td--action">
- <button class="btn btn-primary">Join</button>
- </td>
- </tr>
- <tr>
- <td>
- <p class="more-channel-name">Sky scraping</p>
- </td>
- <td class="td--action">
- <button class="btn btn-primary">Join</button>
- </td>
- </tr>
- <tr>
- <td>
- <p class="more-channel-name">Outdoors</p>
- </td>
- <td class="td--action">
- <button class="btn btn-primary">Join</button>
- </td>
- </tr>
- <tr>
- <td>
- <p class="more-channel-name">Microsoft</p>
- </td>
- <td class="td--action">
- <button class="btn btn-primary">Join</button>
- </td>
- </tr>
- <tr>
- <td>
- <p class="more-channel-name">Apple</p>
- </td>
- <td class="td--action">
- <button class="btn btn-primary">Join</button>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
- </div>
- </div>
- </div>
- </div>
-
- <div class="sidebar--menu" id="sidebar-menu"></div>
- <div class="sidebar--left sidebar--collapsable" id="sidebar-left">
- <div>
- <div class='team__header theme'>
- <a href='#'>
- <div class='header__info'>
- <div class='user__name'>@asaad</div>
- <div class='team__name'>Mattermost</div>
- </div>
- </a>
- <ul class="nav navbar-nav navbar-right">
- <li class="dropdown">
- <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false" data-reactid=".4.0.1.0.0"><span class="dropdown__icon" data-reactid=".4.0.1.0.0.0"><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="4px" height="16px" viewBox="0 0 8 32" enable-background="new 0 0 8 32" xml:space="preserve"> <g> <circle cx="4" cy="4.062" r="4"></circle> <circle cx="4" cy="16" r="4"></circle> <circle cx="4" cy="28" r="4"></circle> </g> </svg></span></a>
- </li>
- </ul>
- </div>
- <ul class="nav nav-pills nav-stacked">
- <li>
- <a href="#" class="nav__menu-item active"><span class="icon fa fa-gear"></span> <span>Basic Settings</span></a>
- <ul class="nav nav__sub-menu">
- <li><a href="#" class="active">Email Settings <span class='badge pull-right small'>1</span></a></li>
- <li><a href="#">Sub option 2</a></li>
- <li><a href="#">Sub option 3</a></li>
- <li><a href="#">Sub option 4</a></li>
- <li><a href="#">Sub option 5</a></li>
- </ul>
- </li>
- <li>
- <a href="#" class="nav__menu-item"><span class="icon fa fa-gear"></span> <span>App Center</span></a>
- <ul class="nav nav__sub-menu hide">
- <li><a href="#" class="active">Sub option 1</a></li>
- <li><a href="#">Sub option 2</a></li>
- <li><a href="#">Sub option 3</a></li>
- <li><a href="#">Sub option 4</a></li>
- <li><a href="#">Sub option 5</a></li>
- </ul>
- </li>
- <li>
- <a href="#" class="nav__menu-item"><span class="icon fa fa-gear"></span> <span>Team Settings (306)</span> <span class="menu-icon--right"><i class="fa fa-plus"></i></span></a>
- <ul class="nav nav__sub-menu hide">
- <li><a href="#" class="nav__sub-menu-item active">Adal <span class="menu-icon--right menu__close">x</span></a></li>
- <li>
- <ul class="nav nav__inner-menu">
- <li><a href="#" class="active">- Users</a></li>
- <li><a href="#">- View Statistics</a></li>
- <li><a href="#">- View Audit Log</a></li>
- </ul>
- </li>
- <li><a href="#" class="nav__sub-menu-item">Boole <span class="menu-icon--right menu__close">x</span></a></li>
- <li>
- <ul class="nav nav__inner-menu hide">
- <li><a href="#" class="active">- Users</a></li>
- <li><a href="#">- View Statistics</a></li>
- <li><a href="#">- View Audit Log</a></li>
- </ul>
- </li>
- <li><span data-toggle="modal" data-target="#teamsModal" class="nav-more">Select a team</span></li>
- </ul>
- </li>
- </ul>
- </div>
- </div>
-
- <div class="inner__wrap channel__wrap">
- <div class="row header">
- <div id="navbar">
- <div class="navbar navbar-default navbar-fixed-top">
- <div class="container-fluid theme">
- <div class="navbar-header">
- <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#sidebar-nav" data-reactid=".2.0.0.1"><span class="sr-only" data-reactid=".2.0.0.1.0">Toggle sidebar</span><span class="icon-bar" data-reactid=".2.0.0.1.1"></span><span class="icon-bar" data-reactid=".2.0.0.1.2"></span><span class="icon-bar" data-reactid=".2.0.0.1.3"></span></button>
- <button type="button" class="navbar-toggle menu-toggle pull-right" data-toggle="collapse" data-target="#sidebar-nav" data-reactid=".2.0.0.2"><span data-reactid=".2.0.0.2.0"><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="4px" height="16px" viewBox="0 0 8 32" enable-background="new 0 0 8 32" xml:space="preserve"> <g> <circle cx="4" cy="4.062" r="4"></circle> <circle cx="4" cy="16" r="4"></circle> <circle cx="4" cy="28" r="4"></circle> </g> </svg></span></button>
- <div class="navbar-brand" data-reactid=".2.0.0.3">
- <div class="dropdown" data-reactid=".2.0.0.3.0">
- <a href="#" class="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
- <span class="heading">Email Settings</span>
- </a>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="row main">
- <div id="app-content" class="app__content admin">
- <div class="wrapper--fixed">
-
- <div class="banner">
- <div class="banner__content">
- <h4 class="banner__heading">Banner Heading or whatever</h4>
- <p>Quisque quis lorem id orci consequat euismod. Etiam sagittis erat ut orci fermentum lobortis. Etiam commodo, quam non tristique volutpat, leo dolor tempor nisl, ut placerat neque justo in dui.</p>
- </div>
- </div>
- <h3>Email Settings</h3>
- <form class="form-horizontal" role="form">
- <div class="form-group">
- <label class="control-label col-sm-4" for="email">Bypass Email: <a href="#" data-trigger="hover click" data-toggle="popover" data-position="bottom" data-content="Here's some more help text inside a popover for the Bypass Email field just to show how popovers look.">(?)</a></label>
- <div class="col-sm-8">
- <label class="radio-inline">
- <input type="radio" name="byPassEmail" value="option1"> True
- </label>
- <label class="radio-inline">
- <input type="radio" name="byPassEmail" value="option2"> False
- </label>
- <p class="help-text">This is some sample help text for the Bypass Email field</p>
- </div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-4" for="smtpUsername">SMTP Username:</label>
- <div class="col-sm-8">
- <input type="email" class="form-control" id="smtpUsername" placeholder="Enter your SMTP username" value="">
- <div class="help-text">
- <div class="alert alert-warning"><i class="fa fa-warning"></i> This is some error text for the Bypass Email field</div>
- </div>
- <p class="help-text">This is some sample help text for the SMTP username field</p>
- </div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-4" for="smtpPassword">SMTP Password:</label>
- <div class="col-sm-8">
- <input type="password" class="form-control" id="smtpPassword" placeholder="Enter your SMTP password" value="">
- </div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-4" for="smtpServer">SMTP Server:</label>
- <div class="col-sm-8">
- <input type="text" class="form-control" id="smtpServer" placeholder="Enter your SMTP server" value="">
- <div class="help-text">
- <a href="#" class="help-link">Test Connection</a>
- <div class="alert alert-success"><i class="fa fa-check"></i> Connection successful</div>
- <div class="alert alert-warning hide"><i class="fa fa-warning"></i> Connection unsuccessful</div>
- </div>
- </div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-4">Use TLS:</label>
- <div class="col-sm-8">
- <label class="radio-inline">
- <input type="radio" name="tls" value="option1"> True
- </label>
- <label class="radio-inline">
- <input type="radio" name="tls" value="option2"> False
- </label>
- </div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-4">Use Start TLS:</label>
- <div class="col-sm-8">
- <label class="radio-inline">
- <input type="radio" name="starttls" value="option1"> True
- </label>
- <label class="radio-inline">
- <input type="radio" name="starttls" value="option2"> False
- </label>
- </div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-4" for="feedbackEmail">Feedback Email:</label>
- <div class="col-sm-8">
- <input type="text" class="form-control" id="feedbackEmail" placeholder="Enter your feedback email" value="">
- </div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-4" for="feedbackUsername">Feedback Username:</label>
- <div class="col-sm-8">
- <input type="text" class="form-control" id="feedbackUsername" placeholder="Enter your feedback username" value="">
- </div>
- </div>
- <div class="form-group">
- <div class="col-sm-offset-4 col-sm-8">
- <div class="checkbox">
- <label><input type="checkbox"> Remember me</label>
- </div>
- </div>
- </div>
-
- <!-- Custom Collapsable -->
- <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
- <div class="panel panel-default">
- <div class="panel-heading" role="tab" id="headingOne">
- <h3 class="panel-title">
- <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
- Advanced Settings <i class="fa fa-plus"></i><i class="fa fa-minus"></i>
- </a>
- </h3>
- </div>
- <div id="collapseOne" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
- <div class="panel-body">
- <div class="form-group">
- <label class="control-label col-sm-4" for="feedbackUsername">Apple push server:</label>
- <div class="col-sm-8">
- <input type="text" class="form-control" id="feedbackUsername" placeholder="Enter your Apple push server" value="">
- <p class="help-text">This is some sample help text for the Apple push server field</p>
- </div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-4" for="feedbackUsername">Apple push certificate public:</label>
- <div class="col-sm-8">
- <input type="text" class="form-control" id="feedbackUsername" placeholder="Enter your public apple push certificate" value="">
- </div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-4" for="feedbackUsername">Apple push certificate private:</label>
- <div class="col-sm-8">
- <input type="text" class="form-control" id="feedbackUsername" placeholder="Enter your private apple push certificate" value="">
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <div class="form-group">
- <div class="col-sm-12">
- <button type="submit" class="btn btn-primary">Submit</button>
- </div>
- </div>
-
- </form>
- </div>
- </div>
- </div>
- </div>
- </div>
+<div class='container-fluid'>
+ <div id="error_bar"></div>
+ <div id="admin_controller" ></div>
+ <div id="select_team_modal"></div>
+</div>
<script>
window.setup_admin_console_page();
$(document).ready(function(){
- $('.nav__menu-item').on('click', function(e){
- e.preventDefault();
- $(this).closest('.sidebar--collapsable').find('.nav__menu-item').removeClass('active');
- $(this).addClass('active');
- $(this).closest('.sidebar--collapsable').find('.nav__sub-menu').addClass('hide');
- $(this).next('.nav__sub-menu').removeClass('hide');
- });
-
- $('.nav__sub-menu a').on('click', function(e){
- e.preventDefault();
- $(this).closest('.nav__sub-menu').find('a').removeClass('active');
- $(this).addClass('active');
- });
-
- $('.nav__sub-menu-item').on('click', function(e){
- e.preventDefault();
- $(this).closest('.sidebar--collapsable').find('.nav__inner-menu').addClass('hide');
- $(this).closest('li').next('li').find('.nav__inner-menu').removeClass('hide');
- $(this).closest('li').next('li').find('.nav__inner-menu li:first a').addClass('active');
- });
+ $('.nav__menu-item').on('click', function(e){
+ e.preventDefault();
+ $(this).closest('.sidebar--collapsable').find('.nav__menu-item').removeClass('active');
+ $(this).addClass('active');
+ $(this).closest('.sidebar--collapsable').find('.nav__sub-menu').addClass('hide');
+ $(this).next('.nav__sub-menu').removeClass('hide');
+ });
+
+ $('.nav__sub-menu a').on('click', function(e){
+ e.preventDefault();
+ $(this).closest('.nav__sub-menu').find('a').removeClass('active');
+ $(this).addClass('active');
+ });
+
+ $('.nav__sub-menu-item').on('click', function(e){
+ e.preventDefault();
+ $(this).closest('.sidebar--collapsable').find('.nav__inner-menu').addClass('hide');
+ $(this).closest('li').next('li').find('.nav__inner-menu').removeClass('hide');
+ $(this).closest('li').next('li').find('.nav__inner-menu li:first a').addClass('active');
+ });
+
+ $('.nav__inner-menu a').on('click', function(e){
+ $(this).closest('.nav__inner-menu').closest('li').prev('li').find('a').addClass('active');
+ });
+
+ $('.nav__sub-menu .menu__close').on('click', function(e){
+ var menuItem = $(this).closest('li');
+ menuItem.next('li').remove();
+ menuItem.remove();
+ });
+
+ $('[data-toggle="tooltip"]').tooltip();
+ $('[data-toggle="popover"]').popover()
- $('.nav__inner-menu a').on('click', function(e){
- $(this).closest('.nav__inner-menu').closest('li').prev('li').find('a').addClass('active');
});
-
- $('.nav__sub-menu .menu__close').on('click', function(e){
- var menuItem = $(this).closest('li');
- menuItem.next('li').remove();
- menuItem.remove();
- });
-
- $('[data-toggle="tooltip"]').tooltip();
- $('[data-toggle="popover"]').popover()
-
- });
</script>
</body>
</html>
diff --git a/web/web.go b/web/web.go
index 7996a49be..44c9610a6 100644
--- a/web/web.go
+++ b/web/web.go
@@ -52,7 +52,7 @@ func InitWeb() {
mainrouter.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
mainrouter.Handle("/", api.AppHandlerIndependent(root)).Methods("GET")
-
+
mainrouter.Handle("/signup_team_complete/", api.AppHandlerIndependent(signupTeamComplete)).Methods("GET")
mainrouter.Handle("/signup_user_complete/", api.AppHandlerIndependent(signupUserComplete)).Methods("GET")
mainrouter.Handle("/signup_team_confirm/", api.AppHandlerIndependent(signupTeamConfirm)).Methods("GET")
@@ -62,8 +62,7 @@ func InitWeb() {
mainrouter.Handle("/login/{service:[A-Za-z]+}/complete", api.AppHandlerIndependent(loginCompleteOAuth)).Methods("GET")
mainrouter.Handle("/signup/{service:[A-Za-z]+}/complete", api.AppHandlerIndependent(signupCompleteOAuth)).Methods("GET")
- mainrouter.Handle("/admin", api.AppHandlerIndependent(adminConsole)).Methods("GET")
-
+ mainrouter.Handle("/admin_console", api.UserRequired(adminConsole)).Methods("GET")
// ----------------------------------------------------------------------------------------------
// *ANYTHING* team spefic should go below this line
@@ -74,11 +73,9 @@ func InitWeb() {
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET")
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET")
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET")
- mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
+ mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
mainrouter.Handle("/{team}/channels/{channelname}", api.UserRequired(getChannel)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
- mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
-
-
+ mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
watchAndParseTemplates()
}
@@ -644,6 +641,13 @@ func loginCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request)
}
func adminConsole(c *api.Context, w http.ResponseWriter, r *http.Request) {
- page := NewHtmlTemplatePage("admin_console", "Admin Console")
- page.Render(c, w)
+
+ if !c.IsSystemAdmin() {
+ c.Err = model.NewAppError("adminConsole", "You do not have permission to access the admin console.", "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ } else {
+ page := NewHtmlTemplatePage("admin_console", "Admin Console")
+ page.Render(c, w)
+ }
}