diff --git a/Makefile b/Makefile index b18ba3e16..e564bee1a 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ REPLACE := perl -i -pe RODAN_PATH := ./rodan-main/code/rodan JOBS_PATH := $(RODAN_PATH)/jobs -PROD_TAG := v3.1.0 +PROD_TAG := v3.3.0 DOCKER_TAG := nightly diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 678290475..4f4b687e0 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,5 +1,5 @@ ARG VERSION -FROM ddmal/rodan-main:${VERSION} as rodan-static +FROM ddmal/rodan-main:${VERSION} AS rodan-static RUN touch /code/Rodan/database.log /code/Rodan/rodan.log @@ -18,10 +18,15 @@ RUN export DJANGO_SECRET_KEY=localdev \ ########################################################### FROM nginx:1.19 +# prevents interactive prompts during package installation that would otherwise hang or break automated builds +ENV DEBIAN_FRONTEND=noninteractive + # Install OS dependencies -RUN apt-get -qq update \ - && apt-get -qq install openssl certbot unzip -y \ - && rm -rf /var/lib/apt/lists/* +RUN sed -i 's|http://deb.debian.org/debian|http://archive.debian.org/debian|g' /etc/apt/sources.list && \ + sed -i 's|http://security.debian.org/debian-security|http://archive.debian.org/debian-security|g' /etc/apt/sources.list && \ + apt-get -o Acquire::Check-Valid-Until=false update && \ + apt-get install -y openssl certbot unzip && \ + rm -rf /var/lib/apt/lists/* # Add configuration files. COPY ./config/nginx.conf /etc/nginx/nginx.conf diff --git a/production.yml b/production.yml index 403e51bc5..bbc6dbd71 100644 --- a/production.yml +++ b/production.yml @@ -3,7 +3,7 @@ version: "3.4" services: nginx: - image: "ddmal/nginx:v3.2.0" + image: "ddmal/nginx:v3.3.0" deploy: replicas: 1 resources: @@ -37,7 +37,7 @@ services: - "resources:/rodan/data" rodan-main: - image: "ddmal/rodan-main:v3.2.0" + image: "ddmal/rodan-main:v3.3.0" deploy: replicas: 1 resources: @@ -78,7 +78,7 @@ services: - "resources:/rodan/data" celery: - image: "ddmal/rodan-main:v3.2.0" + image: "ddmal/rodan-main:v3.3.0" deploy: replicas: 1 resources: @@ -109,7 +109,7 @@ services: - "resources:/rodan/data" py3-celery: - image: "ddmal/rodan-python3-celery:v3.2.0" + image: "ddmal/rodan-python3-celery:v3.3.0" deploy: replicas: 1 resources: @@ -139,7 +139,7 @@ services: - "resources:/rodan/data" gpu-celery: - image: "ddmal/rodan-gpu-celery:v3.2.0" + image: "ddmal/rodan-gpu-celery:v3.3.0" deploy: replicas: 1 resources: @@ -195,7 +195,7 @@ services: TZ: America/Toronto postgres: - image: "ddmal/postgres-plpython:v3.2.0" + image: "ddmal/postgres-plpython:v3.3.0" deploy: replicas: 1 endpoint_mode: dnsrr diff --git a/rodan-client/.prettierrc b/rodan-client/.prettierrc new file mode 100644 index 000000000..44cac6c17 --- /dev/null +++ b/rodan-client/.prettierrc @@ -0,0 +1,14 @@ +{ + "printWidth": 200, + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": true, + "quoteProps": "as-needed", + "trailingComma": "none", + "bracketSpacing": true, + "bracketSameLine": false, + "arrowParens": "avoid", + "endOfLine": "lf", + "singleAttributePerLine": false +} diff --git a/rodan-client/code/src/js/Controllers/ControllerProject.js b/rodan-client/code/src/js/Controllers/ControllerProject.js index e96609a57..5667e5691 100644 --- a/rodan-client/code/src/js/Controllers/ControllerProject.js +++ b/rodan-client/code/src/js/Controllers/ControllerProject.js @@ -1,4 +1,4 @@ -import $ from 'jquery'; +import $, { type } from 'jquery'; import _ from 'underscore'; import BaseController from './BaseController'; import BaseViewCollection from 'js/Views/Master/Main/BaseViewCollection'; @@ -8,36 +8,34 @@ import Project from 'js/Models/Project'; import Radio from 'backbone.radio'; import RODAN_EVENTS from 'js/Shared/RODAN_EVENTS'; import UserCollection from 'js/Collections/UserCollection'; +import ViewDeleteConfirm from '../Views/Master/Main/Shared/ViewDeleteConfirm'; import ViewProject from 'js/Views/Master/Main/Project/Individual/ViewProject'; import ViewProjectCollection from 'js/Views/Master/Main/Project/Collection/ViewProjectCollection'; import ViewUserCollectionItem from 'js/Views/Master/Main/User/Collection/ViewUserCollectionItem'; -import ViewWorkflowRunCollection from 'js/Views/Master/Main/WorkflowRun/Collection/ViewWorkflowRunCollection'; -import WorkflowRunCollection from 'js/Collections/WorkflowRunCollection'; +import ViewResourceCollection from 'js/Views/Master/Main/Resource/Collection/ViewResourceCollection'; +import ResourceCollection from 'js/Collections/ResourceCollection'; /** * Controller for Projects. */ -export default class ControllerProject extends BaseController -{ -/////////////////////////////////////////////////////////////////////////////////////// -// PUBLIC METHODS -/////////////////////////////////////////////////////////////////////////////////////// +export default class ControllerProject extends BaseController { + /////////////////////////////////////////////////////////////////////////////////////// + // PUBLIC METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Initialize the instance. */ - initialize() - { + initialize() { this._activeProject = null; } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS - initialization -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS - initialization + /////////////////////////////////////////////////////////////////////////////////////// /** * Initialize Radio. */ - _initializeRadio() - { + _initializeRadio() { // Events. Radio.channel('rodan').on(RODAN_EVENTS.EVENT__PROJECT_ADDED_USER_ADMIN, options => this._handleEventProjectAddedUserAdmin(options)); Radio.channel('rodan').on(RODAN_EVENTS.EVENT__PROJECT_ADDED_USER_WORKER, options => this._handleEventProjectAddedUserWorker(options)); @@ -51,25 +49,25 @@ export default class ControllerProject extends BaseController Radio.channel('rodan').on(RODAN_EVENTS.EVENT__PROJECT_USERS_SELECTED, options => this._handleEventProjectShowUsers(options)); // Requests. - Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_ADD_USER_ADMIN, (options) => this._handleRequestProjectAddUserAdmin(options)); - Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_ADD_USER_WORKER, (options) => this._handleRequestProjectAddUserWorker(options)); + Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_ADD_USER_ADMIN, options => this._handleRequestProjectAddUserAdmin(options)); + Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_ADD_USER_WORKER, options => this._handleRequestProjectAddUserWorker(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_GET_ACTIVE, () => this._handleRequestProjectActive()); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_CREATE, options => this._handleRequestCreateProject(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_SET_ACTIVE, options => this._handleRequestSetActiveProject(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_SAVE, options => this._handleRequestProjectSave(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_DELETE, options => this._handleRequestProjectDelete(options)); + Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_DELETE_CONFIRM, options => this._handleRequestProjectDeleteConfirm(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_REMOVE_USER_ADMIN, options => this._handleRequestRemoveUserAdmin(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__PROJECT_REMOVE_USER_WORKER, options => this._handleRequestRemoveUserWorker(options)); } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS - Event handlers -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS - Event handlers + /////////////////////////////////////////////////////////////////////////////////////// /** * Handle event Project show users. */ - _handleEventProjectShowUsers(options) - { + _handleEventProjectShowUsers(options) { Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_HIDE); // Make sure project is updated. @@ -80,56 +78,80 @@ export default class ControllerProject extends BaseController var workerUserCollection = new UserCollection(); // Get admins and workers for project. - var ajaxSettingsAdmins = {success: (response) => this._handleProjectGetAdminsSuccess(response, adminUserCollection), - error: (response) => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, {response: response}), - type: 'GET', - dataType: 'json', - url: options.project.get('url') + 'admins/'}; - var ajaxSettingsWorkers = {success: (response) => this._handleProjectGetWorkersSuccess(response, workerUserCollection), - error: (response) => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, {response: response}), - type: 'GET', - dataType: 'json', - url: options.project.get('url') + 'workers/'}; - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, {settings: ajaxSettingsAdmins}); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, {settings: ajaxSettingsWorkers}); + var ajaxSettingsAdmins = { + success: response => this._handleProjectGetAdminsSuccess(response, adminUserCollection), + error: response => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, { response: response }), + type: 'GET', + dataType: 'json', + url: options.project.get('url') + 'admins/' + }; + var ajaxSettingsWorkers = { + success: response => this._handleProjectGetWorkersSuccess(response, workerUserCollection), + error: response => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, { response: response }), + type: 'GET', + dataType: 'json', + url: options.project.get('url') + 'workers/' + }; + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, { + settings: ajaxSettingsAdmins + }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, { + settings: ajaxSettingsWorkers + }); // Need collection for all users. var collection = new UserCollection(); collection.fetch(); - var userSelectionView = new BaseViewCollection({collection: collection, - el: '
', - template: _.template($('#template-main_user_selection').text()), - childView: BaseViewCollectionItem, - childViewContainer: 'select', - childViewOptions: {template: _.template($('#template-main_user_selection_item').text()), - tagName: 'option'}}); + var userSelectionView = new BaseViewCollection({ + collection: collection, + el: '', + template: _.template($('#template-main_user_selection').text()), + childView: BaseViewCollectionItem, + childViewContainer: 'select', + childViewOptions: { + template: _.template($('#template-main_user_selection_item').text()), + tagName: 'option' + } + }); // Create view. - var projectAdminsView = new BaseViewCollection({collection: adminUserCollection, - template: _.template($('#template-main_user_collection').text()), - childView: ViewUserCollectionItem, - childViewOptions: {template: _.template($('#template-main_user_collection_item_remove').text()), - project: options.project, - admin: true}}); - var projectWorkersView = new BaseViewCollection({collection: workerUserCollection, - template: _.template($('#template-main_user_collection').text()), - childView: ViewUserCollectionItem, - childViewOptions: {template: _.template($('#template-main_user_collection_item_remove').text()), - project: options.project}}); - var view = new LayoutViewProjectUsers({viewusers: userSelectionView, - viewprojectadmins: projectAdminsView, - viewprojectworkers: projectWorkersView, - project: options.project}); + var projectAdminsView = new BaseViewCollection({ + collection: adminUserCollection, + template: _.template($('#template-main_user_collection').text()), + childView: ViewUserCollectionItem, + childViewOptions: { + template: _.template($('#template-main_user_collection_item_remove').text()), + project: options.project, + admin: true + } + }); + var projectWorkersView = new BaseViewCollection({ + collection: workerUserCollection, + template: _.template($('#template-main_user_collection').text()), + childView: ViewUserCollectionItem, + childViewOptions: { + template: _.template($('#template-main_user_collection_item_remove').text()), + project: options.project + } + }); + var view = new LayoutViewProjectUsers({ + viewusers: userSelectionView, + viewprojectadmins: projectAdminsView, + viewprojectworkers: projectWorkersView, + project: options.project + }); // Show modal. - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW, {content: view, title: 'Project Users'}); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW, { + content: view, + title: 'Project Users' + }); } /** * Handle event Project generic response. */ - _handleEventProjectGenericResponse() - { + _handleEventProjectGenericResponse() { Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_HIDE); Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__GLOBAL_PROJECTS_LOAD, {}); } @@ -137,8 +159,7 @@ export default class ControllerProject extends BaseController /** * Handle event Project delete response. */ - _handleEventProjectDeleteResponse() - { + _handleEventProjectDeleteResponse() { Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_HIDE); Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__GLOBAL_PROJECTS_LOAD, {}); Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_SELECTED_COLLECTION); @@ -147,91 +168,138 @@ export default class ControllerProject extends BaseController /** * Handle request Project save. */ - _handleRequestProjectSave(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Saving Project', content: 'Please wait...'}); - options.project.save(options.fields, {patch: true, success: (model) => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_SAVED, {project: model})}); + _handleRequestProjectSave(options) { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { + title: 'Saving Project', + content: 'Please wait...' + }); + options.project.save(options.fields, { + patch: true, + success: model => + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_SAVED, { + project: model + }) + }); } /** * Handle request Project create. */ - _handleRequestCreateProject(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Creating Project', content: 'Please wait...'}); - var project = new Project({creator: options.user}); - project.save({}, {success: (model) => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_CREATED, {project: model})}); + _handleRequestCreateProject(options) { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { + title: 'Creating Project', + content: 'Please wait...' + }); + var project = new Project({ creator: options.user }); + project.save( + {}, + { + success: model => + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_CREATED, { + project: model + }) + } + ); } /** - * Handle request Project delete. + * Handle request Project delete confirm modal window. */ - _handleRequestProjectDelete(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Deleting Project', content: 'Please wait...'}); + _handleRequestProjectDelete(options) { this._activeProject = null; - options.project.destroy({success: (model) => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_DELETED, {project: model})}); + + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { + title: 'Deleting Project', + content: 'Please wait...' + }); + this._activeProject = null; + options.project.destroy({ + success: () => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_DELETED, {}) + }); + } + + /** + * Handle request Project delete confirm modal window. + */ + _handleRequestProjectDeleteConfirm(options) { + var view = new ViewDeleteConfirm({ + type: 'project', + names: options.project.get('name'), + toDelete: options.project + }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW, { + content: view, + title: 'Deleting Project' + }); } /** * Handle request set active Project. */ - _handleRequestSetActiveProject(options) - { + _handleRequestSetActiveProject(options) { this._activeProject = options.project; } /** * Handle item selection. */ - _handleEventItemSelected(options) - { + _handleEventItemSelected(options) { this._activeProject = options.project; - this._activeProject.fetch(); - // default collection inside project view is the workflowrun collection - var collection = new WorkflowRunCollection(); - collection.fetch({data: {project: this._activeProject.id}}); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__UPDATER_SET_COLLECTIONS, {collections: [collection]}); - var viewProject = new ViewProject({model: this._activeProject}); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MAINREGION_SHOW_VIEW, {view: viewProject, options: {project: this._activeProject}}); - viewProject.showCollection(new ViewWorkflowRunCollection({collection: collection})); + // Fetch project first and wait for it to complete + this._activeProject.fetch({ + success: () => { + // default collection inside project view is the resource collection + var collection = new ResourceCollection(); + collection.fetch({ data: { project: this._activeProject.id } }); + + // Set up view + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__UPDATER_SET_COLLECTIONS, { collections: [collection] }); + var viewProject = new ViewProject({ model: this._activeProject }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MAINREGION_SHOW_VIEW, { + view: viewProject, + options: { project: this._activeProject } + }); + + // Show resource collection by default and trigger resource selection event + viewProject.showCollection(new ViewResourceCollection({ collection: collection })); + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCE_SELECTED_COLLECTION, { project: this._activeProject }); + } + }); } /** * Handle collection selection. */ - _handleEventCollectionSelected() - { + _handleEventCollectionSelected() { var collection = Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__GLOBAL_PROJECT_COLLECTION); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__UPDATER_SET_COLLECTIONS, {collections: [collection]}); - var view = new ViewProjectCollection({collection: collection}); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MAINREGION_SHOW_VIEW, {view: view}); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__UPDATER_SET_COLLECTIONS, { collections: [collection] }); + var view = new ViewProjectCollection({ collection: collection }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MAINREGION_SHOW_VIEW, { + view: view + }); } /** * Handle request for current active project. Returns null. */ - _handleRequestProjectActive() - { + _handleRequestProjectActive() { return this._activeProject; } /** * Handle project admins get success. */ - _handleProjectGetAdminsSuccess(response, collection) - { - collection.fetch({data: {username__in: response.join()}}); + _handleProjectGetAdminsSuccess(response, collection) { + collection.fetch({ data: { username__in: response.join() } }); } /** * Handle project workers get success. */ - _handleProjectGetWorkersSuccess(response, collection) - { - if (response.flat().length !== 0){ - collection.fetch({data: {username__in: response.join()}}); + _handleProjectGetWorkersSuccess(response, collection) { + if (response.flat().length !== 0) { + collection.fetch({ data: { username__in: response.join() } }); } } @@ -240,33 +308,38 @@ export default class ControllerProject extends BaseController * We have to use a custom AJAX call since modifying the users of a Project * has no endpoint at the moment. */ - _handleRequestRemoveUserAdmin(options) - { + _handleRequestRemoveUserAdmin(options) { var admins = options.project.get('admins'); - if (admins.length > 1) - { + if (admins.length > 1) { var userIndex = admins.indexOf(options.user.get('username')); - if (userIndex >= 0) - { + if (userIndex >= 0) { admins.splice(userIndex, 1); var usersSendObject = {}; - admins.map(function(value) { usersSendObject[value] = value; return value; }); - var ajaxSettings = {success: (response) => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_REMOVED_USER_ADMIN, {project: options.project}), - error: (response) => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, {response: response}), - type: 'PUT', - dataType: 'json', - data: usersSendObject, - url: options.project.get('url') + 'admins/'}; - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, {settings: ajaxSettings}); + admins.map(function (value) { + usersSendObject[value] = value; + return value; + }); + var ajaxSettings = { + success: response => + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_REMOVED_USER_ADMIN, { + project: options.project + }), + error: response => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, { response: response }), + type: 'PUT', + dataType: 'json', + data: usersSendObject, + url: options.project.get('url') + 'admins/' + }; + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, { settings: ajaxSettings }); + } else { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_ERROR, { + content: 'An error occured trying to remove this User.' + }); } - else - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_ERROR, {content: 'An error occured trying to remove this User.'}); - } - } - else - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_ERROR, {content: 'At least one project admin, the creator, must exist.'}); + } else { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_ERROR, { + content: 'At least one project admin, the creator, must exist.' + }); } } @@ -275,28 +348,33 @@ export default class ControllerProject extends BaseController * We have to use a custom AJAX call since modifying the users of a Project * has no endpoint at the moment. */ - _handleRequestRemoveUserWorker(options) - { + _handleRequestRemoveUserWorker(options) { var users = options.project.get('workers'); - if (users.length > 0) - { + if (users.length > 0) { var userIndex = users.indexOf(options.user.get('username')); - if (userIndex >= 0) - { + if (userIndex >= 0) { users.splice(userIndex, 1); var usersSendObject = {}; - users.map(function(value) { usersSendObject[value] = value; return value; }); - var ajaxSettings = {success: (response) => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_REMOVED_USER_WORKER, {project: options.project}), - error: (response) => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, {response: response}), - type: 'PUT', - dataType: 'json', - data: usersSendObject, - url: options.project.get('url') + 'workers/'}; - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, {settings: ajaxSettings}); - } - else - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_ERROR, {content: 'An error occured trying to remove this User.'}); + users.map(function (value) { + usersSendObject[value] = value; + return value; + }); + var ajaxSettings = { + success: response => + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_REMOVED_USER_WORKER, { + project: options.project + }), + error: response => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, { response: response }), + type: 'PUT', + dataType: 'json', + data: usersSendObject, + url: options.project.get('url') + 'workers/' + }; + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, { settings: ajaxSettings }); + } else { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_ERROR, { + content: 'An error occured trying to remove this User.' + }); } } } @@ -304,63 +382,78 @@ export default class ControllerProject extends BaseController /** * Handle project removed user. */ - _handleEventProjectRemovedUser(options) - { + _handleEventProjectRemovedUser(options) { this._activeProject.fetch(); - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_USERS_SELECTED, {project: this._activeProject}); + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_USERS_SELECTED, { + project: this._activeProject + }); } /** * Handle request add admin. */ - _handleRequestProjectAddUserAdmin(options) - { + _handleRequestProjectAddUserAdmin(options) { var users = options.project.get('admins'); users.push(options.username); var usersSendObject = {}; - users.map(function(value) { usersSendObject[value] = value; return value; }); - var ajaxSettings = {success: (response) => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_ADDED_USER_ADMIN, {project: options.project}), - error: (response) => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, {response: response}), - type: 'PUT', - dataType: 'json', - data: usersSendObject, - url: options.project.get('url') + 'admins/'}; - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, {settings: ajaxSettings}); - } + users.map(function (value) { + usersSendObject[value] = value; + return value; + }); + var ajaxSettings = { + success: response => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_ADDED_USER_ADMIN, { project: options.project }), + error: response => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, { response: response }), + type: 'PUT', + dataType: 'json', + data: usersSendObject, + url: options.project.get('url') + 'admins/' + }; + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, { + settings: ajaxSettings + }); + } /** * Handle request add worker. */ - _handleRequestProjectAddUserWorker(options) - { + _handleRequestProjectAddUserWorker(options) { var users = options.project.get('workers'); users.push(options.username); var usersSendObject = {}; - users.map(function(value) { usersSendObject[value] = value; return value; }); - var ajaxSettings = {success: (response) => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_ADDED_USER_WORKER, {project: options.project}), - error: (response) => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, {response: response}), - type: 'PUT', - dataType: 'json', - data: usersSendObject, - url: options.project.get('url') + 'workers/'}; - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, {settings: ajaxSettings}); + users.map(function (value) { + usersSendObject[value] = value; + return value; + }); + var ajaxSettings = { + success: response => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_ADDED_USER_WORKER, { project: options.project }), + error: response => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SYSTEM_HANDLE_ERROR, { response: response }), + type: 'PUT', + dataType: 'json', + data: usersSendObject, + url: options.project.get('url') + 'workers/' + }; + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_REQUEST_AJAX, { + settings: ajaxSettings + }); } /** * Handle event added admin. */ - _handleEventProjectAddedUserAdmin() - { + _handleEventProjectAddedUserAdmin() { this._activeProject.fetch(); - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_USERS_SELECTED, {project: this._activeProject}); - } + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_USERS_SELECTED, { + project: this._activeProject + }); + } /** * Handle event added worker. */ - _handleEventProjectAddedUserWorker() - { + _handleEventProjectAddedUserWorker() { this._activeProject.fetch(); - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_USERS_SELECTED, {project: this._activeProject}); + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_USERS_SELECTED, { + project: this._activeProject + }); } } diff --git a/rodan-client/code/src/js/Controllers/ControllerResource.js b/rodan-client/code/src/js/Controllers/ControllerResource.js index 90a649c94..34bae35b2 100644 --- a/rodan-client/code/src/js/Controllers/ControllerResource.js +++ b/rodan-client/code/src/js/Controllers/ControllerResource.js @@ -5,6 +5,7 @@ import RODAN_EVENTS from 'js/Shared/RODAN_EVENTS'; import Radio from 'backbone.radio'; import Resource from 'js/Models/Resource'; import ResourceCollection from 'js/Collections/ResourceCollection'; +import ViewDeleteConfirm from '../Views/Master/Main/Shared/ViewDeleteConfirm'; import ViewResource from 'js/Views/Master/Main/Resource/Individual/ViewResource'; import ViewResourceMulti from 'js/Views/Master/Main/Resource/Individual/ViewResourceMulti'; import ViewResourceCollection from 'js/Views/Master/Main/Resource/Collection/ViewResourceCollection'; @@ -14,20 +15,18 @@ import ViewProject from '../Views/Master/Main/Project/Individual/ViewProject'; /** * Controller for Resources. */ -export default class ControllerResource extends BaseController -{ +export default class ControllerResource extends BaseController { initialize() { - this._selectedResources = new Set(); - this._baseSelectResource = null; + this._selectedResources = new Set(); + this._baseSelectResource = null; } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Initialize Radio. */ - _initializeRadio() - { + _initializeRadio() { // Events Radio.channel('rodan').on(RODAN_EVENTS.EVENT__RESOURCE_SELECTED_COLLECTION, options => this._handleEventCollectionSelected(options)); Radio.channel('rodan').on(RODAN_EVENTS.EVENT__RESOURCE_SELECTED, options => this._handleEventItemSelected(options)); @@ -39,6 +38,7 @@ export default class ControllerResource extends BaseController Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__RESOURCE_SHOWLAYOUTVIEW, options => this._handleCommandShowLayoutView(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__RESOURCE_CREATE, options => this._handleRequestResourceCreate(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__RESOURCE_DELETE, options => this._handleCommandResourceDelete(options)); + Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__RESOURCE_DELETE_CONFIRM, options => this._handleCommandResourceDeleteConfirm(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__RESOURCE_DOWNLOAD, options => this._handleRequestResourceDownload(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__RESOURCE_SAVE, options => this._handleCommandResourceSave(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__RESOURCE_VIEWER_ACQUIRE, options => this._handleRequestViewer(options)); @@ -50,35 +50,32 @@ export default class ControllerResource extends BaseController /** * Handle show LayoutView. */ - _handleCommandShowLayoutView(options) - { + _handleCommandShowLayoutView(options) { this._projectView = options.projectView; } /** * Handle collection selection. */ - _handleEventCollectionSelected(options) - { + _handleEventCollectionSelected(options) { this._collection = new ResourceCollection(); - this._collection.fetch({data: {project: options.project.id}}); + this._collection.fetch({ data: { project: options.project.id } }); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__UPDATER_SET_COLLECTIONS, {collections: [this._collection]}); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__UPDATER_SET_COLLECTIONS, { collections: [this._collection] }); const activeProject = Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__PROJECT_GET_ACTIVE); - this._projectView = new ViewProject({model: activeProject}); + this._projectView = new ViewProject({ model: activeProject }); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MAINREGION_SHOW_VIEW, {view: this._projectView}); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MAINREGION_SHOW_VIEW, { view: this._projectView }); - this._viewCollection = new ViewResourceCollection({collection: this._collection}); + this._viewCollection = new ViewResourceCollection({ collection: this._collection }); this._projectView.showCollection(this._viewCollection); } /** * Handle item selection. */ - _handleEventItemSelected(options) - { + _handleEventItemSelected(options) { if (!options.multiple && !options.range) { this._selectedResources.clear(); this._baseSelectResource = null; @@ -87,70 +84,83 @@ export default class ControllerResource extends BaseController if (options.multiple && this._selectedResources.has(options.resource)) { this._selectedResources.delete(options.resource); this._baseSelectResource = null; - } - else if (options.range && this._baseSelectResource !== null) { + } else if (options.range && this._baseSelectResource !== null) { let indexBase = this._collection.indexOf(this._baseSelectResource); let indexRes = this._collection.indexOf(options.resource); this._selectedResources.clear(); for (let n = Math.min(indexBase, indexRes); n <= Math.max(indexBase, indexRes); n++) { this._selectedResources.add(this._collection.at(n)); } - } - else { + } else { this._selectedResources.add(options.resource); this._baseSelectResource = options.resource; } if (this._selectedResources.size === 0) { this._projectView.clearCollectionItemInfoView(); - } - else if (this._selectedResources.size === 1) { - this._projectView.showCollectionItemInfo(new ViewResource({model: this._selectedResources.values().next().value})); - } - else { - this._projectView.showCollectionItemInfo(new ViewResourceMulti({models: this._selectedResources})); + } else if (this._selectedResources.size === 1) { + this._projectView.showCollectionItemInfo(new ViewResource({ model: this._selectedResources.values().next().value })); + } else { + this._projectView.showCollectionItemInfo(new ViewResourceMulti({ models: this._selectedResources })); } } /** * Handle command add Resource. */ - _handleRequestResourceCreate(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Creating Resource', content: 'Please wait...'}); + _handleRequestResourceCreate(options) { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { title: 'Creating Resource', content: 'Please wait...' }); var resource = null; let opts = { - project: options.project.get('url'), - file: options.file, + project: options.project.get('url'), + file: options.file }; - if (options.resourcetype) - { + if (options.resourcetype) { opts['resource_type'] = options.resourcetype; } - if (options.label_names !== undefined) - { + if (options.label_names !== undefined) { opts['label_names'] = options.label_names; } resource = new Resource(opts); - var jqXHR = resource.save({}, {success: (model) => this._handleCreateSuccess(model, this._collection)}); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__TRANSFERMANAGER_MONITOR_UPLOAD, {request: jqXHR, file: options.file}); + var jqXHR = resource.save({}, { success: model => this._handleCreateSuccess(model, this._collection) }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__TRANSFERMANAGER_MONITOR_UPLOAD, { request: jqXHR, file: options.file }); } /** * Handle command delete Resource. */ - _handleCommandResourceDelete(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Deleting Resource', content: 'Please wait...'}); + _handleCommandResourceDelete(options) { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { title: 'Deleting Resource', content: 'Please wait...' }); this._projectView.clearCollectionItemInfoView(); - options.resource.destroy({success: (model) => this._handleDeleteSuccess(model, this._collection)}); + if (options.resource.size) { + options.resource.forEach(resource => resource.destroy({ success: model => this._handleDeleteSuccess(model, this._collection) })); + } else { + options.resource.destroy({ success: model => this._handleDeleteSuccess(model, this._collection) }); + } + } + + /** + * Handle command delete Resource confirm modal window. + */ + _handleCommandResourceDeleteConfirm(options) { + console.log(options.resource.size); + let names = options.resource.size ? [...options.resource].map(resource => resource.get('name')) : options.resource.get('name'); + console.log(names); + var view = new ViewDeleteConfirm({ + type: 'resource', + names: names, + toDelete: options.resource + }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW, { + content: view, + title: 'Deleting Resource' + }); } /** * Handle command download Resource. */ - _handleRequestResourceDownload(options) - { + _handleRequestResourceDownload(options) { var mimetype = options.resource.get('resource_type_full').mimetype; var ext = options.resource.get('resource_type_full').extension; var filename = options.resource.get('name') + '.' + ext; @@ -166,14 +176,12 @@ export default class ControllerResource extends BaseController /** * Handle command save Resource. */ - _handleCommandResourceSave(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Saving Resource', content: 'Please wait...'}); - options.resource.save(options.fields, {patch: true, success: (model) => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCE_SAVED, {resource: model})}); + _handleCommandResourceSave(options) { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { title: 'Saving Resource', content: 'Please wait...' }); + options.resource.save(options.fields, { patch: true, success: model => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCE_SAVED, { resource: model }) }); } - _handleCurrentResources(options) - { + _handleCurrentResources(options) { try { if (this._collection_no_page['_lastData']['project'] === options.data.project) { return this._collection_no_page; @@ -189,8 +197,7 @@ export default class ControllerResource extends BaseController /** * Handle request Resources. */ - _handleRequestResources(options) - { + _handleRequestResources(options) { this._collection = new ResourceCollection(); this._collection.fetch(options); return this._collection; @@ -199,8 +206,7 @@ export default class ControllerResource extends BaseController /** * Handle request Resources. */ - _handleRequestResourcesNoPagination(options) - { + _handleRequestResourcesNoPagination(options) { options.data.no_page = true; options.async = false; this._collection_no_page = new ResourceCollection(); @@ -211,13 +217,12 @@ export default class ControllerResource extends BaseController /** * Handle request for Resource viewer. */ - _handleRequestViewer(options) - { + _handleRequestViewer(options) { var ajaxOptions = { url: options.resource.get('url') + 'acquire/', type: 'POST', dataType: 'json', - success: (response) => this._handleSuccessAcquire(response) + success: response => this._handleSuccessAcquire(response) }; $.ajax(ajaxOptions); } @@ -225,34 +230,30 @@ export default class ControllerResource extends BaseController /** * Handle acquire success. */ - _handleSuccessAcquire(response) - { + _handleSuccessAcquire(response) { window.open(response.working_url, '', '_blank'); } /** * Handle create success. */ - _handleCreateSuccess(resource, collection) - { + _handleCreateSuccess(resource, collection) { collection.add(resource); - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCE_CREATED, {resource: resource}); + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCE_CREATED, { resource: resource }); } /** * Handle delete success. */ - _handleDeleteSuccess(model, collection) - { + _handleDeleteSuccess(model, collection) { collection.remove(model); - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCE_DELETED, {resource: model}); + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCE_DELETED, { resource: model }); } /** * Handle generic success. */ - _handleSuccessGeneric(options) - { + _handleSuccessGeneric(options) { Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_HIDE); Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__GLOBAL_RESOURCELABELS_LOAD, {}); } diff --git a/rodan-client/code/src/js/Controllers/ControllerWorkflow.js b/rodan-client/code/src/js/Controllers/ControllerWorkflow.js index 5bf5099d1..7cb9faf7b 100644 --- a/rodan-client/code/src/js/Controllers/ControllerWorkflow.js +++ b/rodan-client/code/src/js/Controllers/ControllerWorkflow.js @@ -6,20 +6,19 @@ import ViewWorkflowCollection from 'js/Views/Master/Main/Workflow/Collection/Vie import Workflow from 'js/Models/Workflow'; import WorkflowCollection from 'js/Collections/WorkflowCollection'; import ViewProject from 'js/Views/Master/Main/Project/Individual/ViewProject'; +import ViewDeleteConfirm from '../Views/Master/Main/Shared/ViewDeleteConfirm'; /** * Controller for Workflows. */ -export default class ControllerWorkflow extends BaseController -{ -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS - Initialization -/////////////////////////////////////////////////////////////////////////////////////// +export default class ControllerWorkflow extends BaseController { + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS - Initialization + /////////////////////////////////////////////////////////////////////////////////////// /** * Initialize Radio. */ - _initializeRadio() - { + _initializeRadio() { // Events. Radio.channel('rodan').on(RODAN_EVENTS.EVENT__WORKFLOW_SELECTED_COLLECTION, options => this._handleEventCollectionSelected(options)); Radio.channel('rodan').on(RODAN_EVENTS.EVENT__WORKFLOW_SELECTED, options => this._handleEventItemSelected(options)); @@ -30,99 +29,106 @@ export default class ControllerWorkflow extends BaseController // Requests. Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__WORKFLOW_SAVE, options => this._handleRequestSaveWorkflow(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__WORKFLOW_DELETE, options => this._handleCommandDeleteWorkflow(options)); + Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__WORKFLOW_DELETE_CONFIRM, options => this._handleCommandDeleteWorkflowConfirm(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__WORKFLOW_IMPORT, options => this._handleCommandImportWorkflow(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__WORKFLOW_CREATE, options => this._handleCommandAddWorkflow(options)); Radio.channel('rodan').reply(RODAN_EVENTS.REQUEST__WORKFLOW_EXPORT, options => this._handleCommandExportWorkflow(options)); } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS - Radio handlers -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS - Radio handlers + /////////////////////////////////////////////////////////////////////////////////////// /** * Handle collection selection. */ - _handleEventCollectionSelected(options) - { + _handleEventCollectionSelected(options) { this._collection = new WorkflowCollection(); - this._collection.fetch({data: {project: options.project.id}}); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__UPDATER_SET_COLLECTIONS, {collections: [this._collection]}); + this._collection.fetch({ data: { project: options.project.id } }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__UPDATER_SET_COLLECTIONS, { collections: [this._collection] }); const activeProject = Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__PROJECT_GET_ACTIVE); - this._projectView = new ViewProject({model: activeProject}); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MAINREGION_SHOW_VIEW, {view: this._projectView}); - this._viewCollection = new ViewWorkflowCollection({collection: this._collection}); + this._projectView = new ViewProject({ model: activeProject }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MAINREGION_SHOW_VIEW, { view: this._projectView }); + this._viewCollection = new ViewWorkflowCollection({ collection: this._collection }); this._projectView.showCollection(this._viewCollection); } /** * Handle item selection. */ - _handleEventItemSelected(options) - { - this._viewItem = new ViewWorkflow({model: options.workflow}); + _handleEventItemSelected(options) { + this._viewItem = new ViewWorkflow({ model: options.workflow }); this._projectView.showCollectionItemInfo(this._viewItem); } /** * Handle command delete workflow. */ - _handleCommandDeleteWorkflow(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Deleting Workflow', content: 'Please wait...'}); + _handleCommandDeleteWorkflow(options) { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { title: 'Deleting Workflow', content: 'Please wait...' }); // Clear the individual view (if there). - if (this._viewItem !== null && options.workflow === this._viewItem.model) - { + if (this._viewItem !== null && options.workflow === this._viewItem.model) { this._projectView.clearCollectionItemInfoView(); } - options.workflow.destroy({success: (model) => this._handleDeleteSuccess(model, this._collection)}); + options.workflow.destroy({ success: model => this._handleDeleteSuccess(model, this._collection) }); + } + + /** + * Handle command delete workflow confirm modal window. + */ + _handleCommandDeleteWorkflowConfirm(options) { + var view = new ViewDeleteConfirm({ + type: 'workflow', + names: options.workflow.get('name'), + toDelete: options.workflow + }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW, { + content: view, + title: 'Deleting Workflow' + }); } /** * Handle command add workflow. */ - _handleCommandAddWorkflow(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Creating Workflow', content: 'Please wait...'}); - var workflow = new Workflow({project: options.project.get('url'), name: 'untitled'}); - workflow.save({}, {success: (model) => this._handleCreateSuccess(model, this._collection)}); + _handleCommandAddWorkflow(options) { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { title: 'Creating Workflow', content: 'Please wait...' }); + var workflow = new Workflow({ project: options.project.get('url'), name: 'untitled' }); + workflow.save({}, { success: model => this._handleCreateSuccess(model, this._collection) }); } /** * Handle save workflow. */ - _handleRequestSaveWorkflow(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Saving Workflow', content: 'Please wait...'}); - options.workflow.save(options.fields, {patch: true, success: (model) => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_SAVED, {workflow: model})}); + _handleRequestSaveWorkflow(options) { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { title: 'Saving Workflow', content: 'Please wait...' }); + options.workflow.save(options.fields, { patch: true, success: model => Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_SAVED, { workflow: model }) }); } /** * Handle export workflow. */ - _handleCommandExportWorkflow(options) - { - options.workflow.sync('read', options.workflow, {data: {export: true}, success: (result) => this._handleExportSuccess(result, options.workflow)}); + _handleCommandExportWorkflow(options) { + options.workflow.sync('read', options.workflow, { data: { export: true }, success: result => this._handleExportSuccess(result, options.workflow) }); } /** * Handle import workflow. */ - _handleCommandImportWorkflow(options) - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, {title: 'Importing Workflow', content: 'Please wait...'}); + _handleCommandImportWorkflow(options) { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW_IMPORTANT, { title: 'Importing Workflow', content: 'Please wait...' }); var fileReader = new FileReader(); - fileReader.onerror = (event) => this._handleFileReaderError(event); - fileReader.onload = (event) => this._handleFileReaderLoaded(event, options.project); + fileReader.onerror = event => this._handleFileReaderError(event); + fileReader.onload = event => this._handleFileReaderLoaded(event, options.project); fileReader.readAsText(options.file); } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS - FileReader handlers -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS - FileReader handlers + /////////////////////////////////////////////////////////////////////////////////////// /** * Handle FileReader error. */ - _handleFileReaderError(event) - { + _handleFileReaderError(event) { // TODO error console.error(event); } @@ -130,57 +136,51 @@ export default class ControllerWorkflow extends BaseController /** * Handle FileReader loaded. */ - _handleFileReaderLoaded(event, project) - { - var workflow = new Workflow({project: project.get('url'), serialized: JSON.parse(event.target.result)}); - workflow.save({}, {success: (model) => this._handleImportSuccess(model, this._collection)}); + _handleFileReaderLoaded(event, project) { + var workflow = new Workflow({ project: project.get('url'), serialized: JSON.parse(event.target.result) }); + workflow.save({}, { success: model => this._handleImportSuccess(model, this._collection) }); } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS - REST handlers -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS - REST handlers + /////////////////////////////////////////////////////////////////////////////////////// /** * Handle create success. */ - _handleCreateSuccess(model, collection) - { + _handleCreateSuccess(model, collection) { collection.add(model); - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_CREATED, {workflow: model}); + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_CREATED, { workflow: model }); } /** * Handle delete success. */ - _handleDeleteSuccess(model, collection) - { + _handleDeleteSuccess(model, collection) { collection.remove(model); - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_DELETED, {workflow: model}); + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_DELETED, { workflow: model }); } /** * Handle export success. */ - _handleExportSuccess(result, model) - { + _handleExportSuccess(result, model) { var data = JSON.stringify(result); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__DOWNLOAD_START, {data: data, filename: model.get('name'), mimetype: 'application/json'}); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__DOWNLOAD_START, { data: data, filename: model.get('name'), mimetype: 'application/json' }); } /** * Handle import success. */ - _handleImportSuccess(model, collection) - { + _handleImportSuccess(model, collection) { collection.add(model, {}); - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_CREATED, {workflow: model}); + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_CREATED, { workflow: model }); //Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOWBUILDER_VALIDATE_WORKFLOW, {workflow: model}); } /** * Handle generic success. */ - _handleSuccessGeneric(options) - { + _handleSuccessGeneric(options) { Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_HIDE); } -} \ No newline at end of file +} diff --git a/rodan-client/code/src/js/Items/BaseItem.js b/rodan-client/code/src/js/Items/BaseItem.js index 4f39c3cf7..e40fbf72a 100644 --- a/rodan-client/code/src/js/Items/BaseItem.js +++ b/rodan-client/code/src/js/Items/BaseItem.js @@ -9,18 +9,15 @@ let itemMap = null; /** * Base Item in WorkflowBuilder */ -class BaseItem extends paper.Path -{ -/////////////////////////////////////////////////////////////////////////////////////// -// PUBLIC STATIC METHODS -/////////////////////////////////////////////////////////////////////////////////////// +class BaseItem extends paper.Path { + /////////////////////////////////////////////////////////////////////////////////////// + // PUBLIC STATIC METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Returns associated item given a URL. */ - static getAssociatedItem(url) - { - if (!itemMap) - { + static getAssociatedItem(url) { + if (!itemMap) { itemMap = {}; } return itemMap[url]; @@ -29,10 +26,8 @@ class BaseItem extends paper.Path /** * Associates given item with URL. */ - static associateItemWithUrl(item, url) - { - if (!itemMap) - { + static associateItemWithUrl(item, url) { + if (!itemMap) { itemMap = {}; } itemMap[url] = item; @@ -41,14 +36,11 @@ class BaseItem extends paper.Path /** * Removes item from map with he provided URL. */ - static removeItemFromMap(url) - { - if (!itemMap) - { + static removeItemFromMap(url) { + if (!itemMap) { itemMap = {}; } - if (itemMap[url]) - { + if (itemMap[url]) { delete itemMap[url]; } } @@ -56,14 +48,11 @@ class BaseItem extends paper.Path /** * Update all items. */ - static updateItems() - { - if (!itemMap) - { + static updateItems() { + if (!itemMap) { itemMap = {}; } - for (var url in itemMap) - { + for (var url in itemMap) { var item = itemMap[url]; item.update(); } @@ -72,17 +61,15 @@ class BaseItem extends paper.Path /** * Clears the map. */ - static clearMap() - { + static clearMap() { itemMap = {}; } /** * Returns context menu data for multiple items of this class. */ - static getContextMenuDataMultiple() - { - return [{channel: 'rodan-client_gui', label: 'Cancel', radiorequest: GUI_EVENTS.REQUEST__WORKFLOWBUILDER_GUI_HIDE_CONTEXTMENU}]; + static getContextMenuDataMultiple() { + return [{ channel: 'rodan-client_gui', label: 'Cancel', radiorequest: GUI_EVENTS.REQUEST__WORKFLOWBUILDER_GUI_HIDE_CONTEXTMENU }]; } /** @@ -90,31 +77,34 @@ class BaseItem extends paper.Path * @param {{x: number, y: number}} appearance - The coordinates stored in the database * @returns {{x: number, y: number}} The paper.js project coordinates */ - static appearanceToProject(appearance) - { + static appearanceToProject(appearance) { const multiplier = Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].DATABASE_COORDINATES_MULTIPLIER; - return { x: appearance.x * multiplier, y: appearance.y * multiplier }; + return { + x: appearance.x * multiplier, + y: appearance.y * multiplier + }; } /** * Converts coordinates from the paper.js project coordinates to the database coordinates. * @param {{x: number, y: number}} coordinates - The paper.js project coordinates * @returns {{x: number, y: number}} The coordinates stored in the database - */ - static projectToAppearance(coordinates) - { + */ + static projectToAppearance(coordinates) { const multiplier = Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].DATABASE_COORDINATES_MULTIPLIER; - return { x: coordinates.x / multiplier, y: coordinates.y / multiplier }; + return { + x: coordinates.x / multiplier, + y: coordinates.y / multiplier + }; } -/////////////////////////////////////////////////////////////////////////////////////// -// PUBLIC METHODS -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PUBLIC METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Constructor. */ - constructor(options) - { + constructor(options) { super(options.segments); this._initializeRadio(options); this._initializeAppearance(options); @@ -126,19 +116,16 @@ class BaseItem extends paper.Path /** * Returns associated model. */ - getModel() - { + getModel() { return this._model; } /** * Returns context menu data for single item of this class. */ - getContextMenuDataSingle() - { + getContextMenuDataSingle() { var menuItems = []; - if (this.menuItems) - { + if (this.menuItems) { menuItems = this.menuItems; } return menuItems; @@ -147,20 +134,17 @@ class BaseItem extends paper.Path /** * Return true if this item can be moved by itself. */ - isMoveable() - { + isMoveable() { return true; } /** * Moves the item. */ - move(delta) - { + move(delta) { this.position.x += delta.x; this.position.y += delta.y; - if (this._text !== null) - { + if (this._text !== null) { this._text.position = this.bounds.center; } this._hasMoved = true; @@ -169,11 +153,9 @@ class BaseItem extends paper.Path /** * Set position. */ - setPosition(point) - { + setPosition(point) { this.position = point; - if (this._text !== null) - { + if (this._text !== null) { this._text.position = this.bounds.center; } this._hasMoved = true; @@ -182,8 +164,7 @@ class BaseItem extends paper.Path /** * Set visibility. */ - setVisible(visible) - { + setVisible(visible) { this.visible = visible; this._text.visible = this._useText && this.visible; } @@ -191,8 +172,7 @@ class BaseItem extends paper.Path /** * Destroy. */ - destroy() - { + destroy() { BaseItem.removeItemFromMap(this._modelURL); this._text.remove(); this.remove(); @@ -201,17 +181,18 @@ class BaseItem extends paper.Path /** * Updates the position to the server. */ - updatePositionToServer() - { - if (this.isMoveable() && this._hasMoved) - { + updatePositionToServer() { + if (this.isMoveable() && this._hasMoved) { // If an ID exists, we know it exists on the server, so we can patch it. // Else if we haven't tried saving it before, do it. This should create // a new model on the server. - if (this._modelId || !this._coordinateSetSaveAttempted) - { + if (this._modelId || !this._coordinateSetSaveAttempted) { this._coordinateSetSaveAttempted = true; - const appearance = BaseItem.projectToAppearance(this.position); + const topLeft = { + x: this.bounds.topLeft.x, + y: this.bounds.topLeft.y + }; + const appearance = BaseItem.projectToAppearance(topLeft); this._model.set({ appearance }); this._model.save(); this._hasMoved = false; @@ -222,33 +203,28 @@ class BaseItem extends paper.Path /** * Gets coordinates from server. */ - loadCoordinates() - { - + loadCoordinates() { this._handleCoordinateLoadSuccess(this._model); } /** * Returns associated model ID. */ - getModelID() - { + getModelID() { return this._modelId; } /** * Returns associated model URL. */ - getModelURL() - { + getModelURL() { return this._modelURL; } /** * Highlights this object. */ - setHighlight(highlighted) - { + setHighlight(highlighted) { this.strokeColor = highlighted ? Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].STROKE_COLOR_SELECTED : Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].STROKE_COLOR; this.strokeWidth = highlighted ? Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].STROKE_WIDTH_SELECTED : Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].STROKE_WIDTH; } @@ -256,14 +232,10 @@ class BaseItem extends paper.Path /** * Gets description. */ - getDescription() - { - if (this._description && this._description !== '') - { + getDescription() { + if (this._description && this._description !== '') { return this._description; - } - else - { + } else { return 'no description available'; } } @@ -271,70 +243,61 @@ class BaseItem extends paper.Path /** * Sets temporary color. */ - setTemporaryColor(color) - { + setTemporaryColor(color) { this._temporaryColor = color; } /** * Clears temporary color. */ - clearTemporaryColor() - { + clearTemporaryColor() { this._temporaryColor = null; } -/////////////////////////////////////////////////////////////////////////////////////// -// ABSTRACT METHODS -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // ABSTRACT METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Abstract method. Update. */ - update() - { + update() { // TODO - better way to do abstract methods console.error('This must be defined in sub-class.'); } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS - Backbone event handlers -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS - Backbone event handlers + /////////////////////////////////////////////////////////////////////////////////////// /** * Handle event model sync. */ - _handleEventModelSync(options) - { + _handleEventModelSync(options) { this._model = options.model; - switch (options.options.task) - { - case 'save': - { + switch (options.options.task) { + case 'save': { this._text.content = this._model.get('name'); this._description = this._model.getDescription(); break; } - case 'destroy': - { + case 'destroy': { this.destroy(); break; } - default: - { + default: { break; } } } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Initialize hover. */ - _initializeHover(options) - { + _initializeHover(options) { this._timerEvent = null; this._popup = new paper.PointText(new paper.Point(0, 0)); } @@ -342,8 +305,7 @@ class BaseItem extends paper.Path /** * Initialize appearance. */ - _initializeAppearance(options) - { + _initializeAppearance(options) { this.strokeColor = Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].STROKE_COLOR; this.strokeJoin = 'round'; this.strokeWidth = Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].STROKE_WIDTH; @@ -354,9 +316,8 @@ class BaseItem extends paper.Path /** * Initialize model binding. */ - _initializeModelBinding(options) - { - this._model = options.model ? options.model: null; + _initializeModelBinding(options) { + this._model = options.model ? options.model : null; this._modelId = options.model ? options.model.id : null; this._modelURL = options.model ? options.model.get('url') : null; this._description = options.model ? options.model.getDescription() : null; @@ -369,8 +330,7 @@ class BaseItem extends paper.Path /** * Initialize event handlers. */ - _initializeInputEventHandlers(options) - { + _initializeInputEventHandlers(options) { this.onMouseDown = event => this._handleMouseEvent(event); this.onMouseUp = event => this._handleMouseEvent(event); this.onClick = event => this._handleMouseEvent(event); @@ -388,9 +348,8 @@ class BaseItem extends paper.Path /** * Initialize text. */ - _initializeText(options) - { - this._useText = (options.hasOwnProperty('text') && options.text === true); + _initializeText(options) { + this._useText = options.hasOwnProperty('text') && options.text === true; this._text = new paper.PointText(new paper.Point(0, 0)); this._text.justification = 'center'; this._text.fillColor = Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].STROKE_COLOR; @@ -398,8 +357,7 @@ class BaseItem extends paper.Path this._text.content = ''; this._text.position = this.bounds.center; this.addChild(this._text); - if (options.model) - { + if (options.model) { this._text.content = options.model.get('name'); } } @@ -407,12 +365,10 @@ class BaseItem extends paper.Path /** * Initialize radio. */ - _initializeRadio(options) - { + _initializeRadio(options) { this.guiChannel = Radio.channel('rodan-client_gui'); this.rodanChannel = Radio.channel('rodan'); - if (options && options.model) - { + if (options && options.model) { this.rodanChannel.on(Rodan.RODAN_EVENTS.EVENT__MODEL_SYNC + options.model.get('url'), options => this._handleEventModelSync(options)); } } @@ -420,23 +376,20 @@ class BaseItem extends paper.Path /** * Handle coordinate load success. */ - _handleCoordinateLoadSuccess(model) - { - var appearance = model.get("appearance"); - if (appearance) - { + _handleCoordinateLoadSuccess(model) { + var appearance = model.get('appearance'); + if (appearance) { const { x, y } = BaseItem.appearanceToProject(appearance); - this.position = new paper.Point(x, y); + this.bounds.topLeft = new paper.Point(x, y); + this.position = this.bounds.center; } } /** * Shows popup. */ - _showPopup(event) - { - if ($('div#canvas-tooltip')) - { + _showPopup(event) { + if ($('div#canvas-tooltip')) { var description = this.getDescription(); var tooltip = $('div#canvas-tooltip'); tooltip.css('visibility', 'visible'); @@ -449,10 +402,8 @@ class BaseItem extends paper.Path /** * Hide popup. */ - _hidePopup() - { - if ($('div#canvas-tooltip')) - { + _hidePopup() { + if ($('div#canvas-tooltip')) { $('div#canvas-tooltip').css('visibility', 'hidden'); } } @@ -460,55 +411,46 @@ class BaseItem extends paper.Path /** * Handle mouse event. */ - _handleMouseEvent(event) - { + _handleMouseEvent(event) { // We do this because paperjs doesn't bubble up events. // This line guarantees that events caught by the TEXT actually get to the parent base item. event.target = this; - switch (event.type) - { - case 'mouseenter': - { + switch (event.type) { + case 'mouseenter': { this._timerEvent = setTimeout(() => this._showPopup(event), Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].HOVER_TIME); paper.handleMouseEvent(event); break; } - case 'mouseleave': - { + case 'mouseleave': { this._hidePopup(); clearTimeout(this._timerEvent); paper.handleMouseEvent(event); break; } - case 'mouseup': - { + case 'mouseup': { this._handleMouseUp(event); break; } - case 'mousedown': - { + case 'mousedown': { this._handleMouseDown(event); break; } - case 'click': - { + case 'click': { this._handleClick(event); break; } - case 'doubleclick': - { + case 'doubleclick': { this._handleDoubleClick(event); break; } - default: - { + default: { paper.handleMouseEvent(event); break; } @@ -518,8 +460,7 @@ class BaseItem extends paper.Path /** * Handle mouse enter. */ - _handleMouseEnter(mouseEvent) - { + _handleMouseEnter(mouseEvent) { this._timerEvent = setTimeout(() => this._showPopup(mouseEvent), Rodan.Configuration.PLUGINS['rodan-client-wfbgui'].HOVER_TIME); paper.handleMouseEvent(mouseEvent); } @@ -527,44 +468,39 @@ class BaseItem extends paper.Path /** * Handle mouse leave. */ - _handleMouseLeave(mouseEvent) - { + _handleMouseLeave(mouseEvent) { this._hidePopup(); clearTimeout(this._timerEvent); - paper.handleMouseEvent(mouseEvent); + paper.handleMouseEvent(mouseEvent); } /** * Handle mouse up. */ - _handleMouseUp(mouseEvent) - { + _handleMouseUp(mouseEvent) { paper.handleMouseEvent(mouseEvent); } /** * Handle mouse down. */ - _handleMouseDown(mouseEvent) - { + _handleMouseDown(mouseEvent) { paper.handleMouseEvent(mouseEvent); } /** * Handle mouse click. */ - _handleClick(mouseEvent) - { + _handleClick(mouseEvent) { paper.handleMouseEvent(mouseEvent); } /** * Handle mouse double click. */ - _handleDoubleClick(mouseEvent) - { + _handleDoubleClick(mouseEvent) { paper.handleMouseEvent(mouseEvent); } } -export default BaseItem; \ No newline at end of file +export default BaseItem; diff --git a/rodan-client/code/src/js/Shared/RODAN_EVENTS.js b/rodan-client/code/src/js/Shared/RODAN_EVENTS.js index f60176304..e8c3f2417 100644 --- a/rodan-client/code/src/js/Shared/RODAN_EVENTS.js +++ b/rodan-client/code/src/js/Shared/RODAN_EVENTS.js @@ -21,27 +21,24 @@ let _instance = null; /** * Backbone.Radio events use in the client. Do not instantiate this class. */ -class RODAN_EVENTS -{ +class RODAN_EVENTS { /** @ignore */ - constructor() - { - if (_instance) - { + constructor() { + if (_instance) { throw new Error('this class cannot be instantiated more than once'); } _instance = this; /** @ignore */ - this.REQUEST__RESOURCE_SHOWLAYOUTVIEW = 'REQUEST__RESOURCE_SHOWLAYOUTVIEW'; // Show LayoutView for Resource control (outside of the primary Resources view). This tells the ControllerResource which LayoutView to reference upon events. Takes {layoutView: LayoutView}. + this.REQUEST__RESOURCE_SHOWLAYOUTVIEW = 'REQUEST__RESOURCE_SHOWLAYOUTVIEW'; // Show LayoutView for Resource control (outside of the primary Resources view). This tells the ControllerResource which LayoutView to reference upon events. Takes {layoutView: LayoutView}. /** @ignore */ - this.REQUEST__RUNJOB_SHOWLAYOUTVIEW = 'REQUEST__RUNJOB_SHOWLAYOUTVIEW'; // Show LayoutView for RunJob control (outside of the primary RunJobs view). This tells the ControllerRunJob which LayoutView to reference upon events. Takes {layoutView: LayoutView}. + this.REQUEST__RUNJOB_SHOWLAYOUTVIEW = 'REQUEST__RUNJOB_SHOWLAYOUTVIEW'; // Show LayoutView for RunJob control (outside of the primary RunJobs view). This tells the ControllerRunJob which LayoutView to reference upon events. Takes {layoutView: LayoutView}. /** @ignore */ - this.EVENT__SERVER_WENTAWAY = 'EVENT__SERVER_WENTAWAY'; // Called on server disconnect. No pass. + this.EVENT__SERVER_WENTAWAY = 'EVENT__SERVER_WENTAWAY'; // Called on server disconnect. No pass. /** @ignore */ - this.EVENT__SERVER_PANIC = 'EVENT__SERVER_PANIC'; // Called when the app suspects that something went wrong. + this.EVENT__SERVER_PANIC = 'EVENT__SERVER_PANIC'; // Called when the app suspects that something went wrong. /** @ignore */ - this.REQUEST__SYSTEM_HANDLE_ERROR = 'REQUEST__SYSTEM_HANDLE_ERROR'; // Sends error to error handler. Takes {model: BaseModel, response: HTTP response, option: associated options}. + this.REQUEST__SYSTEM_HANDLE_ERROR = 'REQUEST__SYSTEM_HANDLE_ERROR'; // Sends error to error handler. Takes {model: BaseModel, response: HTTP response, option: associated options}. /////////////////////////////////////////////////////////////////////////////////////// // Authentication @@ -89,9 +86,9 @@ class RODAN_EVENTS // General /////////////////////////////////////////////////////////////////////////////////////// /** Request "API" information to be show. */ - this.REQUEST__SHOW_API = 'REQUEST__SHOW_API', - /** Request last 100 Radio events. Returns [{name (string), event (string), options (object)}]. */ - this.REQUEST__LOG = 'REQUEST__LOG'; + (this.REQUEST__SHOW_API = 'REQUEST__SHOW_API'), + /** Request last 100 Radio events. Returns [{name (string), event (string), options (object)}]. */ + (this.REQUEST__LOG = 'REQUEST__LOG'); /** Request "About" information be shown. */ this.REQUEST__SHOW_ABOUT = 'REQUEST__SHOW_ABOUT'; /** Request "Help" page be shown. */ @@ -100,13 +97,13 @@ class RODAN_EVENTS this.REQUEST__SHOW_NAVIGATION_PAGINATION = 'REQUEST__SHOW_NAVIGATION_PAGINATION'; /** Request update navigation pagination */ this.REQUEST__UPDATE_NAVIGATION_PAGINATION = 'REQUEST__UPDATE_NAVIGATION_PAGINATION'; - /** Request pagination from navigation bar for first */ + /** Request pagination from navigation bar for first */ this.REQUEST__NAVIGATION_PAGINATION_FIRST = 'REQUEST__NAVIGATION_PAGINATION_FIRST'; - /** Request pagination from navigation bar for previous */ + /** Request pagination from navigation bar for previous */ this.REQUEST__NAVIGATION_PAGINATION_PREVIOUS = 'REQUEST__NAVIGATION_PAGINATION_PREVIOUS'; - /** Request pagination from navigation bar for next */ + /** Request pagination from navigation bar for next */ this.REQUEST__NAVIGATION_PAGINATION_NEXT = 'REQUEST__NAVIGATION_PAGINATION_NEXT'; - /** Request pagination from navigation bar for last */ + /** Request pagination from navigation bar for last */ this.REQUEST__NAVIGATION_PAGINATION_LAST = 'REQUEST__NAVIGATION_PAGINATION_LAST'; /////////////////////////////////////////////////////////////////////////////////////// @@ -181,19 +178,19 @@ class RODAN_EVENTS // Project /////////////////////////////////////////////////////////////////////////////////////// /** Triggered when User has been added as Project admin. Sends {project: Project}. */ - this.EVENT__PROJECT_ADDED_USER_ADMIN = 'EVENT__PROJECT_ADDED_USER_ADMIN', - /** Triggered when User has been added as Project worker. Sends {project: Project}. */ - this.EVENT__PROJECT_ADDED_USER_WORKER = 'EVENT__PROJECT_ADDED_USER_WORKER', - /** Triggered when Project has been created. Sends {project: Project}. */ - this.EVENT__PROJECT_CREATED = 'EVENT__PROJECT_CREATED'; + (this.EVENT__PROJECT_ADDED_USER_ADMIN = 'EVENT__PROJECT_ADDED_USER_ADMIN'), + /** Triggered when User has been added as Project worker. Sends {project: Project}. */ + (this.EVENT__PROJECT_ADDED_USER_WORKER = 'EVENT__PROJECT_ADDED_USER_WORKER'), + /** Triggered when Project has been created. Sends {project: Project}. */ + (this.EVENT__PROJECT_CREATED = 'EVENT__PROJECT_CREATED'); /** Triggered when Project has been deleted. Sends {project: Project}. */ this.EVENT__PROJECT_DELETED = 'EVENT__PROJECT_DELETED'; /** Triggered when User has been removed as Project admin. Sends {project: Project}. */ - this.EVENT__PROJECT_REMOVED_USER_ADMIN = 'EVENT__PROJECT_REMOVED_USER_ADMIN', - /** Triggered when User has been removed as Project worker. Sends {project: Project}. */ - this.EVENT__PROJECT_REMOVED_USER_WORKER = 'EVENT__PROJECT_REMOVED_USER_WORKER', - /** Triggered when Project has been saved. Sends {project: Project}. */ - this.EVENT__PROJECT_SAVED = 'EVENT__PROJECT_SAVED'; + (this.EVENT__PROJECT_REMOVED_USER_ADMIN = 'EVENT__PROJECT_REMOVED_USER_ADMIN'), + /** Triggered when User has been removed as Project worker. Sends {project: Project}. */ + (this.EVENT__PROJECT_REMOVED_USER_WORKER = 'EVENT__PROJECT_REMOVED_USER_WORKER'), + /** Triggered when Project has been saved. Sends {project: Project}. */ + (this.EVENT__PROJECT_SAVED = 'EVENT__PROJECT_SAVED'); /** Triggered when the user selects an individual Project. Sends {project: Project}. */ this.EVENT__PROJECT_SELECTED = 'EVENT__PROJECT_SELECTED'; /** Triggered when the user selects to see all available Projects. */ @@ -201,21 +198,23 @@ class RODAN_EVENTS /** Triggered when Project admin interface has been selected. Takes {project: Project}. */ this.EVENT__PROJECT_USERS_SELECTED = 'EVENT__PROJECT_USERS_SELECTED'; /** Request a User be added as Project admin. Takes {project: Project, username: string} */ - this.REQUEST__PROJECT_ADD_USER_ADMIN = 'REQUEST__PROJECT_ADD_USER_ADMIN', - /** Request a User be added as Project worker. Takes {project: Project, username: string} */ - this.REQUEST__PROJECT_ADD_USER_WORKER = 'REQUEST__PROJECT_ADD_USER_WORKER', - /** Request a Project be created. Takes {creator: User}. */ - this.REQUEST__PROJECT_CREATE = 'REQUEST__PROJECT_CREATE'; + (this.REQUEST__PROJECT_ADD_USER_ADMIN = 'REQUEST__PROJECT_ADD_USER_ADMIN'), + /** Request a User be added as Project worker. Takes {project: Project, username: string} */ + (this.REQUEST__PROJECT_ADD_USER_WORKER = 'REQUEST__PROJECT_ADD_USER_WORKER'), + /** Request a Project be created. Takes {creator: User}. */ + (this.REQUEST__PROJECT_CREATE = 'REQUEST__PROJECT_CREATE'); /** Request a Project be deleted. Takes {project: Project}. */ this.REQUEST__PROJECT_DELETE = 'REQUEST__PROJECT_DELETE'; + /** Request a Project delete confirm modal window. Takes {project: Project}. */ + this.REQUEST__PROJECT_DELETE_CONFIRM = 'REQUEST__PROJECT_DELETE_CONFIRM'; /** Request currently active/open Project. Returns Project (or null). */ this.REQUEST__PROJECT_GET_ACTIVE = 'REQUEST__PROJECT_GET_ACTIVE'; /** Request a User be removed as Project admin. Takes {project: Project, user: User} */ - this.REQUEST__PROJECT_REMOVE_USER_ADMIN = 'REQUEST__PROJECT_REMOVE_USER_ADMIN', - /** Request a User be removed as Project worker. Takes {project: Project, user: User} */ - this.REQUEST__PROJECT_REMOVE_USER_WORKER = 'REQUEST__PROJECT_REMOVE_USER_WORKER', - /** Request a Project be saved/updated. Takes {project: Project, fields: {object with attributes to change}}. */ - this.REQUEST__PROJECT_SAVE = 'REQUEST__PROJECT_SAVE'; + (this.REQUEST__PROJECT_REMOVE_USER_ADMIN = 'REQUEST__PROJECT_REMOVE_USER_ADMIN'), + /** Request a User be removed as Project worker. Takes {project: Project, user: User} */ + (this.REQUEST__PROJECT_REMOVE_USER_WORKER = 'REQUEST__PROJECT_REMOVE_USER_WORKER'), + /** Request a Project be saved/updated. Takes {project: Project, fields: {object with attributes to change}}. */ + (this.REQUEST__PROJECT_SAVE = 'REQUEST__PROJECT_SAVE'); /** Request a Project be set as active Project. Takes {project: Project}. */ this.REQUEST__PROJECT_SET_ACTIVE = 'REQUEST__PROJECT_SET_ACTIVE'; @@ -236,6 +235,8 @@ class RODAN_EVENTS this.REQUEST__RESOURCE_CREATE = 'REQUEST__RESOURCE_CREATE'; /** Request a Resource be deleted. Takes {resource: Resource}. */ this.REQUEST__RESOURCE_DELETE = 'REQUEST__RESOURCE_DELETE'; + /** Request a Resource delete confirm modal window. Takes {resource: Resource}. */ + this.REQUEST__RESOURCE_DELETE_CONFIRM = 'REQUEST__RESOURCE_DELETE_CONFIRM'; /** Request a Resource be downloaded. Takes {resource: Resource}. */ this.REQUEST__RESOURCE_DOWNLOAD = 'REQUEST__RESOURCE_DOWNLOAD'; /** Request a Resource be saved/updated. Takes {resource: Resource, fields: {object with attributes to change}}. */ @@ -407,9 +408,11 @@ class RODAN_EVENTS this.REQUEST__WORKFLOW_CREATE = 'REQUEST__WORKFLOW_CREATE'; /** Request a Workflow be deleted. Takes {workflow: Workflow}. */ this.REQUEST__WORKFLOW_DELETE = 'REQUEST__WORKFLOW_DELETE'; + /** Request a Workflow delete confirm modal window. Takes {workflow: Workflow}. */ + this.REQUEST__WORKFLOW_DELETE_CONFIRM = 'REQUEST__WORKFLOW_DELETE_CONFIRM'; /** Request a Workflow be exported. Takes {workflow: Workflow}. */ this.REQUEST__WORKFLOW_EXPORT = 'REQUEST__WORKFLOW_EXPORT'; - /** Request a Workflow be imported. Takes {}. */ + /** Request a Workflow be imported. Takes {project: Project, file: File}. */ this.REQUEST__WORKFLOW_IMPORT = 'REQUEST__WORKFLOW_IMPORT'; /** Request a Workflow be saved/updated. Takes {workflow: Workflow, fields: {object with attributes to change}}. */ this.REQUEST__WORKFLOW_SAVE = 'REQUEST__WORKFLOW_SAVE'; @@ -559,37 +562,32 @@ class RODAN_EVENTS // version it requires. /////////////////////////////////////////////////////////////////////////////////////// /** @ignore **/ - this.VERSION__COMPATIBILITY = - { - 'EVENT__PROJECT_USERS_SELECTED': '1.1.5' + this.VERSION__COMPATIBILITY = { + EVENT__PROJECT_USERS_SELECTED: '1.1.5' }; } /** @ignore **/ - enforceVersionCompatibility() - { + enforceVersionCompatibility() { var serverVersionString = Radio.channel('rodan').request(this.REQUEST__SERVER_GET_VERSION); var serverVersion = serverVersionString.split('.').map(Number); - for (var event in this.VERSION__COMPATIBILITY) - { - if (this[event]) - { + for (var event in this.VERSION__COMPATIBILITY) { + if (this[event]) { var requiredVersionString = this.VERSION__COMPATIBILITY[event]; var requiredVersion = requiredVersionString.split('.').map(Number); if ( // 1) Rodan Major version is smaller - requiredVersion[0] > serverVersion[0] - // 2) Rodan Minor version is smaller, and Major - || (requiredVersion[1] > serverVersion[1] && requiredVersion[0] > serverVersion[0]) + requiredVersion[0] > serverVersion[0] || + // 2) Rodan Minor version is smaller, and Major + (requiredVersion[1] > serverVersion[1] && requiredVersion[0] > serverVersion[0]) || // 3) Rodan Patch version is smaller, and minor, and Major - || (requiredVersion[2] > serverVersion[2] && requiredVersion[1] > serverVersion[1] && requiredVersion[0] > serverVersion[0]) - ) - { + (requiredVersion[2] > serverVersion[2] && requiredVersion[1] > serverVersion[1] && requiredVersion[0] > serverVersion[0]) + ) { var requiresEvent = 'EVENT__REQUIRES_RODAN_VERSION_' + serverVersionString; this[event] = requiresEvent; var messageString = 'This feature requires Rodan Server v' + requiredVersionString + '. The Rodan Server is currently v' + serverVersionString + '.'; messageString += ' (' + event + ')'; - var modalOptions = {content: messageString}; + var modalOptions = { content: messageString }; Radio.channel('rodan').on(requiresEvent, () => Radio.channel('rodan').request(this.REQUEST__MODAL_ERROR, modalOptions)); Radio.channel('rodan').reply(requiresEvent, () => Radio.channel('rodan').request(this.REQUEST__MODAL_ERROR, modalOptions)); } diff --git a/rodan-client/code/src/js/Views/LayoutViewWorkflowBuilder.js b/rodan-client/code/src/js/Views/LayoutViewWorkflowBuilder.js index f3b949b68..e0fa0c780 100644 --- a/rodan-client/code/src/js/Views/LayoutViewWorkflowBuilder.js +++ b/rodan-client/code/src/js/Views/LayoutViewWorkflowBuilder.js @@ -254,6 +254,7 @@ LayoutViewWorkflowBuilder.prototype.ui = { settingsDropdownToggle: '#settings-dropdown-toggle', }; LayoutViewWorkflowBuilder.prototype.events = { + 'click @ui.canvasWorkspace': '_hideDropdowns', 'click @ui.buttonZoomIn': '_handleButtonZoomIn', 'click @ui.buttonZoomOut': '_handleButtonZoomOut', 'click @ui.buttonZoomReset': '_handleButtonZoomReset', diff --git a/rodan-client/code/src/js/Views/Master/Main/Project/Individual/ViewProject.js b/rodan-client/code/src/js/Views/Master/Main/Project/Individual/ViewProject.js index d6898d31b..0004c0cca 100644 --- a/rodan-client/code/src/js/Views/Master/Main/Project/Individual/ViewProject.js +++ b/rodan-client/code/src/js/Views/Master/Main/Project/Individual/ViewProject.js @@ -3,18 +3,19 @@ import _ from 'underscore'; import RODAN_EVENTS from 'js/Shared/RODAN_EVENTS'; import Marionette from 'backbone.marionette'; import Radio from 'backbone.radio'; +import ViewResourceCollection from 'js/Views/Master/Main/Resource/Collection/ViewResourceCollection'; +import ViewWorkflowCollection from 'js/Views/Master/Main/Workflow/Collection/ViewWorkflowCollection'; +import ViewWorkflowRunCollection from 'js/Views/Master/Main/WorkflowRun/Collection/ViewWorkflowRunCollection'; +import ViewRunJobCollection from 'js/Views/Master/Main/RunJob/Collection/ViewRunJobCollection'; /** * Project view. */ -export default class ViewProject extends Marionette.View -{ - +export default class ViewProject extends Marionette.View { /** * Initializes the instance. */ - initialize() - { + initialize() { this.setElement(''); this.addRegions({ regionCollection: '#region-collection-container', @@ -28,120 +29,112 @@ export default class ViewProject extends Marionette.View * * @param {Marionette.View} view Collection view to show */ - showCollection(view) - { + showCollection(view) { this.showChildView('regionCollection', view); + + // Update tab status based on view type + $('.project-nav-bar-btn').removeClass('active'); + if (view instanceof ViewResourceCollection) { + $('#resource_count').addClass('active'); + } else if (view instanceof ViewWorkflowCollection) { + $('#workflow_count').addClass('active'); + } else if (view instanceof ViewWorkflowRunCollection) { + $('#button-workflow_runs').addClass('active'); + } else if (view instanceof ViewRunJobCollection) { + $('#button-runjobs').addClass('active'); + } } /** - * Show an item view. - * - * @param {Marionette.View} view item view to show - */ + * Show an item view. + * + * @param {Marionette.View} view item view to show + */ // showProjectInfo(view) // { // this.showChildView('regionProjectInfo', view); // } /** - * Show an item view. This is for the secondary item view. - * - * @param {Marionette.View} view item view to show - */ + * Show an item view. This is for the secondary item view. + * + * @param {Marionette.View} view item view to show + */ showCollectionItemInfo(view) { this.showChildView('regionCollectionItemInfo', view); } /** - * Clears item view. - */ - clearCollectionItemInfoView() - { + * Clears item view. + */ + clearCollectionItemInfoView() { this.getRegion('regionCollectionItemInfo').empty(); } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Handle save button. */ - _handleButtonSave() - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__PROJECT_SAVE, - { - project: this.model, - fields: { - name: _.escape(this.ui.textName.val()), - description: _.escape(this.ui.textDescription.val()) - } - } - ); + _handleButtonSave() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__PROJECT_SAVE, { + project: this.model, + fields: { + name: _.escape(this.ui.textName.val()), + description: _.escape(this.ui.textDescription.val()) + } + }); } /** * Handle delete button. */ - _handleButtonDelete() - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__PROJECT_DELETE, {project: this.model}); + _handleButtonDelete() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__PROJECT_DELETE_CONFIRM, { project: this.model }); } - + /** - * Handle RunJob button. - */ - _handleButtonRunJobs() - { - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RUNJOB_SELECTED_COLLECTION, {project: this.model}); - $('.project-nav-bar-btn').removeClass('active'); - $('#button-runjobs').addClass('active'); + * Handle RunJob button. + */ + _handleButtonRunJobs() { + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RUNJOB_SELECTED_COLLECTION, { project: this.model }); } /** - * Handle click resource count. - */ - _handleClickResourceCount() - { - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCE_SELECTED_COLLECTION, {project: this.model}); - $('.project-nav-bar-btn').removeClass('active'); - $('#resource_count').addClass('active'); + * Handle click resource count. + */ + _handleClickResourceCount() { + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCE_SELECTED_COLLECTION, { project: this.model }); } /** - * Handle click workflow count. - */ - _handleClickWorkflowCount() - { - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_SELECTED_COLLECTION, {view: this, project: this.model}); - $('.project-nav-bar-btn').removeClass('active'); - $('#workflow_count').addClass('active'); + * Handle click workflow count. + */ + _handleClickWorkflowCount() { + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOW_SELECTED_COLLECTION, { view: this, project: this.model }); } /** * Handle button WorkflowRuns. */ - _handleButtonWorkflowRuns() - { - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOWRUN_SELECTED_COLLECTION, {project: this.model}); - $('.project-nav-bar-btn').removeClass('active'); - $('#button-workflow_runs').addClass('active'); + _handleButtonWorkflowRuns() { + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOWRUN_SELECTED_COLLECTION, { project: this.model }); } /** - * Handle click button ResourceLists. - */ - _handleButtonResourceLists() - { - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCELIST_SELECTED_COLLECTION, {project: this.model}); + * Handle click button ResourceLists. + */ + _handleButtonResourceLists() { + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__RESOURCELIST_SELECTED_COLLECTION, { project: this.model }); $('.project-nav-bar-btn').removeClass('active'); $('#resource_count').addClass('active'); } /** - * Handle button Project users. - */ - _handleButtonProjectUsers() - { - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_USERS_SELECTED, {project: this.model}); + * Handle button Project users. + */ + _handleButtonProjectUsers() { + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__PROJECT_USERS_SELECTED, { project: this.model }); $('.project-nav-bar-btn').removeClass('active'); $('#button-project_users').addClass('active'); } diff --git a/rodan-client/code/src/js/Views/Master/Main/Resource/Individual/ViewResource.js b/rodan-client/code/src/js/Views/Master/Main/Resource/Individual/ViewResource.js index 15c27de37..5db6f83db 100644 --- a/rodan-client/code/src/js/Views/Master/Main/Resource/Individual/ViewResource.js +++ b/rodan-client/code/src/js/Views/Master/Main/Resource/Individual/ViewResource.js @@ -10,20 +10,20 @@ import ViewResourceTypeCollectionItem from 'js/Views/Master/Main/ResourceType/Vi /** * Resource view. */ -export default class ViewResource extends Marionette.CollectionView -{ -/////////////////////////////////////////////////////////////////////////////////////// -// PUBLIC METHODS -/////////////////////////////////////////////////////////////////////////////////////// +export default class ViewResource extends Marionette.CollectionView { + /////////////////////////////////////////////////////////////////////////////////////// + // PUBLIC METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Initializes the instance. */ - initialize() - { + initialize() { /** @ignore */ this.collection = Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__GLOBAL_RESOURCETYPE_COLLECTION); - this.collection.each(function(model) { model.unset('selected'); }); - var resourceType = this.collection.findWhere({url: this.model.get('resource_type')}); + this.collection.each(function (model) { + model.unset('selected'); + }); + var resourceType = this.collection.findWhere({ url: this.model.get('resource_type') }); resourceType.set('selected', 'selected'); this.setElement(''); // set container element } @@ -31,8 +31,7 @@ export default class ViewResource extends Marionette.CollectionView /** * Initialize buttons after render. */ - onRender() - { + onRender() { var disabledDelete = this.model.get('origin') !== null; $(this.ui.buttonDelete).attr('disabled', disabledDelete); var disabledDownload = this.model.get('download') === null; @@ -48,64 +47,61 @@ export default class ViewResource extends Marionette.CollectionView /** * Initialize label field after it's attached to the DOM */ - onAttach() - { + onAttach() { tagsInput(this.ui.resourceLabels[0]); } /** * Destroy callback. */ - onDestroy() - { + onDestroy() { this.collection = null; } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Handle button save. */ - _handleClickButtonSave() - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_SAVE, {resource: this.model, - fields: {resource_type: this.ui.selectResourceType.find(':selected').val(), - name: _.escape(this.ui.resourceName.val()), - description: _.escape(this.ui.resourceDescription.val()), - label_names: _.escape(this.ui.resourceLabels.val())}}); + _handleClickButtonSave() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_SAVE, { + resource: this.model, + fields: { + resource_type: this.ui.selectResourceType.find(':selected').val(), + name: _.escape(this.ui.resourceName.val()), + description: _.escape(this.ui.resourceDescription.val()), + label_names: _.escape(this.ui.resourceLabels.val()) + } + }); } /** * Handle button delete. */ - _handleClickButtonDelete() - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_DELETE, {resource: this.model}); + _handleClickButtonDelete() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_DELETE_CONFIRM, { resource: this.model }); } /** * Handle button download. */ - _handleClickDownload() - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_DOWNLOAD, {resource: this.model}); + _handleClickDownload() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_DOWNLOAD, { resource: this.model }); } /** * Handle button view. */ - _handleClickView() - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_VIEWER_ACQUIRE, {resource: this.model}); + _handleClickView() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_VIEWER_ACQUIRE, { resource: this.model }); } - _handleDblClickTag(evt) - { + _handleDblClickTag(evt) { let labels = Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__GLOBAL_RESOURCELABEL_COLLECTION); - let model = labels.findWhere({name: evt.target.textContent}); + let model = labels.findWhere({ name: evt.target.textContent }); if (model) { - let view = new ViewResourceLabel({model: model}); + let view = new ViewResourceLabel({ model: model }); Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW, { content: view }); @@ -113,7 +109,7 @@ export default class ViewResource extends Marionette.CollectionView } } ViewResource.prototype.modelEvents = { - 'all': 'render' + all: 'render' }; ViewResource.prototype.ui = { buttonSave: '#button-main_resource_individual_save', diff --git a/rodan-client/code/src/js/Views/Master/Main/Resource/Individual/ViewResourceMulti.js b/rodan-client/code/src/js/Views/Master/Main/Resource/Individual/ViewResourceMulti.js index 30897d18f..919f3b45b 100644 --- a/rodan-client/code/src/js/Views/Master/Main/Resource/Individual/ViewResourceMulti.js +++ b/rodan-client/code/src/js/Views/Master/Main/Resource/Individual/ViewResourceMulti.js @@ -9,8 +9,7 @@ import ViewResourceTypeCollectionItem from 'js/Views/Master/Main/ResourceType/Vi /** * Resource Multi-Select View */ -export default class ViewResourceMulti extends Marionette.CollectionView -{ +export default class ViewResourceMulti extends Marionette.CollectionView { constructor(options) { super(options); this._models = options.models; @@ -30,7 +29,7 @@ export default class ViewResourceMulti extends Marionette.CollectionView } else if (rtUrl !== modelResourceTypeURL) { this.isSameType = false; } - + if (labels === undefined) { labels = modelLabels; this.labelNames = model.get('labels'); @@ -40,15 +39,16 @@ export default class ViewResourceMulti extends Marionette.CollectionView } this.collection = Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__GLOBAL_RESOURCETYPE_COLLECTION); - this.collection.each(function(model) { model.unset('selected'); }); - var resourceType = this.collection.findWhere({url: rtUrl}); + this.collection.each(function (model) { + model.unset('selected'); + }); + var resourceType = this.collection.findWhere({ url: rtUrl }); resourceType.set('selected', 'selected'); } /** * Initialize buttons after render. */ - onRender() - { + onRender() { var disabledDelete = false; var disabledDownload = false; for (var model of this._models) { @@ -69,11 +69,15 @@ export default class ViewResourceMulti extends Marionette.CollectionView $(this.ui.buttonView).attr('disabled', true); } - onAttach() - { + onAttach() { if (this.isSameLabel) { - this.ui.labelInput[0].setAttribute('value', _.map(this.labelNames, (label) => { return label.name; })); - tagsInput(this.ui.labelInput[0]); + this.ui.labelInput[0].setAttribute( + 'value', + _.map(this.labelNames, label => { + return label.name; + }) + ); + tagsInput(this.ui.labelInput[0]); } } @@ -84,34 +88,31 @@ export default class ViewResourceMulti extends Marionette.CollectionView }; } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Handle button delete. */ - _handleClickButtonDelete() - { - for (var model of this._models) { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_DELETE, {resource: model}); - } + _handleClickButtonDelete() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_DELETE_CONFIRM, { resource: this._models }); } - _handleClickButtonDownload() - { + _handleClickButtonDownload() { let modelArray = [...this._models.values()]; - let uuids = _.map(modelArray, val => { return val.id; }); + let uuids = _.map(modelArray, val => { + return val.id; + }); let baseUrl = Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__SERVER_GET_ROUTE, 'resource-archive'); let a = document.createElement('a'); a.download = 'Archive.zip'; - a.href = baseUrl + '?' + $.param({resource_uuid: uuids}, true); + a.href = baseUrl + '?' + $.param({ resource_uuid: uuids }, true); document.body.append(a); a.click(); a.remove(); } - _handleClickButtonSave() - { + _handleClickButtonSave() { let fields = { resource_type: this.ui.selectResourceType.find(':selected').val() }; @@ -119,12 +120,11 @@ export default class ViewResourceMulti extends Marionette.CollectionView fields['label_names'] = this.ui.labelInput.val(); } for (var model of this._models) { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_SAVE, {resource: model, fields: fields}); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_SAVE, { resource: model, fields: fields }); } } } -ViewResourceMulti.prototype.modelEvents = { -}; +ViewResourceMulti.prototype.modelEvents = {}; ViewResourceMulti.prototype.ui = { buttonSave: '#button-main_resource_individual_save', buttonDelete: '#button-main_resource_individual_delete', diff --git a/rodan-client/code/src/js/Views/Master/Main/Shared/ViewDeleteConfirm.js b/rodan-client/code/src/js/Views/Master/Main/Shared/ViewDeleteConfirm.js new file mode 100644 index 000000000..6e28082c6 --- /dev/null +++ b/rodan-client/code/src/js/Views/Master/Main/Shared/ViewDeleteConfirm.js @@ -0,0 +1,78 @@ +import $ from 'jquery'; +import _ from 'underscore'; +import Marionette from 'backbone.marionette'; +import Radio from 'backbone.radio'; +import RODAN_EVENTS from 'js/Shared/RODAN_EVENTS'; + +/** + * Project admin view. + */ +export default class ViewDeleteConfirm extends Marionette.View { + /////////////////////////////////////////////////////////////////////////////////////// + // PUBLIC METHODS + /////////////////////////////////////////////////////////////////////////////////////// + /** + * Initializes the instance. + * + * @param {object} options Marionette.View options object; + */ + initialize(options) { + this._type = options.type; + this._names = options.names; + this._toDelete = options.toDelete; + this.setElement(''); + } + + /** + * Serialize data to be passed to the template. + * + * @returns {object} Data to be used in the template. + */ + serializeData() { + return { + type: this._type, + names: this._names + }; + } + + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS + /////////////////////////////////////////////////////////////////////////////////////// + /** + * Handle button cancel delete. + */ + _handleCancelDelete() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_HIDE); + } + + /** + * Handle button confirm delete. + */ + _handleConfirmDelete() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_HIDE); + + switch (this._type) { + case 'project': + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__PROJECT_DELETE, { project: this._toDelete }); + break; + case 'resource': + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__RESOURCE_DELETE, { resource: this._toDelete }); + break; + case 'workflow': + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOW_DELETE, { workflow: this._toDelete }); + break; + default: + throw new Error('Invalid type: ' + this._type); + } + } +} + +ViewDeleteConfirm.prototype.template = _.template($('#template-main_shared_delete_confirm').text()); +ViewDeleteConfirm.prototype.ui = { + buttonCancelDelete: '#button-cancel_delete', + buttonConfirmDelete: '#button-confirm_delete' +}; +ViewDeleteConfirm.prototype.events = { + 'click @ui.buttonCancelDelete': '_handleCancelDelete', + 'click @ui.buttonConfirmDelete': '_handleConfirmDelete' +}; diff --git a/rodan-client/code/src/js/Views/Master/Main/Workflow/Individual/ViewWorkflow.js b/rodan-client/code/src/js/Views/Master/Main/Workflow/Individual/ViewWorkflow.js index 29ead9ef1..399904866 100644 --- a/rodan-client/code/src/js/Views/Master/Main/Workflow/Individual/ViewWorkflow.js +++ b/rodan-client/code/src/js/Views/Master/Main/Workflow/Individual/ViewWorkflow.js @@ -7,66 +7,60 @@ import Radio from 'backbone.radio'; /** * Workflow view. */ -export default class ViewWorkflow extends Marionette.View -{ - +export default class ViewWorkflow extends Marionette.View { initialize() { this.setElement(''); } -/////////////////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS -/////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS + /////////////////////////////////////////////////////////////////////////////////////// /** * Handle button run workflow. */ - _handleButtonRunWorkflow() - { - Radio.channel('rodan').trigger(RODAN_EVENTS.REQUEST__WORKFLOWBUILDER_CREATE_WORKFLOWRUN, {workflow: this.model}); + _handleButtonRunWorkflow() { + Radio.channel('rodan').trigger(RODAN_EVENTS.REQUEST__WORKFLOWBUILDER_CREATE_WORKFLOWRUN, { workflow: this.model }); } /** * Handle button delete workflow. */ - _handleButtonDeleteWorkflow() - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOW_DELETE, {workflow: this.model}); + _handleButtonDeleteWorkflow() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOW_DELETE_CONFIRM, { workflow: this.model }); } /** * Handle button edit workflow. */ - _handleButtonEditWorkflow() - { - Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOWBUILDER_SELECTED, {workflow: this.model}); + _handleButtonEditWorkflow() { + Radio.channel('rodan').trigger(RODAN_EVENTS.EVENT__WORKFLOWBUILDER_SELECTED, { workflow: this.model }); } /** * Handle button copy workflow. */ - _handleButtonCopyWorkflow() - { - } + _handleButtonCopyWorkflow() {} /** * Handle button export workflow. */ - _handleButtonExport() - { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOW_EXPORT, {workflow: this.model}); + _handleButtonExport() { + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOW_EXPORT, { workflow: this.model }); } /** * Handle save button. */ - _handleButtonSave() - { + _handleButtonSave() { Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_HIDE); - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOW_SAVE, {workflow: this.model, fields: {name: _.escape(this.ui.textName.val()), description: _.escape(this.ui.textDescription.val())}}); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOW_SAVE, { + workflow: this.model, + fields: { name: _.escape(this.ui.textName.val()), description: _.escape(this.ui.textDescription.val()) } + }); } } ViewWorkflow.prototype.modelEvents = { - 'all': 'render' - }; + all: 'render' +}; ViewWorkflow.prototype.ui = { runWorkflowButton: '#button-run_workflow', deleteWorkflowButton: '#button-delete_workflow', @@ -77,7 +71,7 @@ ViewWorkflow.prototype.ui = { buttonSave: '#button-save_workflow', textName: '#text-workflow_name', textDescription: '#text-workflow_description' - }; +}; ViewWorkflow.prototype.events = { 'click @ui.runWorkflowButton': '_handleButtonRunWorkflow', 'click @ui.deleteWorkflowButton': '_handleButtonDeleteWorkflow', @@ -86,5 +80,5 @@ ViewWorkflow.prototype.events = { 'click @ui.buttonSaveData': '_handleButtonSave', 'click @ui.buttonSave': '_handleButtonSave', 'click @ui.exportWorkflowButton': '_handleButtonExport' - }; +}; ViewWorkflow.prototype.template = _.template($('#template-main_workflow_individual').text()); diff --git a/rodan-client/code/styles/default.css b/rodan-client/code/styles/default.css index cf307085a..b852bf772 100644 --- a/rodan-client/code/styles/default.css +++ b/rodan-client/code/styles/default.css @@ -26,7 +26,7 @@ * General parameters *************************************************************************************/ - /* Set values for variables used throughout the stylesheet */ +/* Set values for variables used throughout the stylesheet */ :root { /* Navigation tree area */ --navigation-node-color: #939393; @@ -41,8 +41,8 @@ --secondary-app-color: #589ed5; --text-color-navigation: white; --app-font: Montserrat, sans-serif; - --btn-remove-color: #E76B6B; - --btn-save-color: #EB9E3E; + --btn-remove-color: #e76b6b; + --btn-save-color: #eb9e3e; /* Navigation area */ --background-color-navigation: #000000; @@ -75,7 +75,6 @@ --font-weight: 100; } - /* ------------------------------------ */ /* General styles /* ------------------------------------ */ @@ -86,12 +85,10 @@ * PLEASE MAKE SURE YOU KNOW WHAT YOU'RE DOING! */ - * { box-sizing: border-box; } -.text-small -{ +.text-small { font-size: x-small; } body { @@ -103,7 +100,8 @@ body { margin: 0px; overflow: hidden; } -input, select { +input, +select { border: 1px solid #cecece; border-radius: 3px; font-family: Montserrat, sans-serif; @@ -121,7 +119,7 @@ textarea { border-radius: 3px; outline: none; } - + /* Table styles */ table { user-select: none; @@ -137,7 +135,8 @@ thead { position: sticky; top: 0px; } -th, td { +th, +td { cursor: pointer; padding: 10px; } @@ -283,12 +282,16 @@ tbody > tr:hover { background-color: white; color: var(--secondary-app-color); } -.btn.btn-delete, .btn.btn-remove, .btn.btn-logout { +.btn.btn-delete, +.btn.btn-remove, +.btn.btn-logout { color: white; background-color: var(--btn-remove-color); border: 1px solid var(--btn-remove-color); } -.btn.btn-delete:hover, .btn.btn-remove:hover, .btn.btn-logout:hover { +.btn.btn-delete:hover, +.btn.btn-remove:hover, +.btn.btn-logout:hover { color: var(--btn-remove-color); background-color: white; } @@ -301,15 +304,13 @@ tbody > tr:hover { background-color: white; color: var(--btn-save-color); } -:disabled -{ +:disabled { color: white !important; background: #dddddd !important; border: 1px solid #dddddd !important; cursor: not-allowed !important; } -:disabled:hover -{ +:disabled:hover { background: #dddddd !important; border: 1px solid #dddddd !important; color: white !important; @@ -363,14 +364,10 @@ tbody > tr:hover { background-color: var(--primary-app-color); } - /* ------------------------------------ */ /* General styles end /* ------------------------------------ */ - - - #app { width: 100%; height: 100%; @@ -498,22 +495,20 @@ tbody > tr:hover { color: white; } .btn.main-navbar-btn#button-navigation_logout { - color: #E76B6B; + color: #e76b6b; } .btn.main-navbar-btn#button-navigation_logout:hover { - background-color: #E76B6B; + background-color: #e76b6b; color: white; } - /* /////////////////////////////////////////////////////////////////////////////////////// // Main region /////////////////////////////////////////////////////////////////////////////////////////*/ /* Main region general styles */ -#region-main -{ - Background-repeat: no-repeat; +#region-main { + background-repeat: no-repeat; background-position: center center; width: 83.33333333%; overflow-y: auto; @@ -531,7 +526,7 @@ tbody > tr:hover { .region-main-section.region-main-header { width: 100%; height: 40px; - justify-content: space-between; + justify-content: space-between; border-bottom: 2px solid #e5e5e5; } #region-main-header { @@ -734,7 +729,6 @@ tbody > tr:hover { width: 98%; } - /* Project and collection details panels */ .detail-panels-container { width: 25%; @@ -748,7 +742,8 @@ tbody > tr:hover { justify-content: space-between; /* height: 50%; */ } -#region-project-details-panel, #region-collection-item-details-panel { +#region-project-details-panel, +#region-collection-item-details-panel { height: 50%; } .details-panel-title-section { @@ -808,12 +803,12 @@ tbody > tr:hover { #region-main_workflowrun_individual_resources { padding-top: 20px; } -#region-main_workflowrun_individual_resources, #region-main_workflowrun_individual_runjobs { +#region-main_workflowrun_individual_resources, +#region-main_workflowrun_individual_runjobs { height: fit-content; max-height: 82%; } - /*////////////////////////////////////////////////////////////////////////////////////// // Canvas /////////////////////////////////////////////////////////////////////////////////////// */ @@ -847,16 +842,14 @@ tbody > tr:hover { gap: 3vw; font-size: 13px; } -#canvas-wrap -{ +#canvas-wrap { /* flex-grow: 0.9; */ overflow: hidden; padding: 0px; height: 100%; /* height: -webkit-fill-available; */ } -#canvas-tooltip -{ +#canvas-tooltip { position: fixed; background-color: #ffff88; margin-left: 3px; @@ -868,8 +861,7 @@ tbody > tr:hover { visibility: hidden; display: inline-block; } -canvas#canvas-workspace -{ +canvas#canvas-workspace { background-color: var(--background-color-workflowbuilder-workspace); padding: 0px; } @@ -884,8 +876,7 @@ canvas[resize] { display: flex; align-items: center; } -.horizontal-center -{ +.horizontal-center { width: 100%; text-align: center; } @@ -899,14 +890,12 @@ div.gui { display: flex; flex-direction: column; } -div#main_workflowbuilder -{ +div#main_workflowbuilder { background-color: white; height: 100%; overflow: auto; } -.instruction -{ +.instruction { font-style: italic; } @@ -915,8 +904,12 @@ div#main_workflowbuilder /* ------------------------------------ */ @keyframes fade-in { - from {opacity: 0;} - to {opacity: 1;} + from { + opacity: 0; + } + to { + opacity: 1; + } } /* general modal styles */ @@ -959,8 +952,7 @@ div#main_workflowbuilder .modal-close { cursor: pointer; } -.modal-body -{ +.modal-body { max-height: 600px; padding: 20px; box-sizing: border-box; @@ -976,14 +968,13 @@ div#main_workflowbuilder font-size: 13px; font-weight: bold; } -.modal-footer -{ +.modal-footer { padding: 10px; max-height: 40px; height: fit-content; } -.modal-footer-error, .modal-input-error -{ +.modal-footer-error, +.modal-input-error { color: #aa0000; } .modal-body-section-left { @@ -1032,24 +1023,24 @@ div#main_workflowbuilder .table-modal { overflow-y: scroll; } -.table-modal>tbody { +.table-modal > tbody { overflow: scroll; } -.table-modal>thead>tr>th, -.table-modal>tbody>tr>th { +.table-modal > thead > tr > th, +.table-modal > tbody > tr > th { max-width: 70px; word-wrap: break-word; } -.table-modal>thead>tr>td, -.table-modal>tbody>tr>td { +.table-modal > thead > tr > td, +.table-modal > tbody > tr > td { max-width: 70px; word-wrap: break-word; border-bottom: 1px solid rgb(215 215 215); } -.table-modal>thead>tr>th:first-child, -.table-modal>tbody>tr>th:first-child, -.table-modal>thead>tr>td:first-child, -.table-modal>tbody>tr>td:first-child { +.table-modal > thead > tr > th:first-child, +.table-modal > tbody > tr > th:first-child, +.table-modal > thead > tr > td:first-child, +.table-modal > tbody > tr > td:first-child { max-width: 180px; } .tbody-scroll { @@ -1064,14 +1055,17 @@ div#main_workflowbuilder } #workflowjob-settings { - } .this-is-my-class { background-color: green; } - +/* Delete confirm modal */ +#delete-confirm-modal-body-wrapper { + align-items: flex-start; + padding: 0 2em; +} /* ------------ */ /* Table styles */ @@ -1080,8 +1074,7 @@ div#main_workflowbuilder /** * Make text in table unselectable. This helps make multiple selection look better. */ -table -{ +table { -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; @@ -1091,47 +1084,41 @@ table } /* Table controls */ -.table-hover tbody tr:hover td, .table-hover tbody tr:hover th, .table tr.clickable-row.active:hover td -{ +.table-hover tbody tr:hover td, +.table-hover tbody tr:hover th, +.table tr.clickable-row.active:hover td { background-color: #7eb0dd; } -.table tr.clickable-row.active td -{ +.table tr.clickable-row.active td { background-color: var(--secondary-app-color); } -.table-control span -{ - display:inline-block; +.table-control span { + display: inline-block; font-size: var(--size-font); font-weight: bold; } -div.table-control span -{ - display:inline-block; +div.table-control span { + display: inline-block; width: 50%; } -div.table-control span.align-right -{ +div.table-control span.align-right { text-align: right; } -div.table-control p -{ - font-size:0; /* Fixes inline block spacing */ +div.table-control p { + font-size: 0; /* Fixes inline block spacing */ } /* Sortable tables */ -th.sort-ascending .glyphicon .glyphicon-arrow-up -{ +th.sort-ascending .glyphicon .glyphicon-arrow-up { font-family: Arial, Helvetica, sans-serif; } /* Scrollable tables */ -table.scroll -{ +table.scroll { display: block; height: 400px; width: 100%; @@ -1170,4 +1157,56 @@ table.scroll } */ } -.tags-input{display:inline-block;padding:0 2px;background:#FFF;border:1px solid #CCC;width:16em;border-radius:2px;box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.tags-input .tag{display:inline-block;background:#EEE;color:#444;padding:0 4px;margin:2px;border:1px solid #CCC;border-radius:2px;font:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;transition:all .1s ease}.tags-input .tag.selected{background-color:#777;border-color:#777;color:#EEE}.tags-input .tag.dupe{-webkit-transform:scale3d(1.2,1.2,1.2);transform:scale3d(1.2,1.2,1.2);background-color:#FCC;border-color:#700}.tags-input input{-webkit-appearance:none!important;-moz-appearance:none!important;appearance:none!important;display:inline-block!important;padding:3px;margin:0!important;background:0 0!important;border:none!important;box-shadow:none!important;font:inherit!important;font-size:100%!important;outline:0!important}.tags-input .selected~input{opacity:.3} \ No newline at end of file +.tags-input { + display: inline-block; + padding: 0 2px; + background: #fff; + border: 1px solid #ccc; + width: 16em; + border-radius: 2px; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} +.tags-input .tag { + display: inline-block; + background: #eee; + color: #444; + padding: 0 4px; + margin: 2px; + border: 1px solid #ccc; + border-radius: 2px; + font: inherit; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + transition: all 0.1s ease; +} +.tags-input .tag.selected { + background-color: #777; + border-color: #777; + color: #eee; +} +.tags-input .tag.dupe { + -webkit-transform: scale3d(1.2, 1.2, 1.2); + transform: scale3d(1.2, 1.2, 1.2); + background-color: #fcc; + border-color: #700; +} +.tags-input input { + -webkit-appearance: none !important; + -moz-appearance: none !important; + appearance: none !important; + display: inline-block !important; + padding: 3px; + margin: 0 !important; + background: 0 0 !important; + border: none !important; + box-shadow: none !important; + font: inherit !important; + font-size: 100% !important; + outline: 0 !important; +} +.tags-input .selected ~ input { + opacity: 0.3; +} diff --git a/rodan-client/code/templates/Views/Master/Main/Project/Individual/template-main_project_individual.html b/rodan-client/code/templates/Views/Master/Main/Project/Individual/template-main_project_individual.html index cc6ef5e97..c86f1021d 100644 --- a/rodan-client/code/templates/Views/Master/Main/Project/Individual/template-main_project_individual.html +++ b/rodan-client/code/templates/Views/Master/Main/Project/Individual/template-main_project_individual.html @@ -9,10 +9,10 @@This item will be deleted immediately. You can't undo this action.
+