3 * Copyright 2016 RIFT.IO Inc
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
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';
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';
30 //Temporary, until api server is on same port as webserver
31 import rw from 'utils/rw.js';
33 var API_SERVER = rw.getSearchParams(window.location).api_server;
34 var DOWNLOAD_SERVER = rw.getSearchParams(window.location).dev_download_server;
37 // Internal classes/functions
40 class SelectProject extends React.Component {
45 let value = JSON.parse(e.currentTarget.value);
46 // console.log('selected project', value)
49 let props = this.props;
50 let hasProjects = props.projects;
51 let userAssignedProjects = hasProjects && (props.projects.length > 0)
55 <a style={{textTransform:'none'}}>
58 (userAssignedProjects ? 'PROJECT: ' + props.currentProject : 'No Projects Assigned')
59 : 'Projects Loading...'
63 userAssignedProjects ? <span className="oi" data-glyph="caret-bottom"></span> : null
67 userAssignedProjects ?
68 <ul className="project menu">
70 props.projects.map(function (p, k) {
71 return <li key={k} onClick={props.onSelectProject.bind(null, p.name)}><a>{p.name}</a></li>
87 defaultValue={currentValue}
88 onChange={props.onSelectProject}
89 className="projectSelect" />
94 class UserNav extends React.Component {
99 Utils.clearAuthentication();
102 let value = JSON.parse(e.currentTarget.value)
103 // console.log('selected project', value)
106 let props = this.props;
107 let userProfileLink = null;
108 this.props.nav['user_management'] && this.props.nav['user_management'].routes.map((r) => {
113 return !userProfileLink ? null : (
114 <div className="app">
115 <h2 className="username">
116 USERNAME: {returnLinkItem(userProfileLink, props.currentUser)}
117 <span className="oi" data-glyph="caret-bottom"></span>
119 <ul className="menu">
121 {returnLinkItem(userProfileLink, "My Profile")}
124 <a onClick={this.handleLogout}>
134 UserNav.defaultProps = {
141 // Exported classes and functions
146 * Skyquake Nav Component. Provides navigation functionality between all plugins
148 export default class skyquakeNav extends React.Component {
152 this.state.validateErrorEvent = 0;
153 this.state.validateErrorMsg = '';
155 componentDidMount() {
156 this.props.store.openProjectSocket();
158 validateError = (msg) => {
160 validateErrorEvent: true,
161 validateErrorMsg: msg
164 validateReset = () => {
166 validateErrorEvent: false
169 returnCrouton = () => {
172 message={this.state.validateErrorMsg}
174 hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
175 onDismiss={this.validateReset}
182 {this.returnCrouton()}
183 <nav className="skyquakeNav">
184 {buildNav.call(this, this.props.nav, this.props.currentPlugin, this.props)}
192 skyquakeNav.defaultProps = {
195 skyquakeNav.contextTypes = {
196 userProfile: React.PropTypes.object
199 * Returns a React Component
200 * @param {object} link Information about the nav link
201 * @param {string} link.route Hash route that the SPA should resolve
202 * @param {string} link.name Link name to be displayed
203 * @param {number} index index of current array item
204 * @return {object} component A React LI Component
206 //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.
207 export function buildNavListItem(k, link, index) {
209 if (link.type == 'external') {
210 this.hasSubNav[k] = true;
213 {returnLinkItem(link)}
221 * Builds a link to a React Router route or a new plugin route.
222 * @param {object} link Routing information from nav object.
223 * @return {object} component returns a react component that links to a new route.
225 export function returnLinkItem(link, label) {
227 let route = link.route;
228 if (link.isExternal) {
230 <a href={route}>{label || link.label}</a>
233 if (link.path && link.path.replace(' ', '') != '') {
238 query[link.query] = '';
246 {label || link.label}
257 * Constructs nav for each plugin, along with available subnavs
258 * @param {array} nav List returned from /nav endpoint.
259 * @return {array} List of constructed nav element for each plugin
261 export function buildNav(navData, currentPlugin, props) {
263 let navListHTML = [];
264 let secondaryNav = [];
266 //For monitoring when admin panel is active
267 let adminNavList = [];
269 const User = this.context.userProfile;
270 //The way the nav is sorting needs to be refactored.
271 let navArray = navData && Object.keys(navData).sort((a, b) => navData[a].order - navData[b].order)
273 for (let i = 0; i < navArray.length; i++) {
275 if (navData.hasOwnProperty(k)) {
276 self.hasSubNav[k] = false;
278 let navClass = "app";
279 let routes = navData[k].routes;
281 //Primary plugin title and link to dashboard.
285 route = routes[0].isExternal ?
286 '/' + k + '/index.html?api_server=' + API_SERVER + '' + (DOWNLOAD_SERVER ? '&dev_download_server=' + DOWNLOAD_SERVER : '')
289 route = routes[0].isExternal ? '/' + k + '/' : '';
291 if(navData[k].route) {
292 route = route + navData[k].route;
294 let dashboardLink = returnLinkItem({
295 isExternal: routes[0].isExternal,
296 pluginName: navData[k].pluginName,
297 label: navData[k].label || k,
300 let shouldAllow = navData[k].allow || ['*'];
301 if (navData[k].pluginName == currentPlugin) {
302 navClass += " active";
304 NavList = navData[k].routes.filter((r) => {
305 const User = self.context.userProfile;
306 const shouldAllow = r.allow || ['*'];
307 return isRBACValid(User, shouldAllow);
308 }).map(buildNavListItem.bind(self, k));
309 navItem.priority = navData[k].priority;
310 navItem.order = navData[k].order;
311 if (navData[k].admin_link) {
312 if (isRBACValid(User, shouldAllow)) {
313 adminNavList.push(navData[k].pluginName);
315 <li key={navData[k].pluginName}>
321 if (isRBACValid(User, shouldAllow)) {
323 <div key={k} className={navClass}>
324 <h2>{dashboardLink} {self.hasSubNav[k] ? <span className="oi" data-glyph="caret-bottom"></span> : ''}</h2>
325 <ul className="menu">
331 navList.push(navItem)
337 //Sorts nav items by order and returns only the markup
338 navListHTML = navList.map(function (n) {
339 if ((n.priority < 2)) {
342 secondaryNav.push(n.html);
345 if (adminNav.length) {
347 <div key="Adminstration" className={"app " + (adminNavList.indexOf(currentPlugin) > -1 ? 'active' : '')}>
352 <span className="oi" data-glyph="caret-bottom"></span>
354 <ul className="menu">
362 let secondaryNavHTML = (
363 <div className="secondaryNav" key="secondaryNav">
366 onSelectProject={props.store.selectActiveProject}
367 projects={props.projects}
368 currentProject={props.currentProject} />
370 currentUser={props.currentUser}
374 // console.log("app admin " + (adminNavList.indexOf(currentPlugin) > -1 ? 'active' : ''))
375 navListHTML.push(secondaryNavHTML);