type={notificationType}
hidden={!(displayNotification && notificationMessage)}
onDismiss={SkyquakeContainerActions.hideNotification}
+ timeout= {5000}
/>
<ScreenLoader show={displayScreenLoader}/>
- <SkyquakeNav nav={this.state.nav}
- currentPlugin={this.state.currentPlugin}
- store={SkyquakeContainerStore} />
+ <SkyquakeNav nav={nav}
+ currentPlugin={this.state.user.currentPlugin}
+ currentUser={this.state.user.userId}
+ currentProject={this.state.user.projectId}
+ store={SkyquakeContainerStore}
+ projects={this.state.projects} />
<div className="titleBar">
- <h1>{this.state.currentPlugin + tag}</h1>
+ <h1>{(this.state.nav.name ? this.state.nav.name.replace('_', ' ').replace('-', ' ') : this.state.currentPlugin && this.state.currentPlugin.replace('_', ' ').replace('-', ' ')) + tag}</h1>
</div>
<div className={"application " + routeName}>
{this.props.children}
import Alt from './skyquakeAltInstance.js';
import SkyquakeContainerSource from './skyquakeContainerSource.js';
import SkyquakeContainerActions from './skyquakeContainerActions';
- import _ from 'lodash';
+let Utils = require('utils/utils.js');
+ import _indexOf from 'lodash/indexOf';
++import _isEqual from 'lodash/isEqual';
//Temporary, until api server is on same port as webserver
- var rw = require('utils/rw.js');
+ import rw from 'utils/rw.js';
+
var API_SERVER = rw.getSearchParams(window.location).api_server;
var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
})
}
- if (!_.isEqual(data.project, self.projects)) {
+ openProjectSocketSuccess = (connection) => {
+ var self = this;
+ var ws = window.multiplexer.channel(connection);
+ if (!connection) return;
+ self.setState({
+ socket: ws.ws,
+ channelId: connection
+ });
+ ws.onmessage = function(socket) {
+ try {
+ var data = JSON.parse(socket.data);
+ Utils.checkAuthentication(data.statusCode, function() {
+ self.closeSocket();
+ });
++ if (!_isEqual(data.project, self.projects)) {
+ let user = self.user;
+ user.projects = data.project;
+ self.setState({
+ user: user,
+ projects: data.project
+ });
+ }
+ } catch(e) {
+ console.log('HIT an exception in openProjectSocketSuccess', e);
+ }
+ };
+ }
+ getUserProfileSuccess = (user) => {
+ this.alt.actions.global.hideScreenLoader.defer();
+ this.setState({user})
+ }
+ selectActiveProjectSuccess = (projectId) => {
+ let user = this.user;
+ user.projectId = projectId;
+ this.setState({user});
+ window.location.reload(true);
+ }
//Notifications
showNotification = (data) => {
let state = {
--- /dev/null
- var rw = require('utils/rw.js');
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import React from 'react';
+import { Link } from 'react-router';
+import Utils from 'utils/utils.js';
+import Crouton from 'react-crouton';
+import 'style/common.scss';
+
+import './skyquakeNav.scss';
+import SelectOption from '../form_controls/selectOption.jsx';
+import {FormSection} from '../form_controls/formControls.jsx';
+import {isRBACValid, SkyquakeRBAC} from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
+
+//Temporary, until api server is on same port as webserver
++import rw from 'utils/rw.js';
++
+var API_SERVER = rw.getSearchParams(window.location).api_server;
+var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
+
+//
+// Internal classes/functions
+//
+
+class LogoutAppMenuItem extends React.Component {
+ handleLogout() {
+ Utils.clearAuthentication();
+ }
+ render() {
+ return (
+ <div className="app">
+ <h2>
+ <a onClick={this.handleLogout}>
+ Logout
+ </a>
+ </h2>
+ </div>
+ );
+ }
+}
+
+class SelectProject extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ selectProject(e) {
+ let value = JSON.parse(e.currentTarget.value);
+ console.log('selected project', value)
+ }
+ render() {
+ let props = this.props;
+ let currentValue = JSON.stringify(props.currentProject);
+ let projects = this.props.projects && this.props.projects.map((p,i) => {
+ return {
+ label: p.name,
+ value: p.name
+ }
+ });
+ let hasProjects = (this.props.projects && (this.props.projects.length > 0))
+ return (
+ <div className="userSection app">
+ {
+ hasProjects ? 'Project:' : 'No Projects Assigned'
+ }
+ {
+ hasProjects ?
+ <SelectOption
+ options={projects}
+ value={currentValue}
+ defaultValue={currentValue}
+ onChange={props.onSelectProject}
+ className="projectSelect" />
+ : null
+ }
+ </div>
+ )
+ }
+}
+
+class UserNav extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ handleLogout() {
+ Utils.clearAuthentication();
+ }
+ selectProject(e) {
+ let value = JSON.parse(e.currentTarget.value)
+ console.log('selected project', value)
+ }
+ render() {
+ let props = this.props;
+ let userProfileLink = '';
+ this.props.nav['user_management'] && this.props.nav['user_management'].routes.map((r) => {
+ if(r.unique) {
+ userProfileLink = returnLinkItem(r, props.currentUser)
+ }
+ })
+ return (
+ <div className="app">
+ <h2>
+ USER: {userProfileLink}
+ <span className="oi" data-glyph="caret-bottom"></span>
+ </h2>
+ <ul className="menu">
+ <li>
+ <a onClick={this.handleLogout}>
+ Logout
+ </a>
+ </li>
+ </ul>
+ </div>
+ )
+ }
+}
+
+UserNav.defaultProps = {
+ projects: [
+
+ ]
+}
+
+//
+// Exported classes and functions
+//
+
+//
+/**
+ * Skyquake Nav Component. Provides navigation functionality between all plugins
+ */
+export default class skyquakeNav extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ this.state.validateErrorEvent = 0;
+ this.state.validateErrorMsg = '';
+ }
+ componentDidMount() {
+ this.props.store.openProjectSocket();
+ this.props.store.getUserProfile();
+ }
+ validateError = (msg) => {
+ this.setState({
+ validateErrorEvent: true,
+ validateErrorMsg: msg
+ });
+ }
+ validateReset = () => {
+ this.setState({
+ validateErrorEvent: false
+ });
+ }
+ returnCrouton = () => {
+ return <Crouton
+ id={Date.now()}
+ message={this.state.validateErrorMsg}
+ type={"error"}
+ hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
+ onDismiss={this.validateReset}
+ />;
+ }
+ render() {
+ let html;
+ html = (
+ <div>
+ {this.returnCrouton()}
+ <nav className="skyquakeNav">
+ {buildNav.call(this, this.props.nav, this.props.currentPlugin, this.props)}
+ </nav>
+
+ </div>
+ )
+ return html;
+ }
+}
+skyquakeNav.defaultProps = {
+ nav: {}
+}
+skyquakeNav.contextTypes = {
+ userProfile: React.PropTypes.object
+};
+/**
+ * Returns a React Component
+ * @param {object} link Information about the nav link
+ * @param {string} link.route Hash route that the SPA should resolve
+ * @param {string} link.name Link name to be displayed
+ * @param {number} index index of current array item
+ * @return {object} component A React LI Component
+ */
+//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.
+export function buildNavListItem (k, link, index) {
+ let html = false;
+ if (link.type == 'external') {
+ this.hasSubNav[k] = true;
+ html = (
+ <li key={index}>
+ {returnLinkItem(link)}
+ </li>
+ );
+ }
+ return html;
+}
+
+/**
+ * Builds a link to a React Router route or a new plugin route.
+ * @param {object} link Routing information from nav object.
+ * @return {object} component returns a react component that links to a new route.
+ */
+export function returnLinkItem(link, label) {
+ let ref;
+ let route = link.route;
+ if(link.isExternal) {
+ ref = (
+ <a href={route}>{label || link.label}</a>
+ )
+ } else {
+ if(link.path && link.path.replace(' ', '') != '') {
+ route = link.path;
+ }
+ if(link.query) {
+ let query = {};
+ query[link.query] = '';
+ route = {
+ pathname: route,
+ query: query
+ }
+ }
+ ref = (
+ <Link to={route}>
+ {label || link.label}
+ </Link>
+ )
+ }
+ return ref;
+}
+
+
+
+
+/**
+ * Constructs nav for each plugin, along with available subnavs
+ * @param {array} nav List returned from /nav endpoint.
+ * @return {array} List of constructed nav element for each plugin
+ */
+export function buildNav(nav, currentPlugin, props) {
+ let navList = [];
+ let navListHTML = [];
+ let secondaryNav = [];
+ let adminNav = [];
+ let self = this;
+ const User = this.context.userProfile;
+ self.hasSubNav = {};
+ let secondaryNavHTML = (
+ <div className="secondaryNav" key="secondaryNav">
+ {secondaryNav}
+ <div className="app admin">
+ <h2>
+ <a>
+ ADMIN <span className="oi" data-glyph="caret-bottom"></span>
+ </a>
+ </h2>
+ <ul className="menu">
+ {
+ adminNav
+ }
+ </ul>
+ </div>
+ <SelectProject
+ onSelectProject={props.store.selectActiveProject}
+ projects={props.projects}
+ currentProject={props.currentProject} />
+ <UserNav
+ currentUser={props.currentUser}
+ nav={nav} />
+ </div>
+ )
+ for (let k in nav) {
+ if (nav.hasOwnProperty(k)) {
+ self.hasSubNav[k] = false;
+ let header = null;
+ let navClass = "app";
+ let routes = nav[k].routes;
+ let navItem = {};
+ //Primary plugin title and link to dashboard.
+ let route;
+ let NavList;
+ if (API_SERVER) {
+ route = routes[0].isExternal ? '/' + k + '/index.html?api_server=' + API_SERVER + '' + '&upload_server=' + UPLOAD_SERVER + '' : '';
+ } else {
+ route = routes[0].isExternal ? '/' + k + '/' : '';
+ }
+ let dashboardLink = returnLinkItem({
+ isExternal: routes[0].isExternal,
+ pluginName: nav[k].pluginName,
+ label: nav[k].label || k,
+ route: route
+ });
+ let shouldAllow = nav[k].allow || ['*'];
+ if (nav[k].pluginName == currentPlugin) {
+ navClass += " active";
+ }
+ NavList = nav[k].routes.map(buildNavListItem.bind(self, k));
+ navItem.priority = nav[k].priority;
+ navItem.order = nav[k].order;
+ if(nav[k].admin_link) {
+
+ if (isRBACValid(User, shouldAllow) ){
+ adminNav.push((
+ <li key={nav[k].name}>
+ {dashboardLink}
+ </li>
+ ))
+ }
+ } else {
+ if (isRBACValid(User, shouldAllow) ){
+ navItem.html = (
+ <div key={k} className={navClass}>
+ <h2>{dashboardLink} {self.hasSubNav[k] ? <span className="oi" data-glyph="caret-bottom"></span> : ''}</h2>
+ <ul className="menu">
+ {
+ NavList
+ }
+ </ul>
+ </div>
+ );
+ }
+ navList.push(navItem)
+ }
+
+ }
+ }
+ //Sorts nav items by order and returns only the markup
+ navListHTML = navList.sort((a,b) => a.order - b.order).map(function(n) {
+ if((n.priority < 2)){
+ return n.html;
+ } else {
+ secondaryNav.push(n.html);
+ }
+ });
+ navListHTML.push(secondaryNavHTML);
+ return navListHTML;
+}
var download_host = req.query['dev_download_server'];
if (!download_host) {
- download_host = req.protocol + '://' + req.get('host');//api_server + ':' + utils.getPortForProtocol(req.protocol);
+ download_host = req.protocol + '://' + req.get('host');//req.api_server + ':' + utils.getPortForProtocol(req.protocol);
}
- 'external-url': download_host + '/composer/update/' + req.file.filename,
+
+ var input = {
- var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-update');
++ 'external-url': download_host + '/composer/upload/' + req.file.filename,
+ 'package-type': 'VNFD',
+ 'package-id': uuid()
+ }
+
++ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-create');
+
+ input = utils.addProjectContextToRPCPayload(req, uri, input);
+
return new Promise(function(resolve, reject) {
Promise.all([
rp({
var download_host = req.query['dev_download_server'];
if (!download_host) {
- download_host = req.protocol + '://' + req.get('host');//req.api_server + ':' + utils.getPortForProtocol(req.protocol);
+ download_host = req.protocol + '://' + req.get('host');//api_server + ':' + utils.getPortForProtocol(req.protocol);
}
-
var input = {
- 'external-url': download_host + '/composer/upload/' + req.file.filename,
+ 'external-url': download_host + '/composer/update/' + req.file.filename,
'package-type': 'VNFD',
'package-id': uuid()
- }
+ };
+
- var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-create');
++ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-update');
+
+ input = utils.addProjectContextToRPCPayload(req, uri, input);
+
return new Promise(function(resolve, reject) {
Promise.all([
rp({
});
};
-
-
- Composer.addFile = function(req) {
- console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
+ PackageManager.export = function(req) {
+ // /api/operations/package-export
var api_server = req.query['api_server'];
- var download_host = req.query['dev_download_server'];
- var package_id = req.query['package_id'];
- var package_type = req.query['package_type'].toUpperCase();
- var package_path = req.query['package_path'];
- if (!download_host) {
- download_host = req.protocol + '://' + req.get('host');//api_server + ':' + utils.getPortForProtocol(req.protocol);
- }
- var input = {
- 'external-url': download_host + '/composer/upload/' + req.query['package_id'] + '/' + req.file.filename,
- 'package-type': package_type,
- 'package-id': package_id,
- 'package-path': package_path + '/' + req.file.filename
- }
-
- var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-file-add');
-
++ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-export');
++ var input = req.body;
+ input = utils.addProjectContextToRPCPayload(req, uri, input);
-
return new Promise(function(resolve, reject) {
Promise.all([
rp({
- uri: uri,
+ uri: utils.confdPort(api_server) + '/api/operations/package-export',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
- 'Authorization': req.get('Authorization')
+ 'Authorization': req.session && req.session.authorization
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true,
json: true,
- body: {
- input: input
- }
- body: { "input": req.body}
++ body: { "input": input }
})
]).then(function(result) {
var data = {};
});
}
- Composer.exportPackage = function(req) {
+ PackageManager.copy = function(req) {
+ // /api/operations/package-copy
var api_server = req.query['api_server'];
- var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-export');
++ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-copy');
+ var input = req.body;
+ input = utils.addProjectContextToRPCPayload(req, uri, input);
++
return new Promise(function(resolve, reject) {
Promise.all([
rp({
- uri: utils.confdPort(api_server) + '/api/operations/package-copy',
+ uri: uri,
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
- 'Authorization': req.session && req.session.authorization
+ 'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true,
json: true,
- body: { "input": input }
- body: { "input": req.body}
++ body: { "input": input}
})
]).then(function(result) {
var data = {};
});
}
- var url = uri + '/api/operational/copy-jobs' + (id ? '/job/' + id : '');
+ /**
+ * This methods retrieves the status of package operations. It takes an optional
+ * transaction id (id) this if present will return only that status otherwise
+ * an array of status' will be response.
+ */
+ PackageManager.getJobStatus = function(req) {
+ var api_server = req.query["api_server"];
+ var uri = utils.confdPort(api_server);
+ var id = req.params['id'];
- }
++ var url = utils.projectContextUrl(req, uri + '/api/operational/copy-jobs' + (id ? '/job/' + id : ''));
+ return new Promise(function(resolve, reject) {
+ request({
+ url: url,
+ method: 'GET',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
+ 'Authorization': req.get('Authorization')
+ }),
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false
+ }, function(error, response, body) {
+ if (utils.validateResponse('restconfAPI.streams', error, response, body, resolve, reject)) {
+ var returnData;
+ if (id) {
+ returnData = JSON.parse(response.body)['rw-pkg-mgmt:job'];
+ } else {
+ var data = JSON.parse(response.body)['rw-pkg-mgmt:copy-jobs'];
+ returnData = (data && data.job) || [];
+ }
+ resolve({
+ statusCode: response.statusCode,
+ data: returnData
+ })
+ };
+ })
+ })
+ }
+
+ FileManager.addFile = function(req) {
+ console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
+ var api_server = req.query['api_server'];
+ var download_host = req.query['dev_download_server'];
+ var package_id = req.query['package_id'];
+ var package_type = req.query['package_type'].toUpperCase();
+ var package_path = req.query['package_path'];
+ if (!download_host) {
+ download_host = req.protocol + '://' + req.get('host');//api_server + ':' + utils.getPortForProtocol(req.protocol);
+ }
+ var input = {
+ 'external-url': download_host + '/composer/upload/' + req.query['package_id'] + '/' + req.file.filename,
+ 'package-type': package_type,
+ 'package-id': package_id,
+ 'package-path': package_path + '/' + req.file.filename
- uri: utils.confdPort(api_server) + '/api/operations/package-file-add',
++ };
++
++ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-file-add');
++
++ input = utils.addProjectContextToRPCPayload(req, uri, input);
++
++
+ return new Promise(function(resolve, reject) {
+ Promise.all([
+ rp({
++ uri: uri,
+ method: 'POST',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
+ 'Authorization': req.get('Authorization')
+ }),
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false,
+ resolveWithFullResponse: true,
+ json: true,
+ body: {
+ input: input
+ }
+ })
+ ]).then(function(result) {
+ var data = {};
+ data['transaction_id'] = result[0].body['output']['task-id'];
+ resolve({
+ statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
+ data: data
+ });
+ }).catch(function(error) {
+ var res = {};
+ console.log('Problem with Composer.upload', error);
+ res.statusCode = error.statusCode || 500;
+ res.errorMessage = {
+ error: 'Failed to upload package ' + req.file.originalname + '. Error: ' + error
+ };
+ reject(res);
+ });
+ });
+ }
+
FileManager.get = function(req) {
var api_server = req.query['api_server'];
var type = req.query['package_type'] && req.query['package_type'].toUpperCase();
const isValueSet = enumeration.filter(d => d.isSelected).length > 0;
if (!isValueSet || property.cardinality === '0..1') {
const noValueDisplayText = changeCase.title(property.name);
- options.unshift(<option key={'(value-not-in-enum)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+ options.unshift(<option key={'(value-not-in-enum)'} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
}
- return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} disabled={!isEditable}>{options}</select>;
+ return (
+ <select
+ key={fieldKey}
+ id={fieldKey}
+ className={ClassNames({'-value-not-set': !isValueSet})}
+ defaultValue={value}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
- readOnly={!isEditable}>
++ disabled={!isEditable}>
+ {options}
+ </select>
+ );
}
if (isLeafRef) {
const isValueSet = leafRefPathValues.filter(d => d.isSelected).length > 0;
if (!isValueSet || property.cardinality === '0..1') {
const noValueDisplayText = changeCase.title(property.name);
- options.unshift(<option key={'(value-not-in-leafref)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+ options.unshift(<option key={'(value-not-in-leafref)'} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
}
- return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} disabled={!isEditable}>{options}</select>;
+ return (
+ <select
+ key={fieldKey}
+ id={fieldKey}
+ className={ClassNames({'-value-not-set': !isValueSet})}
+ defaultValue={value}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
- readOnly={!isEditable}>
++ disabled={!isEditable}>
+ {options}
+ </select>
+ );
}
if (isBoolean) {
val = value ? "TRUE" : "FALSE"
}
const isValueSet = (val != '' && val)
- return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={val && val.toUpperCase()} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} disabled={!isEditable}>{options}</select>;
+ return (
+ <select
+ key={fieldKey}
+ id={fieldKey}
+ className={ClassNames({'-value-not-set': !isValueSet})}
+ defaultValue={val && val.toUpperCase()}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
- readOnly={!isEditable}>
++ disabled={!isEditable}>
+ {options}
+ </select>
+ );
+ }
+
+ if (Property.isLeafEmpty(property)) {
+ // A null value indicates the leaf exists (as opposed to undefined).
+ // We stick in a string when the user actually sets it to simplify things
+ // but the correct thing happens when we serialize to user data
+ let isEmptyLeafPresent = (value === EMPTY_LEAF_PRESENT || value === null);
+ let present = isEmptyLeafPresent ? EMPTY_LEAF_PRESENT : "";
+ const options = [
+ <option key={'true'} value={EMPTY_LEAF_PRESENT}>Enabled</option>,
+ <option key={'false'} value="">Not Enabled</option>
+ ]
+
+ return (
+ <select
+ key={fieldKey}
+ id={fieldKey}
+ className={ClassNames({'-value-not-set': !isEmptyLeafPresent})}
+ defaultValue={present}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
- readOnly={!isEditable}>
++ disabled={!isEditable}>
+ {options}
+ </select>
+ );
}
if (property['preserve-line-breaks']) {
- return <textarea key={fieldKey.toString()} cols="5" id={fieldKey.toString()} name={name} value={value} placeholder={placeholder} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing} readOnly={!!isEditable} />;
+ return (
+ <textarea
+ key={fieldKey}
+ cols="5"
+ id={fieldKey}
+ defaultValue={value}
+ placeholder={placeholder}
+ onChange={onTextChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ onMouseOut={endEditing}
+ onMouseLeave={endEditing}
- readOnly={!isEditable} />
++ disabled={!isEditable} />
+ );
}
- return <input
- key={fieldKey.toString()}
- id={fieldKey.toString()}
- type="text"
- name={name}
- value={fieldValue}
- className={className}
- placeholder={placeholder}
- onChange={onChange}
- onFocus={onFocus}
- onBlur={endEditing}
- onMouseDown={startEditing}
- onMouseOver={startEditing}
- onMouseOut={endEditing}
- onMouseLeave={endEditing}
- readOnly={!isEditable}
- />;
+ return (
+ <input
+ key={fieldKey}
+ id={fieldKey}
+ type="text"
+ defaultValue={fieldValue}
+ className={className}
+ placeholder={placeholder}
+ onChange={onTextChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ onMouseOut={endEditing}
+ onMouseLeave={endEditing}
- readOnly={!isEditable}
++ disabled={!isEditable}
+ />
+ );
}
return (
<div key={key} className="choice">
- <select key={Date.now()} className={ClassNames({'-value-not-set': !selectedOptionValue})} name={selectName} value={selectedOptionValue} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing} readOnly={!isEditable}>
+ <select
+ key={Date.now()}
+ className={ClassNames({'-value-not-set': !selectedOptionValue})}
+ defaultValue={selectedOptionValue}
+ onChange={onChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ onMouseOut={endEditing}
+ onMouseLeave={endEditing}
++ disabled={!isEditable}
+ >
{options}
</select>
{valueResponse}
}
} else {
// contains no predicate
+ if (!objectCopy) {
+ break;
+ }
objectCopy = objectCopy[fragment];
+ if (!objectCopy) {
+ // contains no value
+ break;
+ }
}
}
}
import NsListPanel from './nsListPanel/nsListPanel.jsx';
import Crouton from 'react-crouton'
import AppHeader from 'widgets/header/header.jsx';
- import Utils from 'utils/utils.js';
import './launchpad.scss';
+
+import {SkyquakeRBAC, isRBACValid} from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
+import ROLES from 'utils/roleConstants.js';
+
+const PROJECT_ROLES = ROLES.PROJECT;
+
let ReactCSSTransitionGroup = require('react-addons-css-transition-group');
var LaunchpadFleetActions = require('./launchpadFleetActions.js');
var LaunchpadFleetStore = require('./launchpadFleetStore.js');