/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
var utils = require('../../../framework/core/api_utils/utils.js');
var constants = require('../../../framework/core/api_utils/constants.js');
var _ = require('underscore');
+var URL = require('url');
+var uuid = require('uuid');
var APIVersion = '/v1';
-var Composer = {};
+var PackageFileHandler = require('./packageFileHandler.js');
+var Composer = {};
+var FileManager = {};
var DataCenters = {};
// Catalog module methods
Composer.get = function(req) {
});
});
};
-module.exports = Composer;
+
+Composer.upload = function(req) {
+ console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
+ var api_server = req.query['api_server'];
+ // dev_download_server is for testing purposes.
+ // It is the direct IP address of the Node server where the
+ // package will be hosted.
+ var download_host = req.query['dev_download_server'];
+
+ if (!download_host) {
+ download_host = req.protocol + '://' + req.headers.host;
+ }
+
+ return new Promise(function(resolve, reject) {
+ Promise.all([
+ rp({
+ uri: utils.confdPort(api_server) + '/api/operations/package-create',
+ 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: {
+ 'external-url': download_host + '/composer/upload/' + req.file.filename,
+ 'package-type': 'VNFD',
+ 'package-id': uuid()
+ }
+ }
+ })
+ ]).then(function(result) {
+ var data = {};
+ data['transaction_id'] = result[0].body['output']['transaction-id'];
+
+ // Add a status checker on the transaction and then to delete the file later
+ PackageFileHandler.checkCreatePackageStatusAndHandleFile(req, data['transaction_id']);
+
+ // Return status to composer UI to update the status.
+ 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);
+ });
+ });
+};
+Composer.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.headers.host;
+ }
+
+ return new Promise(function(resolve, reject) {
+ Promise.all([
+ rp({
+ uri: utils.confdPort(api_server) + '/api/operations/package-file-add',
+ 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: {
+ '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
+ }
+ }
+ })
+ ]).then(function(result) {
+ var data = {};
+ data['transaction_id'] = result[0].body['output']['transaction-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);
+ });
+ });
+}
+
+Composer.exportPackage = function(req) {
+ // /api/operations/package-export
+ var api_server = req.query['api_server'];
+ return new Promise(function(resolve, reject) {
+ Promise.all([
+ rp({
+ uri: utils.confdPort(api_server) + '/api/operations/package-export',
+ 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": req.body}
+ })
+ ]).then(function(result) {
+ var data = {};
+ resolve({
+ statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
+ data: result[0].body
+ });
+ }).catch(function(error) {
+ var res = {};
+ console.log('Problem with Composer.exportPackage', error);
+ res.statusCode = error.statusCode || 500;
+ res.errorMessage = {
+ 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();
+ var id = req.query['package_id'];
+ var downloadUrl = req.query['url'];
+ var path = req.query['package_path'];
+ var payload = {
+ "input": {
+ "package-type": type,
+ "package-id": id
+ }
+ }
+ if(req.method == 'GET') {
+ if(downloadUrl && path) {
+ payload.input['external-url'] = downloadUrl;
+ payload.input['package-path'] = path;
+ return download(payload);
+ } else {
+ return list(payload);
+ }
+ }
+ if(req.method == 'DELETE') {
+ payload.input['package-path'] = path;
+ return deleteFile(payload)
+ }
+
+ function deleteFile(payload) {
+ return new Promise(function(resolve, reject) {
+ rp({
+ uri: utils.confdPort(api_server) + '/api/operations/rw-pkg-mgmt:package-file-delete',
+ method: 'POST',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
+ 'Authorization': req.get('Authorization')
+ }),
+ json: payload,
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false,
+ resolveWithFullResponse: true
+ }).then(function(data) {
+ if (utils.validateResponse('FileManager.delete', data.error, data, data.body, resolve, reject)) {
+ resolve({
+ statusCode: data.statusCode,
+ data: data.body
+ });
+ }
+ })
+ })
+ }
+ function download(payload) {
+ return new Promise(function(resolve, reject) {
+ rp({
+ uri: utils.confdPort(api_server) + '/api/operations/rw-pkg-mgmt:package-file-add',
+ method: 'POST',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
+ 'Authorization': req.get('Authorization')
+ }),
+ json: payload,
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false,
+ resolveWithFullResponse: true
+ }).then(function(data) {
+ if (utils.validateResponse('FileManager.get', data.error, data, data.body, resolve, reject)) {
+ resolve({
+ statusCode: data.statusCode,
+ data: data.body
+ });
+ }
+ })
+ })
+ }
+ function list(payload) {
+ return new Promise(function(resolve, reject) {
+ rp({
+ uri: utils.confdPort(api_server) + '/api/operations/get-package-endpoint',
+ method: 'POST',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
+ 'Authorization': req.get('Authorization')
+ }),
+ json: payload,
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false,
+ resolveWithFullResponse: true
+ }).then(function(data) {
+ if (utils.validateResponse('FileManager.get', data.error, data, data.body, resolve, reject)) {
+ var endpoint = null;
+ var parsedEndpoint = null;
+ try {
+ endpoint = data.body.output.endpoint
+ } catch(e) {
+ console.log('Something went wrong with the FileManager.get data that was returned');
+ reject({});
+ }
+ parsedEndpoint = URL.parse(endpoint);
+ rp({
+ uri: api_server + ':' + parsedEndpoint.port + parsedEndpoint.path,
+ method: 'GET',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
+ 'Authorization': req.get('Authorization')
+ }),
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false,
+ resolveWithFullResponse: true
+ }).then(function(data) {
+ if (utils.validateResponse('FileManager.get', data.error, data, data.body, resolve, reject)) {
+ resolve({
+ statusCode: data.statusCode,
+ data: data.body
+ });
+ }
+ })
+ }
+ })
+ })
+ }
+}
+FileManager.job = function(req) {
+ var api_server = req.query["api_server"];
+ var uri = utils.confdPort(api_server);
+ var url = '/api/operational/download-jobs';
+ var id = req.params['id'];
+ return new Promise(function(resolve, reject) {
+ request({
+ url: uri + url + '?deep',
+ 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 data = JSON.parse(response.body)['rw-pkg-mgmt:download-jobs'];
+ var returnData = [];
+ data && data.job.map(function(d) {
+ if(d['package-id'] == id) {
+ returnData.push(d)
+ }
+ })
+ resolve({
+ statusCode: response.statusCode,
+ data: returnData
+ })
+ };
+ })
+ })
+}
+module.exports = {
+ Composer:Composer,
+ FileManager: FileManager
+};
--- /dev/null
+var request = require('request');
+var Promise = require('bluebird');
+var rp = require('request-promise');
+var utils = require('../../../framework/core/api_utils/utils.js');
+var constants = require('../../../framework/core/api_utils/constants.js');
+var fs = require('fs');
+var _ = require('lodash');
+
+PackageFileHandler = {};
+
+function deleteFile(filename) {
+ setTimeout(function() {
+ fs.unlinkSync(constants.BASE_PACKAGE_UPLOAD_DESTINATION + filename);
+ }, constants.PACKAGE_FILE_DELETE_DELAY_MILLISECONDS);
+};
+
+function checkStatus(req, transactionId) {
+ var upload_server = req.query['upload_server'];
+ var headers = _.extend({},
+ {
+ 'Authorization': req.get('Authorization')
+ }
+ );
+ request({
+ url: upload_server + ':' + constants.PACKAGE_MANAGER_SERVER_PORT + '/api/upload/' + transactionId + '/state',
+ type: 'GET',
+ headers: headers,
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false
+ }, function(error, response, body) {
+ if (error) {
+ console.log('Error checking status for transaction', transactionId, '. Will delete file', req.file.filename, ' in ', constants.PACKAGE_FILE_DELETE_DELAY_MILLISECONDS ,' milliseconds');
+ deleteFile(req.file.filename);
+ } else {
+ var jsonStatus = null;
+ if (typeof body == 'string' || body instanceof String) {
+ jsonStatus = JSON.parse(body);
+ } else {
+ jsonStatus = body;
+ }
+
+ if (jsonStatus.status && (jsonStatus.status == 'success' || jsonStatus.status == 'failure')) {
+ console.log('Transaction ', transactionId, ' completed with status ', jsonStatus.status ,'. Will delete file', req.file.filename, ' in ', constants.PACKAGE_FILE_DELETE_DELAY_MILLISECONDS ,' milliseconds');
+ deleteFile(req.file.filename);
+ } else {
+ setTimeout(function() {
+ checkStatus(req, transactionId);
+ }, constants.PACKAGE_FILE_ONBOARD_TRANSACTION_STATUS_CHECK_DELAY_MILLISECONDS);
+ }
+ }
+ });
+};
+
+PackageFileHandler.checkCreatePackageStatusAndHandleFile = function(req, transactionId) {
+ checkStatus(req, transactionId);
+};
+
+
+module.exports = PackageFileHandler;
\ No newline at end of file
{
+ "copyright": "2016 RIFT.IO Inc",
+ "license": "Apache 2.0",
"root": "public",
"name": "Catalog",
"dashboard" : "./src/components/ComposerApp.js",
"grunt-cli": "^0.1.13",
"jquery": "^2.1.4",
"loaders.css": "^0.1.1",
- "lodash": "^3.10.1",
+ "lodash": "^4.0.0",
+ "mkdirp": "^0.5.1",
"moment": "^2.10.6",
+ "multer": "^1.2.0",
"normalize.css": "^3.0.3",
"numeral": "^1.5.3",
"object-assign": "^4.0.1",
"react-crouton": "^0.2.7",
"react-dom": "^0.14.3",
"react-popout": "^0.4.0",
+ "react-treeview": "^0.4.2",
"request-promise": "^3.0.0",
- "require-json": "0.0.1"
+ "require-json": "0.0.1",
+ "uuid": "^3.0.0"
},
"devDependencies": {
"babel-core": "^6.4.5",
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* limitations under the License.
*
*/
-var router = require('express').Router();
+var express = require('express');
+var router = express.Router();
var cors = require('cors');
var utils = require('../../framework/core/api_utils/utils.js')
-var Composer = require('./api/composer.js');
+var constants = require('../../framework/core/api_utils/constants.js');
+var C = require('./api/composer.js');
+var Composer = C.Composer;
+var FileManager = C.FileManager;
+var multer = require('multer');
+var fs = require('fs');
+var path = require('path');
+var mkdirp = require('mkdirp');
+
+var storage = multer.diskStorage({
+ // destination: 'upload/packages/',
+ destination: function(req, file, cb) {
+ var dir = constants.BASE_PACKAGE_UPLOAD_DESTINATION;
+ if (req.query['package_id']) {
+ dir += req.query['package_id'] + '/';
+ }
+ if (!fs.existsSync(dir)){
+ mkdirp(dir, function(err) {
+ if (err) {
+ console.log('Error creating folder for uploads. All systems FAIL!');
+ throw err;
+ }
+ cb(null, dir);
+ });
+ } else {
+ cb(null, dir);
+ }
+ },
+ filename: function (req, file, cb) {
+ if (req.query['package_id']) {
+ cb(null, file.originalname);
+ } else {
+ cb(null, Date.now() + '_' + file.fieldname + '_' + file.originalname);
+ }
+ },
+ // limits: {
+ // fieldNameSize: 100,
+ // fieldSize: 10000,
+ // fields: Infinity,
+ // fileSize: 10000,
+ // files: Infinity
+ // parts: Infinity
+ // headerPairs: 2000
+ // }
+});
+
+
+var upload = multer({
+ storage: storage
+});
+
+
router.get('/api/catalog', cors(), function(req, res) {
Composer.get(req).then(function(data) {
utils.sendSuccessResponse(data, res);
res.send(error.errorMessage);
});
});
+router.post('/upload', cors(), upload.single('package'), function (req, res, next) {
+ Composer.upload(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
+router.use('/upload', cors(), express.static('upload/packages'));
+
+
+
+router.post('/api/file-manager', cors(), upload.single('package'), function (req, res, next) {
+ Composer.addFile(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
+
+router.get('/api/file-manager', cors(), function(req, res) {
+ FileManager.get(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+})
+router.get('/api/file-manager/jobs/:id', cors(), function(req, res) {
+ FileManager.job(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
+router.delete('/api/file-manager', cors(), function(req, res) {
+ FileManager.get(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
+
+router.post('/api/package-export', cors(), function (req, res, next) {
+ Composer.exportPackage(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
module.exports = router;
#!/bin/bash
-#
+#
# Copyright 2016 RIFT.IO Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
class ComposerAppActions {
constructor() {
- this.generateActions('showError', 'clearError', 'setDragState', 'propertySelected', 'showJsonViewer', 'closeJsonViewer', 'selectModel', 'outlineModel', 'clearSelection', 'enterFullScreenMode', 'exitFullScreenMode');
+ this.generateActions('showError', 'clearError', 'setDragState', 'propertySelected', 'showJsonViewer', 'closeJsonViewer', 'selectModel', 'outlineModel', 'clearSelection', 'enterFullScreenMode', 'exitFullScreenMode',
+ 'showAssets', 'showDescriptor');
}
}
-export default alt.createActions(ComposerAppActions);
\ No newline at end of file
+export default alt.createActions(ComposerAppActions);
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
const className = ClassNames(this.props.className, 'Button');
return (
<div className={className} onClick={this.props.onClick} title={title} draggable={draggable} onDragStart={this.props.onDragStart}>
- <img src={src} />
- <span>{label}</span>
+ { src ? <img src={src} /> : null }
+ {label}
</div>
);
}
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
import SelectionManager from '../libraries/SelectionManager'
import DescriptorModelIconFactory from '../libraries/model/IconFactory'
+import FileManager from './filemanager/FileManager.jsx';
+
import '../styles/CanvasPanel.scss'
const CanvasPanel = React.createClass({
var req = require.context("../", true, /^\.\/.*\.svg$/);
const hasItem = this.props.containers.length !== 0;
const isEditingNSD = DescriptorModelFactory.isNetworkService(this.props.containers[0]);
+ const isDescriptorView = (this.props.panelTabShown == 'descriptor');
const hasNoCatalogs = this.props.hasNoCatalogs;
const bodyComponent = hasItem ? <CatalogItemCanvasEditor zoom={this.props.zoom} isShowingMoreInfo={this.props.showMore} containers={this.props.containers}/> : messages.canvasWelcome();
+ const viewFiles = this.props.panelTabShown == 'assets';
+ const viewButtonTabs = !hasItem ? null : (
+ <div className="CanvasPanelTabs">
+ <div className="CatalogFilter">
+ <button className={isDescriptorView ? '-selected' : ''} onClick={ComposerAppActions.showDescriptor}>
+ Descriptor
+ </button>
+ <button className={!isDescriptorView ? '-selected' : ''} onClick={ComposerAppActions.showAssets}>
+ Assets
+ </button>
+ </div>
+ </div>
+ )
return (
<div id="canvasPanelDiv" className="CanvasPanel" style={style} onDragOver={this.onDragOver} onDrop={this.onDrop}>
<div onDoubleClick={this.onDblClickOpenFullScreen} className="CanvasPanelHeader panel-header" data-resizable="limit_bottom">
<span className="model-name">{this.props.title}</span>
</h1>
</div>
+ {viewButtonTabs}
<div className="CanvasPanelBody panel-body" style={{marginRight: this.props.layout.right, bottom: this.props.layout.bottom}} >
- {hasNoCatalogs ? null : bodyComponent}
+ {hasNoCatalogs ? null : viewFiles ? <FileManager files={this.props.files} type={this.props.type} item={this.props.item} filesState={this.props.filesState} /> : bodyComponent}
</div>
- <CanvasZoom zoom={this.props.zoom} style={{bottom: this.props.layout.bottom + 20}}/>
- <CanvasPanelTray layout={this.props.layout} show={isEditingNSD}>
+ {
+ isDescriptorView ?
+ <CanvasZoom zoom={this.props.zoom} style={{bottom: this.props.layout.bottom + 20}}/>
+ : null
+ }
+ <CanvasPanelTray layout={this.props.layout} show={isEditingNSD && isDescriptorView}>
<EditForwardingGraphPaths containers={this.props.containers} />
</CanvasPanelTray>
</div>
);
},
onDragOver(event) {
- const isDraggingFiles = _.contains(event.dataTransfer.types, 'Files');
+ const isDraggingFiles = _.includes(event.dataTransfer.types, 'Files');
if (!isDraggingFiles) {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
uiTransientState.isDrop = false;
uiTransientState.isDragging = true;
uiTransientState.wasTrayOpen = this.state.isTrayOpen;
- uiTransientState.isDraggingFiles = _.contains(e.dataTransfer.types, 'Files');
+ uiTransientState.isDraggingFiles = _.includes(e.dataTransfer.types, 'Files');
const dragState = ComposerAppStore.getState().drag || {};
if (uiTransientState.isDraggingFiles || (dragState.type === 'catalog-item')) {
CatalogPanelTrayActions.open();
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
import TooltipManager from '../libraries/TooltipManager'
import CatalogItemsActions from '../actions/CatalogItemsActions'
import CommonUtils from 'utils/utils.js'
-
+import FileManagerActions from './filemanager/FileManagerActions';
import 'normalize.css'
import '../styles/AppRoot.scss'
import 'style/layout.scss'
+
const resizeManager = new ResizableManager(window);
const clearLocalStorage = utils.getSearchParams(window.location).hasOwnProperty('clearLocalStorage');
const preventDefault = e => e.preventDefault();
const clearDragState = () => ComposerAppActions.setDragState(null);
+
const ComposerApp = React.createClass({
mixins: [PureRenderMixin],
getInitialState() {
if (clearLocalStorage) {
window.localStorage.clear();
}
+ if(this.item) {
+ FileManagerActions.openFileManagerSockets();
+ }
this.state.isLoading = CatalogDataStore.getState().isLoading;
ComposerAppStore.listen(this.onChange);
CatalogDataStore.listen(this.onCatalogDataChanged);
window.removeEventListener('dragover', preventDefault);
window.removeEventListener('drop', preventDefault);
window.removeEventListener('drop', clearDragState);
+ FileManagerActions.closeFileManagerSockets();
// resizeManager automatically registered its event handlers
resizeManager.removeAllEventListeners();
ComposerAppStore.unlisten(this.onChange);
const hasNoCatalogs = CatalogDataStore.getState().catalogs.length === 0;
const isLoading = self.state.isLoading;
+ //Bridge element for Crouton fix. Should eventually put Composer on same flux context
+ const Bridge = this.state.ComponentBridgeElement;
+
html = (
<div ref="appRoot" id="RIFT_wareLaunchpadComposerAppRoot" className="AppRoot" onClick={onClickUpdateSelection}>
+ <Bridge />
<i className="corner-accent top left" />
<i className="corner-accent top right" />
<i className="corner-accent bottom left" />
<i className="corner-accent bottom right" />
{AppHeader}
- <Crouton id={Date.now()} type={self.state.messageType} message={self.state.message} onDismiss={ComposerAppActions.clearError} />
<div className="AppBody">
<div className={classNames}>
<CatalogPanel layout={self.state.layout}
showMore={self.state.showMore}
containers={containers}
title={canvasTitle}
- zoom={self.state.zoom} />
- <DetailsPanel layout={self.state.layout}
+ zoom={self.state.zoom}
+ panelTabShown={self.state.panelTabShown}
+ files={self.state.files}
+ filesState={self.state.filesState}
+ item={self.state.item}
+ type={self.state.filterCatalogByTypeValue}
+ />
+ {
+ (self.state.panelTabShown == 'descriptor') ?
+ <DetailsPanel layout={self.state.layout}
hasNoCatalogs={hasNoCatalogs}
showMore={self.state.showMore}
containers={containers}
showJSONViewer={self.state.showJSONViewer} />
+ : null
+ }
+
<ComposerAppToolbar layout={self.state.layout}
showMore={self.state.showMore}
isEditingNSD={isEditingNSD}
isModified={isModified}
isNew={isNew}
disabled={!hasItem}
- onClick={event => event.stopPropagation()}/>
+ onClick={event => event.stopPropagation()}
+ panelTabShown={self.state.panelTabShown}/>
</div>
</div>
<ModalOverlay />
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
);
}
const hasSelection = SelectionManager.getSelections().length > 0;
+ if(this.props.panelTabShown != 'descriptor') {
+ style.pointerEvents = 'none';
+ }
return (
<div className="ComposerAppToolbar" style={style}>
+ {
+ (this.props.panelTabShown != 'descriptor') ?
+ <div className="disableOverlay"></div>
+ : null
+ }
{(()=>{
if (this.props.isEditingNSD || this.props.isEditingVNFD) {
return (
import '../styles/EditDescriptorModelProperties.scss'
function getDescriptorMetaBasicForType(type) {
- const basicPropertiesFilter = d => _.contains(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+ const basicPropertiesFilter = d => _.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
}
function getDescriptorMetaAdvancedForType(type) {
- const advPropertiesFilter = d => !_.contains(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+ const advPropertiesFilter = d => !_.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
return DescriptorModelMetaFactory.getModelMetaForType(type, advPropertiesFilter) || {properties: []};
}
--- /dev/null
+/*
+ *
+ * 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.
+ *
+ */
+
+
+//https://raw.githubusercontent.com/RIFTIO/RIFT.ware/master/rift-shell
+import _ from 'lodash'
+import React from 'react';
+import ReactDOM from 'react-dom';
+import TreeView from 'react-treeview';
+import TextInput from 'widgets/form_controls/textInput.jsx';
+import Button from '../Button';
+import './FileMananger.scss';
+import FileManagerActions from './FileManagerActions.js';
+import imgSave from '../../../../node_modules/open-iconic/svg/data-transfer-upload.svg'
+import {Panel, PanelWrapper} from 'widgets/panel/panel';
+import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
+import LoadingIndicator from 'widgets/loading-indicator/loadingIndicator.jsx';
+
+import DropZone from 'dropzone'
+import Utils from '../../libraries/utils'
+import FileManagerUploadDropZone from '../../libraries/FileManagerUploadDropZone';
+let API_SERVER = require('utils/rw.js').getSearchParams(window.location).api_server;
+
+const createDropZone = function (action, clickable, type, id, path, dropTarget) {
+ const dropZone = new FileManagerUploadDropZone(ReactDOM.findDOMNode(dropTarget), [clickable], action, type, id, path);
+ // dropZone.on('dragover', this.onDragOver);
+ // dropZone.on('dragend', this.onDragEnd);
+ // dropZone.on('addedfile', this.onFileAdded);
+ return dropZone;
+};
+//updateFileLocationInput
+class FileManager extends React.Component {
+ constructor(props) {
+ super(props)
+ }
+ componentWillMount() {
+ // FileManagerActions.openFileManagerSockets()
+ }
+ componentWillUnmount() {
+ // FileManagerActions.closeFileManagerSockets();
+ }
+ generateFolder(data, nesting) {
+ let nestingLevel = nesting || 1;
+
+ }
+ deleteFile(name) {
+ return function(e) {
+ FileManagerActions.deletePackageFile(name);
+ }
+
+ }
+ updateFileLocationInput(name) {
+ return function(e) {
+ FileManagerActions.updateFileLocationInput({
+ name: name,
+ value: e.target.value
+ });
+ }
+ }
+ sendDownloadFileRequst = (url, path) => {
+ let self = this;
+ return function(e) {
+ if(!url || url == "") {
+ return self.props.actions.showNotification.defer({type: 'error', msg: 'Value missing in download request'});;
+ }
+ let files = self.props.files.data;
+ let folder = path.split('/');
+ let splitUrl = url.split('/');
+ let fileName = splitUrl[splitUrl.length - 1];
+ folder.pop;
+ let fullPath = _.cloneDeep(folder);
+ fullPath.push(fileName);
+ fullPath = fullPath.join('/');
+ folder = folder.join('/');
+ let fileIndex = _.findIndex(files[folder], function(f) {
+ return f.name == fullPath;
+ })
+ if (fileIndex == -1) {
+ FileManagerActions.sendDownloadFileRequst({
+ url: url,
+ path: path
+ });
+ } else {
+ self.props.actions.showNotification('It seems you\'re attempting to upload a file with a duplicate file name');
+ }
+ }
+ }
+ render() {
+ let self = this;
+ let html = (
+ <div className="FileManager">
+ <PanelWrapper style={{flexDirection: 'column'}}>
+ {self.props.files && self.props.files.id && buildList(self, self.props.files) }
+ </PanelWrapper>
+ </div>
+ )
+ return html;
+ }
+
+}
+
+function buildList(self, data) {
+ let toReturn = [];
+ data.id.map(function(k,i) {
+ toReturn.push (contentFolder(self, data.data[k], k, i, self.props.filesState, self.updateFileLocationInput, self.sendDownloadFileRequst, self.deleteFile));
+ });
+ return toReturn.reverse();
+}
+
+function contentFolder(context, folder, path, key, inputState, updateFn, sendDownloadFileRequst, deleteFn) {
+ let type = context.props.type;
+ let id = context.props.item.id;
+ const onboardDropZone = createDropZone.bind(this, FileManagerUploadDropZone.ACTIONS.onboard, '.ComposerAppAddFile.' + path.replace('/', '-'), type, id, path);
+ return (
+ <Panel title={path} key={key} itemClassName="nested" no-corners>
+ <div className="folder">
+ {
+ folder.map(function(f, i) {
+ if( !f.hasOwnProperty('contents') ){
+ return contentFile(context, f, path, i, deleteFn);
+ }
+ })
+ }
+ <Panel className="addFileSection" no-corners>
+ <ItemUpload type={type} id={id} path={path} key={key} dropZone={onboardDropZone} />
+ <div style={{marginLeft: '0.5rem'}}>
+ OR
+ </div>
+ <div className="inputSection">
+ <TextInput placeholder="URL" className="" label="External URL" value={inputState[path]} onChange={updateFn(path)} />
+ <Button className='ComposerAppSave' label="DOWNLOAD" onClick={sendDownloadFileRequst(inputState[path], path)}/>
+ </div>
+ </Panel>
+
+ </div>
+ </Panel>
+ );
+}
+class ItemUpload extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ componentDidMount() {
+ if (this.props.dropZone) {
+ const dropTarget = this;
+ const dropZone = this.props.dropZone(dropTarget);
+ }
+ }
+ render() {
+ let {type, id, path, key, ...props} = this.props;
+ return (
+ <div className="inputSection">
+ <label className="sqTextInput" style={{flexDirection: 'row', alignItems:'center'}}>
+ <span>Upload File</span>
+ <Button className={'ComposerAppAddFile ' + path.replace('/', '-')} label="BROWSE"/>
+ </label>
+ </div>
+ )
+ }
+}
+function contentFile(context, file, path, key, deleteFn) {
+ const name = stripPath(file.name, path);
+ const id = context.props.item.id;
+ const type = context.props.type;
+ //{`${window.location.protocol}//${API_SERVER}:4567/api/package${type}/${id}/${path}/${name}`}
+ return (
+ <div className="file" key={key}>
+ <div className="file-section">
+ <div className="file-info">
+ <div className="file-status" style={{display: (file.status && file.status.toLowerCase() != 'completed') ? 'inherit' : 'none', color: (file.status == 'FAILED' ? 'red' : 'inherit')}}>
+ {file.status && (file.status == 'IN_PROGRESS' || file.status == 'DOWNLOADING' ) ? <LoadingIndicator size={2} /> : file.status }
+ </div>
+ <div className="file-name">
+ <a target="_blank" href={`${API_SERVER}:4567/api/package/${type}/${id}/${path}/${name}`}>{name}</a>
+ </div>
+ </div>
+ <div className="file-action" style={{display: (!file.status || (file && file.status.toLowerCase() != 'loading...')) ? 'inherit' : 'none', cursor: 'pointer'}} onClick={deleteFn(file.name)}>X</div>
+ </div>
+ </div>
+ )
+}
+
+function stripPath(name, path, returnPath) {
+ let stripSlash = (name.indexOf('/') > -1) ? '/' : '';
+ // return name.split(path + stripSlash)[1].replace('/', '');
+ let split = name.split(path + stripSlash)[returnPath ? 0 : 1];
+ return split ? split.replace('/', '') : name;
+}
+
+
+
+export default SkyquakeComponent(FileManager)
+/**
+ * Sample Data
+ */
+// let files = {
+// "name": ".",
+// "contents": [
+// {
+// "name": "pong_vnfd",
+// "contents": [
+// {
+// "name": "pong_vnfd/checksums.txt",
+// "last_modified_time": 1474458399.6218443,
+// "byte_size": 168
+// },
+// {
+// "name": "pong_vnfd/pong_vnfd.yaml",
+// "last_modified_time": 1474458399.6258445,
+// "byte_size": 3514
+// },
+// {
+// "name": "pong_vnfd/icons",
+// "contents": [
+// {
+// "name": "pong_vnfd/icons/rift_logo.png",
+// "last_modified_time": 1474458399.6218443,
+// "byte_size": 1658
+// }
+// ],
+// "last_modified_time": 1474458399.6218443,
+// "byte_size": 3
+// },
+// {
+// "name": "pong_vnfd/cloud_init",
+// "contents": [
+// {
+// "name": "pong_vnfd/cloud_init/pong_cloud_init.cfg",
+// "last_modified_time": 1474458399.6258445,
+// "byte_size": 227
+// }
+// ],
+// "last_modified_time": 1474458399.6258445,
+// "byte_size": 3
+// }
+// ],
+// "last_modified_time": 1474458399.6258445,
+// "byte_size": 6
+// }
+// ],
+// "last_modified_time": 1474458399.6218443,
+// "byte_size": 3
+// };
--- /dev/null
+/*
+ *
+ * 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 alt from '../../alt';
+
+class FileManagerActions {
+
+ constructor() {
+ this.generateActions('getFilelistSuccess', 'getFilelistError', 'updateFileLocationInput','sendDownloadFileRequst', 'addFileSuccess', 'addFileError','deletePackageFile','deleteFileSuccess','deleteFileError','openDownloadMonitoringSocketSuccess', 'openDownloadMonitoringSocketError',
+ 'getFilelistSocketSuccess',
+ 'openFileManagerSockets', 'closeFileManagerSockets');
+ }
+
+}
+
+export default alt.createActions(FileManagerActions);
--- /dev/null
+
+/*
+ *
+ * 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.
+ *
+ */
+'use strict';
+
+import _ from 'lodash'
+import $ from 'jquery'
+import alt from '../../alt'
+import utils from '../../libraries/utils'
+import FileManagerActions from './FileManagerActions'
+let Utils = require('utils/utils.js');
+let API_SERVER = require('utils/rw.js').getSearchParams(window.location).api_server;
+let HOST = API_SERVER;
+let NODE_PORT = require('utils/rw.js').getSearchParams(window.location).api_port || ((window.location.protocol == 'https:') ? 8443 : 8000);
+const FileManagerSource = {
+
+ getFilelist: function() {
+ return {
+ remote: function(state, id, type) {
+ return new Promise(function(resolve, reject) {
+ console.log('Getting File Manager');
+ $.ajax({
+ beforeSend: Utils.addAuthorizationStub,
+ url: 'api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id,
+ success: function(data) {
+ resolve(JSON.parse(data));
+ },
+ error: function(error) {
+ if (typeof error == 'string') {
+ error = JSON.parse(error);
+ }
+ reject(error);
+ }
+ }).fail(function(xhr){
+ //Authentication and the handling of fail states should be wrapped up into a connection class.
+ Utils.checkAuthentication(xhr.status);
+ });
+ });
+ },
+ success: FileManagerActions.getFilelistSuccess,
+ error: FileManagerActions.getFilelistError
+ }
+ },
+ addFile: function() {
+ return {
+ remote: function(state, id, type, path, url) {
+ return new Promise(function(resolve, reject) {
+ console.log('Adding file');
+ console.log(id, type, path, url);
+ let splitUrl = url.split('/');
+ let fileName = splitUrl[splitUrl.length -1];
+ $.ajax({
+ beforeSend: Utils.addAuthorizationStub,
+ url: 'api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id + '&package_path=' + path + '/' + fileName + '&url=' + url,
+ success: function(data) {
+ resolve({
+ data:data,
+ path: path,
+ fileName: fileName
+ });
+ },
+ error: function(error) {
+ if (typeof error == 'string') {
+ error = JSON.parse(error);
+ }
+ reject(error);
+ }
+ }).fail(function(xhr){
+ //Authentication and the handling of fail states should be wrapped up into a connection class.
+ Utils.checkAuthentication(xhr.status);
+ });
+ });
+ },
+ success: FileManagerActions.addFileSuccess,
+ error: FileManagerActions.addFileError
+ }
+ },
+ deleteFile: function() {
+ return {
+ remote: function(state, id, type, path) {
+ return new Promise(function(resolve, reject) {
+ $.ajax({
+ method: 'DELETE',
+ beforeSend: Utils.addAuthorizationStub,
+ url: 'api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id + '&package_path=' + path ,
+ success: function(data) {
+ resolve({
+ data: data,
+ path: path
+ });
+ },
+ error: function(error) {
+ if (typeof error == 'string') {
+ error = JSON.parse(error);
+ }
+ reject(error);
+ }
+ }).fail(function(xhr){
+ //Authentication and the handling of fail states should be wrapped up into a connection class.
+ Utils.checkAuthentication(xhr.status);
+ });
+ });
+ },
+ success: FileManagerActions.deleteFileSuccess,
+ error: FileManagerActions.deleteFileError
+ }
+ },
+ updateFile: function() {
+ return {
+ remote: function(state, file) {
+ return new Promise(function(resolve, reject) {
+ console.log('Getting File Manager');
+ if(file) {
+ console.log('Updating single file');
+ }
+ if(!file) {
+ console.log('Update all files')
+ }
+ resolve({});
+ });
+ },
+ success: FileManagerActions.getFilelistSuccess,
+ error: FileManagerActions.getFilelistError
+ }
+ },
+ openDownloadMonitoringSocket: function() {
+ return {
+ remote: function(state, packageID) {
+ return new Promise(function(resolve, reject) {
+ //api/operational/download-jobs/job/
+ $.ajax({
+ url: '/socket-polling?api_server=' + API_SERVER ,
+ type: 'POST',
+ beforeSend: Utils.addAuthorizationStub,
+ data: {
+ url: 'http://localhost:8000/composer/api/file-manager/jobs/' + packageID + '?api_server=' + API_SERVER,
+ },
+ success: function(data, textStatus, jqXHR) {
+ Utils.checkAndResolveSocketRequest(data, resolve, reject);
+ }
+ }).fail(function(xhr){
+ //Authentication and the handling of fail states should be wrapped up into a connection class.
+ Utils.checkAuthentication(xhr.status);
+ });
+ });
+ },
+ success: FileManagerActions.openDownloadMonitoringSocketSuccess,
+ error: FileManagerActions.openDownloadMonitoringSocketError
+ }
+ },
+ openFileMonitoringSocket: function() {
+ return {
+ remote: function(state, id, type) {
+ return new Promise(function(resolve, reject) {
+ //api/operational/download-jobs/job/
+ $.ajax({
+ url: '/socket-polling?api_server=' + API_SERVER ,
+ type: 'POST',
+ beforeSend: Utils.addAuthorizationStub,
+ data: {
+ url: 'http://localhost:8000/composer/api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id
+ },
+ success: function(data, textStatus, jqXHR) {
+ Utils.checkAndResolveSocketRequest(data, resolve, reject);
+ }
+ }).fail(function(xhr){
+ //Authentication and the handling of fail states should be wrapped up into a connection class.
+ Utils.checkAuthentication(xhr.status);
+ });
+ });
+ },
+ success: FileManagerActions.getFilelistSocketSuccess,
+ error: FileManagerActions.getFilelistError
+ }
+ }
+};
+
+export default FileManagerSource;
--- /dev/null
+/*
+ *
+ * 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 'style/_colors.scss';
+
+.FileManager {
+ width:600px;
+ .skyquakePanel {
+ display:block;
+ -ms-flex:0;
+ flex:0;
+ &-wrapper {
+ height:auto;
+ }
+ &-body {
+ padding: 0.5rem;
+ }
+ }
+ .addFileSection {
+ background:$gray;
+ margin:1rem 0;
+ &-body {
+ padding-bottom: 1rem;
+ }
+ .inputSection {
+ display:-ms-flexbox;
+ display:flex;
+ -ms-flex-align: end;
+ align-items: flex-end;
+ padding:0 0 0 0.5rem;
+ margin:0.5rem 0;
+ .sqTextInput {
+ margin-bottom:0rem;
+ input {
+ font-size:10px;
+ height:auto;
+ }
+ }
+ &:last-child {
+ }
+ }
+ .ComposerAppAddFile {
+ span {
+ color: black;
+ }
+ }
+ }
+ .tree-view {
+ margin:0 0.5rem;
+ margin-bottom: 0.5rem;
+ padding:0 0.5rem;
+ /* border-top:1px solid $gray-lightest;*/
+ /* border-left:0.5px solid $gray-lightest;*
+ /* border-bottom:1px solid white;*/
+ /* border: 1px solid rgba(143, 143, 143, 0.5);*/
+ /* background-color: rgba(147, 161, 161, 0.5);*/
+ border-radius: 3px;
+ font-size:10px;
+ &_item {
+ padding:0.5rem;
+ display:-ms-flexbox;
+ display:flex;
+ -ms-flex-align: center;
+ align-items: center;
+ color: #586e75;
+ text-transform: uppercase;
+ font-size:10px;
+ background:white;
+ }
+ &_children {
+ margin-left:0;
+ /* background: $gray;*/
+ padding: 0.125rem;
+ margin-bottom: 0.5rem;
+ /* background: $gray-light;*/
+ &-collapsed {
+ height: 0px;
+ overflow: hidden;
+ }
+ }
+ &_arrow{
+ height: 2rem;
+ width: 2rem;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-pack: center;
+ justify-content: center;
+ -ms-flex-align: center;
+ align-items: center;
+
+ margin-right: 6px;
+
+ font-size: 1.25rem;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+
+ display:none;
+
+ &:after {
+ content: '\25BE';
+ }
+ &-collapsed {
+ transform: rotate(-90deg);
+ }
+ }
+ }
+ .folder {
+ /* margin:0.5rem 0.5rem;*/
+ /* padding: 0.5rem;*/
+ /* background-color: rgba(238, 232, 213, 0.33);*/
+ /* border-radius:3px;*/
+ }
+ .nested {
+ & + div {
+ & > div:not(.nested-alt):not(.folder) {
+ &:nth-child(even) {
+ background: $gray-light;
+ }
+ &:nth-child(odd) {
+ background: $gray;
+ }
+ }
+ }
+ }
+ .file {
+ margin: 0.5rem 0;
+ -ms-flex-pack:justify;
+ justify-content:space-between;
+ &-section {
+ display:-ms-flexbox;
+ display:flex;
+ -ms-flex-pack:justify;
+ justify-content:space-between;
+ &>div {
+ margin:0 0.5rem;
+ }
+ }
+ &-info{
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-pack: center;
+ justify-content: center;
+ -ms-flex-align: center;
+ align-items: center;
+ }
+ &-status {
+ padding-right:0.5rem;
+ }
+ &-name {
+ color:#586e75;
+ }
+ }
+}
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
};
function getCatalogPackageManagerServerOrigin() {
- return Utils.getSearchParams(window.location).upload_server + ':4567';
+ // return Utils.getSearchParams(window.location).upload_server + ':4567';
+ return window.location.origin;
}
function initializeDropZone(element = '#dropzone', button = false, action = ACTIONS.onboard) {
+ let Auth = 'Basic ' + window.sessionStorage.getItem("auth");
+ let dev_download_server = Utils.getSearchParams(window.location).dev_download_server;
DropZone.autoDiscover = false;
return new DropZone(element, {
- paramName: 'descriptor',
+ paramName: 'package',
url() {
if (action === ACTIONS.update) {
return getCatalogPackageManagerServerOrigin() + '/api/update';
}
- return getCatalogPackageManagerServerOrigin() + '/api/upload';
+ return getCatalogPackageManagerServerOrigin() + '/composer/upload?api_server=' + Utils.getSearchParams(window.location).api_server + '&upload_server=' + Utils.getSearchParams(window.location).upload_server + ( dev_download_server ? '&dev_download_server=' + dev_download_server : '');
+ },
+ headers: {
+ 'Authorization': Auth
},
maxFilesize: 10000000000,
clickable: button,
--- /dev/null
+
+/*
+ *
+ * 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.
+ *
+ */
+/**
+ * Created by onvelocity on 10/27/15.
+ */
+
+import guid from '../libraries/guid'
+import DropZone from 'dropzone'
+import Utils from '../libraries/utils'
+import CatalogPackageManagerActions from '../actions/CatalogPackageManagerActions';
+import FileManagerActions from '../components/filemanager/FileManagerActions.js';
+
+/**
+ * This class is responsible for wiring the DropZone.js to our React actions.
+ */
+
+const ACTIONS = {
+ onboard: 'onboard',
+ update: 'update'
+};
+
+function getCatalogPackageManagerServerOrigin() {
+ // return Utils.getSearchParams(window.location).upload_server + ':4567';
+ return window.location.origin;
+}
+
+function initializeDropZone(element = '#dropzone', button = false, action = ACTIONS.onboard, type, id, path) {
+ let Auth = 'Basic ' + window.sessionStorage.getItem("auth");
+ let dev_download_server = Utils.getSearchParams(window.location).dev_download_server;
+ DropZone.autoDiscover = false;
+ return new DropZone(element, {
+ paramName: 'package',
+ url() {
+ if (action === ACTIONS.update) {
+ return getCatalogPackageManagerServerOrigin() + '/api/update';
+ }
+ return getCatalogPackageManagerServerOrigin() + '/composer/api/file-manager?api_server=' + Utils.getSearchParams(window.location).api_server + '&package_type=' + type + '&package_id=' + id + '&package_path=' + path + ( dev_download_server ? '&dev_download_server=' + dev_download_server : '');
+ },
+ headers: {
+ 'Authorization': Auth
+ },
+ maxFilesize: 10000000000,
+ clickable: button,
+ autoProcessQueue: true,
+ previewTemplate: '',
+ sending(file, xhr, formData) {
+ // NOTE ie11 does not get this form data
+ formData.append('id', file.id);
+ FileManagerActions.addFileSuccess({
+ fileName: file.name,
+ path: path
+ })
+ },
+ error(file, errorMessage) {
+ const response = {
+ state: file,
+ data: {
+ status: 'upload-error',
+ message: errorMessage
+ }
+ };
+ CatalogPackageManagerActions.uploadCatalogPackageError(response);
+ },
+ success(file) {
+ const data = JSON.parse(file.xhr.responseText);
+ data.status = 'upload-success';
+ const response = {
+ state: file,
+ data: data
+ };
+ //CatalogPackageManagerActions.uploadCatalogPackageStatusUpdated(response);
+ },
+ addedfile(file) {
+ file.id = file.id || guid();
+ file.riftAction = action;
+ //CatalogPackageManagerActions.uploadCatalogPackage(file);
+ },
+ thumbnail(file, dataUrl) {
+ const response = {
+ state: file,
+ data: {
+ status: 'upload-thumbnail',
+ dataUrl: dataUrl
+ }
+ };
+ //CatalogPackageManagerActions.uploadCatalogPackageStatusUpdated(response);
+ },
+ uploadprogress(file, progress, bytesSent) {
+ // FileManagerActions.addFileSuccess({
+ // path: path,
+ // fileName: file.name
+ // });
+ const response = {
+ state: file,
+ data: {
+ status: 'upload-progress',
+ progress: progress,
+ bytesSent: bytesSent
+ }
+ };
+ //CatalogPackageManagerActions.uploadCatalogPackageStatusUpdated(response);
+ }
+ });
+}
+
+export default class CatalogPackageManagerUploadDropZone {
+
+ constructor(element, button, action, type, id, path) {
+ this.dropZone = initializeDropZone(element, button, action, type, id, path);
+ }
+
+ static get ACTIONS() {
+ return ACTIONS;
+ }
+
+ on(eventName, eventCallback) {
+ this.dropZone.on(eventName, eventCallback);
+ }
+
+}
--- /dev/null
+/*
+ *
+ * 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 guid from '../libraries/guid'
+import DropZone from 'dropzone'
+import Utils from '../libraries/utils'
+import CatalogPackageManagerActions from '../actions/CatalogPackageManagerActions'
+import ReactDOM from 'react-dom'
+import $ from 'jquery'
+
+const API_SERVER = Utils.getSearchParams(window.location).api_server;
+
+
+
+
+export default class PackageManager {
+ constructor(element, button, action) {
+ this.stagingArea = {
+ packages: {
+ ids: []
+ }
+ }
+ this.stagingAreaMonitor = null;
+ }
+ createStagingArea(type, name) {
+ return $.ajax({
+ url: Utils.getSearchParams(window.location).api_server + ':8008/api/operations/create-staging-area',
+ type: 'POST',
+ data: {
+ "input" : {
+ // Package type not important for package upload.
+ "package-type": type || "NSD",
+ "name": name || "Package Staging Area"
+ }
+ },
+ error: function() {
+ console.log('Something went wrong creating the staging area: ', arguments)
+ }
+ }).then(function(data) {
+ /*
+ {
+ "output": {
+ "endpoint": "api/upload/85f8e2dc-638b-46e7-89cb-ee8de322066f",
+ "port": "4568"
+ }
+ }
+ */
+ const id = data.output.endpoint.split('/')[2];
+ const port = data.output.port;
+ this.stagingArea.packages.ids.push(id);
+ this.stagingArea.packages[id] = {
+ port: port
+ };
+ return data
+ })
+ }
+ monitoringStagingAreaSocket() {
+ let self = this;
+ if(self.stagingAreaMonitor) {
+ return self.stagingAreaMonitor;
+ }
+ new Promise(function(resolve, reject) {
+ $.ajax({
+ url: '/socket-polling?api_server=' + API_SERVER ,
+ type: 'POST',
+ beforeSend: Utils.addAuthorizationStub,
+ data: {
+ url: 'launchpad/api/nsr?api_server=' + API_SERVER
+ },
+ success: function(data, textStatus, jqXHR) {
+ Utils.checkAndResolveSocketRequest(data, resolve, reject, self.monitoringStagingAreaSocketHandler);
+ }
+ })
+ })
+
+ return undefined;
+ }
+ monitoringStagingAreaSocketHandler(connection) {
+ let self = this;
+ let ws = window.multiplexer.channel(connection);
+ if (!connection) return;
+ self.stagingAreaMonitor = connection;
+ ws.onmessage = function(socket) {
+ try {
+ Utils.checkAuthentication(data.statusCode, function() {
+ ws.close();
+ });
+
+ } catch(e) {
+ console.log('An exception occurred in monitoringStagingAreaSocketHandler', e)
+ }
+ }
+ }
+
+}
+
+
+
return !/^(leaf|leaf_list)$/.test(property.type);
},
isSimpleList(property = {}) {
- return _.contains(DescriptorModelFields.simpleList, property.name);
+ return _.includes(DescriptorModelFields.simpleList, property.name);
},
isPrimativeDataType(property = {}) {
const Property = this;
if (uiState.name === 'name') {
return changeCase.param(parentMeta.name) + '-' + InstanceCounter.count(parentMeta[':qualified-type']);
}
- if (_.isArray(parentMeta.key) && _.contains(parentMeta.key, uiState.name)) {
+ if (_.isArray(parentMeta.key) && _.includes(parentMeta.key, uiState.name)) {
if (/uuid/.test(uiState['data-type'])) {
return guid();
}
return utils.getSearchParams(window.location).upload_server + ':4567';
}
-function ajaxRequest(path, catalogPackage, resolve, reject, method = 'GET') {
- $.ajax({
+function ajaxRequest(path, catalogPackage, resolve, reject, method = 'GET', input, urlOverride) {
+ let options = {
url: getApiServerOrigin() + path,
type: method,
beforeSend: Utils.addAuthorizationStub,
state: catalogPackage
});
}
- }).fail(function(xhr){
+ };
+ if(input) {
+ options.data = input;
+ }
+ if (urlOverride) {
+ options.url = window.location.origin + path;
+ }
+ $.ajax(options).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
Utils.checkAuthentication(xhr.status);
});
}
+
+
const CatalogPackageManagerSource = {
- requestCatalogPackageDownload: function () {
+ requestCatalogPackageDownload: function () {
return {
- remote: function (state, download, format, grammar) {
+ remote: function (state, download, format, grammar, schema) {
return new Promise((resolve, reject) => {
// the server does not add a status in the payload
// so we add one so that the success handler will
// Backend no longer supports mixed multi-package download.
// Probably does not even support multi-package download of same type.
// Hence, pick the type from the first element.
- const path = '/api/export/' + download['catalogItems'][0]['uiState']['type'] + '?schema=' + format + '&grammar=' + grammar + '&format=yaml&ids=' + download.ids;
- ajaxRequest(path, download, setStatusBeforeResolve, reject);
- });
+ const data = {
+ "package-type": download['catalogItems'][0]['uiState']['type'].toUpperCase(),
+ "package-id": download.ids,
+ "export-format": format && format.toUpperCase() || 'YAML',
+ "export-grammar": grammar && grammar.toUpperCase() || 'OSM',
+ "export-schema": schema && schema.toUpperCase() || "RIFT"
+ }
+ const path = "/composer/api/package-export?api_server=" + utils.getSearchParams(window.location).api_server;
+ ajaxRequest(path, download, setStatusBeforeResolve, reject, 'POST', data, true);
+ })
+ //.then(function(data) {
+ // let filename = data.data.output.filename;
+ // window.open(getApiServerOrigin() + "/api/export/" + filename, "_blank")
+ //});
},
success: CatalogPackageManagerActions.downloadCatalogPackageStatusUpdated,
error: CatalogPackageManagerActions.downloadCatalogPackageError
downloadCatalogPackage(data) {
let catalogItems = data['selectedItems'] || [];
- let format = data['selectedFormat'] || 'mano';
+ let schema = data['selectedFormat'] || 'mano';
let grammar = data['selectedGrammar'] || 'osm';
+ let format = "YAML";
if (catalogItems.length) {
const catalogPackage = Object.assign({}, defaults.downloadPackage, {id: guid()});
catalogPackage.name = catalogItems[0].name;
catalogPackage.ids = catalogItems.map(d => d.id).sort().toString();
catalogPackage.catalogItems = catalogItems;
this.addPackage(catalogPackage);
- this.getInstance().requestCatalogPackageDownload(catalogPackage, format, grammar).catch(exception);
+ this.getInstance().requestCatalogPackageDownload(catalogPackage, format, grammar, schema).catch(exception);
}
}
success: false,
error: false
};
- const responseData = response.data;
+ const responseData = (response.data.output) ? response.data.output : response.data;
const catalogPackage = response.state;
- switch(responseData.status) {
+ switch(response.data.status) {
case 'upload-progress':
statusInfo.pending = true;
statusInfo.progress = parseFloat(responseData.progress) || 0;
statusInfo.pending = true;
statusInfo.progress = 100;
statusInfo.message = 'Upload completed.';
- statusInfo.transactionId = responseData.transaction_id;
+ statusInfo.transactionId = responseData['transaction-id'] || catalogPackage.transactionId;
break;
case 'upload-error':
statusInfo.error = true;
case 'download-requested':
statusInfo.pending = true;
statusInfo.progress = 25;
- statusInfo.transactionId = responseData.transaction_id;
+ statusInfo.transactionId = responseData['transaction-id'] || catalogPackage.transactionId;
break;
case 'pending':
statusInfo.pending = true;
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
import CatalogDataStore from '../stores/CatalogDataStore'
import isFullScreen from '../libraries/isFullScreen'
+import FileManagerSource from '../components/filemanager/FileManagerSource';
+import FileManagerActions from '../components/filemanager/FileManagerActions';
+
+import React from 'react';
+
+//Hack for crouton fix. Should eventually put composer in skyquake alt context
+import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
+let NotificationError = null;
+class ComponentBridge extends React.Component {
+ constructor(props) {
+ super(props);
+ NotificationError = this.props.flux.actions.global.showNotification;
+ }
+ render(){
+ return <i></i>
+ }
+}
const getDefault = (name, defaultValue) => {
const val = window.localStorage.getItem('defaults-' + name);
if (val) {
class ComposerAppStore {
constructor() {
+ //Bridge for crouton fix
+ this.ComponentBridgeElement = SkyquakeComponent(ComponentBridge);
+
+ this.exportAsync(FileManagerSource)
// the catalog item currently being edited in the composer
this.item = null;
// the left and right sides of the canvas area
this.showClassifiers = {};
this.editPathsMode = false;
this.fullScreenMode = false;
+ this.panelTabShown = 'descriptor';
+ //File manager values
+ this.files = [];
+ this.filesState = {};
+ this.downloadJobs = {};
+ //End File manager values
this.bindListeners({
onResize: PanelResizeAction.RESIZE,
editCatalogItem: CatalogItemsActions.EDIT_CATALOG_ITEM,
openCanvasPanelTray: CanvasPanelTrayActions.OPEN,
closeCanvasPanelTray: CanvasPanelTrayActions.CLOSE,
enterFullScreenMode: ComposerAppActions.ENTER_FULL_SCREEN_MODE,
- exitFullScreenMode: ComposerAppActions.EXIT_FULL_SCREEN_MODE
+ exitFullScreenMode: ComposerAppActions.EXIT_FULL_SCREEN_MODE,
+ showAssets: ComposerAppActions.showAssets,
+ showDescriptor: ComposerAppActions.showDescriptor,
+ getFilelistSuccess: FileManagerActions.getFilelistSuccess,
+ updateFileLocationInput: FileManagerActions.updateFileLocationInput,
+ sendDownloadFileRequst: FileManagerActions.sendDownloadFileRequst,
+ addFileSuccess: FileManagerActions.addFileSuccess,
+ deletePackageFile: FileManagerActions.deletePackageFile,
+ deleteFileSuccess: FileManagerActions.deleteFileSuccess,
+ closeFileManagerSockets: FileManagerActions.closeFileManagerSockets,
+ openFileManagerSockets: FileManagerActions.openFileManagerSockets,
+ openDownloadMonitoringSocketSuccess: FileManagerActions.openDownloadMonitoringSocketSuccess,
+ getFilelistSocketSuccess: FileManagerActions.getFilelistSocketSuccess
});
+ this.exportPublicMethods({
+ closeFileManagerSockets: this.closeFileManagerSockets.bind(this)
+ })
}
onResize(e) {
}
editCatalogItem(item) {
+ let self = this;
if (item && item.uiState) {
item.uiState.isOpenForEdit = true;
if (item.uiState.type !== 'nsd') {
}
SelectionManager.select(item);
this.updateItem(item);
+ this.openFileManagerSockets(item)
}
-
catalogItemMetaDataChanged(item) {
this.updateItem(item);
}
}
showError(data) {
- this.setState({message: data.errorMessage, messageType: 'error'});
+ NotificationError.defer({msg: data.errorMessage, type: 'error'})
+ // this.setState({message: data.errorMessage, messageType: 'error'});
}
clearError() {
this.setState({fullScreenMode: false});
}
+ showAssets() {
+ this.setState({
+ panelTabShown: 'assets'
+ });
+ }
+ showDescriptor() {
+ this.setState({
+ panelTabShown: 'descriptor'
+ });
+ }
+
+ //File Manager methods
+ getFilelistSuccess(data) {
+ let self = this;
+ let filesState = null;
+ if (self.fileMonitoringSocketID) {
+ filesState = addInputState( _.cloneDeep(this.filesState),data);
+ // filesState = _.merge(self.filesState, addInputState({},data));
+ let normalizedData = normalizeTree(data);
+ this.setState({
+ files: {
+ data: _.mergeWith(normalizedData.data, self.files.data, function(obj, src) {
+ return _.uniqBy(obj? obj.concat(src) : src, 'name');
+ }),
+ id: self.files.id || normalizedData.id
+ },
+ filesState: filesState
+ });
+ }
+ function normalizeTree(data) {
+ let f = {
+ id:[],
+ data:{}
+ };
+ data.contents.map(getContents);
+ function getContents(d) {
+ if(d.hasOwnProperty('contents')) {
+ let contents = [];
+ d.contents.map(function(c,i) {
+ if (!c.hasOwnProperty('contents')) {
+ contents.push(c);
+ } else {
+ getContents(c);
+ }
+ })
+ f.id.push(d.name);
+ f.data[d.name] = contents;
+ }
+ }
+ return f;
+ }
+ function addInputState(obj, d) {
+ d.newFile = '';
+ if(d.hasOwnProperty('contents')) {
+ d.contents.map(addInputState.bind(null, obj))
+ }
+ if(!obj[d.name]) {
+ obj[d.name] = '';
+ }
+ return obj;
+ }
+ }
+ sendDownloadFileRequst(data) {
+ let id = data.id || this.item.id;
+ let type = data.type || this.item.uiState.type;
+ let path = data.path;
+ let url = data.url;
+ this.getInstance().addFile(id, type, path, url);
+ }
+ updateFileLocationInput = (data) => {
+ let name = data.name;
+ let value = data.value;
+ var filesState = _.cloneDeep(this.filesState);
+ filesState[name] = value;
+ this.setState({
+ filesState: filesState
+ });
+ }
+ addFileSuccess = (data) => {
+ let path = data.path;
+ let fileName = data.fileName;
+ let files = _.cloneDeep(this.files);
+ let loadingIndex = files.data[path].push({
+ status: 'DOWNLOADING',
+ name: path + '/' + fileName
+ }) - 1;
+ this.setState({files: files});
+
+ }
+ startWatchingJob = () => {
+ let ws = window.multiplexer.channel(this.jobSocketId);
+ this.setState({
+ jobSocket:null
+ })
+ }
+ openDownloadMonitoringSocketSuccess = (id) => {
+ let self = this;
+ let ws = window.multiplexer.channel(id);
+ let downloadJobs = _.cloneDeep(self.downloadJobs);
+ let newFiles = {};
+ ws.onmessage = (socket) => {
+ if (self.files && self.files.length > 0) {
+ let jobs = [];
+ try {
+ jobs = JSON.parse(socket.data);
+ } catch(e) {}
+ newFiles = _.cloneDeep(self.files);
+ jobs.map(function(j) {
+ //check if not in completed state
+ let fullPath = j['package-path'];
+ let path = fullPath.split('/');
+ let fileName = path.pop();
+ path = path.join('/');
+ let index = _.findIndex(self.files.data[path], function(o){
+ return fullPath == o.name
+ });
+ if((index > -1) && newFiles.data[path][index]) {
+ newFiles.data[path][index].status = j.status
+ } else {
+ if(j.status.toUpperCase() == 'LOADING...' || j.status.toUpperCase() == 'IN_PROGRESS') {
+ newFiles.data[path].push({
+ status: j.status,
+ name: fullPath
+ })
+ } else {
+ // if ()
+ }
+ }
+ })
+ self.setState({
+ files: newFiles
+ })
+ // console.log(JSON.parse(socket.data));
+ }
+ }
+ this.setState({
+ jobSocketId: id,
+ jobSocket: ws
+ })
+
+ }
+ getFilelistSocketSuccess = (id) => {
+ let self = this;
+ let ws = window.multiplexer.channel(id);
+ ws.onmessage = (socket) => {
+ if (self.fileMonitoringSocketID) {
+ let data = [];
+ try {
+ data = JSON.parse(socket.data);
+ } catch(e) {}
+ self.getFilelistSuccess(data)
+ }
+ }
+ this.setState({
+ fileMonitoringSocketID: id,
+ fileMonitoringSocket: ws
+ })
+
+ }
+ closeFileManagerSockets() {
+ this.fileMonitoringSocketID = null;
+ this.setState({
+ jobSocketId : null,
+ fileMonitoringSocketID : null
+ // jobSocket : null,
+ // fileMonitoringSocket : null,
+ });
+ this.jobSocket && this.jobSocket.close();
+ this.fileMonitoringSocket && this.fileMonitoringSocket.close();
+ console.log('closing');
+ }
+ openFileManagerSockets(i) {
+ let self = this;
+ let item = i || self.item;
+ this.files = {data:[]};
+ // this.closeFileManagerSockets();
+ this.getInstance().openFileMonitoringSocket(item.id, item.uiState.type).then(function() {
+ // // self.getInstance().openDownloadMonitoringSocket(item.id);
+ });
+ this.getInstance().openDownloadMonitoringSocket(item.id);
+ }
+ endWatchingJob(id) {
+
+ }
+ deletePackageFile(name) {
+ let id = this.item.id;
+ let type = this.item.uiState.type;
+ this.getInstance().deleteFile(id, type, name);
+ }
+ deleteFileSuccess = (data) => {
+ let path = data.path.split('/')
+ let files = _.cloneDeep(this.files);
+ path.pop();
+ path = path.join('/');
+ let pathFiles = files.data[path]
+ _.remove(pathFiles, function(c) {
+ return c.name == data.path;
+ });
+
+ this.setState({
+ files: files
+ })
+ }
}
export default alt.createStore(ComposerAppStore, 'ComposerAppStore');
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
.CanvasPanelBody {
position: absolute;
bottom: 0;
+ top: 142px;
+ }
+ .CanvasPanelTabs {
+ height: 32px;
+ margin-top: 49px;
+ background-color: #cbd1d1;
+ .CatalogFilter {
+ max-width:25%;
+ margin-left: 1rem;
+ margin-top: 3px;
+ button {
+ -ms-flex-pack: center;
+ justify-content: center;
+ &.-selected {
+ background:#e5e5e5;
+ }
+ }
+ }
}
}
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
@import 'main';
.ComposerAppToolbar {
- @extend .panel-header;
- top: 56px;
- left: 300px;
+ @extend .panel-header;
+ top: 56px;
+ left: 300px;
right: 0;
- height: 55px;
- z-index: 4;
+ height: 55px;
+ z-index: 4;
position: absolute;
padding: 10px 20px;
background-color: $panel-bg-color-contrast;
- white-space: nowrap;
- div {
- display: inline-block;
- }
+ white-space: nowrap;
+ div {
+ display: inline-block;
+ }
+ .disableOverlay {
+ background-color: #cbd1d1;
+ width: 100%;
+ height: 40px;
+ opacity: 0.8;
+ position: absolute;
+ pointer-events: none;
+ }
}
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
@import 'variables';
path[data-outline-indicator-svg-outline-path] {
- stroke-dasharray: 0;
- stroke: #b58900;
- stroke-width: 5px;
+ stroke-dasharray: 0;
+ stroke: #b58900;
+ stroke-width: 5px;
}
$font-path: '../assets';
$font-family-default: $font-family-sans-serif;
html, body {
- background: $primary-bg-color;
- cursor: auto;
- font-family: $font-family-sans-serif;
- overflow: hidden;
+ background: $primary-bg-color;
+ cursor: auto;
+ font-family: $font-family-sans-serif;
+ overflow: hidden;
}
::-webkit-scrollbar {
- //display: none;
+ /*display: none;*/
}
html {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
+ box-sizing: border-box;
}
* {
- margin: 0;
- border: 0;
- padding: 0;
- border-collapse: collapse;
+ margin: 0;
+ border: 0;
+ padding: 0;
+ border-collapse: collapse;
}
*, *:before, *:after {
- -webkit-box-sizing: inherit;
- -moz-box-sizing: inherit;
- box-sizing: inherit;
+ box-sizing: inherit;
}
h1 {
- margin: 0;
- padding: 3px 6px;
- color: #323232;
- font-size: 0.75em;
- font-weight: normal;
- text-align: center;
- font-variant-caps: all-small-caps;
- font-variant: small-caps;
- text-transform: uppercase;
+ margin: 0;
+ padding: 3px 6px;
+ color: #323232;
+ font-size: 0.75em;
+ font-weight: normal;
+ text-align: center;
+ font-variant-caps: all-small-caps;
+ font-variant: small-caps;
+ text-transform: uppercase;
}
img {
- vertical-align: middle;
+ vertical-align: middle;
}
button,
text-decoration: none;
text-transform: uppercase;
- &:hover {
- background-color: #7E9BC1;
- color:#ffffff;
- }
- &:active, &:visited{
- background-color:#4C5E74;
- color:#ffffff;
- }
+ &:hover {
+ background-color: #7E9BC1;
+ color:#ffffff;
+ }
+ &:active, &:visited{
+ background-color:#4C5E74;
+ color:#ffffff;
+ }
- &.-selected {
- background-color: rgb(159, 196, 244);
- }
+ &.-selected {
+ background-color: rgb(159, 196, 244);
+ }
}
input[type="image"] {
- width: 28px;
- height: 28px;
- margin-right: 12px;
- border: 0;
- border-radius: 4px;
- padding: 4px;
+ width: 28px;
+ height: 28px;
+ margin-right: 12px;
+ border: 0;
+ border-radius: 4px;
+ padding: 4px;
}
input,
select,
textarea {
- height: 25px;
- line-height: 25px;
- max-width: 100%;
- min-width: 100%;
- margin: 0;
- padding: 0 8px;
- border: 1px solid $field-background-color !important;;
- border-radius: $field-border-radius;
- color: #002b36;
- background-color: $field-background-color !important;
- vertical-align: top;
- &:focus {
- color: #002b36;
- background-color: white !important;
- }
- &::-webkit-input-placeholder {
- color: #eee8d5 !important;
- }
-
- &:-moz-placeholder { /* Firefox 18- */
- color: #eee8d5 !important;
- }
-
- &::-moz-placeholder { /* Firefox 19+ */
- color: #eee8d5 !important;
- }
-
- &:-ms-input-placeholder {
- color: #eee8d5 !important;
- }
+ height: 25px;
+ line-height: 25px;
+ max-width: 100%;
+ min-width: 100%;
+ margin: 0;
+ padding: 0 8px;
+ border: 1px solid $field-background-color !important;;
+ border-radius: $field-border-radius;
+ color: #002b36;
+ background-color: $field-background-color !important;
+ vertical-align: top;
+ &:focus {
+ color: #002b36;
+ background-color: white !important;
+ }
+ &::-webkit-input-placeholder {
+ color: #eee8d5 !important;
+ }
+
+ &:-moz-placeholder { /* Firefox 18- */
+ color: #eee8d5 !important;
+ }
+
+ &::-moz-placeholder { /* Firefox 19+ */
+ color: #eee8d5 !important;
+ }
+
+ &:-ms-input-placeholder {
+ color: #eee8d5 !important;
+ }
}
select {
- padding-right: 0;
- margin-right: 0;
- -moz-appearance: none;
- text-indent: 0.01px;
- text-overflow: '';
- -webkit-appearance: none;
- -webkit-border-radius: $field-border-radius;
- &.-value-not-set {
- color: #eee8d5;
- }
+ padding-right: 0;
+ margin-right: 0;
+ -moz-appearance: none;
+ text-indent: 0.01px;
+ text-overflow: '';
+ -webkit-appearance: none;
+ -webkit-border-radius: $field-border-radius;
+ &.-value-not-set {
+ color: #eee8d5;
+ }
}
select {
- appearance: none; // using -prefix-free http://leaverou.github.io/prefixfree/
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none; /* using -prefix-free http://leaverou.github.io/prefixfree/*/
background: $field-background-color url(../../../node_modules/open-iconic/svg/caret-bottom.svg) no-repeat right 6px center;
- background-size: 10px;
- border: {
- color: $field-background-color;
- radius: $field-border-radius;
- style: solid;
- width: 1px;
- }
-}
-
-// Removes default arrow for IE10+
-// IE 8/9 get dafault arrow which covers caret image
-// as long as caret image is small than and positioned
-// behind default arrow
+ background-size: 10px;
+ border: {
+ color: $field-background-color;
+ radius: $field-border-radius;
+ style: solid;
+ width: 1px;
+ }
+}
+
+/* Removes default arrow for IE10+*/
+/* IE 8/9 get dafault arrow which covers caret image*/
+/* as long as caret image is small than and positioned*/
+/* behind default arrow*/
select::-ms-expand {
- display: none;
+ display: none;
}
textarea {
- height: 50px;
+ height: 50px;
}
input[name$="id"],
input.-is-guid {
- font-size: 10px;
- font-family: monospace;
+ font-size: 10px;
+ font-family: monospace;
}
.ContentEditableDiv {
- margin: 0;
- border: 0;
- padding: 0;
- overflow: hidden;
- border-radius: $field-border-radius;
- height: 25px;
- line-height: 25px;
+ margin: 0;
+ border: 0;
+ padding: 0;
+ overflow: hidden;
+ border-radius: $field-border-radius;
+ height: 25px;
+ line-height: 25px;
}
.btn-group {
- white-space: nowrap;
- button {
- border-right: 1px solid $button-border-color;
- &:first-of-type {
- border-top-left-radius: 2px;
- border-bottom-left-radius: 2px;
- }
- &:last-of-type {
- border-right: none;
- border-top-right-radius: 2px;
- border-bottom-right-radius: 2px;
- }
- }
+ white-space: nowrap;
+ button {
+ border-right: 1px solid $button-border-color;
+ &:first-of-type {
+ border-top-left-radius: 2px;
+ border-bottom-left-radius: 2px;
+ }
+ &:last-of-type {
+ border-right: none;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ }
+ }
}
.panel {
- overflow: hidden;
- -ms-overflow-style: -ms-autohiding-scrollbar;
- background-color: $panel-bg-color;
- > div {
- min-width: 200px;
- }
+ overflow: hidden;
+ -ms-overflow-style: -ms-autohiding-scrollbar;
+ background-color: $panel-bg-color;
+ > div {
+ min-width: 200px;
+ }
}
.panel-header {
- h1 {
- padding: 16px;
- white-space: nowrap;
- text-align: center;
- }
+ h1 {
+ padding: 16px;
+ white-space: nowrap;
+ text-align: center;
+ }
}
.panel-body {
- position: absolute;
- overflow: hidden;
- &:hover {
- overflow: auto;
- }
- -ms-overflow-style: -ms-autohiding-scrollbar;
- top: 111px;
- bottom: 0px;
- left: 0;
- right: 0;
+ position: absolute;
+ overflow: hidden;
+ &:hover {
+ overflow: auto;
+ }
+ -ms-overflow-style: -ms-autohiding-scrollbar;
+ top: 111px;
+ bottom: 0px;
+ left: 0;
+ right: 0;
}
.panel-footer {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- height: 40px;
- padding: 0 12px;
- background-color: $panel-bg-color-contrast;
- border-top: 1px solid $panel-border-color-light;
- white-space: nowrap;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 40px;
+ padding: 0 12px;
+ background-color: $panel-bg-color-contrast;
+ border-top: 1px solid $panel-border-color-light;
+ white-space: nowrap;
}
.welcome-message {
- margin: 20px;
- color: #06173c;
- font-size: x-large;
- font-weight: 200;
- .Button {
- display: inline-block;
- }
+ margin: 20px;
+ color: #06173c;
+ font-size: x-large;
+ font-weight: 200;
+ .Button {
+ display: inline-block;
+ }
}
/* react-tooltip overrides */
.__react_component_tooltip {
- transition: opacity 300ms cubic-bezier(0.230, 1.000, 0.320, 1.000) !important;
+ transition: opacity 300ms cubic-bezier(0.230, 1.000, 0.320, 1.000) !important;
}
/* simple css-only drop-down menu */
.menu {
- display: inline-flex;
- position: relative;
- z-index: 9999;
- > .Button:after {
- content: "ˇ";
- position: absolute;
- right: 4px;
- bottom: -13px;
- font-size: 20px;
- }
- .sub-menu {
- display: none;
- position: absolute;
- top: 36px;
- left: -4px;
- padding: 4px;
- background-color: #f1f1f1;
- box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
- border-radius: 3px;
- .Button {
- display: block;
- text-align: left;
- }
- &:hover {
- display: block;
- }
- }
- > .Button:hover .sub-menu {
- display: block;
- }
- &:hover .sub-menu {
- display: block;
- }
+ display: -ms-inline-flexbox;
+ display: inline-flex;
+ position: relative;
+ z-index: 9999;
+ > .Button:after {
+ content: "ˇ";
+ position: absolute;
+ right: 4px;
+ bottom: -13px;
+ font-size: 20px;
+ }
+ .sub-menu {
+ display: none;
+ position: absolute;
+ top: 36px;
+ left: -4px;
+ padding: 4px;
+ background-color: #f1f1f1;
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+ border-radius: 3px;
+ .Button {
+ display: block;
+ text-align: left;
+ }
+ &:hover {
+ display: block;
+ }
+ }
+ > .Button:hover .sub-menu {
+ display: block;
+ }
+ &:hover .sub-menu {
+ display: block;
+ }
}
.descriptor {
- text {
- &.badge {
- font-weight: 100;
- font-size: 12px;
- text-anchor: middle;
- fill: white;/* font color */
- stroke: white !important;/* font color */
- }
- }
+ text {
+ &.badge {
+ font-weight: 100;
+ font-size: 12px;
+ text-anchor: middle;
+ fill: white;/* font color */
+ stroke: white !important;/* font color */
+ }
+ }
}
.hint {
- margin: 5px;
+ margin: 5px;
+}
+
+.FileManager {
+ margin-top:1rem;
}