Package Mananger 83/883/1
authorLaurence Maultsby <laurence.maultsby@riftio.com>
Fri, 6 Jan 2017 16:01:19 +0000 (16:01 +0000)
committerLaurence Maultsby <laurence.maultsby@riftio.com>
Fri, 6 Jan 2017 16:01:19 +0000 (16:01 +0000)
Signed-off-by: Laurence Maultsby <laurence.maultsby@riftio.com>
27 files changed:
skyquake/plugins/composer/api/composer.js
skyquake/plugins/composer/api/packageFileHandler.js [new file with mode: 0644]
skyquake/plugins/composer/config.json
skyquake/plugins/composer/package.json
skyquake/plugins/composer/routes.js
skyquake/plugins/composer/scripts/install.sh
skyquake/plugins/composer/src/src/actions/ComposerAppActions.js
skyquake/plugins/composer/src/src/components/Button.js
skyquake/plugins/composer/src/src/components/CanvasPanel.js
skyquake/plugins/composer/src/src/components/CatalogPanel.js
skyquake/plugins/composer/src/src/components/ComposerApp.js
skyquake/plugins/composer/src/src/components/ComposerAppToolbar.js
skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
skyquake/plugins/composer/src/src/components/filemanager/FileManager.jsx [new file with mode: 0644]
skyquake/plugins/composer/src/src/components/filemanager/FileManagerActions.js [new file with mode: 0644]
skyquake/plugins/composer/src/src/components/filemanager/FileManagerSource.js [new file with mode: 0644]
skyquake/plugins/composer/src/src/components/filemanager/FileMananger.scss [new file with mode: 0644]
skyquake/plugins/composer/src/src/libraries/CatalogPackageManagerUploadDropZone.js
skyquake/plugins/composer/src/src/libraries/FileManagerUploadDropZone.js [new file with mode: 0644]
skyquake/plugins/composer/src/src/libraries/PackageManagerApi.js [new file with mode: 0644]
skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaProperty.js
skyquake/plugins/composer/src/src/sources/CatalogPackageManagerSource.js
skyquake/plugins/composer/src/src/stores/CatalogPackageManagerStore.js
skyquake/plugins/composer/src/src/stores/ComposerAppStore.js
skyquake/plugins/composer/src/src/styles/CanvasPanel.scss
skyquake/plugins/composer/src/src/styles/ComposerAppToolbar.scss
skyquake/plugins/composer/src/src/styles/_main.scss

index d9dc5e2..ce12860 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,9 +22,13 @@ var rp = require('request-promise');
 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) {
@@ -298,4 +302,295 @@ Composer.update = 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
+};
diff --git a/skyquake/plugins/composer/api/packageFileHandler.js b/skyquake/plugins/composer/api/packageFileHandler.js
new file mode 100644 (file)
index 0000000..f199610
--- /dev/null
@@ -0,0 +1,59 @@
+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
index 2a8a516..80a2a0c 100644 (file)
@@ -1,4 +1,6 @@
 {
+    "copyright": "2016 RIFT.IO Inc",
+    "license": "Apache 2.0",
     "root": "public",
     "name": "Catalog",
     "dashboard" : "./src/components/ComposerApp.js",
index 1cf415a..6a01af1 100644 (file)
     "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",
index af67bfa..9fcf104 100644 (file)
@@ -1,5 +1,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);
@@ -51,5 +103,53 @@ router.put('/api/catalog/:catalogType/:id', cors(), function(req, 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;
index 76e5e75..9d27f5e 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-# 
+#
 #   Copyright 2016 RIFT.IO Inc
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
index 03e784a..80ec497 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,9 +24,10 @@ import alt from '../alt';
 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);
index 7a5a7f5..76a569f 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -59,8 +59,8 @@ const Button = React.createClass({
                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>
                );
        }
index 6b04bd9..96904ea 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,6 +35,8 @@ import EditForwardingGraphPaths from './EditorForwardingGraph/EditForwardingGrap
 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({
@@ -69,8 +71,22 @@ 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">
@@ -79,18 +95,23 @@ const CanvasPanel = React.createClass({
                                                <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';
index 535a928..d7ebf0f 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -167,7 +167,7 @@ const CatalogPanel = React.createClass({
                        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();
index 9e6daa4..b5cfa75 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -47,11 +47,12 @@ import CatalogDataStore from '../stores/CatalogDataStore'
 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');
@@ -59,6 +60,7 @@ const clearLocalStorage = utils.getSearchParams(window.location).hasOwnProperty(
 const preventDefault = e => e.preventDefault();
 const clearDragState = () => ComposerAppActions.setDragState(null);
 
+
 const ComposerApp = React.createClass({
        mixins: [PureRenderMixin],
        getInitialState() {
@@ -71,6 +73,9 @@ const ComposerApp = React.createClass({
                if (clearLocalStorage) {
                        window.localStorage.clear();
                }
+        if(this.item) {
+            FileManagerActions.openFileManagerSockets();
+        }
                this.state.isLoading = CatalogDataStore.getState().isLoading;
                ComposerAppStore.listen(this.onChange);
                CatalogDataStore.listen(this.onCatalogDataChanged);
@@ -88,6 +93,7 @@ const ComposerApp = React.createClass({
                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);
@@ -178,14 +184,17 @@ const ComposerApp = React.createClass({
                        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}
@@ -197,12 +206,23 @@ const ComposerApp = React.createClass({
                                                                                 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}
@@ -210,7 +230,8 @@ const ComposerApp = React.createClass({
                                                                                                isModified={isModified}
                                                                                                isNew={isNew}
                                                                                                disabled={!hasItem}
-                                                                                               onClick={event => event.stopPropagation()}/>
+                                                                                               onClick={event => event.stopPropagation()}
+                                                                                               panelTabShown={self.state.panelTabShown}/>
                                                </div>
                                        </div>
                                        <ModalOverlay />
index 8515182..458e774 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -142,8 +142,16 @@ const ComposerAppToolbar = React.createClass({
                        );
                }
                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 (
index b510715..4ee4345 100644 (file)
@@ -47,12 +47,12 @@ import imgRemove from '../../../node_modules/open-iconic/svg/trash.svg'
 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: []};
 }
 
diff --git a/skyquake/plugins/composer/src/src/components/filemanager/FileManager.jsx b/skyquake/plugins/composer/src/src/components/filemanager/FileManager.jsx
new file mode 100644 (file)
index 0000000..ce490a0
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ *
+ *   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
+// };
diff --git a/skyquake/plugins/composer/src/src/components/filemanager/FileManagerActions.js b/skyquake/plugins/composer/src/src/components/filemanager/FileManagerActions.js
new file mode 100644 (file)
index 0000000..f39c2a9
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *
+ *   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);
diff --git a/skyquake/plugins/composer/src/src/components/filemanager/FileManagerSource.js b/skyquake/plugins/composer/src/src/components/filemanager/FileManagerSource.js
new file mode 100644 (file)
index 0000000..d2d9765
--- /dev/null
@@ -0,0 +1,193 @@
+
+/*
+ *
+ *   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;
diff --git a/skyquake/plugins/composer/src/src/components/filemanager/FileMananger.scss b/skyquake/plugins/composer/src/src/components/filemanager/FileMananger.scss
new file mode 100644 (file)
index 0000000..136c7f5
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *
+ *   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;
+        }
+    }
+}
index 5f1ee24..87afb6a 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,18 +35,24 @@ const ACTIONS = {
 };
 
 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,
diff --git a/skyquake/plugins/composer/src/src/libraries/FileManagerUploadDropZone.js b/skyquake/plugins/composer/src/src/libraries/FileManagerUploadDropZone.js
new file mode 100644 (file)
index 0000000..963e57e
--- /dev/null
@@ -0,0 +1,136 @@
+
+/*
+ *
+ *   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);
+       }
+
+}
diff --git a/skyquake/plugins/composer/src/src/libraries/PackageManagerApi.js b/skyquake/plugins/composer/src/src/libraries/PackageManagerApi.js
new file mode 100644 (file)
index 0000000..6b8862c
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *
+ *   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)
+            }
+        }
+    }
+
+}
+
+
+
index 8ba295a..89d1fe9 100644 (file)
@@ -66,7 +66,7 @@ export default {
         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;
@@ -152,7 +152,7 @@ export default {
                                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();
                                        }
index be11dfc..48d697b 100644 (file)
@@ -27,8 +27,8 @@ function getApiServerOrigin() {
        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,
@@ -51,17 +51,26 @@ function ajaxRequest(path, catalogPackage, resolve, reject, method = 'GET') {
                                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
@@ -74,9 +83,20 @@ const CatalogPackageManagerSource = {
                                        // 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
index 733e938..22d6359 100644 (file)
@@ -136,8 +136,9 @@ class CatalogPackageManagerStore {
 
        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;
@@ -148,7 +149,7 @@ class CatalogPackageManagerStore {
                        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);
                }
        }
 
@@ -189,9 +190,9 @@ function updateStatusInfo(response) {
                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;
@@ -201,7 +202,7 @@ function updateStatusInfo(response) {
                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;
@@ -210,7 +211,7 @@ function updateStatusInfo(response) {
        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;
index 67e8e8d..4f27868 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,6 +33,23 @@ import SelectionManager from '../libraries/SelectionManager'
 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) {
@@ -79,6 +96,10 @@ const uiTransientState = {};
 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
@@ -99,6 +120,12 @@ class ComposerAppStore {
                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,
@@ -124,8 +151,23 @@ class ComposerAppStore {
                        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) {
@@ -166,6 +208,7 @@ class ComposerAppStore {
        }
 
        editCatalogItem(item) {
+               let self = this;
                if (item && item.uiState) {
                        item.uiState.isOpenForEdit = true;
                        if (item.uiState.type !== 'nsd') {
@@ -174,8 +217,8 @@ class ComposerAppStore {
                }
                SelectionManager.select(item);
                this.updateItem(item);
+               this.openFileManagerSockets(item)
        }
-
        catalogItemMetaDataChanged(item) {
                this.updateItem(item);
        }
@@ -193,7 +236,8 @@ class ComposerAppStore {
        }
 
        showError(data) {
-               this.setState({message: data.errorMessage, messageType: 'error'});
+        NotificationError.defer({msg: data.errorMessage, type: 'error'})
+        // this.setState({message: data.errorMessage, messageType: 'error'});
        }
 
        clearError() {
@@ -396,7 +440,210 @@ class ComposerAppStore {
                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');
index 4b9f153..9fa1149 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   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;
+                           }
+                       }
+           }
        }
 }
index 7b2ef16..423e09e 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   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;
+    }
 }
index 15ff765..e8cee07 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +19,9 @@
 @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';
@@ -161,49 +161,45 @@ $font-family-icons: 'FontAwesome';
 $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,
@@ -217,239 +213,246 @@ input[type="image"] {
     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;
 }