Admin: project and user management hide/show based on role
[osm/UI.git] / skyquake / framework / widgets / skyquake_nav / skyquakeNav.jsx
1 /*
2  *
3  *   Copyright 2016 RIFT.IO Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  *
17  */
18
19 import React from 'react';
20 import { Link } from 'react-router';
21 import Utils from 'utils/utils.js';
22 import Crouton from 'react-crouton';
23 import 'style/common.scss';
24
25 import './skyquakeNav.scss';
26 import SelectOption from '../form_controls/selectOption.jsx';
27 import {FormSection} from '../form_controls/formControls.jsx';
28 import {isRBACValid, SkyquakeRBAC} from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
29
30 //Temporary, until api server is on same port as webserver
31 var rw = require('utils/rw.js');
32 var API_SERVER = rw.getSearchParams(window.location).api_server;
33 var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
34
35 //
36 // Internal classes/functions
37 //
38
39 class LogoutAppMenuItem extends React.Component {
40     handleLogout() {
41         Utils.clearAuthentication();
42     }
43     render() {
44         return (
45             <div className="app">
46                 <h2>
47                     <a onClick={this.handleLogout}>
48                         Logout
49                     </a>
50                 </h2>
51             </div>
52         );
53     }
54 }
55
56 class SelectProject extends React.Component {
57     constructor(props) {
58         super(props);
59     }
60     selectProject(e) {
61         let value = JSON.parse(e.currentTarget.value);
62         console.log('selected project', value)
63     }
64     render() {
65         let props = this.props;
66         let currentValue = JSON.stringify(props.currentProject);
67         let projects = this.props.projects && this.props.projects.map((p,i) => {
68             return {
69                 label: p.name,
70                 value: p.name
71             }
72         });
73         let hasProjects = (this.props.projects && (this.props.projects.length > 0))
74         return (
75             <div className="userSection app">
76                 {
77                     hasProjects ?  'Project:' : 'No Projects Assigned'
78                 }
79                 {
80                     hasProjects ?
81                     <SelectOption
82                         options={projects}
83                         value={currentValue}
84                         defaultValue={currentValue}
85                         onChange={props.onSelectProject}
86                         className="projectSelect" />
87                     : null
88                 }
89             </div>
90         )
91     }
92 }
93
94 class UserNav extends React.Component {
95     constructor(props) {
96         super(props);
97     }
98     handleLogout() {
99         Utils.clearAuthentication();
100     }
101     selectProject(e) {
102         let value = JSON.parse(e.currentTarget.value)
103         console.log('selected project', value)
104     }
105     render() {
106         let props = this.props;
107         let userProfileLink = '';
108         this.props.nav['user_management'] && this.props.nav['user_management'].routes.map((r) => {
109             if(r.unique) {
110                 userProfileLink = returnLinkItem(r, props.currentUser)
111             }
112         })
113         return (
114             <div className="app">
115                 <h2>
116                     USER: {userProfileLink}
117                     <span className="oi" data-glyph="caret-bottom"></span>
118                 </h2>
119                 <ul className="menu">
120                     <li>
121                         <a onClick={this.handleLogout}>
122                             Logout
123                         </a>
124                     </li>
125                 </ul>
126             </div>
127         )
128     }
129 }
130
131 UserNav.defaultProps = {
132     projects: [
133
134     ]
135 }
136
137 //
138 // Exported classes and functions
139 //
140
141 //
142 /**
143  * Skyquake Nav Component. Provides navigation functionality between all plugins
144  */
145 export default class skyquakeNav extends React.Component {
146     constructor(props) {
147         super(props);
148         this.state = {};
149         this.state.validateErrorEvent = 0;
150         this.state.validateErrorMsg = '';
151     }
152     componentDidMount() {
153         this.props.store.openProjectSocket();
154         this.props.store.getUserProfile();
155     }
156     validateError = (msg) => {
157         this.setState({
158             validateErrorEvent: true,
159             validateErrorMsg: msg
160         });
161     }
162     validateReset = () => {
163         this.setState({
164             validateErrorEvent: false
165         });
166     }
167     returnCrouton = () => {
168         return <Crouton
169             id={Date.now()}
170             message={this.state.validateErrorMsg}
171             type={"error"}
172             hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
173             onDismiss={this.validateReset}
174         />;
175     }
176     render() {
177         let html;
178         html = (
179                 <div>
180                 {this.returnCrouton()}
181             <nav className="skyquakeNav">
182                 {buildNav.call(this, this.props.nav, this.props.currentPlugin, this.props)}
183             </nav>
184
185             </div>
186         )
187         return html;
188     }
189 }
190 skyquakeNav.defaultProps = {
191     nav: {}
192 }
193 skyquakeNav.contextTypes = {
194   userProfile: React.PropTypes.object
195 };
196 /**
197  * Returns a React Component
198  * @param  {object} link  Information about the nav link
199  * @param  {string} link.route Hash route that the SPA should resolve
200  * @param  {string} link.name Link name to be displayed
201  * @param  {number} index index of current array item
202  * @return {object} component A React LI Component
203  */
204 //This should be extended to also make use of internal/external links and determine if the link should refer to an outside plugin or itself.
205 export function buildNavListItem (k, link, index) {
206     let html = false;
207     if (link.type == 'external') {
208         this.hasSubNav[k] = true;
209         html = (
210             <li key={index}>
211                 {returnLinkItem(link)}
212             </li>
213         );
214     }
215     return html;
216 }
217
218 /**
219  * Builds a link to a React Router route or a new plugin route.
220  * @param  {object} link Routing information from nav object.
221  * @return {object}  component   returns a react component that links to a new route.
222  */
223 export function returnLinkItem(link, label) {
224     let ref;
225     let route = link.route;
226     if(link.isExternal) {
227         ref = (
228             <a href={route}>{label || link.label}</a>
229         )
230     } else {
231         if(link.path && link.path.replace(' ', '') != '') {
232             route = link.path;
233         }
234         if(link.query) {
235             let query = {};
236             query[link.query] = '';
237             route = {
238                 pathname: route,
239                 query: query
240             }
241         }
242         ref = (
243             <Link to={route}>
244                 {label || link.label}
245             </Link>
246         )
247     }
248     return ref;
249 }
250
251
252
253
254 /**
255  * Constructs nav for each plugin, along with available subnavs
256  * @param  {array} nav List returned from /nav endpoint.
257  * @return {array}     List of constructed nav element for each plugin
258  */
259 export function buildNav(nav, currentPlugin, props) {
260     let navList = [];
261     let navListHTML = [];
262     let secondaryNav = [];
263     let adminNav = [];
264     let self = this;
265     const User = this.context.userProfile;
266     self.hasSubNav = {};
267     let secondaryNavHTML = (
268         <div className="secondaryNav" key="secondaryNav">
269             {secondaryNav}
270             <div className="app admin">
271                 <h2>
272                     <a>
273                         ADMIN <span className="oi" data-glyph="caret-bottom"></span>
274                     </a>
275                 </h2>
276                 <ul className="menu">
277                     {
278                         adminNav
279                     }
280                 </ul>
281             </div>
282             <SelectProject
283                 onSelectProject={props.store.selectActiveProject}
284                 projects={props.projects}
285                 currentProject={props.currentProject} />
286             <UserNav
287                 currentUser={props.currentUser}
288                 nav={nav}  />
289         </div>
290     )
291     for (let k in nav) {
292         if (nav.hasOwnProperty(k)) {
293             self.hasSubNav[k] = false;
294             let header = null;
295             let navClass = "app";
296             let routes = nav[k].routes;
297             let navItem = {};
298             //Primary plugin title and link to dashboard.
299             let route;
300             let NavList;
301             if (API_SERVER) {
302                 route = routes[0].isExternal ? '/' + k + '/index.html?api_server=' + API_SERVER + '' + '&upload_server=' + UPLOAD_SERVER + '' : '';
303             } else {
304                 route = routes[0].isExternal ? '/' + k + '/' : '';
305             }
306             let dashboardLink = returnLinkItem({
307                 isExternal: routes[0].isExternal,
308                 pluginName: nav[k].pluginName,
309                 label: nav[k].label || k,
310                 route: route
311             });
312             let shouldAllow = nav[k].allow || ['*'];
313             if (nav[k].pluginName == currentPlugin) {
314                 navClass += " active";
315             }
316             NavList = nav[k].routes.map(buildNavListItem.bind(self, k));
317             navItem.priority = nav[k].priority;
318             navItem.order = nav[k].order;
319             if(nav[k].admin_link) {
320
321                 if (isRBACValid(User, shouldAllow) ){
322                     adminNav.push((
323                         <li key={nav[k].name}>
324                             {dashboardLink}
325                         </li>
326                     ))
327                 }
328             } else {
329                 if (isRBACValid(User, shouldAllow) ){
330                     navItem.html = (
331                         <div  key={k} className={navClass}>
332                             <h2>{dashboardLink} {self.hasSubNav[k] ? <span className="oi" data-glyph="caret-bottom"></span> : ''}</h2>
333                             <ul className="menu">
334                                 {
335                                     NavList
336                                 }
337                             </ul>
338                         </div>
339                     );
340                 }
341             navList.push(navItem)
342             }
343
344         }
345     }
346     //Sorts nav items by order and returns only the markup
347     navListHTML = navList.sort((a,b) => a.order - b.order).map(function(n) {
348         if((n.priority  < 2)){
349             return n.html;
350         } else {
351             secondaryNav.push(n.html);
352         }
353     });
354     navListHTML.push(secondaryNavHTML);
355     return navListHTML;
356 }