mirror of
				https://github.com/mmumshad/ansible-playable.git
				synced 2025-03-09 23:38:54 +00:00 
			
		
		
		
	Initial Commit
This commit is contained in:
		
						commit
						c92f737237
					
				
					 273 changed files with 16964 additions and 0 deletions
				
			
		
							
								
								
									
										6
									
								
								.babelrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.babelrc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
    "presets": ["es2015"],
 | 
			
		||||
    "plugins": [
 | 
			
		||||
      "transform-class-properties"
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								.buildignore
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								.buildignore
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										21
									
								
								.editorconfig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.editorconfig
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
# EditorConfig helps developers define and maintain consistent
 | 
			
		||||
# coding styles between different editors and IDEs
 | 
			
		||||
# editorconfig.org
 | 
			
		||||
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[*]
 | 
			
		||||
 | 
			
		||||
# Change these settings to your own preference
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 2
 | 
			
		||||
 | 
			
		||||
# We recommend you to keep these unchanged
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
charset = utf-8
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
 | 
			
		||||
[*.md]
 | 
			
		||||
trim_trailing_whitespace = false
 | 
			
		||||
							
								
								
									
										254
									
								
								.eslintrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								.eslintrc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,254 @@
 | 
			
		|||
{
 | 
			
		||||
    "parser": "babel-eslint",
 | 
			
		||||
    "env": {
 | 
			
		||||
        "es6": true
 | 
			
		||||
    },
 | 
			
		||||
    "globals": {},
 | 
			
		||||
    "plugins": [],
 | 
			
		||||
    "rules": {
 | 
			
		||||
        //Possible Errors
 | 
			
		||||
        "comma-dangle": 0,                                          //disallow or enforce trailing commas
 | 
			
		||||
        "no-cond-assign": 2,                                        //disallow assignment in conditional expressions
 | 
			
		||||
        "no-console": 0,                                            //disallow use of console in the node environment
 | 
			
		||||
        "no-constant-condition": 1,                                 //disallow use of constant expressions in conditions
 | 
			
		||||
        "no-control-regex": 2,                                      //disallow control characters in regular expressions
 | 
			
		||||
        "no-debugger": 2,                                           //disallow use of debugger
 | 
			
		||||
        "no-dupe-args": 2,                                          //disallow duplicate arguments in functions
 | 
			
		||||
        "no-dupe-keys": 2,                                          //disallow duplicate keys when creating object literals
 | 
			
		||||
        "no-duplicate-case": 0,                                     //disallow a duplicate case label.
 | 
			
		||||
        "no-empty-character-class": 2,                              //disallow the use of empty character classes in regular expressions
 | 
			
		||||
        "no-empty": 2,                                              //disallow empty statements
 | 
			
		||||
        "no-ex-assign": 2,                                          //disallow assigning to the exception in a catch block
 | 
			
		||||
        "no-extra-boolean-cast": 2,                                 //disallow double-negation boolean casts in a boolean context
 | 
			
		||||
        "no-extra-parens": 1,                                       //disallow unnecessary parentheses
 | 
			
		||||
        "no-extra-semi": 2,                                         //disallow unnecessary semicolons
 | 
			
		||||
        "no-func-assign": 2,                                        //disallow overwriting functions written as function declarations
 | 
			
		||||
        "no-inner-declarations": 1,                                 //disallow function or variable declarations in nested blocks
 | 
			
		||||
        "no-invalid-regexp": 2,                                     //disallow invalid regular expression strings in the RegExp constructor
 | 
			
		||||
        "no-irregular-whitespace": 2,                               //disallow irregular whitespace outside of strings and comments
 | 
			
		||||
        "no-negated-in-lhs": 2,                                     //disallow negation of the left operand of an in expression
 | 
			
		||||
        "no-obj-calls": 2,                                          //disallow the use of object properties of the global object (Math and JSON) as functions
 | 
			
		||||
        "no-prototype-builtins": 0,                                 //Disallow use of Object.prototypes builtins directly
 | 
			
		||||
        "no-regex-spaces": 2,                                       //disallow multiple spaces in a regular expression literal
 | 
			
		||||
        "no-sparse-arrays": 1,                                      //disallow sparse arrays
 | 
			
		||||
        "no-unexpected-multiline": 2,                               //Avoid code that looks like two expressions but is actually one
 | 
			
		||||
        "no-unreachable": 2,                                        //disallow unreachable statements after a return, throw, continue, or break statement
 | 
			
		||||
        "no-unsafe-finally": 2,                                     //disallow control flow statements in finally blocks
 | 
			
		||||
        "use-isnan": 2,                                             //disallow comparisons with the value NaN
 | 
			
		||||
        "valid-jsdoc": 0,                                           //Ensure JSDoc comments are valid
 | 
			
		||||
        "valid-typeof": 2,                                          //Ensure that the results of typeof are compared against a valid string
 | 
			
		||||
 | 
			
		||||
        //Best Practices
 | 
			
		||||
        "accessor-pairs": 0,                                        //Enforces getter/setter pairs in objects
 | 
			
		||||
        "array-callback-return": 2,                                 //Enforces return statements in callbacks of array's methods
 | 
			
		||||
        "block-scoped-var": 0,                                      //treat var statements as if they were block scoped
 | 
			
		||||
        "complexity": 0,                                            //specify the maximum cyclomatic complexity allowed in a program
 | 
			
		||||
        "consistent-return": 0,                                     //require return statements to either always or never specify values
 | 
			
		||||
        "curly": [2, "multi-line"],                                 //specify curly brace conventions for all control statements
 | 
			
		||||
        "default-case": 0,                                          //require default case in switch statements
 | 
			
		||||
        "dot-location": [2, "property"],                            //enforces consistent newlines before or after dots
 | 
			
		||||
        "dot-notation": 2,                                          //encourages use of dot notation whenever possible
 | 
			
		||||
        "eqeqeq": 0,                                                //require the use of === and !==
 | 
			
		||||
        "guard-for-in": 0,                                          //make sure for-in loops have an if statement
 | 
			
		||||
        "no-alert": 2,                                              //disallow the use of alert, confirm, and prompt
 | 
			
		||||
        "no-caller": 0,                                             //disallow use of arguments.caller or arguments.callee
 | 
			
		||||
        "no-case-declarations": 0,                                  //disallow lexical declarations in case clauses
 | 
			
		||||
        "no-div-regex": 2,                                          //disallow division operators explicitly at beginning of regular expression
 | 
			
		||||
        "no-else-return": 0,                                        //disallow else after a return in an if
 | 
			
		||||
        "no-empty-function": 0,                                     //disallow use of empty functions
 | 
			
		||||
        "no-empty-pattern": 2,                                      //disallow use of empty destructuring patterns
 | 
			
		||||
        "no-eq-null": 2,                                            //disallow comparisons to null without a type-checking operator
 | 
			
		||||
        "no-eval": 2,                                               //disallow use of eval()
 | 
			
		||||
        "no-extend-native": 0,                                      //disallow adding to native types
 | 
			
		||||
        "no-extra-bind": 1,                                         //disallow unnecessary function binding
 | 
			
		||||
        "no-extra-label": 2,                                        //disallow unnecessary labels
 | 
			
		||||
        "no-fallthrough": 0,                                        //disallow fallthrough of case statements
 | 
			
		||||
        "no-floating-decimal": 0,                                   //disallow the use of leading or trailing decimal points in numeric literals
 | 
			
		||||
        "no-implicit-coercion": 0,                                  //disallow the type conversions with shorter notations
 | 
			
		||||
        "no-implicit-globals": 0,                                   //disallow var and named functions in global scope
 | 
			
		||||
        "no-implied-eval": 2,                                       //disallow use of eval()-like methods
 | 
			
		||||
        "no-invalid-this": 1,                                       //disallow this keywords outside of classes or class-like objects
 | 
			
		||||
        "no-iterator": 2,                                           //disallow usage of __iterator__ property
 | 
			
		||||
        "no-labels": 2,                                             //disallow use of labeled statements
 | 
			
		||||
        "no-lone-blocks": 2,                                        //disallow unnecessary nested blocks
 | 
			
		||||
        "no-loop-func": 2,                                          //disallow creation of functions within loops
 | 
			
		||||
        "no-magic-numbers": 0,                                      //disallow the use of magic numbers
 | 
			
		||||
        "no-multi-spaces": 2,                                       //disallow use of multiple spaces
 | 
			
		||||
        "no-multi-str": 0,                                          //disallow use of multiline strings
 | 
			
		||||
        "no-native-reassign": 2,                                    //disallow reassignments of native objects
 | 
			
		||||
        "no-new-func": 1,                                           //disallow use of new operator for Function object
 | 
			
		||||
        "no-new-wrappers": 2,                                       //disallows creating new instances of String,Number, and Boolean
 | 
			
		||||
        "no-new": 2,                                                //disallow use of the new operator when not part of an assignment or comparison
 | 
			
		||||
        "no-octal-escape": 0,                                       //disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251";
 | 
			
		||||
        "no-octal": 0,                                              //disallow use of octal literals
 | 
			
		||||
        "no-param-reassign": 0,                                     //disallow reassignment of function parameters
 | 
			
		||||
        "no-process-env": 0,                                        //disallow use of process.env
 | 
			
		||||
        "no-proto": 2,                                              //disallow usage of __proto__ property
 | 
			
		||||
        "no-redeclare": 2,                                          //disallow declaring the same variable more than once
 | 
			
		||||
        "no-return-assign": 2,                                      //disallow use of assignment in return statement
 | 
			
		||||
        "no-script-url": 2,                                         //disallow use of javascript: urls.
 | 
			
		||||
        "no-self-assign": 2,                                        //disallow assignments where both sides are exactly the same
 | 
			
		||||
        "no-self-compare": 2,                                       //disallow comparisons where both sides are exactly the same
 | 
			
		||||
        "no-sequences": 2,                                          //disallow use of the comma operator
 | 
			
		||||
        "no-throw-literal": 2,                                      //restrict what can be thrown as an exception
 | 
			
		||||
        "no-unmodified-loop-condition": 0,                          //disallow unmodified conditions of loops
 | 
			
		||||
        "no-unused-expressions": 0,                                 //disallow usage of expressions in statement position
 | 
			
		||||
        "no-unused-labels": 2,                                      //disallow unused labels
 | 
			
		||||
        "no-useless-call": 2,                                       //disallow unnecessary .call() and .apply()
 | 
			
		||||
        "no-useless-concat": 2,                                     //disallow unnecessary concatenation of literals or template literals
 | 
			
		||||
        "no-useless-escape": 2,                                     //disallow unnecessary escape characters
 | 
			
		||||
        "no-void": 0,                                               //disallow use of the void operator
 | 
			
		||||
        "no-warning-comments": 1,                                   //disallow usage of configurable warning terms in comments (e.g. TODO or FIXME)
 | 
			
		||||
        "no-with": 2,                                               //disallow use of the with statement
 | 
			
		||||
        "radix": 2,                                                 //require use of the second argument for parseInt()
 | 
			
		||||
        "vars-on-top": 0,                                           //require declaration of all vars at the top of their containing scope
 | 
			
		||||
        "wrap-iife": 2,                                             //require immediate function invocation to be wrapped in parentheses
 | 
			
		||||
        "yoda": 2,                                                  //require or disallow Yoda conditions
 | 
			
		||||
 | 
			
		||||
        //Strict Mode
 | 
			
		||||
        "strict": 0,                                                //controls location of Use Strict Directives
 | 
			
		||||
 | 
			
		||||
        //Variables
 | 
			
		||||
        "init-declarations": 0,                                     //enforce or disallow variable initializations at definition
 | 
			
		||||
        "no-catch-shadow": 2,                                       //disallow the catch clause parameter name being the same as a variable in the outer scope
 | 
			
		||||
        "no-delete-var": 2,                                         //disallow deletion of variables
 | 
			
		||||
        "no-label-var": 2,                                          //disallow labels that share a name with a variable
 | 
			
		||||
        "no-restricted-globals": 0,                                 //restrict usage of specified global variables
 | 
			
		||||
        "no-shadow-restricted-names": 2,                            //disallow shadowing of names such as arguments
 | 
			
		||||
        "no-shadow": [2, {"allow": ["err"]}],                       //disallow declaration of variables already declared in the outer scope
 | 
			
		||||
        "no-undef-init": 2,                                         //disallow use of undefined when initializing variables
 | 
			
		||||
        "no-undef": 2,                                              //disallow use of undeclared variables unless mentioned in a /*global */ block
 | 
			
		||||
        "no-undefined": 0,                                          //disallow use of undefined variable
 | 
			
		||||
        "no-unused-vars": 2,                                        //disallow declaration of variables that are not used in the code
 | 
			
		||||
        "no-use-before-define": 0,                                  //disallow use of variables before they are defined
 | 
			
		||||
 | 
			
		||||
        //Node.js and CommonJS
 | 
			
		||||
        "callback-return": 2,                                       //enforce return after a callback
 | 
			
		||||
        "global-require": 0,                                        //enforce require() on top-level module scope
 | 
			
		||||
        "handle-callback-err": 2,                                   //enforce error handling in callbacks
 | 
			
		||||
        "no-mixed-requires": 2,                                     //disallow mixing regular variable and require declarations
 | 
			
		||||
        "no-new-require": 2,                                        //disallow use of new operator with the require function
 | 
			
		||||
        "no-path-concat": 2,                                        //disallow string concatenation with __dirname and __filename
 | 
			
		||||
        "no-process-exit": 2,                                       //disallow process.exit()
 | 
			
		||||
        "no-restricted-imports": 0,                                 //restrict usage of specified node imports
 | 
			
		||||
        "no-restricted-modules": 0,                                 //restrict usage of specified node modules
 | 
			
		||||
        "no-sync": 1,                                               //disallow use of synchronous methods
 | 
			
		||||
 | 
			
		||||
        //Stylistic Issues
 | 
			
		||||
        "array-bracket-spacing": [2, "never"],                      //enforce spacing inside array brackets
 | 
			
		||||
        "block-spacing": 0,                                         //disallow or enforce spaces inside of single line blocks
 | 
			
		||||
        "brace-style": 2,                                           //enforce one true brace style
 | 
			
		||||
        "camelcase": 1,                                             //require camel case names
 | 
			
		||||
        "comma-spacing": [2, {"before": false, "after": true}],     //enforce spacing before and after comma
 | 
			
		||||
        "comma-style": 2,                                           //enforce one true comma style
 | 
			
		||||
        "computed-property-spacing": 2,                             //require or disallow padding inside computed properties
 | 
			
		||||
        "consistent-this": 2,                                       //enforce consistent naming when capturing the current execution context
 | 
			
		||||
        "eol-last": 2,                                              //enforce newline at the end of file, with no multiple empty lines
 | 
			
		||||
        "func-names": 0,                                            //require function expressions to have a name
 | 
			
		||||
        "func-style": 0,                                            //enforce use of function declarations or expressions
 | 
			
		||||
        "id-blacklist": 0,                                          //blacklist certain identifiers to prevent them being used
 | 
			
		||||
        "id-length": 0,                                             //this option enforces minimum and maximum identifier lengths (variable names, property names etc.)
 | 
			
		||||
        "id-match": 0,                                              //require identifiers to match the provided regular expression
 | 
			
		||||
        "indent": ["error", 2],                                     //specify tab or space width for your code
 | 
			
		||||
        "jsx-quotes": 0,                                            //specify whether double or single quotes should be used in JSX attributes
 | 
			
		||||
        "key-spacing": 2,                                           //enforce spacing between keys and values in object literal properties
 | 
			
		||||
        "keyword-spacing": [2, {
 | 
			
		||||
            "before": true,
 | 
			
		||||
            "after": true,
 | 
			
		||||
            "overrides": {
 | 
			
		||||
                "if": {"after": false},
 | 
			
		||||
                "for": {"after": false},
 | 
			
		||||
                "while": {"after": false},
 | 
			
		||||
                "catch": {"after": false}
 | 
			
		||||
            }
 | 
			
		||||
        }],                                                         //enforce spacing before and after keywords
 | 
			
		||||
        "linebreak-style": 2,                                       //disallow mixed 'LF' and 'CRLF' as linebreaks
 | 
			
		||||
        "lines-around-comment": 0,                                  //enforce empty lines around comments
 | 
			
		||||
        "max-depth": 1,                                             //specify the maximum depth that blocks can be nested
 | 
			
		||||
        "max-len": [1, 200],                                        //specify the maximum length of a line in your program
 | 
			
		||||
        "max-lines": 0,                                             //enforce a maximum file length
 | 
			
		||||
        "max-nested-callbacks": 2,                                  //specify the maximum depth callbacks can be nested
 | 
			
		||||
        "max-params": 0,                                            //limits the number of parameters that can be used in the function declaration.
 | 
			
		||||
        "max-statements": 0,                                        //specify the maximum number of statement allowed in a function
 | 
			
		||||
        "max-statements-per-line": 0,                               //enforce a maximum number of statements allowed per line
 | 
			
		||||
        "new-cap": 0,                                               //require a capital letter for constructors
 | 
			
		||||
        "new-parens": 2,                                            //disallow the omission of parentheses when invoking a constructor with no arguments
 | 
			
		||||
        "newline-after-var": 0,                                     //require or disallow an empty newline after variable declarations
 | 
			
		||||
        "newline-before-return": 0,                                 //require newline before return statement
 | 
			
		||||
        "newline-per-chained-call": [
 | 
			
		||||
            "error",
 | 
			
		||||
            {"ignoreChainWithDepth": 2}
 | 
			
		||||
        ],                                                          //enforce newline after each call when chaining the calls
 | 
			
		||||
        "no-array-constructor": 2,                                  //disallow use of the Array constructor
 | 
			
		||||
        "no-bitwise": 0,                                            //disallow use of bitwise operators
 | 
			
		||||
        "no-continue": 0,                                           //disallow use of the continue statement
 | 
			
		||||
        "no-inline-comments": 0,                                    //disallow comments inline after code
 | 
			
		||||
        "no-lonely-if": 2,                                          //disallow if as the only statement in an else block
 | 
			
		||||
        "no-mixed-operators": 0,                                    //disallow mixes of different operators
 | 
			
		||||
        "no-mixed-spaces-and-tabs": 2,                              //disallow mixed spaces and tabs for indentation
 | 
			
		||||
        "no-multiple-empty-lines": 2,                               //disallow multiple empty lines
 | 
			
		||||
        "no-negated-condition": 0,                                  //disallow negated conditions
 | 
			
		||||
        "no-nested-ternary": 0,                                     //disallow nested ternary expressions
 | 
			
		||||
        "no-new-object": 2,                                         //disallow the use of the Object constructor
 | 
			
		||||
        "no-plusplus": 0,                                           //disallow use of unary operators, ++ and --
 | 
			
		||||
        "no-restricted-syntax": 0,                                  //disallow use of certain syntax in code
 | 
			
		||||
        "no-spaced-func": 2,                                        //disallow space between function identifier and application
 | 
			
		||||
        "no-ternary": 0,                                            //disallow the use of ternary operators
 | 
			
		||||
        "no-trailing-spaces": 2,                                    //disallow trailing whitespace at the end of lines
 | 
			
		||||
        "no-underscore-dangle": 0,                                  //disallow dangling underscores in identifiers
 | 
			
		||||
        "no-unneeded-ternary": 2,                                   //disallow the use of ternary operators when a simpler alternative exists
 | 
			
		||||
        "no-whitespace-before-property": 2,                         //disallow whitespace before properties
 | 
			
		||||
        "object-curly-newline": 0,                                  //enforce consistent line breaks inside braces
 | 
			
		||||
        "object-curly-spacing": 0,                                  //require or disallow padding inside curly braces
 | 
			
		||||
        "object-property-newline": 0,                               //enforce placing object properties on separate lines
 | 
			
		||||
        "one-var": [2, "never"],                                    //require or disallow one variable declaration per function
 | 
			
		||||
        "one-var-declaration-per-line": 2,                          //require or disallow an newline around variable declarations
 | 
			
		||||
        "operator-assignment": 0,                                   //require assignment operator shorthand where possible or prohibit it entirely
 | 
			
		||||
        "operator-linebreak": [1, "before"],                        //enforce operators to be placed before or after line breaks
 | 
			
		||||
        "padded-blocks": [2, "never"],                              //enforce padding within blocks
 | 
			
		||||
        "quote-props": [2, "as-needed"],                            //require quotes around object literal property names
 | 
			
		||||
        "quotes": [2, "single"],                                    //specify whether backticks, double or single quotes should be used
 | 
			
		||||
        "require-jsdoc": 0,                                         //Require JSDoc comment
 | 
			
		||||
        "semi-spacing": 2,                                          //enforce spacing before and after semicolons
 | 
			
		||||
        "sort-imports": 0,                                          //sort import declarations within module
 | 
			
		||||
        "semi": 2,                                                  //require or disallow use of semicolons instead of ASI
 | 
			
		||||
        "sort-vars": 0,                                             //sort variables within the same declaration block
 | 
			
		||||
        "space-before-blocks": 2,                                   //require or disallow a space before blocks
 | 
			
		||||
        "space-before-function-paren": [2, "never"],                //require or disallow a space before function opening parenthesis
 | 
			
		||||
        "space-in-parens": 2,                                       //require or disallow spaces inside parentheses
 | 
			
		||||
        "space-infix-ops": 2,                                       //require spaces around operators
 | 
			
		||||
        "space-unary-ops": 2,                                       //require or disallow spaces before/after unary operators
 | 
			
		||||
        "spaced-comment": 0,                                        //require or disallow a space immediately following the // or /* in a comment
 | 
			
		||||
        "unicode-bom": 0,                                           //require or disallow the Unicode BOM
 | 
			
		||||
        "wrap-regex": 0,                                            //require regex literals to be wrapped in parentheses
 | 
			
		||||
 | 
			
		||||
        //ECMAScript 6
 | 
			
		||||
        "arrow-body-style": [2, "as-needed"],                       //require braces in arrow function body
 | 
			
		||||
        "arrow-parens": [2, "as-needed"],                           //require parens in arrow function arguments
 | 
			
		||||
        "arrow-spacing": 2,                                         //require space before/after arrow function's arrow
 | 
			
		||||
        "constructor-super": 2,                                     //verify calls of super() in constructors
 | 
			
		||||
        "generator-star-spacing": 0,                                //enforce spacing around the * in generator functions
 | 
			
		||||
        "no-class-assign": 2,                                       //disallow modifying variables of class declarations
 | 
			
		||||
        "no-confusing-arrow": 2,                                    //disallow arrow functions where they could be confused with comparisons
 | 
			
		||||
        "no-const-assign": 2,                                       //disallow modifying variables that are declared using const
 | 
			
		||||
        "no-dupe-class-members": 2,                                 //disallow duplicate name in class members
 | 
			
		||||
        "no-duplicate-imports": 2,                                  //disallow duplicate module imports
 | 
			
		||||
        "no-new-symbol": 2,                                         //disallow use of the new operator with the Symbol object
 | 
			
		||||
        "no-this-before-super": 2,                                  //disallow use of this/super before calling super() in constructors.
 | 
			
		||||
        "no-useless-computed-key": 2,                               //disallow unnecessary computed property keys in object literals
 | 
			
		||||
        "no-useless-constructor": 2,                                //disallow unnecessary constructor
 | 
			
		||||
        "no-useless-rename": 2,                                     //disallow renaming import, export, and destructured assignments to the same name
 | 
			
		||||
        "no-var": 0,                                                //require let or const instead of var
 | 
			
		||||
        "object-shorthand": 1,                                      //require method and property shorthand syntax for object literals
 | 
			
		||||
        "prefer-arrow-callback": 0,                                 //suggest using arrow functions as callbacks
 | 
			
		||||
        "prefer-const": 0,                                          //suggest using const declaration for variables that are never modified after declared
 | 
			
		||||
        "prefer-reflect": 1,                                        //suggest using Reflect methods where applicable
 | 
			
		||||
        "prefer-rest-params": 1,                                    //suggest using the rest parameters instead of arguments
 | 
			
		||||
        "prefer-spread": 1,                                         //suggest using the spread operator instead of .apply().
 | 
			
		||||
        "prefer-template": 1,                                       //suggest using template literals instead of strings concatenation
 | 
			
		||||
        "require-yield": 2,                                         //disallow generator functions that do not have yield
 | 
			
		||||
        "rest-spread-spacing": ["error", "never"],                  //enforce spacing between rest and spread operators and their expressions
 | 
			
		||||
        "template-curly-spacing": 2,                                //enforce spacing around embedded expressions of template strings
 | 
			
		||||
        "yield-star-spacing": [2, "after"]                          //enforce spacing around the * in yield* expressions
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								.gitattributes
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								.gitattributes
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
* text=auto
 | 
			
		||||
 | 
			
		||||
# These files are text and should be normalized (Convert crlf => lf)
 | 
			
		||||
*.php  text
 | 
			
		||||
*.css  text
 | 
			
		||||
*.js   text
 | 
			
		||||
*.htm  text
 | 
			
		||||
*.html text
 | 
			
		||||
*.xml  text
 | 
			
		||||
*.txt  text
 | 
			
		||||
*.ini  text
 | 
			
		||||
*.inc  text
 | 
			
		||||
.htaccess text
 | 
			
		||||
 | 
			
		||||
# Denote all files that are truly binary and should not be modified.
 | 
			
		||||
# (binary is a macro for -text -diff)
 | 
			
		||||
*.png  binary
 | 
			
		||||
*.jpg  binary
 | 
			
		||||
*.jpeg binary
 | 
			
		||||
*.gif  binary
 | 
			
		||||
*.ico  binary
 | 
			
		||||
*.mov  binary
 | 
			
		||||
*.mp4  binary
 | 
			
		||||
*.mp3  binary
 | 
			
		||||
*.flv  binary
 | 
			
		||||
*.fla  binary
 | 
			
		||||
*.swf  binary
 | 
			
		||||
*.gz   binary
 | 
			
		||||
*.zip  binary
 | 
			
		||||
*.7z   binary
 | 
			
		||||
*.ttf  binary
 | 
			
		||||
 | 
			
		||||
# Documents
 | 
			
		||||
*.doc  diff=astextplain
 | 
			
		||||
*.DOC  diff=astextplain
 | 
			
		||||
*.docx diff=astextplain
 | 
			
		||||
*.DOCX diff=astextplain
 | 
			
		||||
*.dot  diff=astextplain
 | 
			
		||||
*.DOT  diff=astextplain
 | 
			
		||||
*.pdf  diff=astextplain
 | 
			
		||||
*.PDF  diff=astextplain
 | 
			
		||||
*.rtf  diff=astextplain
 | 
			
		||||
*.RTF  diff=astextplain
 | 
			
		||||
							
								
								
									
										10
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
node_modules
 | 
			
		||||
public
 | 
			
		||||
.tmp
 | 
			
		||||
.idea
 | 
			
		||||
client/bower_components
 | 
			
		||||
client/index.html
 | 
			
		||||
dist
 | 
			
		||||
/server/config/local.env.js
 | 
			
		||||
npm-debug.log
 | 
			
		||||
coverage
 | 
			
		||||
							
								
								
									
										21
									
								
								.travis.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.travis.yml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
language: node_js
 | 
			
		||||
node_js:
 | 
			
		||||
  - 6
 | 
			
		||||
matrix:
 | 
			
		||||
  fast_finish: true
 | 
			
		||||
  allow_failures:
 | 
			
		||||
    - node_js: 5.12.0
 | 
			
		||||
before_script:
 | 
			
		||||
  - npm install -g gulp-cli node-gyp
 | 
			
		||||
services: mongodb
 | 
			
		||||
cache:
 | 
			
		||||
  directories:
 | 
			
		||||
    - node_modules
 | 
			
		||||
env:
 | 
			
		||||
  - CXX=g++-4.8
 | 
			
		||||
addons:
 | 
			
		||||
  apt:
 | 
			
		||||
    sources:
 | 
			
		||||
    - ubuntu-toolchain-r-test
 | 
			
		||||
    packages:
 | 
			
		||||
    - g++-4.8
 | 
			
		||||
							
								
								
									
										72
									
								
								.yo-rc.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								.yo-rc.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
{
 | 
			
		||||
  "generator-angular-fullstack": {
 | 
			
		||||
    "generatorVersion": "4.2.2",
 | 
			
		||||
    "endpointDirectory": "server/api/",
 | 
			
		||||
    "insertRoutes": true,
 | 
			
		||||
    "registerRoutesFile": "server/routes.js",
 | 
			
		||||
    "routesNeedle": "// Insert routes below",
 | 
			
		||||
    "routesBase": "/api/",
 | 
			
		||||
    "pluralizeRoutes": true,
 | 
			
		||||
    "insertSockets": true,
 | 
			
		||||
    "registerSocketsFile": "server/config/socketio.js",
 | 
			
		||||
    "socketsNeedle": "// Insert sockets below",
 | 
			
		||||
    "insertModels": true,
 | 
			
		||||
    "registerModelsFile": "server/sqldb/index.js",
 | 
			
		||||
    "modelsNeedle": "// Insert models below",
 | 
			
		||||
    "filters": {
 | 
			
		||||
      "js": true,
 | 
			
		||||
      "babel": true,
 | 
			
		||||
      "flow": false,
 | 
			
		||||
      "html": true,
 | 
			
		||||
      "css": true,
 | 
			
		||||
      "uirouter": true,
 | 
			
		||||
      "bootstrap": true,
 | 
			
		||||
      "uibootstrap": true,
 | 
			
		||||
      "auth": true,
 | 
			
		||||
      "models": true,
 | 
			
		||||
      "mongooseModels": true,
 | 
			
		||||
      "mongoose": true,
 | 
			
		||||
      "oauth": true,
 | 
			
		||||
      "googleAuth": true,
 | 
			
		||||
      "facebookAuth": true,
 | 
			
		||||
      "mocha": true,
 | 
			
		||||
      "jasmine": false,
 | 
			
		||||
      "should": false,
 | 
			
		||||
      "expect": true
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "generator-ng-component": {
 | 
			
		||||
    "routeDirectory": "client/app/",
 | 
			
		||||
    "directiveDirectory": "client/app/",
 | 
			
		||||
    "componentDirectory": "client/app/components/",
 | 
			
		||||
    "filterDirectory": "client/app/",
 | 
			
		||||
    "serviceDirectory": "client/app/",
 | 
			
		||||
    "basePath": "client",
 | 
			
		||||
    "moduleName": "",
 | 
			
		||||
    "modulePrompt": true,
 | 
			
		||||
    "filters": [
 | 
			
		||||
      "uirouter",
 | 
			
		||||
      "mocha",
 | 
			
		||||
      "expect",
 | 
			
		||||
      "uirouter",
 | 
			
		||||
      "es6",
 | 
			
		||||
      "webpack"
 | 
			
		||||
    ],
 | 
			
		||||
    "extensions": [
 | 
			
		||||
      "babel",
 | 
			
		||||
      "js",
 | 
			
		||||
      "html",
 | 
			
		||||
      "css"
 | 
			
		||||
    ],
 | 
			
		||||
    "directiveSimpleTemplates": "",
 | 
			
		||||
    "directiveComplexTemplates": "",
 | 
			
		||||
    "filterTemplates": "",
 | 
			
		||||
    "serviceTemplates": "",
 | 
			
		||||
    "factoryTemplates": "",
 | 
			
		||||
    "controllerTemplates": "",
 | 
			
		||||
    "componentTemplates": "",
 | 
			
		||||
    "decoratorTemplates": "",
 | 
			
		||||
    "providerTemplates": "",
 | 
			
		||||
    "routeTemplates": ""
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								Dockerfile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								Dockerfile
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
# Pull base image.
 | 
			
		||||
FROM node:6.2.2
 | 
			
		||||
 | 
			
		||||
# Reset Root Password
 | 
			
		||||
RUN echo "root:P@ssw0rd@123" | chpasswd
 | 
			
		||||
 | 
			
		||||
# Install Ansible
 | 
			
		||||
RUN apt-get update && \
 | 
			
		||||
    apt-get install python-setuptools python-dev build-essential -y && \
 | 
			
		||||
    easy_install pip && \
 | 
			
		||||
    pip install ansible
 | 
			
		||||
 | 
			
		||||
# TO fix a bug
 | 
			
		||||
RUN mkdir -p /root/.config/configstore && chmod g+rwx /root /root/.config /root/.config/configstore
 | 
			
		||||
RUN useradd -u 1003 -d /home/app_user -m -s /bin/bash -p $(echo P@ssw0rd@123 | openssl passwd -1 -stdin) app_user
 | 
			
		||||
 | 
			
		||||
# Create data directory
 | 
			
		||||
RUN mkdir -p /data
 | 
			
		||||
 | 
			
		||||
RUN chown -R app_user /usr/local && chown -R app_user /home/app_user && chown -R app_user /data
 | 
			
		||||
 | 
			
		||||
# Install VIM and Openssh-Server
 | 
			
		||||
RUN apt-get update && apt-get install -y vim openssh-server
 | 
			
		||||
 | 
			
		||||
# Permit Root login
 | 
			
		||||
RUN sed -i '/PermitRootLogin */cPermitRootLogin yes' /etc/ssh/sshd_config
 | 
			
		||||
 | 
			
		||||
# Generate SSH Keys
 | 
			
		||||
RUN /usr/bin/ssh-keygen -A
 | 
			
		||||
 | 
			
		||||
# Start Open-ssh server
 | 
			
		||||
RUN service ssh start
 | 
			
		||||
 | 
			
		||||
# Change user to app_user
 | 
			
		||||
USER app_user
 | 
			
		||||
 | 
			
		||||
RUN mkdir -p /data/web-app
 | 
			
		||||
COPY * /data/web-app
 | 
			
		||||
 | 
			
		||||
USER root
 | 
			
		||||
RUN chown -R app_user /data/web-app
 | 
			
		||||
 | 
			
		||||
USER app_user
 | 
			
		||||
WORKDIR /data/web-app
 | 
			
		||||
 | 
			
		||||
RUN npm install -g yo gulp-cli generator-angular-fullstack
 | 
			
		||||
RUN npm install
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT gulp serve
 | 
			
		||||
							
								
								
									
										28
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
# app2
 | 
			
		||||
 | 
			
		||||
This project was generated with the [Angular Full-Stack Generator](https://github.com/DaftMonk/generator-angular-fullstack) version 4.2.2.
 | 
			
		||||
 | 
			
		||||
## Getting Started
 | 
			
		||||
 | 
			
		||||
### Prerequisites
 | 
			
		||||
 | 
			
		||||
- [Git](https://git-scm.com/)
 | 
			
		||||
- [Node.js and npm](nodejs.org) Node >= 4.x.x, npm >= 2.x.x
 | 
			
		||||
- [Gulp](http://gulpjs.com/) (`npm install --global gulp`)
 | 
			
		||||
- [MongoDB](https://www.mongodb.org/) - Keep a running daemon with `mongod`
 | 
			
		||||
 | 
			
		||||
### Developing
 | 
			
		||||
 | 
			
		||||
1. Run `npm install` to install server dependencies.
 | 
			
		||||
 | 
			
		||||
2. Run `mongod` in a separate shell to keep an instance of the MongoDB Daemon running
 | 
			
		||||
 | 
			
		||||
3. Run `gulp serve` to start the development server. It should automatically open the client in your browser when ready.
 | 
			
		||||
 | 
			
		||||
## Build & development
 | 
			
		||||
 | 
			
		||||
Run `gulp build` for building and `gulp serve` for preview.
 | 
			
		||||
 | 
			
		||||
## Testing
 | 
			
		||||
 | 
			
		||||
Running `npm test` will run the unit tests with karma.
 | 
			
		||||
							
								
								
									
										7
									
								
								client/.eslintrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								client/.eslintrc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
    "extends": "../.eslintrc",
 | 
			
		||||
    "env": {
 | 
			
		||||
        "browser": true,
 | 
			
		||||
        "commonjs": true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										543
									
								
								client/.htaccess
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										543
									
								
								client/.htaccess
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,543 @@
 | 
			
		|||
# Apache Configuration File
 | 
			
		||||
 | 
			
		||||
# (!) Using `.htaccess` files slows down Apache, therefore, if you have access
 | 
			
		||||
# to the main server config file (usually called `httpd.conf`), you should add
 | 
			
		||||
# this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
 | 
			
		||||
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
# # CROSS-ORIGIN RESOURCE SHARING (CORS)                                       #
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Cross-domain AJAX requests                                                 |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Enable cross-origin AJAX requests.
 | 
			
		||||
# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
 | 
			
		||||
# http://enable-cors.org/
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_headers.c>
 | 
			
		||||
#    Header set Access-Control-Allow-Origin "*"
 | 
			
		||||
# </IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | CORS-enabled images                                                        |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Send the CORS header for images when browsers request it.
 | 
			
		||||
# https://developer.mozilla.org/en/CORS_Enabled_Image
 | 
			
		||||
# http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
 | 
			
		||||
# http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
 | 
			
		||||
 | 
			
		||||
<IfModule mod_setenvif.c>
 | 
			
		||||
    <IfModule mod_headers.c>
 | 
			
		||||
        <FilesMatch "\.(gif|ico|jpe?g|png|svg|svgz|webp)$">
 | 
			
		||||
            SetEnvIf Origin ":" IS_CORS
 | 
			
		||||
            Header set Access-Control-Allow-Origin "*" env=IS_CORS
 | 
			
		||||
        </FilesMatch>
 | 
			
		||||
    </IfModule>
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Web fonts access                                                           |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Allow access from all domains for web fonts
 | 
			
		||||
 | 
			
		||||
<IfModule mod_headers.c>
 | 
			
		||||
    <FilesMatch "\.(eot|font.css|otf|ttc|ttf|woff)$">
 | 
			
		||||
        Header set Access-Control-Allow-Origin "*"
 | 
			
		||||
    </FilesMatch>
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
# # ERRORS                                                                     #
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | 404 error prevention for non-existing redirected folders                   |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Prevent Apache from returning a 404 error for a rewrite if a directory
 | 
			
		||||
# with the same name does not exist.
 | 
			
		||||
# http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
 | 
			
		||||
# http://www.webmasterworld.com/apache/3808792.htm
 | 
			
		||||
 | 
			
		||||
Options -MultiViews
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Custom error messages / pages                                              |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# You can customize what Apache returns to the client in case of an error (see
 | 
			
		||||
# http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
 | 
			
		||||
 | 
			
		||||
ErrorDocument 404 /404.html
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
# # INTERNET EXPLORER                                                          #
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Better website experience                                                  |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Force IE to render pages in the highest available mode in the various
 | 
			
		||||
# cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
 | 
			
		||||
 | 
			
		||||
<IfModule mod_headers.c>
 | 
			
		||||
    Header set X-UA-Compatible "IE=edge"
 | 
			
		||||
    # `mod_headers` can't match based on the content-type, however, we only
 | 
			
		||||
    # want to send this header for HTML pages and not for the other resources
 | 
			
		||||
    <FilesMatch "\.(appcache|crx|css|eot|gif|htc|ico|jpe?g|js|m4a|m4v|manifest|mp4|oex|oga|ogg|ogv|otf|pdf|png|safariextz|svg|svgz|ttf|vcf|webapp|webm|webp|woff|xml|xpi)$">
 | 
			
		||||
        Header unset X-UA-Compatible
 | 
			
		||||
    </FilesMatch>
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Cookie setting from iframes                                                |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Allow cookies to be set from iframes in IE.
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_headers.c>
 | 
			
		||||
#   Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
 | 
			
		||||
# </IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Screen flicker                                                             |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Stop screen flicker in IE on CSS rollovers (this only works in
 | 
			
		||||
# combination with the `ExpiresByType` directives for images from below).
 | 
			
		||||
 | 
			
		||||
# BrowserMatch "MSIE" brokenvary=1
 | 
			
		||||
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
 | 
			
		||||
# BrowserMatch "Opera" !brokenvary
 | 
			
		||||
# SetEnvIf brokenvary 1 force-no-vary
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
# # MIME TYPES AND ENCODING                                                    #
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Proper MIME types for all files                                            |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
<IfModule mod_mime.c>
 | 
			
		||||
 | 
			
		||||
  # Audio
 | 
			
		||||
    AddType audio/mp4                                   m4a f4a f4b
 | 
			
		||||
    AddType audio/ogg                                   oga ogg
 | 
			
		||||
 | 
			
		||||
  # JavaScript
 | 
			
		||||
    # Normalize to standard type (it's sniffed in IE anyways):
 | 
			
		||||
    # http://tools.ietf.org/html/rfc4329#section-7.2
 | 
			
		||||
    AddType application/javascript                      js jsonp
 | 
			
		||||
    AddType application/json                            json
 | 
			
		||||
 | 
			
		||||
  # Video
 | 
			
		||||
    AddType video/mp4                                   mp4 m4v f4v f4p
 | 
			
		||||
    AddType video/ogg                                   ogv
 | 
			
		||||
    AddType video/webm                                  webm
 | 
			
		||||
    AddType video/x-flv                                 flv
 | 
			
		||||
 | 
			
		||||
  # Web fonts
 | 
			
		||||
    AddType application/font-woff                       woff
 | 
			
		||||
    AddType application/vnd.ms-fontobject               eot
 | 
			
		||||
 | 
			
		||||
    # Browsers usually ignore the font MIME types and sniff the content,
 | 
			
		||||
    # however, Chrome shows a warning if other MIME types are used for the
 | 
			
		||||
    # following fonts.
 | 
			
		||||
    AddType application/x-font-ttf                      ttc ttf
 | 
			
		||||
    AddType font/opentype                               otf
 | 
			
		||||
 | 
			
		||||
    # Make SVGZ fonts work on iPad:
 | 
			
		||||
    # https://twitter.com/FontSquirrel/status/14855840545
 | 
			
		||||
    AddType     image/svg+xml                           svg svgz
 | 
			
		||||
    AddEncoding gzip                                    svgz
 | 
			
		||||
 | 
			
		||||
  # Other
 | 
			
		||||
    AddType application/octet-stream                    safariextz
 | 
			
		||||
    AddType application/x-chrome-extension              crx
 | 
			
		||||
    AddType application/x-opera-extension               oex
 | 
			
		||||
    AddType application/x-shockwave-flash               swf
 | 
			
		||||
    AddType application/x-web-app-manifest+json         webapp
 | 
			
		||||
    AddType application/x-xpinstall                     xpi
 | 
			
		||||
    AddType application/xml                             atom rdf rss xml
 | 
			
		||||
    AddType image/webp                                  webp
 | 
			
		||||
    AddType image/x-icon                                ico
 | 
			
		||||
    AddType text/cache-manifest                         appcache manifest
 | 
			
		||||
    AddType text/vtt                                    vtt
 | 
			
		||||
    AddType text/x-component                            htc
 | 
			
		||||
    AddType text/x-vcard                                vcf
 | 
			
		||||
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | UTF-8 encoding                                                             |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
 | 
			
		||||
AddDefaultCharset utf-8
 | 
			
		||||
 | 
			
		||||
# Force UTF-8 for certain file formats.
 | 
			
		||||
<IfModule mod_mime.c>
 | 
			
		||||
    AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
# # URL REWRITES                                                               #
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Rewrite engine                                                             |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Turning on the rewrite engine and enabling the `FollowSymLinks` option is
 | 
			
		||||
# necessary for the following directives to work.
 | 
			
		||||
 | 
			
		||||
# If your web host doesn't allow the `FollowSymlinks` option, you may need to
 | 
			
		||||
# comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
 | 
			
		||||
# performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
 | 
			
		||||
 | 
			
		||||
# Also, some cloud hosting services require `RewriteBase` to be set:
 | 
			
		||||
# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
 | 
			
		||||
 | 
			
		||||
<IfModule mod_rewrite.c>
 | 
			
		||||
    Options +FollowSymlinks
 | 
			
		||||
  # Options +SymLinksIfOwnerMatch
 | 
			
		||||
    RewriteEngine On
 | 
			
		||||
  # RewriteBase /
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Suppressing / Forcing the "www." at the beginning of URLs                  |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# The same content should never be available under two different URLs especially
 | 
			
		||||
# not with and without "www." at the beginning. This can cause SEO problems
 | 
			
		||||
# (duplicate content), therefore, you should choose one of the alternatives and
 | 
			
		||||
# redirect the other one.
 | 
			
		||||
 | 
			
		||||
# By default option 1 (no "www.") is activated:
 | 
			
		||||
# http://no-www.org/faq.php?q=class_b
 | 
			
		||||
 | 
			
		||||
# If you'd prefer to use option 2, just comment out all the lines from option 1
 | 
			
		||||
# and uncomment the ones from option 2.
 | 
			
		||||
 | 
			
		||||
# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
 | 
			
		||||
 | 
			
		||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
			
		||||
 | 
			
		||||
# Option 1: rewrite www.example.com → example.com
 | 
			
		||||
 | 
			
		||||
<IfModule mod_rewrite.c>
 | 
			
		||||
    RewriteCond %{HTTPS} !=on
 | 
			
		||||
    RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
 | 
			
		||||
    RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
			
		||||
 | 
			
		||||
# Option 2: rewrite example.com → www.example.com
 | 
			
		||||
 | 
			
		||||
# Be aware that the following might not be a good idea if you use "real"
 | 
			
		||||
# subdomains for certain parts of your website.
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_rewrite.c>
 | 
			
		||||
#    RewriteCond %{HTTPS} !=on
 | 
			
		||||
#    RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
 | 
			
		||||
#    RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
 | 
			
		||||
# </IfModule>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
# # SECURITY                                                                   #
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Content Security Policy (CSP)                                              |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# You can mitigate the risk of cross-site scripting and other content-injection
 | 
			
		||||
# attacks by setting a Content Security Policy which whitelists trusted sources
 | 
			
		||||
# of content for your site.
 | 
			
		||||
 | 
			
		||||
# The example header below allows ONLY scripts that are loaded from the current
 | 
			
		||||
# site's origin (no inline scripts, no CDN, etc). This almost certainly won't
 | 
			
		||||
# work as-is for your site!
 | 
			
		||||
 | 
			
		||||
# To get all the details you'll need to craft a reasonable policy for your site,
 | 
			
		||||
# read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
 | 
			
		||||
# see the specification: http://w3.org/TR/CSP).
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_headers.c>
 | 
			
		||||
#    Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
 | 
			
		||||
#    <FilesMatch "\.(appcache|crx|css|eot|gif|htc|ico|jpe?g|js|m4a|m4v|manifest|mp4|oex|oga|ogg|ogv|otf|pdf|png|safariextz|svg|svgz|ttf|vcf|webapp|webm|webp|woff|xml|xpi)$">
 | 
			
		||||
#        Header unset Content-Security-Policy
 | 
			
		||||
#    </FilesMatch>
 | 
			
		||||
# </IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | File access                                                                |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Block access to directories without a default document.
 | 
			
		||||
# Usually you should leave this uncommented because you shouldn't allow anyone
 | 
			
		||||
# to surf through every directory on your server (which may includes rather
 | 
			
		||||
# private places like the CMS's directories).
 | 
			
		||||
 | 
			
		||||
<IfModule mod_autoindex.c>
 | 
			
		||||
    Options -Indexes
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
			
		||||
 | 
			
		||||
# Block access to hidden files and directories.
 | 
			
		||||
# This includes directories used by version control systems such as Git and SVN.
 | 
			
		||||
 | 
			
		||||
<IfModule mod_rewrite.c>
 | 
			
		||||
    RewriteCond %{SCRIPT_FILENAME} -d [OR]
 | 
			
		||||
    RewriteCond %{SCRIPT_FILENAME} -f
 | 
			
		||||
    RewriteRule "(^|/)\." - [F]
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
			
		||||
 | 
			
		||||
# Block access to backup and source files.
 | 
			
		||||
# These files may be left by some text editors and can pose a great security
 | 
			
		||||
# danger when anyone has access to them.
 | 
			
		||||
 | 
			
		||||
<FilesMatch "(^#.*#|\.(bak|config|dist|fla|inc|ini|log|psd|sh|sql|sw[op])|~)$">
 | 
			
		||||
    Order allow,deny
 | 
			
		||||
    Deny from all
 | 
			
		||||
    Satisfy All
 | 
			
		||||
</FilesMatch>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Secure Sockets Layer (SSL)                                                 |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
 | 
			
		||||
# prevent `https://www.example.com` when your certificate only allows
 | 
			
		||||
# `https://secure.example.com`.
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_rewrite.c>
 | 
			
		||||
#    RewriteCond %{SERVER_PORT} !^443
 | 
			
		||||
#    RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
 | 
			
		||||
# </IfModule>
 | 
			
		||||
 | 
			
		||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
			
		||||
 | 
			
		||||
# Force client-side SSL redirection.
 | 
			
		||||
 | 
			
		||||
# If a user types "example.com" in his browser, the above rule will redirect him
 | 
			
		||||
# to the secure version of the site. That still leaves a window of opportunity
 | 
			
		||||
# (the initial HTTP connection) for an attacker to downgrade or redirect the
 | 
			
		||||
# request. The following header ensures that browser will ONLY connect to your
 | 
			
		||||
# server via HTTPS, regardless of what the users type in the address bar.
 | 
			
		||||
# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_headers.c>
 | 
			
		||||
#    Header set Strict-Transport-Security max-age=16070400;
 | 
			
		||||
# </IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Server software information                                                |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Avoid displaying the exact Apache version number, the description of the
 | 
			
		||||
# generic OS-type and the information about Apache's compiled-in modules.
 | 
			
		||||
 | 
			
		||||
# ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
 | 
			
		||||
 | 
			
		||||
# ServerTokens Prod
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
# # WEB PERFORMANCE                                                            #
 | 
			
		||||
# ##############################################################################
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Compression                                                                |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
<IfModule mod_deflate.c>
 | 
			
		||||
 | 
			
		||||
    # Force compression for mangled headers.
 | 
			
		||||
    # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
 | 
			
		||||
    <IfModule mod_setenvif.c>
 | 
			
		||||
        <IfModule mod_headers.c>
 | 
			
		||||
            SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
 | 
			
		||||
            RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
 | 
			
		||||
        </IfModule>
 | 
			
		||||
    </IfModule>
 | 
			
		||||
 | 
			
		||||
    # Compress all output labeled with one of the following MIME-types
 | 
			
		||||
    # (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
 | 
			
		||||
    #  and can remove the `<IfModule mod_filter.c>` and `</IfModule>` lines
 | 
			
		||||
    #  as `AddOutputFilterByType` is still in the core directives).
 | 
			
		||||
    <IfModule mod_filter.c>
 | 
			
		||||
        AddOutputFilterByType DEFLATE application/atom+xml \
 | 
			
		||||
                                      application/javascript \
 | 
			
		||||
                                      application/json \
 | 
			
		||||
                                      application/rss+xml \
 | 
			
		||||
                                      application/vnd.ms-fontobject \
 | 
			
		||||
                                      application/x-font-ttf \
 | 
			
		||||
                                      application/x-web-app-manifest+json \
 | 
			
		||||
                                      application/xhtml+xml \
 | 
			
		||||
                                      application/xml \
 | 
			
		||||
                                      font/opentype \
 | 
			
		||||
                                      image/svg+xml \
 | 
			
		||||
                                      image/x-icon \
 | 
			
		||||
                                      text/css \
 | 
			
		||||
                                      text/html \
 | 
			
		||||
                                      text/plain \
 | 
			
		||||
                                      text/x-component \
 | 
			
		||||
                                      text/xml
 | 
			
		||||
    </IfModule>
 | 
			
		||||
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Content transformations                                                    |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Prevent some of the mobile network providers from modifying the content of
 | 
			
		||||
# your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_headers.c>
 | 
			
		||||
#    Header set Cache-Control "no-transform"
 | 
			
		||||
# </IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | ETag removal                                                               |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Since we're sending far-future expires headers (see below), ETags can
 | 
			
		||||
# be removed: http://developer.yahoo.com/performance/rules.html#etags.
 | 
			
		||||
 | 
			
		||||
# `FileETag None` is not enough for every server.
 | 
			
		||||
<IfModule mod_headers.c>
 | 
			
		||||
    Header unset ETag
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
FileETag None
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Expires headers (for better cache control)                                 |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# The following expires headers are set pretty far in the future. If you don't
 | 
			
		||||
# control versioning with filename-based cache busting, consider lowering the
 | 
			
		||||
# cache time for resources like CSS and JS to something like 1 week.
 | 
			
		||||
 | 
			
		||||
<IfModule mod_expires.c>
 | 
			
		||||
 | 
			
		||||
    ExpiresActive on
 | 
			
		||||
    ExpiresDefault                                      "access plus 1 month"
 | 
			
		||||
 | 
			
		||||
  # CSS
 | 
			
		||||
    ExpiresByType text/css                              "access plus 1 year"
 | 
			
		||||
 | 
			
		||||
  # Data interchange
 | 
			
		||||
    ExpiresByType application/json                      "access plus 0 seconds"
 | 
			
		||||
    ExpiresByType application/xml                       "access plus 0 seconds"
 | 
			
		||||
    ExpiresByType text/xml                              "access plus 0 seconds"
 | 
			
		||||
 | 
			
		||||
  # Favicon (cannot be renamed!)
 | 
			
		||||
    ExpiresByType image/x-icon                          "access plus 1 week"
 | 
			
		||||
 | 
			
		||||
  # HTML components (HTCs)
 | 
			
		||||
    ExpiresByType text/x-component                      "access plus 1 month"
 | 
			
		||||
 | 
			
		||||
  # HTML
 | 
			
		||||
    ExpiresByType text/html                             "access plus 0 seconds"
 | 
			
		||||
 | 
			
		||||
  # JavaScript
 | 
			
		||||
    ExpiresByType application/javascript                "access plus 1 year"
 | 
			
		||||
 | 
			
		||||
  # Manifest files
 | 
			
		||||
    ExpiresByType application/x-web-app-manifest+json   "access plus 0 seconds"
 | 
			
		||||
    ExpiresByType text/cache-manifest                   "access plus 0 seconds"
 | 
			
		||||
 | 
			
		||||
  # Media
 | 
			
		||||
    ExpiresByType audio/ogg                             "access plus 1 month"
 | 
			
		||||
    ExpiresByType image/gif                             "access plus 1 month"
 | 
			
		||||
    ExpiresByType image/jpeg                            "access plus 1 month"
 | 
			
		||||
    ExpiresByType image/png                             "access plus 1 month"
 | 
			
		||||
    ExpiresByType video/mp4                             "access plus 1 month"
 | 
			
		||||
    ExpiresByType video/ogg                             "access plus 1 month"
 | 
			
		||||
    ExpiresByType video/webm                            "access plus 1 month"
 | 
			
		||||
 | 
			
		||||
  # Web feeds
 | 
			
		||||
    ExpiresByType application/atom+xml                  "access plus 1 hour"
 | 
			
		||||
    ExpiresByType application/rss+xml                   "access plus 1 hour"
 | 
			
		||||
 | 
			
		||||
  # Web fonts
 | 
			
		||||
    ExpiresByType application/font-woff                 "access plus 1 month"
 | 
			
		||||
    ExpiresByType application/vnd.ms-fontobject         "access plus 1 month"
 | 
			
		||||
    ExpiresByType application/x-font-ttf                "access plus 1 month"
 | 
			
		||||
    ExpiresByType font/opentype                         "access plus 1 month"
 | 
			
		||||
    ExpiresByType image/svg+xml                         "access plus 1 month"
 | 
			
		||||
 | 
			
		||||
</IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Filename-based cache busting                                               |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# If you're not using a build process to manage your filename version revving,
 | 
			
		||||
# you might want to consider enabling the following directives to route all
 | 
			
		||||
# requests such as `/css/style.12345.css` to `/css/style.css`.
 | 
			
		||||
 | 
			
		||||
# To understand why this is important and a better idea than `*.css?v231`, read:
 | 
			
		||||
# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_rewrite.c>
 | 
			
		||||
#    RewriteCond %{REQUEST_FILENAME} !-f
 | 
			
		||||
#    RewriteCond %{REQUEST_FILENAME} !-d
 | 
			
		||||
#    RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
 | 
			
		||||
# </IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | File concatenation                                                         |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Allow concatenation from within specific CSS and JS files, e.g.:
 | 
			
		||||
# Inside of `script.combined.js` you could have
 | 
			
		||||
#   <!--#include file="libs/jquery.js" -->
 | 
			
		||||
#   <!--#include file="plugins/jquery.idletimer.js" -->
 | 
			
		||||
# and they would be included into this single file.
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_include.c>
 | 
			
		||||
#    <FilesMatch "\.combined\.js$">
 | 
			
		||||
#        Options +Includes
 | 
			
		||||
#        AddOutputFilterByType INCLUDES application/javascript application/json
 | 
			
		||||
#        SetOutputFilter INCLUDES
 | 
			
		||||
#    </FilesMatch>
 | 
			
		||||
#    <FilesMatch "\.combined\.css$">
 | 
			
		||||
#        Options +Includes
 | 
			
		||||
#        AddOutputFilterByType INCLUDES text/css
 | 
			
		||||
#        SetOutputFilter INCLUDES
 | 
			
		||||
#    </FilesMatch>
 | 
			
		||||
# </IfModule>
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# | Persistent connections                                                     |
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Allow multiple requests to be sent over the same TCP connection:
 | 
			
		||||
# http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
 | 
			
		||||
 | 
			
		||||
# Enable if you serve a lot of static content but, be aware of the
 | 
			
		||||
# possible disadvantages!
 | 
			
		||||
 | 
			
		||||
# <IfModule mod_headers.c>
 | 
			
		||||
#    Header set Connection Keep-Alive
 | 
			
		||||
# </IfModule>
 | 
			
		||||
							
								
								
									
										36
									
								
								client/_index.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								client/_index.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
<!doctype html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <meta http-equiv="x-ua-compatible" content="ie=edge">
 | 
			
		||||
    <base href="/">
 | 
			
		||||
    <title>Angular Full-Stack Generator</title>
 | 
			
		||||
    <meta name="description" content="">
 | 
			
		||||
    <meta name="viewport" content="width=device-width">
 | 
			
		||||
    <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <!--[if lt IE 9]>
 | 
			
		||||
      <p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
 | 
			
		||||
    <![endif]-->
 | 
			
		||||
 | 
			
		||||
    <!-- Google Analytics: change UA-XXXXX-X to be your site's ID -->
 | 
			
		||||
    <script>
 | 
			
		||||
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
 | 
			
		||||
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
 | 
			
		||||
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
 | 
			
		||||
      })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
 | 
			
		||||
 | 
			
		||||
      ga('create', 'UA-XXXXX-X');
 | 
			
		||||
      ga('send', 'pageview');
 | 
			
		||||
    </script>
 | 
			
		||||
 | 
			
		||||
    <!--Unable to bundle ace.js using webpack. Hence have it imported here.-->
 | 
			
		||||
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script>
 | 
			
		||||
 | 
			
		||||
    <navbar></navbar>
 | 
			
		||||
    <div ui-view=""></div>
 | 
			
		||||
    <footer></footer>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										37
									
								
								client/app/account/account.routes.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								client/app/account/account.routes.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default function routes($stateProvider) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
 | 
			
		||||
  $stateProvider.state('login', {
 | 
			
		||||
    url: '/login',
 | 
			
		||||
    template: require('./login/login.html'),
 | 
			
		||||
    controller: 'LoginController',
 | 
			
		||||
    controllerAs: 'vm'
 | 
			
		||||
  })
 | 
			
		||||
    .state('logout', {
 | 
			
		||||
      url: '/logout?referrer',
 | 
			
		||||
      referrer: 'main',
 | 
			
		||||
      template: '',
 | 
			
		||||
      controller($state, Auth) {
 | 
			
		||||
        'ngInject';
 | 
			
		||||
 | 
			
		||||
        var referrer = $state.params.referrer || $state.current.referrer || 'main';
 | 
			
		||||
        Auth.logout();
 | 
			
		||||
        $state.go(referrer);
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .state('signup', {
 | 
			
		||||
      url: '/signup',
 | 
			
		||||
      template: require('./signup/signup.html'),
 | 
			
		||||
      controller: 'SignupController',
 | 
			
		||||
      controllerAs: 'vm'
 | 
			
		||||
    })
 | 
			
		||||
    .state('settings', {
 | 
			
		||||
      url: '/settings',
 | 
			
		||||
      template: require('./settings/settings.html'),
 | 
			
		||||
      controller: 'SettingsController',
 | 
			
		||||
      controllerAs: 'vm',
 | 
			
		||||
      authenticate: true
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								client/app/account/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								client/app/account/index.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
import angular from 'angular';
 | 
			
		||||
 | 
			
		||||
import uiRouter from 'angular-ui-router';
 | 
			
		||||
 | 
			
		||||
import routing from './account.routes';
 | 
			
		||||
import login from './login';
 | 
			
		||||
import settings from './settings';
 | 
			
		||||
import signup from './signup';
 | 
			
		||||
import oauthButtons from '../../components/oauth-buttons';
 | 
			
		||||
 | 
			
		||||
export default angular.module('app2App.account', [uiRouter, login, settings, signup, oauthButtons])
 | 
			
		||||
  .config(routing)
 | 
			
		||||
  .run(function($rootScope) {
 | 
			
		||||
    'ngInject';
 | 
			
		||||
 | 
			
		||||
    $rootScope.$on('$stateChangeStart', function(event, next, nextParams, current) {
 | 
			
		||||
      if(next.name === 'logout' && current && current.name && !current.authenticate) {
 | 
			
		||||
        next.referrer = current.name;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  })
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										8
									
								
								client/app/account/login/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								client/app/account/login/index.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
import angular from 'angular';
 | 
			
		||||
import LoginController from './login.controller';
 | 
			
		||||
 | 
			
		||||
export default angular.module('app2App.login', [])
 | 
			
		||||
  .controller('LoginController', LoginController)
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										38
									
								
								client/app/account/login/login.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								client/app/account/login/login.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default class LoginController {
 | 
			
		||||
  user = {
 | 
			
		||||
    name: '',
 | 
			
		||||
    email: '',
 | 
			
		||||
    password: ''
 | 
			
		||||
  };
 | 
			
		||||
  errors = {
 | 
			
		||||
    login: undefined
 | 
			
		||||
  };
 | 
			
		||||
  submitted = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor(Auth, $state) {
 | 
			
		||||
    this.Auth = Auth;
 | 
			
		||||
    this.$state = $state;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  login(form) {
 | 
			
		||||
    this.submitted = true;
 | 
			
		||||
 | 
			
		||||
    if(form.$valid) {
 | 
			
		||||
      this.Auth.login({
 | 
			
		||||
        email: this.user.email,
 | 
			
		||||
        password: this.user.password
 | 
			
		||||
      })
 | 
			
		||||
        .then(() => {
 | 
			
		||||
          // Logged in, redirect to home
 | 
			
		||||
          this.$state.go('main');
 | 
			
		||||
        })
 | 
			
		||||
        .catch(err => {
 | 
			
		||||
          this.errors.login = err.message;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								client/app/account/login/login.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								client/app/account/login/login.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
<div class="container">
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-sm-12">
 | 
			
		||||
      <h1>Login</h1>
 | 
			
		||||
      <p>Accounts are reset on server restart from <code>server/config/seed.js</code>. Default account is <code>test@example.com</code> / <code>test</code></p>
 | 
			
		||||
      <p>Admin account is <code>admin@example.com</code> / <code>admin</code></p>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="col-sm-12">
 | 
			
		||||
      <form class="form" name="form" ng-submit="vm.login(form)" novalidate>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
          <label>Email</label>
 | 
			
		||||
 | 
			
		||||
          <input type="email" name="email" class="form-control" ng-model="vm.user.email" required>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
          <label>Password</label>
 | 
			
		||||
 | 
			
		||||
          <input type="password" name="password" class="form-control" ng-model="vm.user.password" required>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group has-error">
 | 
			
		||||
          <p class="help-block" ng-show="form.email.$error.required && form.password.$error.required && vm.submitted">
 | 
			
		||||
             Please enter your email and password.
 | 
			
		||||
          </p>
 | 
			
		||||
          <p class="help-block" ng-show="form.email.$error.email && vm.submitted">
 | 
			
		||||
             Please enter a valid email.
 | 
			
		||||
          </p>
 | 
			
		||||
 | 
			
		||||
          <p class="help-block">{{ vm.errors.login }}</p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
          <button class="btn btn-inverse btn-lg btn-login" type="submit">
 | 
			
		||||
            Login
 | 
			
		||||
          </button>
 | 
			
		||||
          <a class="btn btn-default btn-lg btn-register" ui-sref="signup">
 | 
			
		||||
            Register
 | 
			
		||||
          </a>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <hr/>
 | 
			
		||||
        <div class="row">
 | 
			
		||||
          <div class="col-sm-4 col-md-3">
 | 
			
		||||
            <oauth-buttons classes="btn-block"></oauth-buttons>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </form>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <hr>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										8
									
								
								client/app/account/settings/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								client/app/account/settings/index.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
import angular from 'angular';
 | 
			
		||||
import SettingsController from './settings.controller';
 | 
			
		||||
 | 
			
		||||
export default angular.module('app2App.settings', [])
 | 
			
		||||
  .controller('SettingsController', SettingsController)
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										36
									
								
								client/app/account/settings/settings.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								client/app/account/settings/settings.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default class SettingsController {
 | 
			
		||||
  user = {
 | 
			
		||||
    oldPassword: '',
 | 
			
		||||
    newPassword: '',
 | 
			
		||||
    confirmPassword: ''
 | 
			
		||||
  };
 | 
			
		||||
  errors = {
 | 
			
		||||
    other: undefined
 | 
			
		||||
  };
 | 
			
		||||
  message = '';
 | 
			
		||||
  submitted = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor(Auth) {
 | 
			
		||||
    this.Auth = Auth;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  changePassword(form) {
 | 
			
		||||
    this.submitted = true;
 | 
			
		||||
 | 
			
		||||
    if(form.$valid) {
 | 
			
		||||
      this.Auth.changePassword(this.user.oldPassword, this.user.newPassword)
 | 
			
		||||
        .then(() => {
 | 
			
		||||
          this.message = 'Password successfully changed.';
 | 
			
		||||
        })
 | 
			
		||||
        .catch(() => {
 | 
			
		||||
          form.password.$setValidity('mongoose', false);
 | 
			
		||||
          this.errors.other = 'Incorrect password';
 | 
			
		||||
          this.message = '';
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								client/app/account/settings/settings.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								client/app/account/settings/settings.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
<div class="container">
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-sm-12">
 | 
			
		||||
      <h1>Change Password</h1>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="col-sm-12">
 | 
			
		||||
      <form class="form" name="form" ng-submit="vm.changePassword(form)" novalidate>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
          <label>Current Password</label>
 | 
			
		||||
 | 
			
		||||
          <input type="password" name="password" class="form-control" ng-model="vm.user.oldPassword"
 | 
			
		||||
                 mongoose-error/>
 | 
			
		||||
          <p class="help-block" ng-show="form.password.$error.mongoose">
 | 
			
		||||
              {{ vm.errors.other }}
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
          <label>New Password</label>
 | 
			
		||||
 | 
			
		||||
          <input type="password" name="newPassword" class="form-control" ng-model="vm.user.newPassword"
 | 
			
		||||
                 ng-minlength="3"
 | 
			
		||||
                 required/>
 | 
			
		||||
          <p class="help-block"
 | 
			
		||||
             ng-show="(form.newPassword.$error.minlength || form.newPassword.$error.required) && (form.newPassword.$dirty || vm.submitted)">
 | 
			
		||||
            Password must be at least 3 characters.
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
          <label>Confirm New Password</label>
 | 
			
		||||
 | 
			
		||||
          <input type="password" name="confirmPassword" class="form-control" ng-model="vm.user.confirmPassword"
 | 
			
		||||
                 match="vm.user.newPassword"
 | 
			
		||||
                 ng-minlength="3"
 | 
			
		||||
                 required=""/>
 | 
			
		||||
          <p class="help-block"
 | 
			
		||||
             ng-show="form.confirmPassword.$error.match && vm.submitted">
 | 
			
		||||
            Passwords must match.
 | 
			
		||||
          </p>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <p class="help-block"> {{ vm.message }} </p>
 | 
			
		||||
 | 
			
		||||
        <button class="btn btn-lg btn-primary" type="submit">Save changes</button>
 | 
			
		||||
      </form>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										8
									
								
								client/app/account/signup/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								client/app/account/signup/index.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
import angular from 'angular';
 | 
			
		||||
import SignupController from './signup.controller';
 | 
			
		||||
 | 
			
		||||
export default angular.module('app2App.signup', [])
 | 
			
		||||
  .controller('SignupController', SignupController)
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										45
									
								
								client/app/account/signup/signup.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								client/app/account/signup/signup.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
import angular from 'angular';
 | 
			
		||||
 | 
			
		||||
export default class SignupController {
 | 
			
		||||
  user = {
 | 
			
		||||
    name: '',
 | 
			
		||||
    email: '',
 | 
			
		||||
    password: ''
 | 
			
		||||
  };
 | 
			
		||||
  errors = {};
 | 
			
		||||
  submitted = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor(Auth, $state) {
 | 
			
		||||
    this.Auth = Auth;
 | 
			
		||||
    this.$state = $state;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  register(form) {
 | 
			
		||||
    this.submitted = true;
 | 
			
		||||
 | 
			
		||||
    if(form.$valid) {
 | 
			
		||||
      return this.Auth.createUser({
 | 
			
		||||
        name: this.user.name,
 | 
			
		||||
        email: this.user.email,
 | 
			
		||||
        password: this.user.password
 | 
			
		||||
      })
 | 
			
		||||
        .then(() => {
 | 
			
		||||
          // Account created, redirect to home
 | 
			
		||||
          this.$state.go('main');
 | 
			
		||||
        })
 | 
			
		||||
        .catch(err => {
 | 
			
		||||
          err = err.data;
 | 
			
		||||
          this.errors = {};
 | 
			
		||||
          // Update validity of form fields that match the mongoose errors
 | 
			
		||||
          angular.forEach(err.errors, (error, field) => {
 | 
			
		||||
            form[field].$setValidity('mongoose', false);
 | 
			
		||||
            this.errors[field] = error.message;
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								client/app/account/signup/signup.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								client/app/account/signup/signup.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,86 @@
 | 
			
		|||
<div class="container">
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-sm-12">
 | 
			
		||||
      <h1>Sign up</h1>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="col-sm-12">
 | 
			
		||||
      <form class="form" name="form" ng-submit="vm.register(form)" novalidate>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group" ng-class="{ 'has-success': form.name.$valid && vm.submitted,
 | 
			
		||||
                                            'has-error': form.name.$invalid && vm.submitted }">
 | 
			
		||||
          <label>Name</label>
 | 
			
		||||
 | 
			
		||||
          <input type="text" name="name" class="form-control" ng-model="vm.user.name"
 | 
			
		||||
                 required/>
 | 
			
		||||
          <p class="help-block" ng-show="form.name.$error.required && vm.submitted">
 | 
			
		||||
            A name is required
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group" ng-class="{ 'has-success': form.email.$valid && vm.submitted,
 | 
			
		||||
                                            'has-error': form.email.$invalid && vm.submitted }">
 | 
			
		||||
          <label>Email</label>
 | 
			
		||||
 | 
			
		||||
          <input type="email" name="email" class="form-control" ng-model="vm.user.email"
 | 
			
		||||
                 required
 | 
			
		||||
                 mongoose-error/>
 | 
			
		||||
          <p class="help-block" ng-show="form.email.$error.email && vm.submitted">
 | 
			
		||||
            Doesn't look like a valid email.
 | 
			
		||||
          </p>
 | 
			
		||||
          <p class="help-block" ng-show="form.email.$error.required && vm.submitted">
 | 
			
		||||
            What's your email address?
 | 
			
		||||
          </p>
 | 
			
		||||
          <p class="help-block" ng-show="form.email.$error.mongoose">
 | 
			
		||||
            {{ vm.errors.email }}
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group" ng-class="{ 'has-success': form.password.$valid && vm.submitted,
 | 
			
		||||
                                            'has-error': form.password.$invalid && vm.submitted }">
 | 
			
		||||
          <label>Password</label>
 | 
			
		||||
 | 
			
		||||
          <input type="password" name="password" class="form-control" ng-model="vm.user.password"
 | 
			
		||||
                 ng-minlength="3"
 | 
			
		||||
                 required
 | 
			
		||||
                 mongoose-error/>
 | 
			
		||||
          <p class="help-block"
 | 
			
		||||
             ng-show="(form.password.$error.minlength || form.password.$error.required) && vm.submitted">
 | 
			
		||||
            Password must be at least 3 characters.
 | 
			
		||||
          </p>
 | 
			
		||||
          <p class="help-block" ng-show="form.password.$error.mongoose">
 | 
			
		||||
            {{ vm.errors.password }}
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="form-group" ng-class="{ 'has-success': form.confirmPassword.$valid && vm.submitted,
 | 
			
		||||
                                            'has-error': form.confirmPassword.$invalid && vm.submitted }">
 | 
			
		||||
          <label>Confirm Password</label>
 | 
			
		||||
          <input type="password" name="confirmPassword" class="form-control" ng-model="vm.user.confirmPassword"
 | 
			
		||||
                 match="vm.user.password"
 | 
			
		||||
                 ng-minlength="3" required/>
 | 
			
		||||
          <p class="help-block"
 | 
			
		||||
             ng-show="form.confirmPassword.$error.match && vm.submitted">
 | 
			
		||||
            Passwords must match.
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
          <button class="btn btn-inverse btn-lg btn-register" type="submit">
 | 
			
		||||
            Sign up
 | 
			
		||||
          </button>
 | 
			
		||||
          <a class="btn btn-default btn-lg btn-login" ui-sref="login">
 | 
			
		||||
            Login
 | 
			
		||||
          </a>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <hr/>
 | 
			
		||||
        <div class="row">
 | 
			
		||||
          <div class="col-sm-4 col-md-3">
 | 
			
		||||
            <oauth-buttons classes="btn-block"></oauth-buttons>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </form>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <hr>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										14
									
								
								client/app/admin/admin.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								client/app/admin/admin.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default class AdminController {
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor(User) {
 | 
			
		||||
    // Use the User $resource to fetch all users
 | 
			
		||||
    this.users = User.query();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  delete(user) {
 | 
			
		||||
    user.$remove();
 | 
			
		||||
    this.users.splice(this.users.indexOf(user), 1);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								client/app/admin/admin.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								client/app/admin/admin.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
.trash { color:rgb(209, 91, 71); }
 | 
			
		||||
 | 
			
		||||
.user-list li {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	border: none;
 | 
			
		||||
	border-bottom: 1px lightgray solid;
 | 
			
		||||
	margin-bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
.user-list li:last-child {
 | 
			
		||||
	border-bottom: none;
 | 
			
		||||
}
 | 
			
		||||
.user-list li .user-info {
 | 
			
		||||
	flex-grow: 1;
 | 
			
		||||
}
 | 
			
		||||
.user-list li .trash {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	align-items: center;
 | 
			
		||||
	text-decoration: none;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								client/app/admin/admin.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								client/app/admin/admin.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
<div class="container">
 | 
			
		||||
  <p>The delete user and user index api routes are restricted to users with the 'admin' role.</p>
 | 
			
		||||
  <ul class="list-group user-list">
 | 
			
		||||
    <li class="list-group-item" ng-repeat="user in admin.users">
 | 
			
		||||
	    <div class="user-info">
 | 
			
		||||
	        <strong>{{user.name}}</strong><br>
 | 
			
		||||
	        <span class="text-muted">{{user.email}}</span>
 | 
			
		||||
	    </div>
 | 
			
		||||
        <a ng-click="admin.delete(user)" class="trash"><span class="fa fa-trash fa-2x"></span></a>
 | 
			
		||||
    </li>
 | 
			
		||||
  </ul>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										13
									
								
								client/app/admin/admin.routes.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								client/app/admin/admin.routes.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default function routes($stateProvider) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
 | 
			
		||||
  $stateProvider.state('admin', {
 | 
			
		||||
    url: '/admin',
 | 
			
		||||
    template: require('./admin.html'),
 | 
			
		||||
    controller: 'AdminController',
 | 
			
		||||
    controllerAs: 'admin',
 | 
			
		||||
    authenticate: 'admin'
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								client/app/admin/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/app/admin/index.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
import angular from 'angular';
 | 
			
		||||
import routes from './admin.routes';
 | 
			
		||||
import AdminController from './admin.controller';
 | 
			
		||||
 | 
			
		||||
export default angular.module('app2App.admin', ['app2App.auth', 'ui.router'])
 | 
			
		||||
  .config(routes)
 | 
			
		||||
  .controller('AdminController', AdminController)
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										9
									
								
								client/app/app.config.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								client/app/app.config.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export function routeConfig($urlRouterProvider, $locationProvider) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
 | 
			
		||||
  $urlRouterProvider.otherwise('/');
 | 
			
		||||
 | 
			
		||||
  $locationProvider.html5Mode(true);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								client/app/app.constants.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								client/app/app.constants.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
import angular from 'angular';
 | 
			
		||||
 | 
			
		||||
export default angular.module('app2App.constants', [])
 | 
			
		||||
  .constant('appConfig', require('../../server/config/environment/shared'))
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										125
									
								
								client/app/app.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								client/app/app.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,125 @@
 | 
			
		|||
@import '~bootstrap/dist/css/bootstrap.css';
 | 
			
		||||
@import '~bootstrap-social/bootstrap-social.css';
 | 
			
		||||
/**
 | 
			
		||||
 * Bootstrap Fonts
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@font-face {
 | 
			
		||||
  font-family: 'Glyphicons Halflings';
 | 
			
		||||
  src: url('/assets/fonts/bootstrap/glyphicons-halflings-regular.eot');
 | 
			
		||||
  src: url('/assets/fonts/bootstrap/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
 | 
			
		||||
  url('/assets/fonts/bootstrap/glyphicons-halflings-regular.woff') format('woff'),
 | 
			
		||||
  url('/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf') format('truetype'),
 | 
			
		||||
  url('/assets/fonts/bootstrap/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@import '~font-awesome/css/font-awesome.css';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *Font Awesome Fonts
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@font-face {
 | 
			
		||||
  font-family: 'FontAwesome';
 | 
			
		||||
  src: url('/assets/fonts/font-awesome/fontawesome-webfont.eot?v=4.1.0');
 | 
			
		||||
  src: url('/assets/fonts/font-awesome/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),
 | 
			
		||||
  url('/assets/fonts/font-awesome/fontawesome-webfont.woff?v=4.1.0') format('woff'),
 | 
			
		||||
  url('/assets/fonts/font-awesome/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),
 | 
			
		||||
  url('/assets/fonts/font-awesome/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');
 | 
			
		||||
  font-weight: normal;
 | 
			
		||||
  font-style: normal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * App-wide Styles
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
.browserupgrade {
 | 
			
		||||
  margin: 0.2em 0;
 | 
			
		||||
  background: #ccc;
 | 
			
		||||
  color: #000;
 | 
			
		||||
  padding: 0.2em 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Component styles are injected through gulp */
 | 
			
		||||
/* inject:css */
 | 
			
		||||
@import 'admin/admin.css';
 | 
			
		||||
@import 'main/main.css';
 | 
			
		||||
@import '../components/footer/footer.css';
 | 
			
		||||
@import '../components/modal/modal.css';
 | 
			
		||||
@import '../components/oauth-buttons/oauth-buttons.css';
 | 
			
		||||
/* endinject */
 | 
			
		||||
 | 
			
		||||
.navbar {
 | 
			
		||||
  margin-bottom: 0;
 | 
			
		||||
  background-color: #2d363a;
 | 
			
		||||
  border-radius: 0;
 | 
			
		||||
  border-top: 0;
 | 
			
		||||
  border-right: 0;
 | 
			
		||||
  border-left: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.navbar-brand {
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  padding-top: 15px;
 | 
			
		||||
  margin-right: 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus {
 | 
			
		||||
  color: whitesmoke;
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.navbar-default .navbar-brand {
 | 
			
		||||
  color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.navbar-default .navbar-nav > li > a {
 | 
			
		||||
  color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.navbar-brand-header {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  margin-left: 100px;
 | 
			
		||||
  font-size: 25px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.navbar-brand-header a:hover, a:focus {
 | 
			
		||||
  color: #2d363a;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#header li a.nav-button:hover {
 | 
			
		||||
  background-color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#header li.dropdown.open a.dropdown-toggle {
 | 
			
		||||
  background-color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#header #logo {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  width: 91px;
 | 
			
		||||
  height: 70px;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  background-color: #2c95dd;
 | 
			
		||||
  z-index: 100;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#header #logo img {
 | 
			
		||||
  margin-top: 30px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#header a {
 | 
			
		||||
  color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#header .dropdown li a {
 | 
			
		||||
  color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ace_editor { height: 800px; }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										126
									
								
								client/app/app.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								client/app/app.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,126 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
import angular from 'angular';
 | 
			
		||||
// import ngAnimate from 'angular-animate';
 | 
			
		||||
import ngCookies from 'angular-cookies';
 | 
			
		||||
import ngResource from 'angular-resource';
 | 
			
		||||
import ngSanitize from 'angular-sanitize';
 | 
			
		||||
 | 
			
		||||
import uiRouter from 'angular-ui-router';
 | 
			
		||||
import uiBootstrap from 'angular-ui-bootstrap';
 | 
			
		||||
import 'angular-validation-match';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import 'angular-ui-ace';
 | 
			
		||||
 | 
			
		||||
import 'yamljs/dist/yaml.min';
 | 
			
		||||
 | 
			
		||||
import 'ansi-to-html';
 | 
			
		||||
 | 
			
		||||
import 'angular-markdown-directive';
 | 
			
		||||
 | 
			
		||||
import treecontrol from 'angular-tree-control';
 | 
			
		||||
 | 
			
		||||
import 'angular-tree-control/css/tree-control-attribute.css';
 | 
			
		||||
import 'angular-tree-control/css/tree-control.css';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  routeConfig
 | 
			
		||||
} from './app.config';
 | 
			
		||||
 | 
			
		||||
import _Auth from '../components/auth/auth.module';
 | 
			
		||||
import account from './account';
 | 
			
		||||
import admin from './admin';
 | 
			
		||||
import navbar from '../components/navbar/navbar.component';
 | 
			
		||||
import footer from '../components/footer/footer.component';
 | 
			
		||||
import main from './main/main.component';
 | 
			
		||||
import DesignerComponent from './designer/designer.component';
 | 
			
		||||
import ProjectComponent from './project/project.component';
 | 
			
		||||
import InventoryComponent from './designer/inventory/inventory.component';
 | 
			
		||||
import PlaybookComponent from './designer/playbook/playbook.component';
 | 
			
		||||
import FileBrowserComponent from './designer/file_browser/file_browser.component';
 | 
			
		||||
import RolesComponent from './designer/roles/roles.component';
 | 
			
		||||
import RunsComponent from './runs/runs.component';
 | 
			
		||||
import CustomModulesComponent from './custom_modules/custom_modules.component';
 | 
			
		||||
 | 
			
		||||
import Projects from './services/projects/projects.service';
 | 
			
		||||
import ansible from './services/ansible/ansible.service';
 | 
			
		||||
import YAML from './providers/yaml/yaml.service';
 | 
			
		||||
import yamlFile from './services/yamlFile/yamlFile.service';
 | 
			
		||||
 | 
			
		||||
import customModules from './custom_modules/custom_modules.service';
 | 
			
		||||
 | 
			
		||||
import ansi2html from './providers/ansi2html/ansi2html.service';
 | 
			
		||||
 | 
			
		||||
import constants from './app.constants';
 | 
			
		||||
import util from '../components/util/util.module';
 | 
			
		||||
 | 
			
		||||
import NewInventoryController from './designer/inventory/new_inventory/new_inventory.controller';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import NewGroupController from './designer/inventory/new_group/new_group.controller';
 | 
			
		||||
import NewHostController from './designer/inventory/new_host/new_host.controller';
 | 
			
		||||
import ComplexVarController from './directives/complexVar/complexVar.controller';
 | 
			
		||||
import NewPlaybookController from './designer/playbook/new_playbook/new_playbook.controller';
 | 
			
		||||
import ExecutionController from './designer/execution/execution.controller';
 | 
			
		||||
import NewPlayController from './designer/playbook/new_play/new_play.controller';
 | 
			
		||||
import NewTaskController from './designer/tasks/new_task/new_task.controller';
 | 
			
		||||
 | 
			
		||||
import NewFileController from './designer/roles/new_file/new_file.controller';
 | 
			
		||||
import NewRoleController from './designer/roles/new_role/new_role.controller';
 | 
			
		||||
import SearchRoleController from './designer/roles/search_role/search_role.controller';
 | 
			
		||||
 | 
			
		||||
import ComplexVarModalController from './modals/complex_var_modal/complex_var_modal.controller';
 | 
			
		||||
 | 
			
		||||
import NewModuleController from './custom_modules/new_module/new_module.controller';
 | 
			
		||||
 | 
			
		||||
import dictToKeyValueArray from './filters/dictToKeyValueArray/dictToKeyValueArray.filter';
 | 
			
		||||
import dictToKeyValueArraySimple from './filters/dictToKeyValueArraySimple/dictToKeyValueArraySimple.filter';
 | 
			
		||||
import keyValueArrayToDict from './filters/keyValueArrayToDict/keyValueArrayToDict.filter';
 | 
			
		||||
import keyValueArrayToArray from './filters/keyValueArrayToArray/keyValueArrayToArray.filter';
 | 
			
		||||
import addDotInKey from './filters/addDotInKey/addDotInKey.filter';
 | 
			
		||||
import removeDotInKey from './filters/removeDotInKey/removeDotInKey.filter';
 | 
			
		||||
import json2yaml from './filters/json2yaml/json2yaml.filter';
 | 
			
		||||
 | 
			
		||||
import complexVar from './directives/complexVar/complexVar.directive';
 | 
			
		||||
import tasks from './designer/tasks/tasks.directive';
 | 
			
		||||
 | 
			
		||||
import editor from './services/editor/editor.service';
 | 
			
		||||
 | 
			
		||||
import './app.css';
 | 
			
		||||
 | 
			
		||||
angular.module('app2App', [ngCookies, ngResource, ngSanitize, uiRouter, uiBootstrap, _Auth, account,
 | 
			
		||||
  admin, 'validation.match', 'ui.ace', navbar, footer, main, constants, util, ansi2html,
 | 
			
		||||
  // Components
 | 
			
		||||
  DesignerComponent, ProjectComponent, InventoryComponent, PlaybookComponent, FileBrowserComponent, RolesComponent, RunsComponent, CustomModulesComponent,
 | 
			
		||||
  // Services
 | 
			
		||||
  YAML, yamlFile, Projects, ansible, ansi2html, editor, customModules,
 | 
			
		||||
  // Controllers
 | 
			
		||||
  NewInventoryController, NewGroupController, NewHostController, ComplexVarController, NewPlaybookController, ExecutionController, NewPlayController, NewTaskController, ComplexVarModalController,
 | 
			
		||||
  NewFileController, NewRoleController, SearchRoleController, NewModuleController,
 | 
			
		||||
  // Filters
 | 
			
		||||
  dictToKeyValueArray, dictToKeyValueArraySimple, keyValueArrayToDict, keyValueArrayToArray, addDotInKey, removeDotInKey, json2yaml,
 | 
			
		||||
  // Directives
 | 
			
		||||
  complexVar, tasks, treecontrol, 'btford.markdown'
 | 
			
		||||
 | 
			
		||||
])
 | 
			
		||||
  .config(routeConfig)
 | 
			
		||||
  .run(function($rootScope, $location, Auth) {
 | 
			
		||||
    'ngInject';
 | 
			
		||||
    // Redirect to login if route requires auth and you're not logged in
 | 
			
		||||
 | 
			
		||||
    $rootScope.$on('$stateChangeStart', function(event, next) {
 | 
			
		||||
      Auth.isLoggedIn(function(loggedIn) {
 | 
			
		||||
        if(next.authenticate && !loggedIn) {
 | 
			
		||||
          $location.path('/login');
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
angular.element(document)
 | 
			
		||||
  .ready(() => {
 | 
			
		||||
    angular.bootstrap(document, ['app2App'], {
 | 
			
		||||
      strictDi: true
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
							
								
								
									
										235
									
								
								client/app/custom_modules/custom_modules.component.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								client/app/custom_modules/custom_modules.component.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,235 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
const uiRouter = require('angular-ui-router');
 | 
			
		||||
 | 
			
		||||
import routes from './custom_modules.routes';
 | 
			
		||||
 | 
			
		||||
export class CustomModulesComponent {
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor($scope,customModules,$sce,ansi2html,Projects,$uibModal,YAML) {
 | 
			
		||||
    'ngInject';
 | 
			
		||||
 | 
			
		||||
    $scope.custom_modules = [];
 | 
			
		||||
    $scope.selectedModule = {module:{},module_code:"",module_unchanged_code:""};
 | 
			
		||||
    $scope.selected_module_code = "something";
 | 
			
		||||
 | 
			
		||||
    $scope.showNewModuleForm = {value:false};
 | 
			
		||||
 | 
			
		||||
    $scope.getProjects = function(){
 | 
			
		||||
      $scope.projects = Projects.resource.query(function(){
 | 
			
		||||
        if($scope.projects.length){
 | 
			
		||||
          $scope.selectedProjectID = localStorage.selectedProjectID || $scope.projects[0]._id;
 | 
			
		||||
          $scope.projectSelected($scope.selectedProjectID)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.projectSelected = function(projectID){
 | 
			
		||||
 | 
			
		||||
      localStorage.selectedProjectID = projectID;
 | 
			
		||||
 | 
			
		||||
      $scope.selectedProject = Projects.resource.get({id: projectID},function(){
 | 
			
		||||
        Projects.selectedProject = $scope.selectedProject;
 | 
			
		||||
        $scope.getCustomModules();
 | 
			
		||||
      })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.getProjects();
 | 
			
		||||
 | 
			
		||||
    $scope.getCustomModules = function(){
 | 
			
		||||
      customModules.get(function(response){
 | 
			
		||||
        console.log(response.data);
 | 
			
		||||
        var lines = response.data.split("\n");
 | 
			
		||||
        if(lines.length)
 | 
			
		||||
          lines = lines
 | 
			
		||||
            .filter(function(line){return line.indexOf(".py") > -1})
 | 
			
		||||
            .map(function(item){return {name:item}});
 | 
			
		||||
        $scope.custom_modules = lines;
 | 
			
		||||
 | 
			
		||||
        if($scope.selectedModule.module.name){
 | 
			
		||||
          $scope.selectedModule.module = $scope.custom_modules.filter(function(item){
 | 
			
		||||
            return (item.name == $scope.selectedModule.module.name)
 | 
			
		||||
          })[0]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.loadingModuleCode = false
 | 
			
		||||
 | 
			
		||||
    $scope.showModuleCode = function(module_name){
 | 
			
		||||
      $scope.loadingModuleCode = true;
 | 
			
		||||
      if(!module_name){
 | 
			
		||||
        $scope.selectedModule.module_code = "Select a module";
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      customModules.show(module_name,function(response) {
 | 
			
		||||
        $scope.loadingModuleCode = false;
 | 
			
		||||
        $scope.selectedModule.module_code = response.data.split("Stream :: close")[0];
 | 
			
		||||
        $scope.selectedModule.module_unchanged_code = angular.copy($scope.selectedModule.module_code);
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.$watch('selectedModule.module',function(newValue,oldValue){
 | 
			
		||||
      if(newValue.name && newValue.name !== oldValue.name){
 | 
			
		||||
        $scope.selectedModule.module_code = "Loading Module Code...";
 | 
			
		||||
        $scope.showModuleCode(newValue.name)
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $scope.code_has_changed = false;
 | 
			
		||||
 | 
			
		||||
    $scope.codeChanged = function(){
 | 
			
		||||
      console.log("Code Changed");
 | 
			
		||||
      if($scope.selectedModule.module_unchanged_code !== $scope.selectedModule.module_code){
 | 
			
		||||
        $scope.code_has_changed = true
 | 
			
		||||
      }else{
 | 
			
		||||
        $scope.code_has_changed = false
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.discardCodeChanges = function(){
 | 
			
		||||
      $scope.selectedModule.module_code = angular.copy($scope.selectedModule.module_unchanged_code);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.saveModule = function(){
 | 
			
		||||
      $scope.saving = true;
 | 
			
		||||
      customModules.save($scope.selectedModule.module.name,$scope.selectedModule.module_code,function(response){
 | 
			
		||||
        $scope.saving = false;
 | 
			
		||||
        $scope.code_has_changed = false;
 | 
			
		||||
        $scope.selectedModule.module_unchanged_code = angular.copy($scope.selectedModule.module_code);
 | 
			
		||||
        console.log("Success")
 | 
			
		||||
      },function(response){
 | 
			
		||||
        $scope.saving = false;
 | 
			
		||||
        console.error(response.data)
 | 
			
		||||
      })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.testModule = function(){
 | 
			
		||||
 | 
			
		||||
      var re = /([^]+DOCUMENTATION = '''\s+)([^]+?)(\s+'''[^]+)/;
 | 
			
		||||
      var module_string = $scope.selectedModule.module_code.replace(re,'$2');
 | 
			
		||||
 | 
			
		||||
      $scope.selectedModuleObject = YAML.parse(module_string);
 | 
			
		||||
 | 
			
		||||
      //var options_copy = angular.copy($scope.selectedModuleObject.options);
 | 
			
		||||
      var options_copy = {};
 | 
			
		||||
      /*options_copy = options_copy.map(function(item){
 | 
			
		||||
       var temp_obj = {};
 | 
			
		||||
       temp_obj[item.name] = "";
 | 
			
		||||
       return temp_obj
 | 
			
		||||
       });*/
 | 
			
		||||
 | 
			
		||||
      var module_name = $scope.selectedModule.module.name;
 | 
			
		||||
      var module_cached_args = null;
 | 
			
		||||
 | 
			
		||||
      try{
 | 
			
		||||
        module_cached_args = JSON.parse(localStorage['test_args_'+module_name]);
 | 
			
		||||
      }catch (e){
 | 
			
		||||
        console.log("Error getting cached arguments.");
 | 
			
		||||
        module_cached_args = null;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      angular.forEach($scope.selectedModuleObject.options,function(value,key){
 | 
			
		||||
        //var temp_obj = {};
 | 
			
		||||
        //temp_obj[key] = "";
 | 
			
		||||
        options_copy[key] = "";
 | 
			
		||||
 | 
			
		||||
        if(module_cached_args && key in module_cached_args){
 | 
			
		||||
          options_copy[key] = module_cached_args[key];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      var variable = {name:'',complexValue:options_copy};
 | 
			
		||||
      $scope.showComplexVariable(variable);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.newModule = function(){
 | 
			
		||||
      $scope.showNewModuleForm.value = true;
 | 
			
		||||
      $scope.$broadcast ('newModule');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.editModule = function(){
 | 
			
		||||
      $scope.showNewModuleForm.value = true;
 | 
			
		||||
      $scope.$broadcast ('editModule');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $scope.showComplexVariable = function(variable){
 | 
			
		||||
      $scope.result = "";
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/modals/complex_var_modal/complexVariable.html',
 | 
			
		||||
        controller: 'ComplexVarModalController',
 | 
			
		||||
        size: 'sm',
 | 
			
		||||
        backdrop: 'static',
 | 
			
		||||
        keyboard: false,
 | 
			
		||||
        closeByEscape: false,
 | 
			
		||||
        closeByDocument: false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          path: function () {
 | 
			
		||||
            return variable.name
 | 
			
		||||
          },
 | 
			
		||||
          hostvars: function(){
 | 
			
		||||
            return null
 | 
			
		||||
          },
 | 
			
		||||
          members: function(){
 | 
			
		||||
            return variable.complexValue
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      modalInstance.result.then(function (module_args) {
 | 
			
		||||
 | 
			
		||||
        var module_name = $scope.selectedModule.module.name;
 | 
			
		||||
 | 
			
		||||
        /*var args = "";
 | 
			
		||||
         angular.forEach(selectedItem,function(value,key){
 | 
			
		||||
 | 
			
		||||
         if(value){
 | 
			
		||||
         args += " " + key + "=" + value
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
         });
 | 
			
		||||
 | 
			
		||||
         if(args){
 | 
			
		||||
         module_name += " -a " + args
 | 
			
		||||
         }*/
 | 
			
		||||
 | 
			
		||||
        localStorage['test_args_'+module_name] = JSON.stringify(module_args);
 | 
			
		||||
 | 
			
		||||
        $scope.testing = true;
 | 
			
		||||
 | 
			
		||||
        customModules.test(module_name,module_args,function(response) {
 | 
			
		||||
            $scope.testing = false;
 | 
			
		||||
            $scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data.split("Stream :: close")[0]).replace(/\n/g, "<br>"));
 | 
			
		||||
          },
 | 
			
		||||
          function(response) {
 | 
			
		||||
            $scope.testing = false;
 | 
			
		||||
            $scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data.split("Stream :: close")[0]).replace(/\n/g, "<br>"));
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
      }, function () {
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.custom_modules', [uiRouter])
 | 
			
		||||
  .config(routes)
 | 
			
		||||
  .component('customModules', {
 | 
			
		||||
    template: require('./custom_modules.html'),
 | 
			
		||||
    controller: CustomModulesComponent,
 | 
			
		||||
    controllerAs: 'customModulesCtrl'
 | 
			
		||||
  })
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										17
									
								
								client/app/custom_modules/custom_modules.component.spec.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								client/app/custom_modules/custom_modules.component.spec.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Component: CustomModulesComponent', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.custom_modules'));
 | 
			
		||||
 | 
			
		||||
  var CustomModulesComponent;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($componentController) {
 | 
			
		||||
    CustomModulesComponent = $componentController('custom_modules', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										55
									
								
								client/app/custom_modules/custom_modules.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								client/app/custom_modules/custom_modules.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
<div class="row" style="margin:20px;">
 | 
			
		||||
  <div class="col-md-7">
 | 
			
		||||
    <div ng-show="!showNewModuleForm.value">
 | 
			
		||||
      <div style="display: inline-block"><select class="form-control" ng-model="selectedProjectID" ng-change="projectSelected(selectedProjectID)" ng-options="project._id as project.name for project in projects">
 | 
			
		||||
      </select></div>
 | 
			
		||||
      <button class="btn btn-default" ng-click="newModule()"> New Module <span class="fa fa-plus"></span> </button>
 | 
			
		||||
      <button class="btn btn-default" ng-disabled="!selectedModule.module.name || loadingModuleCode" ng-click="editModule()"> Edit Module <span class="fa fa-edit"></span> </button>
 | 
			
		||||
 | 
			
		||||
      <div class="table-responsive">
 | 
			
		||||
        <table class="table">
 | 
			
		||||
          <thead>
 | 
			
		||||
          <tr>
 | 
			
		||||
            <th>Select</th>
 | 
			
		||||
            <th>Name</th>
 | 
			
		||||
            <!--<th>Actions</th>-->
 | 
			
		||||
          </tr>
 | 
			
		||||
          </thead>
 | 
			
		||||
          <tbody>
 | 
			
		||||
          <tr ng-repeat="module in custom_modules">
 | 
			
		||||
            <td><input name="moduleGroup" type="radio" ng-model="selectedModule.module" ng-value="module">
 | 
			
		||||
            </td>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>{{module.name}}</td>
 | 
			
		||||
            <!--<td><div class="btn-group">
 | 
			
		||||
              <label class="btn btn-default" ng-click="showTaskModal($index)" ><span class="fa fa-edit"></span></label>
 | 
			
		||||
              <label class="btn btn-danger"  ng-click="deleteTask($index)" confirm="Are you sure you want to delete?"><span class="fa fa-trash"></span></label>
 | 
			
		||||
              <div style="display: inline-block" tooltip-enable="!task.tags" uib-tooltip="Tag must be assigned to play individually"><label class="btn btn-success" ng-disabled="!task.tags"  ng-click="executeAnsiblePlayBook(task.tags,'Task',task.name, selectedPlay)" ><span class="fa fa-play"></span></label></div>
 | 
			
		||||
              <label class="btn btn-primary" ng-disabled="$first"  ng-click="moveUp(selectedPlay.play.tasks,$index,'saveTaskListLoading')" ><span class="fa fa-arrow-up"></span></label>
 | 
			
		||||
              <label class="btn btn-primary" ng-disabled="$last" ng-click="moveDown(selectedPlay.play.tasks,$index,'saveTaskListLoading')" ><span class="fa fa-arrow-down"></span></label>
 | 
			
		||||
            </div></td>-->
 | 
			
		||||
          </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <!--<button class="btn btn-primary" ng-disabled="!selectedModule.module.name || !code_has_changed" ng-click="saveModule()"> Save <span ng-if="!saving" class="fa fa-save"></span> <span ng-if="saving" class="fa fa-spin fa-spinner"></span> </button>
 | 
			
		||||
      <button class="btn btn-warning" ng-disabled="!selectedModule.module.name || !code_has_changed" confirm="Are you sure you want to discard code changes?" ng-click="discardCodeChanges()"> Discard <span class="fa fa-rotate-left"></span> </button>-->
 | 
			
		||||
      <button class="btn btn-default" ng-disabled="!selectedModule.module.name || loadingModuleCode" ng-click="testModule()"> Test <span ng-if="!testing" class="fa fa-check-circle-o"></span> <span ng-if="testing" class="fa fa-spin fa-spinner"></span> </button>
 | 
			
		||||
 | 
			
		||||
      <div style="background:black;color:lightgrey;width:100%;padding:20px;word-wrap: break-word;" ng-if="result">
 | 
			
		||||
        <p class="logconsole" ng-bind-html="result"></p>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="slide-animate" ng-show="showNewModuleForm.value" ng-include="'app/custom_modules/new_module/new_module.html'"></div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="col-md-5">
 | 
			
		||||
    <div ng-readonly="!showNewModuleForm.value" ui-ace="{theme:'twilight',document:'Python',mode:'python',onChange:codeChanged}" ng-model="selectedModule.module_code">
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								client/app/custom_modules/custom_modules.routes.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/app/custom_modules/custom_modules.routes.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default function($stateProvider) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
  $stateProvider
 | 
			
		||||
    .state('custom_modules', {
 | 
			
		||||
      url: '/custom_modules',
 | 
			
		||||
      template: '<custom-modules></custom-modules>'
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								client/app/custom_modules/custom_modules.service.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								client/app/custom_modules/custom_modules.service.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function customModulesService($http,Projects) {
 | 
			
		||||
	// AngularJS will instantiate a singleton by calling "new" on this function
 | 
			
		||||
 | 
			
		||||
  var uri = '/api/custom_modules';
 | 
			
		||||
 | 
			
		||||
  this.get = function(successCallback,errorCallback){
 | 
			
		||||
    $http.post(uri + '/query',{ansibleEngine:Projects.selectedProject.ansibleEngine}).then(successCallback,errorCallback)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  this.show = function(customModule,successCallback,errorCallback){
 | 
			
		||||
    $http.post(uri+ '/' + customModule+'/get',{ansibleEngine:Projects.selectedProject.ansibleEngine}).then(successCallback,errorCallback)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  this.test = function(customModule,module_args,successCallback,errorCallback){
 | 
			
		||||
    $http.post(uri + '/' + customModule + '/test',{ansibleEngine:Projects.selectedProject.ansibleEngine,moduleArgs:module_args}).then(successCallback,errorCallback)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  this.save = function(customModule,customModuleCode,successCallback,errorCallback){
 | 
			
		||||
    $http.post(uri + '/' + customModule,{ansibleEngine:Projects.selectedProject.ansibleEngine,custom_module_code:customModuleCode}).then(successCallback,errorCallback)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.custom_modules_service', [])
 | 
			
		||||
  .service('customModules', customModulesService)
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										16
									
								
								client/app/custom_modules/custom_modules.service.spec.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								client/app/custom_modules/custom_modules.service.spec.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Service: customModules', function() {
 | 
			
		||||
  // load the service's module
 | 
			
		||||
  beforeEach(module('webAppApp.custom_modules'));
 | 
			
		||||
 | 
			
		||||
  // instantiate service
 | 
			
		||||
  var customModules;
 | 
			
		||||
  beforeEach(inject(function(_customModules_) {
 | 
			
		||||
    customModules = _customModules_;
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should do something', function() {
 | 
			
		||||
    expect(!!customModules).to.be.true;
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										280
									
								
								client/app/custom_modules/new_module/new_module.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								client/app/custom_modules/new_module/new_module.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,280 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function newModuleController($scope,$filter,customModules,ansible,YAML) {
 | 
			
		||||
 | 
			
		||||
  $scope.optionTypes = ['str','list','dict','bool','int','float','path','raw','jsonarg','json','bytes','bits'];
 | 
			
		||||
 | 
			
		||||
  var defaultModule = {
 | 
			
		||||
    module:null,
 | 
			
		||||
    short_description:"",
 | 
			
		||||
    description:"",
 | 
			
		||||
    version_added:"",
 | 
			
		||||
    author:"",
 | 
			
		||||
    notes: "",
 | 
			
		||||
    requirements: "",
 | 
			
		||||
    options:[
 | 
			
		||||
      {
 | 
			
		||||
        name:"parameter1",
 | 
			
		||||
        description: 'Description of parameter 1',
 | 
			
		||||
        required: true,
 | 
			
		||||
        default: null,
 | 
			
		||||
        choices: '"choice1", "choice2"',
 | 
			
		||||
        aliases: '"option1", "argument1"',
 | 
			
		||||
        type: ""
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  $scope.loadDefaultTemplate = function(){
 | 
			
		||||
    $scope.newModule = angular.copy(defaultModule);
 | 
			
		||||
    $scope.selectedModule.module_code = "Loading Template..";
 | 
			
		||||
 | 
			
		||||
    customModules.show('template.py',function(response) {
 | 
			
		||||
      $scope.selectedModule.module_code = response.data.split("Stream :: close")[0];
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.$watch('newModule',function(newValue,oldValue){
 | 
			
		||||
 | 
			
		||||
    updateDocumentation(newValue);
 | 
			
		||||
    updateParameters(newValue);
 | 
			
		||||
    updateExamples(newValue);
 | 
			
		||||
 | 
			
		||||
  },true);
 | 
			
		||||
 | 
			
		||||
  var updateParameters = function(newValue){
 | 
			
		||||
    newValue = angular.copy(newValue);
 | 
			
		||||
 | 
			
		||||
    var parameters_definition_lines = [];
 | 
			
		||||
    var parameters_retreive_lines = [];
 | 
			
		||||
    angular.forEach(newValue.options,function(option){
 | 
			
		||||
      if(option.name) {
 | 
			
		||||
        var line = option.name + "=dict(";
 | 
			
		||||
 | 
			
		||||
        var line_arguments = [];
 | 
			
		||||
        if (option.required)line_arguments.push("required=True");
 | 
			
		||||
        if (!option.required && option.default)line_arguments.push("default='" + option.default + "'");
 | 
			
		||||
        if (option.type)line_arguments.push("type='" + option.type + "'");
 | 
			
		||||
        if (option.choices)line_arguments.push("choices=[" + option.choices + "]");
 | 
			
		||||
        if (option.aliases)line_arguments.push("aliases=[" + option.aliases + "]");
 | 
			
		||||
 | 
			
		||||
        line += line_arguments.join(",");
 | 
			
		||||
        line += ")";
 | 
			
		||||
 | 
			
		||||
        parameters_definition_lines.push(line);
 | 
			
		||||
        parameters_retreive_lines.push(option.name + ' = module.params[\'' + option.name + '\']')
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    var parameters_definition_string = parameters_definition_lines.join(",\n            ");
 | 
			
		||||
    var parameters_retreive_string = parameters_retreive_lines.join("\n    ");
 | 
			
		||||
 | 
			
		||||
    var re = /(# <--Begin Parameter Definition -->\s+            )([^]+)(\s+            # <--END Parameter Definition -->)/;
 | 
			
		||||
    $scope.selectedModule.module_code = $scope.selectedModule.module_code.replace(re,"$1" + parameters_definition_string + "$3");
 | 
			
		||||
 | 
			
		||||
    var supports_check_mode_string = '\n';
 | 
			
		||||
    if(newValue.supports_check_mode){
 | 
			
		||||
      supports_check_mode_string = '\n        supports_check_mode=True\n'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var re2 = /(# <--Begin Supports Check Mode -->)([^]+)(        # <--End Supports Check Mode -->)/;
 | 
			
		||||
    $scope.selectedModule.module_code = $scope.selectedModule.module_code.replace(re2,"$1" + supports_check_mode_string + "$3");
 | 
			
		||||
 | 
			
		||||
    var re3 = /(# <--Begin Retreiving Parameters  -->\s+    )([^]+)(\s+    # <--End Retreiving Parameters  -->)/;
 | 
			
		||||
    $scope.selectedModule.module_code = $scope.selectedModule.module_code.replace(re3,"$1" + parameters_retreive_string + "$3");
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var updateDocumentation = function(newValue){
 | 
			
		||||
    newValue = angular.copy(newValue);
 | 
			
		||||
    newValue.options = convertOptionsToObject(newValue.options);
 | 
			
		||||
 | 
			
		||||
    delete newValue['supports_check_mode'];
 | 
			
		||||
 | 
			
		||||
    if(newValue.description)
 | 
			
		||||
      newValue.description = newValue.description.split(";");
 | 
			
		||||
 | 
			
		||||
    if(newValue.notes)
 | 
			
		||||
      newValue.notes = newValue.notes.split(";");
 | 
			
		||||
 | 
			
		||||
    if(newValue.requirements)
 | 
			
		||||
      newValue.requirements = newValue.requirements.split(";");
 | 
			
		||||
 | 
			
		||||
    $scope.documentation_yaml = '---\n' + $filter('json2yaml')(angular.toJson(newValue)).toString().replace(/__dot__/g,".");
 | 
			
		||||
 | 
			
		||||
    //var re = /(.*DOCUMENTATION = '''\n)([^]+?)(\n'''.*)/;
 | 
			
		||||
    var re = /([^]+DOCUMENTATION = '''\s+)([^]+?)(\s+'''[^]+)/;
 | 
			
		||||
    $scope.selectedModule.module_code = $scope.selectedModule.module_code.replace(re,'$1' + $scope.documentation_yaml + '$3');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var updateExamples = function(newValue){
 | 
			
		||||
    newValue = angular.copy(newValue);
 | 
			
		||||
 | 
			
		||||
    var moduleCopy = {
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    moduleCopy[newValue.module] = convertOptionsToExampleObject(newValue.options);
 | 
			
		||||
 | 
			
		||||
    $scope.example_yaml = YAML.stringify(moduleCopy,4);
 | 
			
		||||
 | 
			
		||||
    //var re = /(.*DOCUMENTATION = '''\n)([^]+?)(\n'''.*)/;
 | 
			
		||||
    var re = /([^]+EXAMPLES = '''[^]+# <--  -->\s+)([^]+?)(\s+# <-- \/ -->\s+'''[^]+)/;
 | 
			
		||||
    $scope.selectedModule.module_code = $scope.selectedModule.module_code.replace(re,'$1' + $scope.example_yaml + '$3');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var convertOptionsToObject = function(options){
 | 
			
		||||
 | 
			
		||||
    var result = {};
 | 
			
		||||
 | 
			
		||||
    angular.forEach(options,function(option){
 | 
			
		||||
      if(option.name){
 | 
			
		||||
        result[option.name] = {
 | 
			
		||||
          description: option.description
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if(option.required)
 | 
			
		||||
          result[option.name]['required'] = "True";
 | 
			
		||||
        else
 | 
			
		||||
          delete result[option.name]['required'];
 | 
			
		||||
 | 
			
		||||
        if(!option.required && option.default)
 | 
			
		||||
          result[option.name]['default'] = option.default;
 | 
			
		||||
 | 
			
		||||
        if(option.choices){
 | 
			
		||||
          result[option.name]['choices'] = "[" + option.choices + "]"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(option.aliases){
 | 
			
		||||
          result[option.name]['aliases'] = "[" + option.aliases + "]"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var convertOptionsToExampleObject = function(options){
 | 
			
		||||
 | 
			
		||||
    var result = {};
 | 
			
		||||
 | 
			
		||||
    angular.forEach(options,function(option){
 | 
			
		||||
      if(option.name){
 | 
			
		||||
        result[option.name] = "value";
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var convertOptionsToArrays = function(options){
 | 
			
		||||
 | 
			
		||||
    var result = [];
 | 
			
		||||
 | 
			
		||||
    angular.forEach(options,function(value,key){
 | 
			
		||||
      var option = {
 | 
			
		||||
        name: key,
 | 
			
		||||
        description: value.description,
 | 
			
		||||
        required: value.required,
 | 
			
		||||
        default:value.default
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      if(value.choices && value.choices.length)
 | 
			
		||||
        option['choices'] = value.choices.map(function(item){return ('"' + item + '"')}).join(",")
 | 
			
		||||
 | 
			
		||||
      if(value.aliases && value.aliases.length)
 | 
			
		||||
        option['aliases'] = value.aliases.map(function(item){return ('"' + item + '"')}).join(",")
 | 
			
		||||
 | 
			
		||||
      result.push(option)
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.saveNewModule = function(){
 | 
			
		||||
    $scope.saving = true;
 | 
			
		||||
    customModules.save($scope.newModule.module + '.py',$scope.selectedModule.module_code,function(response){
 | 
			
		||||
      $scope.saving = false;
 | 
			
		||||
      $scope.getCustomModules();
 | 
			
		||||
 | 
			
		||||
      ansible.getAnsibleModules(function(response){
 | 
			
		||||
 | 
			
		||||
      }, function(response){
 | 
			
		||||
 | 
			
		||||
      },null,true);
 | 
			
		||||
      $scope.cancelNewModule();
 | 
			
		||||
    },function(response){
 | 
			
		||||
      $scope.saving = false;
 | 
			
		||||
      console.error(response.data)
 | 
			
		||||
    })
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.cancelNewModule = function(){
 | 
			
		||||
    $scope.showNewModuleForm.value = false;
 | 
			
		||||
    $scope.$parent.showModuleCode($scope.selectedModule.module.name)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var getPropertiesFromCode = function(module_code){
 | 
			
		||||
 | 
			
		||||
    //var re = /([^]+DOCUMENTATION = '''\n)([^]+?)(\n'''[^]+)/;
 | 
			
		||||
    var re = /([^]+DOCUMENTATION = '''\s+)([^]+?)(\s+'''[^]+)/;
 | 
			
		||||
    var module_string = $scope.selectedModule.module_code.replace(re,'$2');
 | 
			
		||||
 | 
			
		||||
    $scope.newModule = YAML.parse(module_string);
 | 
			
		||||
    $scope.newModule.options = convertOptionsToArrays($scope.newModule.options);
 | 
			
		||||
 | 
			
		||||
    if($scope.newModule.description && $scope.newModule.description.length)
 | 
			
		||||
      $scope.newModule.description = $scope.newModule.description.join(";");
 | 
			
		||||
 | 
			
		||||
    if($scope.newModule.notes && $scope.newModule.notes.length)
 | 
			
		||||
      $scope.newModule.notes = $scope.newModule.notes.join(";");
 | 
			
		||||
 | 
			
		||||
    if($scope.newModule.requirements && $scope.newModule.requirements.length)
 | 
			
		||||
      $scope.newModule.requirements = $scope.newModule.requirements.join(";");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    re = /([^]+# <--Begin Parameter Definition -->\s+            )([^]+)(\s+            # <--END Parameter Definition -->[^]+)/;
 | 
			
		||||
    var parameter_string = $scope.selectedModule.module_code.replace(re,"$2");
 | 
			
		||||
 | 
			
		||||
    // Read property type form parameter definition
 | 
			
		||||
    re = /\s+(.*?)=.*type=(.*?)[,\)].*/g;
 | 
			
		||||
    var m;
 | 
			
		||||
 | 
			
		||||
    while ((m = re.exec(parameter_string)) !== null) {
 | 
			
		||||
      if (m.index === re.lastIndex) {
 | 
			
		||||
        re.lastIndex++;
 | 
			
		||||
      }
 | 
			
		||||
      // View your result using the m-variable.
 | 
			
		||||
      // eg m[0] etc.
 | 
			
		||||
      if(m[1]){
 | 
			
		||||
        angular.forEach($scope.newModule.options,function(option){
 | 
			
		||||
          if(option.name === m[1]){
 | 
			
		||||
            option.type = m[2].replace(/'/g,'')
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.$on('editModule', function(e) {
 | 
			
		||||
    getPropertiesFromCode($scope.selected_module_code)
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  $scope.$on('newModule', function(e) {
 | 
			
		||||
    $scope.loadDefaultTemplate();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.new_module', [])
 | 
			
		||||
  .controller('NewModuleController', newModuleController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: NewModuleCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.new_module'));
 | 
			
		||||
 | 
			
		||||
  var NewModuleCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    NewModuleCtrl = $controller('NewModuleCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										144
									
								
								client/app/custom_modules/new_module/new_module.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								client/app/custom_modules/new_module/new_module.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,144 @@
 | 
			
		|||
<div class="row" ng-controller="NewModuleController">
 | 
			
		||||
 | 
			
		||||
    <div class="panel">
 | 
			
		||||
      <div class="panel panel-primary">
 | 
			
		||||
        <div class="panel-heading">New Module</div>
 | 
			
		||||
        <div class="panel-body">
 | 
			
		||||
          <div class="row">
 | 
			
		||||
            <div class="col-md-4">
 | 
			
		||||
              <p class="form-group">
 | 
			
		||||
                <label>Module Name</label>
 | 
			
		||||
                <input type="text" ng-model="newModule.module" class="form-control" ng-required="true" placeholder="modulename">
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col-md-2">
 | 
			
		||||
              <p class="form-group">
 | 
			
		||||
                <label>Version Added</label>
 | 
			
		||||
                <input type="text" ng-model="newModule.version_added" class="form-control" ng-required="true" placeholder="X.Y">
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col-md-2">
 | 
			
		||||
              <p class="form-group">
 | 
			
		||||
                <label>Check Mode</label>
 | 
			
		||||
                <label class="checkbox-inline"><input ng-model="newModule.supports_check_mode" type="checkbox"></label>
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col-md-4">
 | 
			
		||||
              <p class="form-group">
 | 
			
		||||
                <label>Author</label>
 | 
			
		||||
                <input type="text" ng-model="newModule.author" class="form-control" ng-required="true" placeholder="Your AWESOME name, @awesome-github-id">
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="row">
 | 
			
		||||
            <div class="col-md-4">
 | 
			
		||||
              <p class="form-group">
 | 
			
		||||
                <label>Short Description</label>
 | 
			
		||||
                <input type="text" ng-model="newModule.short_description" class="form-control" ng-required="true" placeholder="This is a sentence describing the module">
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col-md-8">
 | 
			
		||||
              <p class="form-group">
 | 
			
		||||
                <label>Description</label>
 | 
			
		||||
                <input type="text" ng-model="newModule.description" class="form-control" ng-required="true" placeholder="Longer description of the module; You might include instructions">
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
          <p class="form-group">
 | 
			
		||||
            <label>Notes</label>
 | 
			
		||||
            <input type="text" ng-model="newModule.notes" class="form-control" ng-required="true" placeholder="Other things consumers of your module should know">
 | 
			
		||||
          </p>
 | 
			
		||||
 | 
			
		||||
          <p class="form-group">
 | 
			
		||||
            <label>Requirements</label>
 | 
			
		||||
            <input type="text" ng-model="newModule.requirements" class="form-control" ng-required="true" placeholder="List of required things separated by semicolon; like the factor package; or a specific platform">
 | 
			
		||||
          </p>
 | 
			
		||||
 | 
			
		||||
          <div class="panel">
 | 
			
		||||
            <div class="panel panel-default">
 | 
			
		||||
              <div class="panel-heading">Options</div>
 | 
			
		||||
              <div class="panel-body">
 | 
			
		||||
                <div class="row">
 | 
			
		||||
                  <div class="col-md-2" style="text-align: center">
 | 
			
		||||
                    <label>Name</label>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="col-md-2" style="text-align: center">
 | 
			
		||||
                    <label>Description</label>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="col-md-1" style="text-align: center">
 | 
			
		||||
                    <label>Type</label>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="col-md-1" style="text-align: center">
 | 
			
		||||
                    <label>Required</label>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="col-md-1" style="text-align: center">
 | 
			
		||||
                    <label>Default</label>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="col-md-2" style="text-align: center">
 | 
			
		||||
                    <label>Aliases</label>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="col-md-2" style="text-align: center">
 | 
			
		||||
                    <label>Choices</label>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="col-md-1" style="text-align: center">
 | 
			
		||||
                    <label>Actions</label>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div ng-repeat="option in newModule.options">
 | 
			
		||||
 | 
			
		||||
                  <div class="row">
 | 
			
		||||
                    <div class="col-md-2" style="padding:2px;">
 | 
			
		||||
                      <input type="text" ng-model="option.name" class="form-control">
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="col-md-2" style="padding:2px;">
 | 
			
		||||
                      <!--<input type="text" ng-model="member.value">-->
 | 
			
		||||
                      <input type="text" ng-model="option.description" class="form-control">
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                    <div class="col-md-1" style="padding:2px;">
 | 
			
		||||
                      <select class="form-control" ng-model="option.type" ng-options="optionType for optionType in optionTypes">
 | 
			
		||||
                      </select>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                    <div class="col-md-1" style="padding:2px;text-align: center">
 | 
			
		||||
                      <label class="checkbox-inline"><input ng-model="option.required" type="checkbox" value=""></label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="col-md-1" style="padding:2px;">
 | 
			
		||||
                      <input type="text" ng-disabled="option.required" ng-model="option.default" class="form-control">
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="col-md-2" style="padding:2px;">
 | 
			
		||||
                      <input type="text" ng-model="option.aliases" class="form-control">
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="col-md-2" style="padding:2px;">
 | 
			
		||||
                      <input type="text" ng-model="option.choices" class="form-control">
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="col-md-1" style="padding:2px;text-align: center;">
 | 
			
		||||
                      <div class="btn-group">
 | 
			
		||||
                        <label class="btn btn-default" uib-tooltip="Insert new row after" ng-click="newModule.options.splice($index+1,0,{})" style="padding-top: 3px;padding-bottom: 3px;padding-left:5px;padding-right:5px;"><span class="fa fa-plus"></span></label>
 | 
			
		||||
                        <label class="btn btn-danger" uib-tooltip="Delete row" ng-click="newModule.options.splice($index,1)" style="padding-top: 3px;padding-bottom: 3px; padding-left:5px; padding-right:5px;"><span class="fa fa-minus"></span></label>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <!--<input type="text" ng-model="member.value" uib-typeahead="('"\{\{' + var.name + '\}\}"') as (var.name + ' = ' + var.value) for var in hostvars | filter: $viewValue"  class="form-control">-->
 | 
			
		||||
                </div>
 | 
			
		||||
                <!--<button class="btn btn-danger" ng-click="deleteProject()" confirm="Are you sure you want to delete?">Delete <span ng-if="!deleteProjectLoading" class="fa fa-trash"></span> <span ng-if="deleteProjectLoading" class="fa fa-spinner fa-spin"></span></button>-->
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      <button class="btn btn-primary" ng-disabled="!newModule.module" ng-click="saveNewModule()"> Save <span ng-if="!saving" class="fa fa-save"></span> <span ng-if="saving" class="fa fa-spin fa-spinner"></span> </button>
 | 
			
		||||
      <button class="btn btn-default" confirm="Are you sure you want to discard the changes?" ng-click="cancelNewModule()"> Cancel <span class="fa fa-times"></span> </button>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										89
									
								
								client/app/designer/designer.component.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								client/app/designer/designer.component.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
const uiRouter = require('angular-ui-router');
 | 
			
		||||
 | 
			
		||||
import routes from './designer.routes';
 | 
			
		||||
 | 
			
		||||
export class DesignerComponent {
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor($scope,Projects,ansible) {
 | 
			
		||||
    'ngInject';
 | 
			
		||||
    $scope.selectedInventoryFileName = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get list of projects from server
 | 
			
		||||
     */
 | 
			
		||||
    $scope.getProjects = function(){
 | 
			
		||||
      $scope.projects = Projects.resource.query(function(){
 | 
			
		||||
        if($scope.projects.length){
 | 
			
		||||
          $scope.selectedProjectID = localStorage.selectedProjectID || $scope.projects[0]._id;
 | 
			
		||||
          $scope.projectSelected($scope.selectedProjectID)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On ProjectSelected - set selectedProjectID in cache
 | 
			
		||||
     * @param projectID
 | 
			
		||||
     */
 | 
			
		||||
    $scope.projectSelected = function(projectID){
 | 
			
		||||
      localStorage.selectedProjectID = projectID;
 | 
			
		||||
 | 
			
		||||
      $scope.selectedProject = Projects.resource.get({id: projectID},function(){
 | 
			
		||||
        Projects.selectedProject = $scope.selectedProject;
 | 
			
		||||
        $scope.listOfInventoryFiles();
 | 
			
		||||
        $scope.$broadcast('projectLoaded');
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get List of inventory files in project root folder
 | 
			
		||||
     */
 | 
			
		||||
    $scope.listOfInventoryFiles = function(){
 | 
			
		||||
 | 
			
		||||
      var rolesTestFolder = null;
 | 
			
		||||
 | 
			
		||||
      /*if(roleName){
 | 
			
		||||
       rolesTestFolder = projectFolder + '/' + roleName + '/tests'
 | 
			
		||||
       }*/
 | 
			
		||||
 | 
			
		||||
      ansible.getInventoryList(function(response){
 | 
			
		||||
          $scope.inventoryFiles = response.data;
 | 
			
		||||
          console.log($scope.inventoryFiles);
 | 
			
		||||
          Projects.selectedInventoryFileName = localStorage.selectedInventoryFileName || $scope.inventoryFiles[0];
 | 
			
		||||
          localStorage.selectedInventoryFileName = $scope.inventoryFiles[0];
 | 
			
		||||
          $scope.selectedInventoryFileName = localStorage.selectedInventoryFileName
 | 
			
		||||
        },
 | 
			
		||||
        function(response){
 | 
			
		||||
          $scope.err_msg = response.data
 | 
			
		||||
        },rolesTestFolder)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set selected inventory file in local cache.
 | 
			
		||||
     * @param selectedInventoryFileName - Selected inventory file name
 | 
			
		||||
     */
 | 
			
		||||
    $scope.inventoryFileSelected = function(selectedInventoryFileName){
 | 
			
		||||
      localStorage.selectedInventoryFileName = selectedInventoryFileName;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Main - Get Projects
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    $scope.getProjects();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.designer', [uiRouter])
 | 
			
		||||
  .config(routes)
 | 
			
		||||
  .component('designer', {
 | 
			
		||||
    template: require('./designer.html'),
 | 
			
		||||
    controller: DesignerComponent,
 | 
			
		||||
    controllerAs: 'designerCtrl'
 | 
			
		||||
  })
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										17
									
								
								client/app/designer/designer.component.spec.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								client/app/designer/designer.component.spec.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Component: DesignerComponent', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.designer'));
 | 
			
		||||
 | 
			
		||||
  var DesignerComponent;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($componentController) {
 | 
			
		||||
    DesignerComponent = $componentController('designer', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										10
									
								
								client/app/designer/designer.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/app/designer/designer.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
.logconsole {
 | 
			
		||||
  font:12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas';
 | 
			
		||||
  color: #fff;
 | 
			
		||||
  padding:5px;
 | 
			
		||||
  margin:5px;
 | 
			
		||||
  font-size:14px;
 | 
			
		||||
  background: black;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ace_editor { height: 800px; }
 | 
			
		||||
							
								
								
									
										55
									
								
								client/app/designer/designer.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								client/app/designer/designer.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
<div style="padding:15px;">
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-md-2">
 | 
			
		||||
      <div class="panel">
 | 
			
		||||
        <div class="panel panel-default">
 | 
			
		||||
          <div class="panel-heading">Projects</div>
 | 
			
		||||
          <div class="panel-body">
 | 
			
		||||
            <select class="form-control" ng-model="selectedProjectID" ng-change="projectSelected(selectedProjectID)" ng-options="project._id as project.name for project in projects">
 | 
			
		||||
            </select>
 | 
			
		||||
            <div ng-if="selectedProject.ansibleVersion" class="hint">
 | 
			
		||||
              Ansible Version:{{selectedProject.ansibleVersion}}
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="panel">
 | 
			
		||||
        <div class="panel panel-default">
 | 
			
		||||
          <div class="panel-heading">Inventory Files</div>
 | 
			
		||||
          <div class="panel-body">
 | 
			
		||||
            <select class="form-control" ng-model="selectedInventoryFileName" ng-options="inventoryFile as inventoryFile for inventoryFile in inventoryFiles" ng-change="inventoryFileSelected(selectedInventoryFileName)">
 | 
			
		||||
            </select>
 | 
			
		||||
            <div ng-if="selectedProject.ansibleVersion" class="hint">
 | 
			
		||||
              An inventory file to work with
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="panel">
 | 
			
		||||
        <div class="panel panel-default">
 | 
			
		||||
          <div class="panel-heading">Menu</div>
 | 
			
		||||
          <div class="panel-body">
 | 
			
		||||
 | 
			
		||||
            <div class="btn-group-vertical" role="group" aria-label="...">
 | 
			
		||||
              <button class="btn btn-default" ui-sref="designer.inventory">Inventory</button>
 | 
			
		||||
              <button class="btn btn-default" ui-sref="designer.playbook">Playbooks</button>
 | 
			
		||||
              <button class="btn btn-default" ui-sref="designer.roles">Roles</button>
 | 
			
		||||
              <button class="btn btn-default" ui-sref="designer.file_browser">File Browser</button>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="col-md-10">
 | 
			
		||||
 | 
			
		||||
      <div ui-view></div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										10
									
								
								client/app/designer/designer.routes.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/app/designer/designer.routes.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default function($stateProvider) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
  $stateProvider
 | 
			
		||||
    .state('designer', {
 | 
			
		||||
      url: '/designer',
 | 
			
		||||
      template: '<designer></designer>'
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										185
									
								
								client/app/designer/execution/executeModal.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								client/app/designer/execution/executeModal.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,185 @@
 | 
			
		|||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <button type="button" class="close" data-dismiss="modal" ng-click="cancel()">×</button>
 | 
			
		||||
    <h4 class="modal-title">Play</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body">
 | 
			
		||||
    <div class="row" id="PlaybookExecutionModal">
 | 
			
		||||
      <!--<button class="btn btn-default" ng-click="view = (view === 'console' ? 'table' : 'console')">Toggle View</button>-->
 | 
			
		||||
 | 
			
		||||
      <div style="background:black;color:lightgrey;width:100%;padding:20px;" ng-if="result" ng-show="view=='console'">
 | 
			
		||||
        <p class="logconsole" ng-bind-html="result"></p>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <!--<div ng-if="ansibleOutputObject" ng-show="view=='table'" style="margin-top:20px;">
 | 
			
		||||
 | 
			
		||||
        <!–<div class="row" ng-repeat="resultItem in ansibleOutputResult">
 | 
			
		||||
          <div class="col-md-1"><div class="btn btn-default">{{resultItem.type}}</div></div>
 | 
			
		||||
          <div class="col-md-5"><div class="btn btn-primary">{{resultItem.name}}</div></div>
 | 
			
		||||
          <div class="col-md-6"><div class="btn btn-info" ng-if="resultItem.host">{{resultItem.host}}</div></div>
 | 
			
		||||
          <div class="col-md-offset-1 col-md-2"><div class="btn " ng-class="{'btn-success': resultItem.status == 'ok' || resultItem.status == 'changed', 'btn-danger': resultItem.status == 'fatal', 'btn-warning': resultItem.status == 'skipping'}" ng-if="resultItem.status">Status = {{resultItem.status}}</div></div>
 | 
			
		||||
          <div class="col-md-3"><div class="btn btn-info" ng-if="resultItem.status_2.trim()">Status2 = {{resultItem.status_2}}</div></div>
 | 
			
		||||
          <div class="col-md-3"><div class="btn" ng-if="resultItem.resultObject.changed.trim()" ng-class="{'btn-success': resultItem.resultObject.changed == true, 'btn-warning': resultItem.resultObject.changed == false}">Changed = {{resultItem.resultObject.changed}}</div></div>
 | 
			
		||||
          <div class="col-md-3"><div class="btn" ng-if="resultItem.resultObject.failed" ng-class="{'btn-danger': resultItem.resultObject.failed}">Failed = {{resultItem.resultObject.failed}}</div></div>
 | 
			
		||||
          <div class="col-md-offset-1 col-md-11" ng-if="resultItem.resultObject.msg" ><p style="background:grey;" class="logconsole">{{resultItem.resultObject.msg}}</p></div>
 | 
			
		||||
          <div class="col-md-offset-1 col-md-11" ng-if="resultItem.resultObject.formattedStdErr" ><p style="color:#cd616b;" class="logconsole" ng-bind-html="resultItem.resultObject.formattedStdErr"></p></div>
 | 
			
		||||
          <div class="col-md-offset-1 col-md-11" ng-if="resultItem.resultObject.formattedStdOut" ><p style="color:green;" class="logconsole" ng-bind-html="resultItem.resultObject.formattedStdOut"></p></div>
 | 
			
		||||
          <!–<div class="col-md-offset-1 col-md-11"><p class="logconsole">{{resultItem.resultString}}</p></div>–>
 | 
			
		||||
          <span class="fa fa-spinner fa-spin" ng-if="AnsiblePlayBookLoading && $last"></span>
 | 
			
		||||
        </div>–>
 | 
			
		||||
 | 
			
		||||
        <!–<div ng-repeat="(host, stats) in ansibleOutputObject.stats">
 | 
			
		||||
 | 
			
		||||
          <span><b>Host : </b>{{host}}</span>
 | 
			
		||||
          <span class="btn btn-default btn-info">OK <span class="badge">{{stats.ok}}</span></span>
 | 
			
		||||
          <span class="btn btn-default" ng-class="{'btn-success': stats.changed}" >Changed <span class="badge">{{stats.changed}}</span></span>
 | 
			
		||||
          <span class="btn btn-default" ng-class="{'btn-warning': stats.skipped}">Skipped <span class="badge">{{stats.skipped}}</span></span>
 | 
			
		||||
          <span class="btn btn-default" ng-class="{'btn-danger': stats.unreachable}">Unreachable <span class="badge">{{stats.unreachable}}</span></span>
 | 
			
		||||
          <span class="btn btn-default" ng-class="{'btn-danger': stats.failures}">Failures <span class="badge">{{stats.failures}}</span></span>
 | 
			
		||||
 | 
			
		||||
        </div>–>
 | 
			
		||||
 | 
			
		||||
        <h3>Run Statistics</h3>
 | 
			
		||||
        <div class="table-responsive">
 | 
			
		||||
          <table class="table">
 | 
			
		||||
            <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <th>Host</th>
 | 
			
		||||
              <th>Ok</th>
 | 
			
		||||
              <th>Changed</th>
 | 
			
		||||
              <th>Skipped</th>
 | 
			
		||||
              <th>Unreachable</th>
 | 
			
		||||
              <th>Failures</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
            <tr ng-repeat="(host, stats) in ansibleOutputObject.stats">
 | 
			
		||||
              <td>{{host}}</td>
 | 
			
		||||
              <td>{{stats.ok}}</td>
 | 
			
		||||
              <td><span ng-class="{'label label-success': stats.changed}">{{stats.changed}}</span></td>
 | 
			
		||||
              <td><span ng-class="{'label label-warning': stats.skipped}">{{stats.skipped}}</span></td>
 | 
			
		||||
              <td><span ng-class="{'label label-danger': stats.unreachable}">{{stats.unreachable}}</span></td>
 | 
			
		||||
              <td><span ng-class="{'label label-danger': stats.failures}">{{stats.failures}}</span></td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        <div class="row" ng-repeat="playObject in ansibleOutputObject.plays">
 | 
			
		||||
 | 
			
		||||
          <!–<div class="col-md-3"><div class="btn btn-default">Play - {{playObject.play.name}}</div></div>–>
 | 
			
		||||
 | 
			
		||||
          <div class="col-md-12">
 | 
			
		||||
            <uib-accordion close-others="false">
 | 
			
		||||
              <div uib-accordion-group class="panel-default" is-open="true" ng-repeat="taskObject in playObject.tasks">
 | 
			
		||||
                <uib-accordion-heading>
 | 
			
		||||
                  Play - {{playObject.play.name}} - Tasks - {{taskObject.task.name}}
 | 
			
		||||
                  <span class="fa fa-spinner fa-spin" ng-if="AnsiblePlayBookLoading && $last"></span>
 | 
			
		||||
                </uib-accordion-heading>
 | 
			
		||||
 | 
			
		||||
                <div class="col-md-12" ng-repeat="(hostName, hostObject) in taskObject.hosts">
 | 
			
		||||
                  <div class="col-md-3"><b>Host :</b> {{hostName}}</div>
 | 
			
		||||
                  <div class="col-md-3"><b>Method :</b> {{hostObject.invocation && hostObject.invocation.module_name}}</div>
 | 
			
		||||
                  <div class="col-md-2"><div class="btn" ng-class="{'btn-success': hostObject.changed == true, 'btn-warning': hostObject.changed == false}" style="padding: 2px 5px">Changed <span class="badge">{{hostObject.changed}}</span></div></div>
 | 
			
		||||
                  <div class="col-md-2" ng-if="hostObject.skipped == true"><div class="btn" ng-class="{'btn-warning': hostObject.skipped == true}" style="padding: 2px 5px">Skipped <span class="badge">{{hostObject.skipped}}</span></div></div>
 | 
			
		||||
                  <div class="col-md-1"><div class="btn btn-default" ng-show="hostObject.module_stderr || hostObject.stdout || hostObject.stderr || hostObject.msg" ng-click="hostObject.showLogs = !hostObject.showLogs" style="padding: 2px 5px">Logs</div></div>
 | 
			
		||||
                  <div class="col-md-1"><div class="btn btn-warning" ng-show="hostObject.warnings.length" ng-click="hostObject.showWarnings = !hostObject.showWarnings" style="padding: 2px 5px">Warnings</div></div>
 | 
			
		||||
 | 
			
		||||
                  <div class="col-md-2"><div class="btn btn-danger" ng-show="hostObject.rc && hostObject.rc != 0">Return Code - {{hostObject.rc}}</div></div>
 | 
			
		||||
 | 
			
		||||
                  <div ng-show="hostObject.showLogs" class="col-md-12" ng-if="hostObject.stdout" ><p style="color:grey;" class="logconsole" ng-bind-html="hostObject.stdout | replaceLineBreaks"></p></div>
 | 
			
		||||
                  <div ng-show="hostObject.showLogs" class="col-md-12" ng-if="hostObject.msg" ><p style="color:grey;" class="logconsole" ng-bind-html="hostObject.msg | replaceLineBreaks"></p></div>
 | 
			
		||||
                  <div ng-show="hostObject.showLogs" class="col-md-12" ng-if="hostObject.stderr" ><p style="color:#cd616b;" class="logconsole" ng-bind-html="hostObject.stderr | replaceLineBreaks"></p></div>
 | 
			
		||||
                  <div ng-show="hostObject.showLogs" class="col-md-12" ng-if="hostObject.module_stderr" ><p style="color:#cd616b;" class="logconsole" ng-bind-html="hostObject.module_stderr | replaceLineBreaks"></p></div>
 | 
			
		||||
                  <div ng-show="hostObject.showWarnings" class="col-md-12" ng-if="hostObject.warnings.length" ><p style="color:orangered;" class="logconsole" ng-bind-html="hostObject.warnings.join('\n') | replaceLineBreaks"></p></div>
 | 
			
		||||
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
              </div>
 | 
			
		||||
            </uib-accordion>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      </div>-->
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="row" ng-if="!readOnly">
 | 
			
		||||
      <div class="col-md-3">
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
          <span class="input-group-addon" >Inventory Files</span>
 | 
			
		||||
          <!--<input ng-model="newPlay.hosts" type="text" class="form-control" placeholder="Playbook Name">-->
 | 
			
		||||
          <select class="form-control" ng-model="selectedInventoryFile.value" ng-options="inventoryFile as inventoryFile for inventoryFile in inventoryFiles" ng-disabled="!inventoryFiles">
 | 
			
		||||
          </select>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-md-9">
 | 
			
		||||
        <btn class="checkbox-inline"><input type="checkbox" ng-true-value="'Check'" ng-false-value="'No_Check'" ng-model="check_mode.value">Check Mode</btn>
 | 
			
		||||
        <btn class="checkbox-inline"><input type="checkbox" ng-true-value="'verbose'" ng-model="verbose.value">Verbose</btn>
 | 
			
		||||
        <btn class="checkbox-inline"><input type="checkbox" ng-true-value="'verbose_detail'" ng-model="verbose_detail.value">Verbose Detail</btn>
 | 
			
		||||
        <btn class="checkbox-inline"><input type="checkbox" ng-model="refreshLog">Refresh Logs</btn>
 | 
			
		||||
        <btn class="checkbox-inline"><input type="checkbox" ng-model="additional_tags.show">Tags</btn>
 | 
			
		||||
        <btn class="checkbox-inline"><input type="checkbox" ng-model="additional_tags.vars">Vars</btn>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="clearfix"></div>
 | 
			
		||||
      <div class="col-md-6" ng-show="additional_tags.show">
 | 
			
		||||
        <div class="table-responsive">
 | 
			
		||||
          <table class="table">
 | 
			
		||||
            <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <th>Select</th>
 | 
			
		||||
              <th>Tag</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
            <tr ng-repeat="tag in all_tags">
 | 
			
		||||
              <td><input type="checkbox" ng-model="tag.selected">
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>{{tag.name}}</td>
 | 
			
		||||
 | 
			
		||||
            </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="col-md-6" ng-show="additional_tags.show">
 | 
			
		||||
        <div class="table-responsive">
 | 
			
		||||
          <table class="table">
 | 
			
		||||
            <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <th>Select</th>
 | 
			
		||||
              <th>Host</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
            <tr ng-repeat="host in all_hosts">
 | 
			
		||||
              <td><input type="checkbox" ng-model="host.selected">
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>{{host.name}}</td>
 | 
			
		||||
 | 
			
		||||
            </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div ng-show="additional_vars.show">
 | 
			
		||||
        <complex-var members="complexVar" path="'Parent'" type="'object'" input-width="'400px'">
 | 
			
		||||
        </complex-var>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <button class="btn btn-success" type="button" ng-if="!readOnly" ng-disabled="!selectedInventoryFile.value || !readyForPlay" ng-click="Run()">Play <span class="fa fa-play" ng-if="!AnsiblePlayBookLoading"></span> <span class="fa fa-spinner fa-spin" ng-if="AnsiblePlayBookLoading"></span></button>
 | 
			
		||||
    <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										475
									
								
								client/app/designer/execution/execution.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								client/app/designer/execution/execution.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,475 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function executionController($scope,$sce, $uibModalInstance, $timeout, ansi2html, ansible, tags, selectedProject, selectedPlaybook, selectedPlay, executionType, executionName, readOnly, runData, projectFolder, roleName) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
  $scope.view = 'console';
 | 
			
		||||
  $scope.selectedInventoryFile = {value:null};
 | 
			
		||||
  $scope.verbose_detail = {value:null};
 | 
			
		||||
  $scope.verbose = {value:'verbose'};
 | 
			
		||||
  $scope.check_mode = {
 | 
			
		||||
    value: 'No_Check'
 | 
			
		||||
  };
 | 
			
		||||
  $scope.additional_tags = {show: false};
 | 
			
		||||
  $scope.additional_vars = {show: false};
 | 
			
		||||
  $scope.refreshLog = true;
 | 
			
		||||
  $scope.all_tags = [];
 | 
			
		||||
  $scope.all_hosts = [];
 | 
			
		||||
  $scope.readOnly = readOnly;
 | 
			
		||||
  $scope.readyForPlay = false;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Execute Ansible Playbook
 | 
			
		||||
   */
 | 
			
		||||
  $scope.executeAnsiblePlayBook = function(){
 | 
			
		||||
    $scope.AnsiblePlayBookLoading = true;
 | 
			
		||||
    var reqBody = {};
 | 
			
		||||
    //reqBody.inventory_file_contents = inventory_file_contents;
 | 
			
		||||
    //reqBody.playbook_file_contents = yaml;
 | 
			
		||||
    //reqBody.tags = tags || [];
 | 
			
		||||
    reqBody.tags = [];
 | 
			
		||||
 | 
			
		||||
    $scope.all_tags.map(tag => {
 | 
			
		||||
      if(tag.selected){
 | 
			
		||||
      if(tag.name)
 | 
			
		||||
        reqBody.tags.push(tag.name.trim())
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
    reqBody.limit_to_hosts = [];
 | 
			
		||||
 | 
			
		||||
    $scope.all_hosts.map(host => {
 | 
			
		||||
      if(host.selected){
 | 
			
		||||
      if(host.name)
 | 
			
		||||
        reqBody.limit_to_hosts.push(host.name.trim())
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
    reqBody.verbose = $scope.verbose_detail.value || $scope.verbose.value;
 | 
			
		||||
    reqBody.check_mode = $scope.check_mode.value;
 | 
			
		||||
 | 
			
		||||
    reqBody.inventory_file_name = $scope.selectedInventoryFile.value;
 | 
			
		||||
 | 
			
		||||
    if(roleName){
 | 
			
		||||
      reqBody.inventory_file_name = roleName + '/tests/' + reqBody.inventory_file_name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    console.log("Check Mode = " + reqBody.check_mode);
 | 
			
		||||
 | 
			
		||||
    reqBody.selectedPlaybook = selectedPlaybook.playbook;
 | 
			
		||||
    reqBody.executionType  = executionType;
 | 
			
		||||
    reqBody.executionName  = executionName;
 | 
			
		||||
 | 
			
		||||
    reqBody.ansibleEngine = angular.copy(selectedProject.ansibleEngine);
 | 
			
		||||
 | 
			
		||||
    // Override project folder for roles
 | 
			
		||||
    if(projectFolder)
 | 
			
		||||
      reqBody.ansibleEngine.projectFolder = projectFolder;
 | 
			
		||||
 | 
			
		||||
    if(selectedPlay && selectedPlay.play)
 | 
			
		||||
      reqBody.host = selectedPlay.play.hosts;
 | 
			
		||||
 | 
			
		||||
    $scope.result = "Running...";
 | 
			
		||||
 | 
			
		||||
    ansible.executeAnsiblePlayBook(reqBody,function(response){
 | 
			
		||||
      //$scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
 | 
			
		||||
      $scope.refreshLog = true;
 | 
			
		||||
      $scope.executionData = response.data;
 | 
			
		||||
 | 
			
		||||
      setTimeout(function(){
 | 
			
		||||
        $scope.refreshLogs();
 | 
			
		||||
      },3000);
 | 
			
		||||
 | 
			
		||||
    }, function(response){
 | 
			
		||||
      if(response.data)
 | 
			
		||||
        $scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
 | 
			
		||||
      $scope.AnsiblePlayBookLoading = false;
 | 
			
		||||
      console.log("error" + $scope.result)
 | 
			
		||||
    }, 'PlaybookExecutionModal');
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /*setTimeout(function(){
 | 
			
		||||
   $scope.executeAnsiblePlayBook();
 | 
			
		||||
   },200);*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get logs
 | 
			
		||||
   */
 | 
			
		||||
  $scope.getLogs = function(){
 | 
			
		||||
    ansible.getLogs($scope.executionData,function(successResponse) {
 | 
			
		||||
      $scope.result = $sce.trustAsHtml(ansi2html.toHtml(successResponse.data.replace('SCRIPT_FINISHED','')).replace(/\n/g, "<br>"));
 | 
			
		||||
 | 
			
		||||
      if(successResponse.data.indexOf('SCRIPT_FINISHED') > -1){
 | 
			
		||||
        $scope.refreshLog = false;
 | 
			
		||||
        $scope.AnsiblePlayBookLoading = false;
 | 
			
		||||
      }
 | 
			
		||||
      $scope.processAnsibleOutput(successResponse.data)
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Refersh Logs
 | 
			
		||||
   */
 | 
			
		||||
  $scope.refreshLogs = function(){
 | 
			
		||||
    if($scope.logRefreshTimer){
 | 
			
		||||
      $timeout.cancel( $scope.logRefreshTimer );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $scope.getLogs();
 | 
			
		||||
    $scope.logRefreshTimer = $timeout(
 | 
			
		||||
      function(){
 | 
			
		||||
        //$scope.getLogs(tile);
 | 
			
		||||
        if($scope.refreshLog) {
 | 
			
		||||
          $scope.refreshLogs();
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      10000
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    $scope.$on(
 | 
			
		||||
      "$destroy",
 | 
			
		||||
      function( event ) {
 | 
			
		||||
        $timeout.cancel( $scope.logRefreshTimer );
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Close the modal
 | 
			
		||||
   */
 | 
			
		||||
  $scope.ok = function () {
 | 
			
		||||
    $uibModalInstance.close(null);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Cancel modal
 | 
			
		||||
   */
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Run a random command on the server
 | 
			
		||||
   * TODO: Remove this later.
 | 
			
		||||
   * @param command
 | 
			
		||||
   */
 | 
			
		||||
  $scope.runCommand = function(command){
 | 
			
		||||
    command = command || $scope.command;
 | 
			
		||||
    ansible.executeCommand( command,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>").replace(/ /g," "));
 | 
			
		||||
 | 
			
		||||
      }, function(response){
 | 
			
		||||
        $scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
 | 
			
		||||
 | 
			
		||||
      })
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Run Ansible Playbook
 | 
			
		||||
   * @constructor
 | 
			
		||||
   */
 | 
			
		||||
  $scope.Run = function(){
 | 
			
		||||
    $scope.executeAnsiblePlayBook();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * This is used when viewing the logs from the Runs view
 | 
			
		||||
   */
 | 
			
		||||
  if($scope.readOnly){
 | 
			
		||||
    $scope.executionData = runData;
 | 
			
		||||
    $scope.refreshLog = true;
 | 
			
		||||
    $scope.refreshLogs()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get List of inventory files
 | 
			
		||||
   */
 | 
			
		||||
  $scope.listOfInventoryFiles = function(){
 | 
			
		||||
 | 
			
		||||
    var rolesTestFolder = null;
 | 
			
		||||
 | 
			
		||||
    if(roleName){
 | 
			
		||||
      rolesTestFolder = projectFolder + '/' + roleName + '/tests'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ansible.getInventoryList(function(response){
 | 
			
		||||
        $scope.inventoryFiles = response.data;
 | 
			
		||||
        if($scope.inventoryFiles.length)
 | 
			
		||||
          $scope.selectedInventoryFile = {value:$scope.inventoryFiles[0]};
 | 
			
		||||
        /**
 | 
			
		||||
         * Run Get Tags
 | 
			
		||||
         */
 | 
			
		||||
        if(!readOnly)
 | 
			
		||||
          $scope.getTags();
 | 
			
		||||
      },
 | 
			
		||||
      function(response){
 | 
			
		||||
        /*$scope.err_msg = response.data;*/
 | 
			
		||||
        $scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
 | 
			
		||||
        $scope.view = 'console'
 | 
			
		||||
      },rolesTestFolder)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.listOfInventoryFiles();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get List of Tags based on playbook and inventory file
 | 
			
		||||
   */
 | 
			
		||||
  $scope.getTags = function(){
 | 
			
		||||
    var inventory_file_name = $scope.selectedInventoryFile.value;
 | 
			
		||||
 | 
			
		||||
    if(roleName){
 | 
			
		||||
      inventory_file_name = roleName + '/tests/' + inventory_file_name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var selectedPlaybookName = selectedPlaybook.playbook;
 | 
			
		||||
 | 
			
		||||
    var ansibleEngine = angular.copy(selectedProject.ansibleEngine);
 | 
			
		||||
 | 
			
		||||
    // Override project folder for roles
 | 
			
		||||
    if(projectFolder)
 | 
			
		||||
      ansibleEngine.projectFolder = projectFolder;
 | 
			
		||||
 | 
			
		||||
    ansible.getTagList(selectedPlaybookName,inventory_file_name,ansibleEngine,
 | 
			
		||||
      function(response){
 | 
			
		||||
        console.log(response.data)
 | 
			
		||||
 | 
			
		||||
        /*var re = /TAGS: \[(.*)\]/g;
 | 
			
		||||
         var m;
 | 
			
		||||
 | 
			
		||||
         var all_tags = []
 | 
			
		||||
 | 
			
		||||
         while ((m = re.exec(response.data)) !== null) {
 | 
			
		||||
         if (m.index === re.lastIndex) {
 | 
			
		||||
         re.lastIndex++;
 | 
			
		||||
         }
 | 
			
		||||
         // View your result using the m-variable.
 | 
			
		||||
         // eg m[0] etc.
 | 
			
		||||
         if(m[1])
 | 
			
		||||
         all_tags.push(m[1])
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
         $scope.all_tags = all_tags.join(',').split(',');*/
 | 
			
		||||
 | 
			
		||||
        if(!response.data.playbooks)return null;
 | 
			
		||||
 | 
			
		||||
        var playbooks = response.data.playbooks;
 | 
			
		||||
        $scope.all_hosts = [];
 | 
			
		||||
        $scope.all_tags = [];
 | 
			
		||||
 | 
			
		||||
        angular.forEach(playbooks, playbook => {
 | 
			
		||||
          angular.forEach(playbook.plays, play => {
 | 
			
		||||
          $scope.all_hosts = $scope.all_hosts.concat(play.hosts);
 | 
			
		||||
        $scope.all_tags = $scope.all_tags.concat(play.tags);
 | 
			
		||||
        angular.forEach(play.tasks, task => {
 | 
			
		||||
          $scope.all_tags = $scope.all_tags.concat(task.tags);
 | 
			
		||||
      })
 | 
			
		||||
      })
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
        // Get Unique List of tags
 | 
			
		||||
        $scope.all_tags = Array.from(new Set($scope.all_tags));
 | 
			
		||||
 | 
			
		||||
        // Get Unique List of hosts
 | 
			
		||||
        $scope.all_hosts = Array.from(new Set($scope.all_hosts));
 | 
			
		||||
 | 
			
		||||
        $scope.all_hosts = $scope.all_hosts.map(host => {return {name:host,selected:false}});
 | 
			
		||||
 | 
			
		||||
        $scope.all_tags = $scope.all_tags.map(tag => {return {name:tag,selected:false}});
 | 
			
		||||
 | 
			
		||||
        if(tags){
 | 
			
		||||
          angular.forEach(tags, tag => {
 | 
			
		||||
            var tag_found = false;
 | 
			
		||||
          angular.forEach($scope.all_tags, (all_tag,index) => {
 | 
			
		||||
            if(tag == all_tag.name){
 | 
			
		||||
            tag_found = true;
 | 
			
		||||
            all_tag.selected = true
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
          if(!tag_found)
 | 
			
		||||
            $scope.all_tags.push({name:tag,selected:true})
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $scope.readyForPlay = true;
 | 
			
		||||
 | 
			
		||||
      },
 | 
			
		||||
      function(error){
 | 
			
		||||
        //console.log(error.data)
 | 
			
		||||
        //$scope.err_msg = error.data;
 | 
			
		||||
        $scope.result = $sce.trustAsHtml(ansi2html.toHtml(error.data).replace(/\n/g, "<br>"));
 | 
			
		||||
        $scope.view = 'console'
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Process Ansible Output and show graphically
 | 
			
		||||
   * @param ansibleOutput
 | 
			
		||||
   */
 | 
			
		||||
  $scope.processAnsibleOutput_old = function(ansibleOutput){
 | 
			
		||||
 | 
			
		||||
    $scope.ansibleOutputResult = [];
 | 
			
		||||
    //https://regex101.com/r/yD6lZ6/1
 | 
			
		||||
    //var re = /(PLAY|TASK) \[(.*)\] (.*)\n(.*?):([^]*?)(?=TASK|PLAY)/gm;
 | 
			
		||||
    //var re = /(PLAY|TASK) \[(.*)\] (.*)\n(?:(.*?)\s?(.*): \[(.*)\](.*)=> ([^]*?)(?=TASK|PLAY)|(.*?): \[(.*)\](.*)|(.*)|(?=TASK|PLAY))/gm
 | 
			
		||||
    //var re = /(PLAY|TASK) \[(.*)\] (.*)\n(?:(.*?)\s?(.*): \[(.*)\](.*)=> ([^]*?)(?=\n\n)|(.*?): \[(.*)\](.*)|(.*)|(?=\n\n))/gm;
 | 
			
		||||
    var re = /({[^]+})/g
 | 
			
		||||
    var m;
 | 
			
		||||
 | 
			
		||||
    while ((m = re.exec(ansibleOutput)) !== null) {
 | 
			
		||||
      if (m.index === re.lastIndex) {
 | 
			
		||||
        re.lastIndex++;
 | 
			
		||||
      }
 | 
			
		||||
      // View your result using the m-variable.
 | 
			
		||||
      // eg m[0] etc.
 | 
			
		||||
 | 
			
		||||
      var type = m[1]; //TASK,PLAY
 | 
			
		||||
      var name = m[2]; // ansible-role-vra : debug delete instance
 | 
			
		||||
      var status = m[5]; //ok, skipping, failed
 | 
			
		||||
      var host = m[6]; //localhost , localhost -> localhost
 | 
			
		||||
      var status_2 = m[7];
 | 
			
		||||
      var result = m[8];
 | 
			
		||||
 | 
			
		||||
      if(result){
 | 
			
		||||
        //var result_object_string = result.replace(/({[^]+})[^]+/,"$1");
 | 
			
		||||
        var result_object_string = result;
 | 
			
		||||
        var resultObject = null;
 | 
			
		||||
        try{
 | 
			
		||||
          resultObject = JSON.parse(result_object_string);
 | 
			
		||||
 | 
			
		||||
          //resultObject.formattedStdErr = resultObject.stderr.replace(/\\r\\n/g,'<br>').replace(/\\n/g, "<br>");
 | 
			
		||||
          resultObject.formattedStdErr = resultObject.stderr.replace(/\n/g,'<br>');
 | 
			
		||||
          resultObject.formattedStdOut = resultObject.stderr.replace(/\n/g,'<br>');
 | 
			
		||||
 | 
			
		||||
        }catch(e){
 | 
			
		||||
          console.log("Error converting ansible output result object to javascript")
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $scope.ansibleOutputResult.push({
 | 
			
		||||
        type:type,
 | 
			
		||||
        name:name,
 | 
			
		||||
        status:status,
 | 
			
		||||
        host:host,
 | 
			
		||||
        status_2:status_2,
 | 
			
		||||
        resultString:result,
 | 
			
		||||
        resultObject:resultObject
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  $scope.processAnsibleOutput = function(ansibleOutput){
 | 
			
		||||
 | 
			
		||||
    $scope.ansibleOutputResult = [];
 | 
			
		||||
    $scope.ansibleOutputObject = {
 | 
			
		||||
      'plays' : [],
 | 
			
		||||
      'stats' : {}
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    //var re = /(.*{[^]+}.*)/g;
 | 
			
		||||
    var re = /--------BEGIN--------([^]+?)--------END--------/gm;
 | 
			
		||||
    var m;
 | 
			
		||||
 | 
			
		||||
    while ((m = re.exec(ansibleOutput)) !== null) {
 | 
			
		||||
      if (m.index === re.lastIndex) {
 | 
			
		||||
        re.lastIndex++;
 | 
			
		||||
      }
 | 
			
		||||
      // View your result using the m-variable.
 | 
			
		||||
      // eg m[0] etc.
 | 
			
		||||
 | 
			
		||||
      try{
 | 
			
		||||
        //$scope.ansibleOutputObject = JSON.parse(m[1]);
 | 
			
		||||
        var resultItem = JSON.parse(m[1]);
 | 
			
		||||
        if('play' in resultItem){
 | 
			
		||||
          $scope.ansibleOutputObject.plays.push(resultItem);
 | 
			
		||||
        } else if('task' in resultItem){
 | 
			
		||||
 | 
			
		||||
          var current_play = $scope.ansibleOutputObject.plays[$scope.ansibleOutputObject.plays.length-1]
 | 
			
		||||
          var newTask = true;
 | 
			
		||||
          angular.forEach(current_play.tasks, (task, index)=>{
 | 
			
		||||
            if(task.task.id === resultItem.task.id){
 | 
			
		||||
            newTask = false;
 | 
			
		||||
            current_play.tasks[index] = resultItem
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
          if(newTask)
 | 
			
		||||
            current_play.tasks.push(resultItem);
 | 
			
		||||
 | 
			
		||||
        } else if('stats' in resultItem){
 | 
			
		||||
          $scope.ansibleOutputObject.stats = resultItem.stats;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      }catch(e){
 | 
			
		||||
        console.log("Error parsing ansible output" + e);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      //var plays = $scope.ansibleOutputObject.plays;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log($scope.ansibleOutputObject);
 | 
			
		||||
 | 
			
		||||
    /*while ((m = re.exec(ansibleOutput)) !== null) {
 | 
			
		||||
     if (m.index === re.lastIndex) {
 | 
			
		||||
     re.lastIndex++;
 | 
			
		||||
     }
 | 
			
		||||
     // View your result using the m-variable.
 | 
			
		||||
     // eg m[0] etc.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
     var type = m[1]; //TASK,PLAY
 | 
			
		||||
     var name = m[2]; // ansible-role-vra : debug delete instance
 | 
			
		||||
     var status = m[5]; //ok, skipping, failed
 | 
			
		||||
     var host = m[6]; //localhost , localhost -> localhost
 | 
			
		||||
     var status_2 = m[7];
 | 
			
		||||
     var result = m[8];
 | 
			
		||||
 | 
			
		||||
     if(result){
 | 
			
		||||
     //var result_object_string = result.replace(/({[^]+})[^]+/,"$1");
 | 
			
		||||
     var result_object_string = result;
 | 
			
		||||
     var resultObject = null;
 | 
			
		||||
     try{
 | 
			
		||||
     resultObject = JSON.parse(result_object_string);
 | 
			
		||||
 | 
			
		||||
     //resultObject.formattedStdErr = resultObject.stderr.replace(/\\r\\n/g,'<br>').replace(/\\n/g, "<br>");
 | 
			
		||||
     resultObject.formattedStdErr = resultObject.stderr.replace(/\n/g,'<br>');
 | 
			
		||||
     resultObject.formattedStdOut = resultObject.stderr.replace(/\n/g,'<br>');
 | 
			
		||||
 | 
			
		||||
     }catch(e){
 | 
			
		||||
     console.log("Error converting ansible output result object to javascript")
 | 
			
		||||
     }
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     $scope.ansibleOutputResult.push({
 | 
			
		||||
     type:type,
 | 
			
		||||
     name:name,
 | 
			
		||||
     status:status,
 | 
			
		||||
     host:host,
 | 
			
		||||
     status_2:status_2,
 | 
			
		||||
     resultString:result,
 | 
			
		||||
     resultObject:resultObject
 | 
			
		||||
     })
 | 
			
		||||
 | 
			
		||||
     }*/
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.execution', [])
 | 
			
		||||
  .controller('ExecutionController', executionController)
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										17
									
								
								client/app/designer/execution/execution.controller.spec.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								client/app/designer/execution/execution.controller.spec.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: ExecutionCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.execution'));
 | 
			
		||||
 | 
			
		||||
  var ExecutionCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    ExecutionCtrl = $controller('ExecutionCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										104
									
								
								client/app/designer/file_browser/file_browser.component.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								client/app/designer/file_browser/file_browser.component.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
const uiRouter = require('angular-ui-router');
 | 
			
		||||
 | 
			
		||||
import routes from './file_browser.routes';
 | 
			
		||||
 | 
			
		||||
export class FileBrowserComponent {
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor($scope,ansible,editor) {
 | 
			
		||||
    'ngInject';
 | 
			
		||||
    $scope.treeOptions = {
 | 
			
		||||
      nodeChildren: "children",
 | 
			
		||||
      dirSelectable: true,
 | 
			
		||||
      isLeaf: function (node) {
 | 
			
		||||
        return !(node.type === 'directory');
 | 
			
		||||
      },
 | 
			
		||||
      injectClasses: {
 | 
			
		||||
        ul: "a1",
 | 
			
		||||
        li: "a2",
 | 
			
		||||
        liSelected: "a7",
 | 
			
		||||
        iExpanded: "a3",
 | 
			
		||||
        iCollapsed: "a4",
 | 
			
		||||
        iLeaf: "a5",
 | 
			
		||||
        label: "a6",
 | 
			
		||||
        labelSelected: "a8"
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.editContent = false;
 | 
			
		||||
    $scope.selectedFile = {showSource: true};
 | 
			
		||||
 | 
			
		||||
    var loadProjectFiles = function(){
 | 
			
		||||
      ansible.getProjectFiles(function(response){
 | 
			
		||||
        $scope.projectFiles = response.data;
 | 
			
		||||
      },function(error){
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.$on('projectLoaded',function(){
 | 
			
		||||
      loadProjectFiles();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if($scope.$parent.selectedProject && $scope.$parent.selectedProject.ansibleEngine) {
 | 
			
		||||
      loadProjectFiles();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show selected item in the tree
 | 
			
		||||
     * @param file
 | 
			
		||||
     * @param parent
 | 
			
		||||
     */
 | 
			
		||||
    $scope.showSelected = function (file, parent) {
 | 
			
		||||
 | 
			
		||||
      if (file.children) {
 | 
			
		||||
        $scope.selectedFile.content = JSON.stringify(file, null, '\t');
 | 
			
		||||
        $scope.docType = 'json';
 | 
			
		||||
        $scope.selectedFile.tasks = null;
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      var command = 'cat "' + file.path + '"';
 | 
			
		||||
      $scope.showSource = true;
 | 
			
		||||
      $scope.markdownContent = '';
 | 
			
		||||
      $scope.docType = 'text';
 | 
			
		||||
      $scope.selectedFile.content = 'Loading..';
 | 
			
		||||
      $scope.selectedFile.tasks = null;
 | 
			
		||||
      $scope.selectedFileName = file.name;
 | 
			
		||||
      $scope.selectedFilePath = file.path;
 | 
			
		||||
      $scope.parentNode = parent;
 | 
			
		||||
 | 
			
		||||
      ansible.executeCommand(command,
 | 
			
		||||
        function (response) {
 | 
			
		||||
          console.log(response.data)
 | 
			
		||||
          editor.setContentAndType(response.data, file, $scope.selectedFile);
 | 
			
		||||
 | 
			
		||||
          var parentDirectory = file.path.replace(/^(.+)\/(.+)\/([^/]+)$/, "$2");
 | 
			
		||||
          if (parentDirectory == 'tasks') {
 | 
			
		||||
            $scope.selectedFile.tasks = YAML.parse(response.data) || [];
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (parentDirectory == 'group_vars' || parentDirectory == 'host_vars') {
 | 
			
		||||
            $scope.selectedFile.docType = 'yaml';
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          //$scope.selectedFile.content = response.data;
 | 
			
		||||
 | 
			
		||||
        }, function (response) {
 | 
			
		||||
          $scope.selectedFile.content = response.data;
 | 
			
		||||
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.file_browser', [uiRouter])
 | 
			
		||||
  .config(routes)
 | 
			
		||||
  .component('fileBrowser', {
 | 
			
		||||
    template: require('./file_browser.html'),
 | 
			
		||||
    controller: FileBrowserComponent,
 | 
			
		||||
    controllerAs: 'fileBrowserCtrl'
 | 
			
		||||
  })
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Component: FileBrowserComponent', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.file_browser'));
 | 
			
		||||
 | 
			
		||||
  var FileBrowserComponent;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($componentController) {
 | 
			
		||||
    FileBrowserComponent = $componentController('file_browser', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										33
									
								
								client/app/designer/file_browser/file_browser.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								client/app/designer/file_browser/file_browser.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
 | 
			
		||||
<div class="row">
 | 
			
		||||
  <h3>File Browser</h3>
 | 
			
		||||
  <div class="col-md-6">
 | 
			
		||||
    <treecontrol ng-show="projectFiles.children" class="tree-classic"
 | 
			
		||||
                 tree-model="projectFiles.children"
 | 
			
		||||
                 options="treeOptions"
 | 
			
		||||
                 on-selection="showSelected(node, $parentNode)"
 | 
			
		||||
                 selected-node="selectedFile"
 | 
			
		||||
                 filter-expression="{name: '!.git'}">
 | 
			
		||||
      {{node.name}}
 | 
			
		||||
    </treecontrol>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="col-md-6">
 | 
			
		||||
    <button class="btn btn-default" ng-show="selectedFile.docType == 'markdown' && !selectedFile.showSource" ng-click="selectedFile.showSource = true">
 | 
			
		||||
      Show Source
 | 
			
		||||
    </button>
 | 
			
		||||
    <button class="btn btn-default" ng-show="selectedFile.docType == 'markdown' && selectedFile.showSource" ng-click="selectedFile.showSource = false">
 | 
			
		||||
      Hide Source
 | 
			
		||||
    </button>
 | 
			
		||||
    <!--{{docType}}-->
 | 
			
		||||
    <!--{{selectedFile.showSource}}-->
 | 
			
		||||
    <div ng-show="selectedFile.showSource" ng-readonly="!editContent"
 | 
			
		||||
         ui-ace="{theme:'twilight',document:selectedFile.docType,mode:selectedFile.docType,onChange:codeChanged,onLoad:aceLoaded}" ng-model="selectedFile.content">
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div ng-show="!selectedFile.showSource" btf-markdown="selectedFile.markdownContent">
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										10
									
								
								client/app/designer/file_browser/file_browser.routes.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/app/designer/file_browser/file_browser.routes.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default function($stateProvider) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
  $stateProvider
 | 
			
		||||
    .state('designer.file_browser', {
 | 
			
		||||
      url: '/file_browser',
 | 
			
		||||
      template: '<file-browser></file-browser>'
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										278
									
								
								client/app/designer/inventory/inventory.component.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								client/app/designer/inventory/inventory.component.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,278 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
const uiRouter = require('angular-ui-router');
 | 
			
		||||
 | 
			
		||||
import routes from './inventory.routes';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export class InventoryComponent {
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor($scope, $uibModal, ansible) {
 | 
			
		||||
 | 
			
		||||
    'ngInject';
 | 
			
		||||
    $scope.selectedInventory = {inventory: "", content: ""};
 | 
			
		||||
 | 
			
		||||
    $scope.editInventory = {value: false};
 | 
			
		||||
    $scope.selectedGroup = {group: null};
 | 
			
		||||
    $scope.selectedHost = {host: null};
 | 
			
		||||
 | 
			
		||||
    $scope.complexVar = {};
 | 
			
		||||
 | 
			
		||||
    $scope.$on('projectLoaded', function () {
 | 
			
		||||
      $scope.getInventorys()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    //To fix a warning message in console
 | 
			
		||||
    $scope.aceLoaded = function(_editor){
 | 
			
		||||
      _editor.$blockScrolling = Infinity;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // --------------------------------------- PLAYBOOKS ----------------
 | 
			
		||||
 | 
			
		||||
    $scope.getInventorys = function () {
 | 
			
		||||
      ansible.getInventoryList(
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.inventorys = response.data;
 | 
			
		||||
        },
 | 
			
		||||
        function (response) {
 | 
			
		||||
          console.log(response.data)
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if ($scope.$parent.selectedProject && $scope.$parent.selectedProject.ansibleEngine) {
 | 
			
		||||
      $scope.getInventorys()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $scope.loadingModuleCode = false;
 | 
			
		||||
 | 
			
		||||
    $scope.showInventoryCode = function (inventory_name) {
 | 
			
		||||
      $scope.loadingModuleCode = true;
 | 
			
		||||
      if (!inventory_name) {
 | 
			
		||||
        $scope.selectedInventory.content = "Select a module";
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ansible.readInventory(inventory_name, function (response) {
 | 
			
		||||
        $scope.loadingModuleCode = false;
 | 
			
		||||
        $scope.selectedInventory.content = response.data.split("Stream :: close")[0];
 | 
			
		||||
 | 
			
		||||
        $scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content);
 | 
			
		||||
        $scope.inventory_data_json['name'] = inventory_name
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.$watch('selectedInventory.inventory', function (newValue, oldValue) {
 | 
			
		||||
      if (newValue && newValue !== oldValue) {
 | 
			
		||||
        $scope.selectedInventory.content = "Loading Code...";
 | 
			
		||||
        $scope.showInventoryCode(newValue)
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $scope.showCreatInventoryModal = function () {
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/inventory/new_inventory/new_inventory.html',
 | 
			
		||||
        controller: 'NewInventoryController',
 | 
			
		||||
        size: 'md',
 | 
			
		||||
        backdrop: 'static',
 | 
			
		||||
        keyboard: false,
 | 
			
		||||
        closeByEscape: false,
 | 
			
		||||
        closeByDocument: false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          selectedProject: function () {
 | 
			
		||||
            return $scope.$parent.selectedProject
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      modalInstance.result.then(function () {
 | 
			
		||||
        $scope.getInventorys();
 | 
			
		||||
      }, function () {
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.editGroup = function (group) {
 | 
			
		||||
      $scope.showCreateGroupModal(group);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.showCreateGroupModal = function (editGroup) {
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/inventory/new_group/new_group.html',
 | 
			
		||||
        controller: 'NewGroupController',
 | 
			
		||||
        size: 'lg',
 | 
			
		||||
        backdrop: 'static',
 | 
			
		||||
        keyboard: false,
 | 
			
		||||
        closeByEscape: false,
 | 
			
		||||
        closeByDocument: false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          selectedProject: function () {
 | 
			
		||||
            return $scope.$parent.selectedProject
 | 
			
		||||
          },
 | 
			
		||||
 | 
			
		||||
          editGroup: function () {
 | 
			
		||||
            return editGroup
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      modalInstance.result.then(function (group) {
 | 
			
		||||
        if(!editGroup)$scope.addGroup(group);
 | 
			
		||||
        else $scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
 | 
			
		||||
 | 
			
		||||
        $scope.saveInventory();
 | 
			
		||||
      }, function () {
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.editHost = function (host) {
 | 
			
		||||
 | 
			
		||||
      var hostMemberOfGroups = getHostMemberOfGroups(host);
 | 
			
		||||
 | 
			
		||||
      $scope.showCreateHostModal({name: host, members: hostMemberOfGroups.join(',')});
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.showCreateHostModal = function (editHost) {
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/inventory/new_host/new_host.html',
 | 
			
		||||
        controller: 'NewHostController',
 | 
			
		||||
        size: 'lg',
 | 
			
		||||
        backdrop: 'static',
 | 
			
		||||
        keyboard: false,
 | 
			
		||||
        closeByEscape: false,
 | 
			
		||||
        closeByDocument: false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          selectedProject: function () {
 | 
			
		||||
            return $scope.$parent.selectedProject
 | 
			
		||||
          },
 | 
			
		||||
          editHost: function () {
 | 
			
		||||
            return editHost
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      modalInstance.result.then(function (host) {
 | 
			
		||||
        $scope.addHost(host);
 | 
			
		||||
        $scope.saveInventory();
 | 
			
		||||
      }, function () {
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.saveInventory = function () {
 | 
			
		||||
      $scope.saveInventoryLoading = true;
 | 
			
		||||
      ansible.createInventory($scope.selectedInventory.inventory, $scope.selectedInventory.content,
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.saveInventoryLoading = false;
 | 
			
		||||
          $scope.editInventory.value = false;
 | 
			
		||||
        },
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.saveInventoryLoading = false;
 | 
			
		||||
          $scope.err_msg = response.data;
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.deleteInventory = function () {
 | 
			
		||||
      $scope.deleteInventoryLoading = true;
 | 
			
		||||
      ansible.deleteInventory($scope.selectedInventory.inventory,
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.deleteInventoryLoading = false;
 | 
			
		||||
          $scope.selectedInventory.inventory = "";
 | 
			
		||||
          $scope.getInventorys();
 | 
			
		||||
        },
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.deleteInventoryLoading = false;
 | 
			
		||||
          $scope.err_msg = response.data;
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.addGroup = function (group) {
 | 
			
		||||
 | 
			
		||||
      $scope.inventory_data_json.groups.push(group);
 | 
			
		||||
      $scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
 | 
			
		||||
      // To refresh All Hosts list
 | 
			
		||||
      $scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.addHost = function (host) {
 | 
			
		||||
      if ($scope.inventory_data_json.hosts.indexOf(host.name) < 0)
 | 
			
		||||
        $scope.inventory_data_json.hosts.push(host.name);
 | 
			
		||||
 | 
			
		||||
      var host_member_of_groups = host.members.split(',');
 | 
			
		||||
 | 
			
		||||
      angular.forEach($scope.inventory_data_json.groups, function (group) {
 | 
			
		||||
        if ((host_member_of_groups.indexOf(group.name)) > -1 && group.members.indexOf(host.name) < 0) {
 | 
			
		||||
          group.members.push(host.name)
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      $scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
 | 
			
		||||
      // To refresh All Hosts list
 | 
			
		||||
      $scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $scope.deleteGroup = function (index) {
 | 
			
		||||
      $scope.inventory_data_json.groups.splice(index, 1);
 | 
			
		||||
      $scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
 | 
			
		||||
      // To refresh All Hosts list
 | 
			
		||||
      $scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content);
 | 
			
		||||
 | 
			
		||||
      $scope.saveInventory();
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.deleteHost = function (index, group) {
 | 
			
		||||
 | 
			
		||||
      var hostname = $scope.inventory_data_json.hosts[index];
 | 
			
		||||
 | 
			
		||||
      $scope.inventory_data_json.hosts.splice(index, 1);
 | 
			
		||||
 | 
			
		||||
      angular.forEach($scope.inventory_data_json.groups, function (group) {
 | 
			
		||||
        var memberIndex = group.members.indexOf(hostname)
 | 
			
		||||
        if (memberIndex > -1) {
 | 
			
		||||
          group.members.splice(memberIndex, 1)
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      $scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
 | 
			
		||||
      // To refresh All Hosts list
 | 
			
		||||
      $scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content);
 | 
			
		||||
 | 
			
		||||
      $scope.saveInventory();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var getHostMemberOfGroups = function (host) {
 | 
			
		||||
      var groups = [];
 | 
			
		||||
      angular.forEach($scope.inventory_data_json.groups, function (group) {
 | 
			
		||||
        var memberIndex = group.members.indexOf(host);
 | 
			
		||||
        if (memberIndex > -1) {
 | 
			
		||||
          groups.push(group.name)
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      return groups;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.inventory', [uiRouter])
 | 
			
		||||
  .config(routes)
 | 
			
		||||
  .component('inventory', {
 | 
			
		||||
    template: require('./inventory.html'),
 | 
			
		||||
    controller: InventoryComponent,
 | 
			
		||||
    controllerAs: 'inventoryCtrl'
 | 
			
		||||
  })
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										17
									
								
								client/app/designer/inventory/inventory.component.spec.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								client/app/designer/inventory/inventory.component.spec.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Component: InventoryComponent', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.inventory'));
 | 
			
		||||
 | 
			
		||||
  var InventoryComponent;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($componentController) {
 | 
			
		||||
    InventoryComponent = $componentController('inventory', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										0
									
								
								client/app/designer/inventory/inventory.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								client/app/designer/inventory/inventory.css
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										154
									
								
								client/app/designer/inventory/inventory.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								client/app/designer/inventory/inventory.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,154 @@
 | 
			
		|||
<div class="col-md-8">
 | 
			
		||||
  <button class="btn btn-default" ng-click="showCreatInventoryModal()">Create Inventory <span class="fa fa-plus"></span></button>
 | 
			
		||||
  <!--<button class="btn btn-default" ng-if="!editInventory.value" ng-click="editInventory.value = true">Edit <span ng-if="!saveInventoryLoading"  class="fa fa-edit"></span></button>
 | 
			
		||||
  <button class="btn btn-primary" ng-if="editInventory.value" ng-disabled="!selectedInventory.inventory" ng-click="saveInventory()">Save <span ng-if="!saveInventoryLoading"  class="fa fa-save"></span><span ng-if="saveInventoryLoading" class="fa fa-spinner fa-spin"></span></button>-->
 | 
			
		||||
  <button class="btn btn-danger" confirm="Are you sure you want to delete inventory? " ng-disabled="!selectedInventory.inventory" ng-click="deleteInventory()">Delete <span ng-if="!deleteInventoryLoading"  class="fa fa-trash-o"></span><span ng-if="deleteInventoryLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
  <div class="table-responsive">
 | 
			
		||||
    <table class="table">
 | 
			
		||||
      <thead>
 | 
			
		||||
      <tr>
 | 
			
		||||
        <th>Select</th>
 | 
			
		||||
        <th>Name</th>
 | 
			
		||||
      </tr>
 | 
			
		||||
      </thead>
 | 
			
		||||
      <tbody>
 | 
			
		||||
      <tr ng-repeat="inventory in inventorys">
 | 
			
		||||
        <td><input name="playGroup" type="radio" ng-model="selectedInventory.inventory" ng-value="inventory">
 | 
			
		||||
        </td>
 | 
			
		||||
        <td>{{inventory}}</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
      </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-md-6">
 | 
			
		||||
 | 
			
		||||
      <div class="panel">
 | 
			
		||||
        <div class="panel panel-default">
 | 
			
		||||
          <div class="panel-heading">Groups - {{inventory_data_json.groups.length}}</div>
 | 
			
		||||
          <div class="panel-body">
 | 
			
		||||
            <button class="btn btn-default" ng-disabled="!selectedInventory.inventory" ng-click="showCreateGroupModal()">Create Group <span class="fa fa-plus"></span></button>
 | 
			
		||||
            <div class="table-responsive">
 | 
			
		||||
              <table class="table">
 | 
			
		||||
                <thead>
 | 
			
		||||
                <tr>
 | 
			
		||||
                  <th>Select</th>
 | 
			
		||||
                  <th>Name</th>
 | 
			
		||||
                  <th>Members</th>
 | 
			
		||||
                  <th>Actions</th>
 | 
			
		||||
                </tr>
 | 
			
		||||
                </thead>
 | 
			
		||||
                <tbody>
 | 
			
		||||
                <tr ng-repeat="group in inventory_data_json.groups">
 | 
			
		||||
                  <td><input name="groupsRadioGroup" type="radio" ng-model="selectedGroup.group" ng-value="group">
 | 
			
		||||
                  </td>
 | 
			
		||||
                  <td>{{group.name}}</td>
 | 
			
		||||
                  <td>{{group.members.length}}</td>
 | 
			
		||||
                  <td>
 | 
			
		||||
                    <div class="btn-group">
 | 
			
		||||
                      <label ng-show="group.type == 'userdefined'" class="btn btn-default btn-xs" ng-click="editGroup(group)" ><span class="fa fa-edit"></span></label>
 | 
			
		||||
                      <label ng-show="group.type == 'userdefined'" class="btn btn-danger btn-xs" confirm="Are you sure you want to delete this group?" ng-click="deleteGroup($index)" ><span class="fa fa-trash-o"></span></label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                </tbody>
 | 
			
		||||
              </table>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="panel" ng-show="selectedGroup.group.members">
 | 
			
		||||
        <div class="panel panel-default">
 | 
			
		||||
          <div class="panel-heading">Group Members - {{selectedGroup.group.name}}</div>
 | 
			
		||||
          <div class="panel-body">
 | 
			
		||||
 | 
			
		||||
            <div class="table-responsive" >
 | 
			
		||||
              <table class="table">
 | 
			
		||||
                <thead>
 | 
			
		||||
                <tr>
 | 
			
		||||
                  <th>Select</th>
 | 
			
		||||
                  <th>Name</th>
 | 
			
		||||
                  <th>Actions</th>
 | 
			
		||||
 | 
			
		||||
                </tr>
 | 
			
		||||
                </thead>
 | 
			
		||||
                <tbody>
 | 
			
		||||
                <tr ng-repeat="host in selectedGroup.group.members">
 | 
			
		||||
                  <td><input name="hostsRadioGroup" type="radio" ng-model="selectedHost.host" ng-value="host">
 | 
			
		||||
                  </td>
 | 
			
		||||
                  <td>{{host}}</td>
 | 
			
		||||
                  <td>
 | 
			
		||||
                    <div class="btn-group">
 | 
			
		||||
                      <label class="btn btn-danger btn-xs" confirm="Are you sure you want to delete host and remove it from all groups?" ng-click="deleteHost($index)" ><span class="fa fa-trash-o"></span></label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                </tbody>
 | 
			
		||||
              </table>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="col-md-6">
 | 
			
		||||
      <div class="panel">
 | 
			
		||||
        <div class="panel panel-default">
 | 
			
		||||
          <div class="panel-heading">Hosts - {{inventory_data_json.hosts.length}}</div>
 | 
			
		||||
          <div class="panel-body">
 | 
			
		||||
            <button class="btn btn-default" ng-disabled="!selectedInventory.inventory" ng-click="showCreateHostModal()">Create Host <span class="fa fa-plus"></span></button>
 | 
			
		||||
            <div class="table-responsive">
 | 
			
		||||
              <table class="table">
 | 
			
		||||
                <thead>
 | 
			
		||||
                <tr>
 | 
			
		||||
                  <th>Select</th>
 | 
			
		||||
                  <th>Name</th>
 | 
			
		||||
                  <th>Actions</th>
 | 
			
		||||
 | 
			
		||||
                </tr>
 | 
			
		||||
                </thead>
 | 
			
		||||
                <tbody>
 | 
			
		||||
                <tr ng-repeat="host in inventory_data_json.hosts">
 | 
			
		||||
                  <td><input name="hostsRadioGroup" type="radio" ng-model="selectedHost.host" ng-value="host">
 | 
			
		||||
                  </td>
 | 
			
		||||
                  <td>{{host}}</td>
 | 
			
		||||
                  <td>
 | 
			
		||||
                    <div class="btn-group">
 | 
			
		||||
                      <label class="btn btn-default btn-xs" ng-click="editHost(host)" ><span class="fa fa-edit"></span></label>
 | 
			
		||||
                      <label class="btn btn-danger btn-xs" confirm="Are you sure you want to delete host and remove it from all groups?" ng-click="deleteHost($index)" ><span class="fa fa-trash-o"></span></label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                </tbody>
 | 
			
		||||
              </table>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-md-6">
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="col-md-4">
 | 
			
		||||
 | 
			
		||||
  <div ng-readonly="!editInventory.value" ui-ace="{theme:'twilight',document:'INI',mode:'ini',onChange:codeChanged,onLoad:aceLoaded}" ng-model="selectedInventory.content">
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										10
									
								
								client/app/designer/inventory/inventory.routes.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/app/designer/inventory/inventory.routes.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default function($stateProvider) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
  $stateProvider
 | 
			
		||||
    .state('designer.inventory', {
 | 
			
		||||
      url: '/inventory',
 | 
			
		||||
      template: '<inventory></inventory>'
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function newGroupController($scope,$uibModalInstance, yamlFile, ansible, selectedProject, editGroup, YAML, $timeout) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
  $scope.newGroup = editGroup || {name:null};
 | 
			
		||||
  $scope.variableViewType = {type:'YAML'};
 | 
			
		||||
 | 
			
		||||
  $scope.complexVar = {};
 | 
			
		||||
  $scope.complexVarString = {};
 | 
			
		||||
 | 
			
		||||
  console.log("$scope.newGroup.name" + $scope.newGroup.name);
 | 
			
		||||
 | 
			
		||||
  $scope.aceLoaded = function (_editor) {
 | 
			
		||||
    _editor.$blockScrolling = Infinity;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if($scope.newGroup.members){
 | 
			
		||||
    $scope.newGroup.members = $scope.newGroup.members.join(',');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if($scope.newGroup.name){
 | 
			
		||||
    $scope.getGroupLoading = true;
 | 
			
		||||
    ansible.getGroupVarsFile($scope.newGroup.name,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.getGroupLoading = false;
 | 
			
		||||
        $scope.complexVarStringYaml = response.data;
 | 
			
		||||
        $scope.complexVar = YAML.parse(response.data);
 | 
			
		||||
 | 
			
		||||
        $timeout(function(){
 | 
			
		||||
          $scope.$broadcast('membersUpdated')
 | 
			
		||||
        },100);
 | 
			
		||||
 | 
			
		||||
      },function(response){
 | 
			
		||||
        $scope.getGroupLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data;
 | 
			
		||||
      })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $scope.$watch('complexVar',function(){
 | 
			
		||||
    $scope.complexVarString = JSON.stringify($scope.complexVar, null, '\t');
 | 
			
		||||
    $scope.complexVarStringYaml = yamlFile.jsonToYamlFile($scope.complexVar, 'Group Variables - ' + $scope.newGroup.name);
 | 
			
		||||
  }, true);
 | 
			
		||||
 | 
			
		||||
  $scope.$watch('newGroup',function(){
 | 
			
		||||
    $scope.complexVarString = JSON.stringify($scope.complexVar, null, '\t');
 | 
			
		||||
    $scope.complexVarStringYaml = yamlFile.jsonToYamlFile($scope.complexVar, 'Group Variables - ' + $scope.newGroup.name);
 | 
			
		||||
  }, true);
 | 
			
		||||
 | 
			
		||||
  $scope.createGroup = function(){
 | 
			
		||||
    $scope.createGroupLoading = true;
 | 
			
		||||
    ansible.updateGroupVarsFile($scope.newGroup.name,$scope.complexVarStringYaml,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createGroupLoading = false;
 | 
			
		||||
        console.log("Success");
 | 
			
		||||
        $scope.ok()
 | 
			
		||||
      },function(response){
 | 
			
		||||
        $scope.createGroupLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data;
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.ok = function () {
 | 
			
		||||
    var resultGroup = {name:$scope.newGroup.name};
 | 
			
		||||
 | 
			
		||||
    if($scope.newGroup.members){
 | 
			
		||||
      resultGroup.members = $scope.newGroup.members.split(',');
 | 
			
		||||
      $scope.newGroup.members = $scope.newGroup.members.split(',')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $uibModalInstance.close(resultGroup);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.new_group', [])
 | 
			
		||||
  .controller('NewGroupController', newGroupController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: NewGroupCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.new_group'));
 | 
			
		||||
 | 
			
		||||
  var NewGroupCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    NewGroupCtrl = $controller('NewGroupCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										45
									
								
								client/app/designer/inventory/new_group/new_group.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								client/app/designer/inventory/new_group/new_group.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <h4 class="modal-title">New Group</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body">
 | 
			
		||||
 | 
			
		||||
    <p class="form-group">
 | 
			
		||||
      <label>Group Name</label>
 | 
			
		||||
      <input type="text" ng-model="newGroup.name" class="form-control">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <p class="form-group">
 | 
			
		||||
      <label>Group Members</label>
 | 
			
		||||
      <input type="text" ng-model="newGroup.members" class="form-control" placeholder="Hosts list separated by coma">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <label> Group Variables </label>
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div class="col-md-6">
 | 
			
		||||
        <complex-var members="complexVar" path="'Parent'" type="'object'" input-width="'400px'">
 | 
			
		||||
 | 
			
		||||
        </complex-var>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="col-md-6">
 | 
			
		||||
        <input name="typeRadioGroup" type="radio" ng-model="variableViewType.type" ng-value="'JSON'"> JSON
 | 
			
		||||
        <input name="typeRadioGroup" type="radio" ng-model="variableViewType.type" ng-value="'YAML'"> YAML
 | 
			
		||||
        <div ng-readonly="true" ui-ace="{th1eme:'twilight',document:'yaml',mode:'yaml',onChange:codeChanged,onLoad:aceLoaded}" ng-show="variableViewType.type == 'YAML'" ng-model="complexVarStringYaml" style="max-height: 200px;">
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
        <div ng-readonly="true" ui-ace="{th1eme:'twilight',document:'json',mode:'json',onChange:codeChanged,onLoad:aceLoaded}" ng-show="variableViewType.type == 'JSON'"  ng-model="complexVarString" style="max-height: 200px;">
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <button class="btn btn-primary" ng-disabled="!newGroup.name" ng-click="createGroup()">Create <span ng-if="createGroupLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
    <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function newHostController($scope,$uibModalInstance, yamlFile, ansible, selectedProject, editHost, YAML, $timeout) {
 | 
			
		||||
  $scope.newHost = editHost || {name:null};
 | 
			
		||||
 | 
			
		||||
  $scope.variableViewType = {type:'YAML'};
 | 
			
		||||
 | 
			
		||||
  $scope.complexVar = {};
 | 
			
		||||
  $scope.complexVarString = {};
 | 
			
		||||
 | 
			
		||||
  if($scope.newHost.name){
 | 
			
		||||
    $scope.getHostLoading = true;
 | 
			
		||||
    ansible.getHostVarsFile($scope.newHost.name,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.getHostLoading = false;
 | 
			
		||||
        $scope.complexVarStringYaml = response.data;
 | 
			
		||||
        $scope.complexVar = YAML.parse(response.data);
 | 
			
		||||
        $timeout(function(){
 | 
			
		||||
          $scope.$broadcast('membersUpdated')
 | 
			
		||||
        },100);
 | 
			
		||||
 | 
			
		||||
      },function(response){
 | 
			
		||||
        $scope.getHostLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data;
 | 
			
		||||
      })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $scope.$watch('complexVar',function(){
 | 
			
		||||
    $scope.complexVarString = JSON.stringify($scope.complexVar, null, '\t');
 | 
			
		||||
    $scope.complexVarStringYaml = yamlFile.jsonToYamlFile($scope.complexVar, 'Host Variables - ' + $scope.newHost.name);
 | 
			
		||||
  }, true);
 | 
			
		||||
 | 
			
		||||
  $scope.$watch('newHost',function(){
 | 
			
		||||
    $scope.complexVarString = JSON.stringify($scope.complexVar, null, '\t');
 | 
			
		||||
    $scope.complexVarStringYaml = yamlFile.jsonToYamlFile($scope.complexVar, 'Host Variables - ' + $scope.newHost.name);
 | 
			
		||||
  }, true);
 | 
			
		||||
 | 
			
		||||
  $scope.aceLoaded = function(_editor){
 | 
			
		||||
    _editor.$blockScrolling = Infinity;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.createHost = function(){
 | 
			
		||||
    $scope.createHostLoading = true;
 | 
			
		||||
    ansible.updateHostVarsFile($scope.newHost.name,$scope.complexVarStringYaml,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createHostLoading = false;
 | 
			
		||||
        console.log("Success");
 | 
			
		||||
        $scope.ok()
 | 
			
		||||
      },function(response){
 | 
			
		||||
        $scope.createHostLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data;
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.ok = function () {
 | 
			
		||||
    if(!$scope.newHost.members)$scope.newHost.members = 'Un grouped';
 | 
			
		||||
    $uibModalInstance.close($scope.newHost);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.new_host', [])
 | 
			
		||||
  .controller('NewHostController', newHostController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: NewHostCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.new_host'));
 | 
			
		||||
 | 
			
		||||
  var NewHostCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    NewHostCtrl = $controller('NewHostCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										46
									
								
								client/app/designer/inventory/new_host/new_host.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								client/app/designer/inventory/new_host/new_host.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <h4 class="modal-title">New Host</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body">
 | 
			
		||||
 | 
			
		||||
    <p class="form-group">
 | 
			
		||||
      <label>Host Name</label>
 | 
			
		||||
      <input type="text" ng-model="newHost.name" class="form-control">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <p class="form-group">
 | 
			
		||||
      <label>Member of Groups</label>
 | 
			
		||||
      <input type="text" ng-model="newHost.members" class="form-control" placeholder="Hosts list separated by coma">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <label> Host Variables </label>
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div class="col-md-6">
 | 
			
		||||
        <complex-var members="complexVar" path="'Parent'" type="'object'" input-width="'400px'">
 | 
			
		||||
 | 
			
		||||
        </complex-var>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="col-md-6">
 | 
			
		||||
        <input name="typeRadioHost" type="radio" ng-model="variableViewType.type" ng-value="'JSON'"> JSON
 | 
			
		||||
        <input name="typeRadioHost" type="radio" ng-model="variableViewType.type" ng-value="'YAML'"> YAML
 | 
			
		||||
        <div ng-readonly="true" ui-ace="{th1eme:'twilight',document:'yaml',mode:'yaml',onChange:codeChanged}" ng-show="variableViewType.type == 'YAML'" ng-model="complexVarStringYaml" style="max-height: 200px;">
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
        <div ng-readonly="true" ui-ace="{th1eme:'twilight',document:'json',mode:'json',onChange:codeChanged,onLoad:aceLoaded}" ng-show="variableViewType.type == 'JSON'"  ng-model="complexVarString" style="max-height: 200px;">
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <button class="btn btn-primary" ng-disabled="!newHost.name" ng-click="createHost()">Create <span ng-if="createHostLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
    <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function newInventoryController($scope,$uibModalInstance,ansible,selectedProject) {
 | 
			
		||||
  $scope.newInventory = {name:null};
 | 
			
		||||
 | 
			
		||||
  $scope.createInventoryLoading = false;
 | 
			
		||||
 | 
			
		||||
  $scope.createInventory = function(){
 | 
			
		||||
 | 
			
		||||
    if($scope.newInventory.name.match(/\./)){
 | 
			
		||||
      $scope.err_msg = "Inventory files should not have extension"
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $scope.createInventoryLoading = true;
 | 
			
		||||
    ansible.createInventory($scope.newInventory.name,'# Inventory File - ' + $scope.newInventory.name,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createInventoryLoading = false;
 | 
			
		||||
        $scope.ok();
 | 
			
		||||
      },
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createInventoryLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data;
 | 
			
		||||
      })
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.ok = function () {
 | 
			
		||||
    $uibModalInstance.close(null);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.new_inventory', [])
 | 
			
		||||
  .controller('NewInventoryController', newInventoryController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: NewInventoryCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.new_inventory'));
 | 
			
		||||
 | 
			
		||||
  var NewInventoryCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    NewInventoryCtrl = $controller('NewInventoryCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <h4 class="modal-title">New Inventory</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body">
 | 
			
		||||
 | 
			
		||||
    <p class="form-group">
 | 
			
		||||
      <label>Inventory Name</label>
 | 
			
		||||
      <input type="text" ng-model="newInventory.name" class="form-control">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <div class="alert alert-info"> Inventory files should be named without an extension </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <button class="btn btn-primary" ng-disabled="!newInventory.name" ng-click="createInventory()">Create <span ng-if="createInventoryLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
    <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										134
									
								
								client/app/designer/playbook/new_play/new_play.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								client/app/designer/playbook/new_play/new_play.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,134 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function newPlayController($scope, $uibModalInstance, ansible, plays, selectedPlayIndex) {
 | 
			
		||||
  $scope.loading_msg = '';
 | 
			
		||||
  $scope.title = "Create Play";
 | 
			
		||||
  $scope.editMode = false;
 | 
			
		||||
  $scope.editHostMode = false;
 | 
			
		||||
 | 
			
		||||
  var selectedPlay;
 | 
			
		||||
  if(selectedPlayIndex > -1){
 | 
			
		||||
    selectedPlay = plays[selectedPlayIndex];
 | 
			
		||||
    $scope.title = "Edit Play";
 | 
			
		||||
    $scope.editMode = true;
 | 
			
		||||
    if(selectedPlay && selectedPlay.tags)$scope.tags = selectedPlay.tags.join(',');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $scope.newPlay = selectedPlay || {};
 | 
			
		||||
 | 
			
		||||
  $scope.newPlay_roles = $scope.newPlay.roles;
 | 
			
		||||
 | 
			
		||||
  $scope.createPlayLoading = false;
 | 
			
		||||
 | 
			
		||||
  $scope.createPlay = function () {
 | 
			
		||||
    $scope.ok($scope.newPlay)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.ok = function (newPlay) {
 | 
			
		||||
    if($scope.tags)
 | 
			
		||||
      newPlay.tags = $scope.tags.split(',');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if($scope.newPlay_roles && $scope.newPlay_roles.length){
 | 
			
		||||
      var roles = [];
 | 
			
		||||
      angular.forEach($scope.newPlay_roles,function(role){
 | 
			
		||||
        roles.push(role.text)
 | 
			
		||||
      });
 | 
			
		||||
      newPlay.roles = roles;
 | 
			
		||||
    }else if(newPlay.roles){
 | 
			
		||||
      delete newPlay.roles;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $uibModalInstance.close(newPlay);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  $scope.getHostsFromInventory = function(){
 | 
			
		||||
 | 
			
		||||
    var hosts = [];
 | 
			
		||||
 | 
			
		||||
    angular.forEach($scope.inventory_data_json.hosts, function(host){
 | 
			
		||||
      hosts.push({name:host})
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    angular.forEach($scope.inventory_data_json.groups, function(group){
 | 
			
		||||
      if(group.name !== 'Un grouped')
 | 
			
		||||
        hosts.push({name:group.name})
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return hosts;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.listOfInventoryFiles = function(){
 | 
			
		||||
    $scope.loading_msg = 'Loading Inventory Files';
 | 
			
		||||
    ansible.getInventoryList(function(response){
 | 
			
		||||
        $scope.loading_msg = '';
 | 
			
		||||
        $scope.inventoryFiles = response.data;
 | 
			
		||||
      },
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.loading_msg = '';
 | 
			
		||||
        $scope.err_msg = response.data
 | 
			
		||||
      })
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  $scope.listOfRoles = function(){
 | 
			
		||||
    $scope.loading_msg = 'Loading Roles';
 | 
			
		||||
    ansible.getRoleList(function(response){
 | 
			
		||||
        $scope.loading_msg = '';
 | 
			
		||||
        $scope.roleList = response.data;
 | 
			
		||||
      },
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.loading_msg = '';
 | 
			
		||||
        $scope.err_msg = response.data
 | 
			
		||||
      })
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.inventorySelected = function(selectedInventoryFile){
 | 
			
		||||
    $scope.loading_msg = 'Loading Hosts';
 | 
			
		||||
    ansible.readInventory(selectedInventoryFile,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.loading_msg = '';
 | 
			
		||||
        $scope.inventory_data_json = ansible.parseINIString(response.data);
 | 
			
		||||
        $scope.hosts = $scope.getHostsFromInventory();
 | 
			
		||||
 | 
			
		||||
      },function(response){
 | 
			
		||||
        $scope.loading_msg = '';
 | 
			
		||||
        $scope.err_msg = response.data
 | 
			
		||||
      })
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.getHostObject = function(hostname){
 | 
			
		||||
    var result = $scope.hosts.filter(function(host){
 | 
			
		||||
      return host.name == hostname
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if(result.length){
 | 
			
		||||
      return result[0]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.listOfInventoryFiles();
 | 
			
		||||
  $scope.listOfRoles();
 | 
			
		||||
 | 
			
		||||
  $scope.loadTags = function(query){
 | 
			
		||||
    if($scope.roleList){
 | 
			
		||||
      var tempList = $scope.roleList.filter(function(role){
 | 
			
		||||
        return role.indexOf(query) > -1
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return tempList
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.new_play', [])
 | 
			
		||||
  .controller('NewPlayController', newPlayController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: NewPlayCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.new_play'));
 | 
			
		||||
 | 
			
		||||
  var NewPlayCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    NewPlayCtrl = $controller('NewPlayCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										96
									
								
								client/app/designer/playbook/new_play/new_play.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								client/app/designer/playbook/new_play/new_play.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,96 @@
 | 
			
		|||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <button type="button" class="close" data-dismiss="modal">×</button>
 | 
			
		||||
    <h4 class="modal-title">{{title}}</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body">
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div class="col-md-6">
 | 
			
		||||
 | 
			
		||||
          <p class="form-group">
 | 
			
		||||
            <label>Name</label>
 | 
			
		||||
            <input ng-model="newPlay.name" type="text" class="form-control" placeholder="Play Name">
 | 
			
		||||
          </p>
 | 
			
		||||
 | 
			
		||||
          <p class="form-group">
 | 
			
		||||
            <label>Tags</label>
 | 
			
		||||
            <input ng-model="tags" type="text" class="form-control" placeholder="Tags separated by comma">
 | 
			
		||||
          </p>
 | 
			
		||||
 | 
			
		||||
          <p class="form-group" ng-show="!editMode || editHostMode">
 | 
			
		||||
            <label>Inventory Files</label>
 | 
			
		||||
            <select class="form-control" ng-disabled="loading_msg" ng-change="inventorySelected(selectedInventoryFile);" ng-model="selectedInventoryFile" ng-options="inventoryFile as inventoryFile for inventoryFile in inventoryFiles">
 | 
			
		||||
            </select>
 | 
			
		||||
          </p>
 | 
			
		||||
 | 
			
		||||
          <p class="form-group" ng-show="!editMode || editHostMode">
 | 
			
		||||
            <label>Hosts</label>
 | 
			
		||||
            <select class="form-control" ng-disabled="loading_msg" ng-model="newPlay.hosts" ng-options="host.name as host.name for host in hosts">
 | 
			
		||||
            </select>
 | 
			
		||||
          </p>
 | 
			
		||||
 | 
			
		||||
          <p class="form-group" ng-show="editMode && !editHostMode">
 | 
			
		||||
            <label>Hosts </label>
 | 
			
		||||
            <input ng-model="newPlay.hosts" ng-disabled="true" type="text" class="form-control" placeholder="Host Name">
 | 
			
		||||
          </p>
 | 
			
		||||
          <div class="input-group-btn" ng-show="editMode && !editHostMode">
 | 
			
		||||
            <button class="btn btn-default" ng-click="editHostMode = true"> <span class="fa fa-edit"></span> Edit Hosts </button>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <label>Roles</label>
 | 
			
		||||
          <tags-input ng-model="newPlay_roles" add-from-autocomplete-only="true" placeholder="Add roles to play">
 | 
			
		||||
            <auto-complete source="loadTags($query)" min-length="0" load-on-empty="true"  load-on-focus="true" load-on-down-arrow="true" ></auto-complete>
 | 
			
		||||
          </tags-input>
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-md-6">
 | 
			
		||||
 | 
			
		||||
        <h4 ng-if="selectedHostInfo.ip">IP - {{selectedHostInfo.ip}}</h4>
 | 
			
		||||
 | 
			
		||||
        <h4 ng-if="selectedHostInfo.members">Members:</h4>
 | 
			
		||||
        <ul class="list-group" ng-if="selectedHostInfo.members">
 | 
			
		||||
          <li class="list-group-item" ng-repeat="member in selectedHostInfo.members.split(',')">{{member}}</li>
 | 
			
		||||
        </ul>
 | 
			
		||||
 | 
			
		||||
        <div ng-if="hostGroups">
 | 
			
		||||
          <h4 >Member Of:</h4>
 | 
			
		||||
          <ul class="list-group">
 | 
			
		||||
            <li class="list-group-item" ng-repeat="group in hostGroups">{{group.name}}</li>
 | 
			
		||||
          </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!--<div ui-ace="{theme:'twilight',document:'INI',mode:'ini',showGutter:false}" ng-model="hostInfo">
 | 
			
		||||
 | 
			
		||||
        </div>-->
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="row" ng-if="selectedHostInfo">
 | 
			
		||||
      <h4>Variables</h4>
 | 
			
		||||
      <div class="input-group">
 | 
			
		||||
        <span class="input-group-addon" >Groups</span>
 | 
			
		||||
        <select class="form-control" ng-change="variableGroupSelected(selectedVariableGroup.value)" ng-model="selectedVariableGroup.value" ng-options="group.name as group.name for group in [selectedHostInfo].concat(hostGroups)">
 | 
			
		||||
        </select>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div ui-ace="{theme:'twilight',document:'INI',mode:'ini'}" ng-model="variableInfo">
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div class="col-md-3" style="text-align: left">
 | 
			
		||||
          <span ng-show="loading_msg" class="fa fa-spin fa-spinner"></span> {{loading_msg}}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-md-9" style="text-align: right">
 | 
			
		||||
        <button class="btn btn-primary" ng-click="createPlay()">Save <span ng-if="createPlayLoading" class="fa fa-spinner fa-spin"></span> </button>
 | 
			
		||||
        <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function newPlaybookController($scope,$uibModalInstance,ansible) {
 | 
			
		||||
  $scope.newPlaybook = {name:null};
 | 
			
		||||
 | 
			
		||||
  $scope.createPlaybookLoading = false;
 | 
			
		||||
 | 
			
		||||
  $scope.createPlaybook = function(){
 | 
			
		||||
    $scope.createPlaybookLoading = true;
 | 
			
		||||
 | 
			
		||||
    ansible.createPlaybook($scope.newPlaybook.name + '.yml',"",
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createPlaybookLoading = false;
 | 
			
		||||
        $scope.ok();
 | 
			
		||||
      },
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createPlaybookLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data;
 | 
			
		||||
      })
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.ok = function () {
 | 
			
		||||
    $uibModalInstance.close(null);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.new_playbook', [])
 | 
			
		||||
  .controller('NewPlaybookController', newPlaybookController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: NewPlaybookCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.new_playbook'));
 | 
			
		||||
 | 
			
		||||
  var NewPlaybookCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    NewPlaybookCtrl = $controller('NewPlaybookCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										19
									
								
								client/app/designer/playbook/new_playbook/new_playbook.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								client/app/designer/playbook/new_playbook/new_playbook.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <h4 class="modal-title">New Playbook</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body">
 | 
			
		||||
 | 
			
		||||
    <p class="form-group">
 | 
			
		||||
      <label>Playbook Name</label>
 | 
			
		||||
      <input type="text" ng-model="newPlaybook.name" class="form-control">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <button class="btn btn-primary" ng-disabled="!newPlaybook.name" ng-click="createPlaybook()">Create <span ng-if="createPlaybookLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
    <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										328
									
								
								client/app/designer/playbook/playbook.component.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								client/app/designer/playbook/playbook.component.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,328 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
const uiRouter = require('angular-ui-router');
 | 
			
		||||
 | 
			
		||||
import routes from './playbook.routes';
 | 
			
		||||
 | 
			
		||||
export class PlaybookComponent {
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor($scope,$uibModal,YAML,ansible,yamlFile) {
 | 
			
		||||
    'ngInject';
 | 
			
		||||
    $scope.isopen = {playbooks:true,plays:false,tasks:false};
 | 
			
		||||
 | 
			
		||||
    $scope.selectedPlaybook = {playbook: "",content: ""};
 | 
			
		||||
    $scope.selectedPlay = {play: ""};
 | 
			
		||||
 | 
			
		||||
    $scope.showSaveButton = {};
 | 
			
		||||
    $scope.loadingButtons = {};
 | 
			
		||||
 | 
			
		||||
    $scope.editPlaybook = {value:false};
 | 
			
		||||
 | 
			
		||||
    $scope.loadingModuleCode = false;
 | 
			
		||||
 | 
			
		||||
    $scope.$on('projectLoaded',function(){
 | 
			
		||||
      $scope.getPlaybooks()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    //To fix a warning message in console
 | 
			
		||||
    $scope.aceLoaded = function(_editor){
 | 
			
		||||
      _editor.$blockScrolling = Infinity;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // --------------------------------------- PLAYBOOKS ----------------
 | 
			
		||||
 | 
			
		||||
    $scope.getPlaybooks = function(){
 | 
			
		||||
      ansible.getPlaybookList(
 | 
			
		||||
        function(response){
 | 
			
		||||
          $scope.playbooks = response.data;
 | 
			
		||||
        },
 | 
			
		||||
        function(response){
 | 
			
		||||
          console.log(response.data)
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if($scope.$parent.selectedProject && $scope.$parent.selectedProject.ansibleEngine){
 | 
			
		||||
      $scope.getPlaybooks()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $scope.showPlaybookCode = function(playbook_name){
 | 
			
		||||
      $scope.loadingModuleCode = true;
 | 
			
		||||
 | 
			
		||||
      if(!playbook_name){
 | 
			
		||||
        $scope.selectedPlaybook.content = "Select a module";
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ansible.readPlaybook(playbook_name,function(response) {
 | 
			
		||||
        $scope.isopen.playbooks = true;
 | 
			
		||||
        $scope.isopen.plays = true
 | 
			
		||||
        $scope.loadingModuleCode = false;
 | 
			
		||||
        $scope.selectedPlaybook.content = response.data.split("Stream :: close")[0];
 | 
			
		||||
        $scope.getPlaysFromPlayBook($scope.selectedPlaybook.content);
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.$watch('selectedPlaybook.playbook',function(newValue,oldValue){
 | 
			
		||||
      if(newValue && newValue !== oldValue){
 | 
			
		||||
        $scope.selectedPlaybook.content = "Loading Code...";
 | 
			
		||||
        $scope.showPlaybookCode(newValue);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $scope.$watch('selectedPlay.play',function(newValue,oldValue){
 | 
			
		||||
      if(newValue && newValue !== oldValue){
 | 
			
		||||
        $scope.selectedPlay.play.tasks = $scope.selectedPlay.play.tasks || [];
 | 
			
		||||
        $scope.isopen.playbooks = false;
 | 
			
		||||
        $scope.isopen.plays = false;
 | 
			
		||||
        $scope.isopen.tasks = true;
 | 
			
		||||
        $scope.isopen.roles = true;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $scope.showCreatePlaybookModal = function(){
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/playbook/new_playbook/new_playbook.html',
 | 
			
		||||
        controller: 'NewPlaybookController',
 | 
			
		||||
        size: 'md',
 | 
			
		||||
        backdrop  : 'static',
 | 
			
		||||
        keyboard  : false,
 | 
			
		||||
        closeByEscape : false,
 | 
			
		||||
        closeByDocument : false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          selectedProject: function(){
 | 
			
		||||
            return $scope.$parent.selectedProject
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      modalInstance.result.then(function () {
 | 
			
		||||
        $scope.getPlaybooks();
 | 
			
		||||
      }, function () {
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.editPlaybookMethod = function(){
 | 
			
		||||
      $scope.editPlaybook.value = true;
 | 
			
		||||
      $scope.uneditedPlaybokContents = $scope.selectedPlaybook.content
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.cancelPlaybookChanges = function(){
 | 
			
		||||
      $scope.editPlaybook.value = false;
 | 
			
		||||
      $scope.selectedPlaybook.content = $scope.uneditedPlaybokContents
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.savePlaybook = function(buttonVariable){
 | 
			
		||||
      console.log("Saving Playbook")
 | 
			
		||||
      $scope.loadingButtons[buttonVariable] = true;
 | 
			
		||||
 | 
			
		||||
      ansible.createPlaybook($scope.selectedPlaybook.playbook,$scope.selectedPlaybook.content,
 | 
			
		||||
        function(response){
 | 
			
		||||
          $scope.loadingButtons[buttonVariable] = false;
 | 
			
		||||
          $scope.showSaveButton[buttonVariable] = false;
 | 
			
		||||
          $scope.editPlaybook.value = false;
 | 
			
		||||
        },
 | 
			
		||||
        function(response){
 | 
			
		||||
          $scope.loadingButtons[buttonVariable] = false;
 | 
			
		||||
          $scope.showSaveButton[buttonVariable] = false;
 | 
			
		||||
          $scope.err_msg = response.data;
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.deletePlaybook = function(){
 | 
			
		||||
      $scope.deletePlaybookLoading = true;
 | 
			
		||||
      ansible.deletePlaybook($scope.selectedPlaybook.playbook,
 | 
			
		||||
        function(response){
 | 
			
		||||
          $scope.deletePlaybookLoading = false;
 | 
			
		||||
          $scope.selectedPlaybook.playbook = "";
 | 
			
		||||
          $scope.getPlaybooks();
 | 
			
		||||
        },
 | 
			
		||||
        function(response){
 | 
			
		||||
          $scope.deletePlaybookLoading = false;
 | 
			
		||||
          $scope.err_msg = response.data;
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //--------------- PLAY --------------
 | 
			
		||||
 | 
			
		||||
    $scope.showCreatePlayModal = function(selectedPlayIndex){
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createPlayContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/playbook/new_play/new_play.html',
 | 
			
		||||
        controller: 'NewPlayController',
 | 
			
		||||
        size: 'lg',
 | 
			
		||||
        backdrop  : 'static',
 | 
			
		||||
        keyboard  : false,
 | 
			
		||||
        closeByEscape : false,
 | 
			
		||||
        closeByDocument : false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          selectedProject: function () {
 | 
			
		||||
            return $scope.$parent.selectedProject;
 | 
			
		||||
          },
 | 
			
		||||
          plays: function () {
 | 
			
		||||
            return $scope.plays;
 | 
			
		||||
          },
 | 
			
		||||
          selectedPlayIndex: function () {
 | 
			
		||||
            return selectedPlayIndex;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      modalInstance.result.then(
 | 
			
		||||
        function (newPlay) {
 | 
			
		||||
          if(selectedPlayIndex == null)
 | 
			
		||||
            $scope.plays.push(newPlay);
 | 
			
		||||
 | 
			
		||||
          $scope.clearEmptyTasks($scope.plays);
 | 
			
		||||
 | 
			
		||||
          $scope.selectedPlaybook.content = yamlFile.jsonToYamlFile($scope.plays, 'Playbook file: ' +  $scope.selectedPlaybook.playbook)
 | 
			
		||||
          $scope.savePlaybook();
 | 
			
		||||
        }, function () {
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // FUNCTION - GET PLAYS FROM PLAYBOOK
 | 
			
		||||
 | 
			
		||||
    $scope.getPlaysFromPlayBook = function(playbookYamlData){
 | 
			
		||||
      $scope.plays = YAML.parse(playbookYamlData) || []
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // FUNCTION - DELETE PLAY
 | 
			
		||||
 | 
			
		||||
    $scope.deletePlay = function(index){
 | 
			
		||||
      $scope.plays.splice(index,1);
 | 
			
		||||
      $scope.selectedPlaybook.content = yamlFile.jsonToYamlFile($scope.plays, 'Playbook file: ' +  $scope.selectedPlaybook.playbook)
 | 
			
		||||
      $scope.savePlaybook();
 | 
			
		||||
      $scope.selectedPlay = {play: ""};
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // ------------------- EXECUTE PLAYBOOK MODAL -------------
 | 
			
		||||
 | 
			
		||||
    $scope.executeAnsiblePlayBook = function(tags,executionType,executionName,selectedPlay){
 | 
			
		||||
      console.log("Tags type" + typeof tags)
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/execution/executeModal.html',
 | 
			
		||||
        controller: 'ExecutionController',
 | 
			
		||||
        size: 'lg',
 | 
			
		||||
        backdrop  : 'static',
 | 
			
		||||
        keyboard  : false,
 | 
			
		||||
        closeByEscape : false,
 | 
			
		||||
        closeByDocument : false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          tags: function(){
 | 
			
		||||
            return tags
 | 
			
		||||
          },
 | 
			
		||||
          selectedProject: function(){
 | 
			
		||||
            return $scope.$parent.selectedProject
 | 
			
		||||
          },
 | 
			
		||||
          selectedPlaybook: function(){
 | 
			
		||||
            return $scope.selectedPlaybook
 | 
			
		||||
          },
 | 
			
		||||
          selectedPlay: function(){
 | 
			
		||||
            return selectedPlay
 | 
			
		||||
          },
 | 
			
		||||
          executionType: function(){
 | 
			
		||||
            return executionType
 | 
			
		||||
          },
 | 
			
		||||
          executionName: function(){
 | 
			
		||||
            return executionName
 | 
			
		||||
          },
 | 
			
		||||
          readOnly: function(){
 | 
			
		||||
            return false
 | 
			
		||||
          },
 | 
			
		||||
          runData: function(){
 | 
			
		||||
            return null
 | 
			
		||||
          },
 | 
			
		||||
          projectFolder: function(){
 | 
			
		||||
            return null
 | 
			
		||||
          },
 | 
			
		||||
          roleName: function(){
 | 
			
		||||
            return null
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $scope.clearEmptyTasks = function(plays){
 | 
			
		||||
      //Check for empty tasks list
 | 
			
		||||
      angular.forEach(plays,function(play){
 | 
			
		||||
        if((play.tasks && !play.tasks.length) || !play.tasks){
 | 
			
		||||
          delete play.tasks
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // ---------------------- TASKS -------------------
 | 
			
		||||
 | 
			
		||||
    $scope.updatePlaybookFileContent = function(save,buttonVariable){
 | 
			
		||||
 | 
			
		||||
      var playsCopy = angular.copy($scope.plays);
 | 
			
		||||
 | 
			
		||||
      $scope.clearEmptyTasks(playsCopy);
 | 
			
		||||
 | 
			
		||||
      $scope.selectedPlaybook.content = yamlFile.jsonToYamlFile(playsCopy, 'Playbook file: ' +  $scope.selectedPlaybook.playbook)
 | 
			
		||||
      if(save)
 | 
			
		||||
        $scope.savePlaybook(buttonVariable);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.moveUp = function(list,index,buttonVariable){
 | 
			
		||||
      if(!$scope.preChangeData) $scope.preChangeData = angular.copy(list);
 | 
			
		||||
      var temp = angular.copy(list[index]);
 | 
			
		||||
      list[index] = list[index-1];
 | 
			
		||||
      list[index-1] = temp;
 | 
			
		||||
 | 
			
		||||
      $scope.updatePlaybookFileContent(false);
 | 
			
		||||
 | 
			
		||||
      $scope.showSaveButton[buttonVariable] = true
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.cancelChange = function(buttonVariable){
 | 
			
		||||
      if($scope.preChangeData){
 | 
			
		||||
        $scope.plays = angular.copy($scope.preChangeData);
 | 
			
		||||
        $scope.preChangeData = null
 | 
			
		||||
 | 
			
		||||
      }
 | 
			
		||||
      $scope.updatePlaybookFileContent(false);
 | 
			
		||||
 | 
			
		||||
      $scope.showSaveButton[buttonVariable] = false
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.moveDown = function(list,index,buttonVariable){
 | 
			
		||||
      if(!$scope.preChangeData) $scope.preChangeData = angular.copy(list);
 | 
			
		||||
      var temp = angular.copy(list[index]);
 | 
			
		||||
      list[index] = list[index+1];
 | 
			
		||||
      list[index+1] = temp;
 | 
			
		||||
 | 
			
		||||
      $scope.updatePlaybookFileContent(false);
 | 
			
		||||
 | 
			
		||||
      $scope.showSaveButton[buttonVariable] = true
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.playbook', [uiRouter])
 | 
			
		||||
  .config(routes)
 | 
			
		||||
  .component('playbook', {
 | 
			
		||||
    template: require('./playbook.html'),
 | 
			
		||||
    controller: PlaybookComponent,
 | 
			
		||||
    controllerAs: 'playbookCtrl'
 | 
			
		||||
  })
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										17
									
								
								client/app/designer/playbook/playbook.component.spec.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								client/app/designer/playbook/playbook.component.spec.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Component: PlaybookComponent', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.playbook'));
 | 
			
		||||
 | 
			
		||||
  var PlaybookComponent;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($componentController) {
 | 
			
		||||
    PlaybookComponent = $componentController('playbook', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										0
									
								
								client/app/designer/playbook/playbook.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								client/app/designer/playbook/playbook.css
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										166
									
								
								client/app/designer/playbook/playbook.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								client/app/designer/playbook/playbook.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,166 @@
 | 
			
		|||
<div class="col-md-6">
 | 
			
		||||
  <uib-accordion close-others="false">
 | 
			
		||||
    <div uib-accordion-group class="panel-default" is-open="isopen.playbooks">
 | 
			
		||||
      <uib-accordion-heading>
 | 
			
		||||
        Playbooks <i class="pull-right glyphicon"
 | 
			
		||||
                     ng-class="{'glyphicon-chevron-down': isopen.playbooks, 'glyphicon-chevron-right': !isopen.playbooks}"></i>
 | 
			
		||||
      </uib-accordion-heading>
 | 
			
		||||
 | 
			
		||||
      <button class="btn btn-default" ng-click="showCreatePlaybookModal()">Create Playbook <span
 | 
			
		||||
        class="fa fa-plus"></span></button>
 | 
			
		||||
      <button class="btn btn-default" ng-if="!editPlaybook.value" ng-click="editPlaybookMethod()">Edit <span
 | 
			
		||||
        ng-if="!showSaveButton.savePlaybookLoading" class="fa fa-edit"></span></button>
 | 
			
		||||
      <button class="btn btn-primary" ng-if="editPlaybook.value" ng-disabled="!selectedPlaybook.playbook"
 | 
			
		||||
              ng-click="savePlaybook('savePlaybookLoading')">Save <span ng-if="!showSaveButton.savePlaybookLoading"
 | 
			
		||||
                                                                        class="fa fa-save"></span><span
 | 
			
		||||
        ng-if="showSaveButton.savePlaybookLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
      <button class="btn btn-warning" ng-if="editPlaybook.value" confirm="Are you sure you want to discard the changes?"
 | 
			
		||||
              ng-click="cancelPlaybookChanges()">Cancel <span class="fa fa-times-circle"></span></button>
 | 
			
		||||
      <button class="btn btn-danger" confirm="Are you sure you want to delete playbook? "
 | 
			
		||||
              ng-disabled="!selectedPlaybook.playbook" ng-click="deletePlaybook()">Delete <span
 | 
			
		||||
        ng-show="!deletePlaybookLoading" class="fa fa-save"></span><span ng-show="deletePlaybookLoading"
 | 
			
		||||
                                                                         class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
      <button class="btn btn-success" ng-disabled="!selectedPlaybook.playbook"
 | 
			
		||||
              ng-click="executeAnsiblePlayBook(null,'Playbook')">Play <span class="fa fa-play"></span></button>
 | 
			
		||||
      <div class="table-responsive">
 | 
			
		||||
        <table class="table">
 | 
			
		||||
          <thead>
 | 
			
		||||
          <tr>
 | 
			
		||||
            <th>Select</th>
 | 
			
		||||
            <th>Name</th>
 | 
			
		||||
            <!--<th>Actions</th>-->
 | 
			
		||||
          </tr>
 | 
			
		||||
          </thead>
 | 
			
		||||
          <tbody>
 | 
			
		||||
          <tr ng-repeat="playbook in playbooks">
 | 
			
		||||
            <td><input name="playbookGroup" type="radio" ng-model="selectedPlaybook.playbook" ng-value="playbook">
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>{{playbook}}</td>
 | 
			
		||||
            <!--<td><div class="btn-group">
 | 
			
		||||
              <label class="btn btn-default btn-sm" ng-click="showCreatePlayModal($index)" ><span class="fa fa-edit"></span></label>
 | 
			
		||||
              <label class="btn btn-danger btn-sm" confirm="Are you sure you want to delete?"  ng-click="deletePlay($index)" ><span class="fa fa-trash-o"></span></label>
 | 
			
		||||
              <div style="display: inline-block" tooltip-enable="!play.tags" uib-tooltip="Tag must be assigned to play individually"><label class="btn btn-success btn-sm" ng-disabled="!play.tags"  ng-click="executeAnsiblePlayBook(play.tags,'Play',play.name,play)" ><span class="fa fa-play"></span></label></div>
 | 
			
		||||
            </div></td>-->
 | 
			
		||||
          </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div uib-accordion-group class="panel-default" is-open="isopen.plays" ng-show="plays">
 | 
			
		||||
      <uib-accordion-heading>
 | 
			
		||||
        Plays <i class="pull-right glyphicon"
 | 
			
		||||
                 ng-class="{'glyphicon-chevron-down': isopen.plays, 'glyphicon-chevron-right': !isopen.plays}"></i>
 | 
			
		||||
      </uib-accordion-heading>
 | 
			
		||||
 | 
			
		||||
      <div class="table-responsive">
 | 
			
		||||
        <table class="table">
 | 
			
		||||
          <thead>
 | 
			
		||||
          <tr>
 | 
			
		||||
          <tr>
 | 
			
		||||
            <th>Select</th>
 | 
			
		||||
            <th>Name</th>
 | 
			
		||||
            <!--<th>Tags</th>-->
 | 
			
		||||
            <th>Hosts</th>
 | 
			
		||||
            <th>Actions</th>
 | 
			
		||||
          </tr>
 | 
			
		||||
          </thead>
 | 
			
		||||
          <tbody>
 | 
			
		||||
          <tr ng-repeat="play in plays">
 | 
			
		||||
            <td><input name="playGroup" type="radio" ng-model="selectedPlay.play" ng-value="play">
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>{{play.name}}</td>
 | 
			
		||||
            <!--<td>{{play.tags.join(', ')}}</td>-->
 | 
			
		||||
            <td>{{play.hosts}}</td>
 | 
			
		||||
            <td>
 | 
			
		||||
              <div class="btn-group">
 | 
			
		||||
                <label class="btn btn-default btn-sm" ng-click="showCreatePlayModal($index)"><span
 | 
			
		||||
                  class="fa fa-edit"></span></label>
 | 
			
		||||
                <label class="btn btn-danger btn-sm" confirm="Are you sure you want to delete?"
 | 
			
		||||
                       ng-click="deletePlay($index)"><span class="fa fa-trash-o"></span></label>
 | 
			
		||||
                <div style="display: inline-block" tooltip-enable="!play.tags"
 | 
			
		||||
                     uib-tooltip="Tag must be assigned to play individually"><label class="btn btn-success btn-sm"
 | 
			
		||||
                                                                                    ng-disabled="!play.tags"
 | 
			
		||||
                                                                                    ng-click="executeAnsiblePlayBook(play.tags,'Play',play.name,play)"><span
 | 
			
		||||
                  class="fa fa-play"></span></label></div>
 | 
			
		||||
                <label class="btn btn-primary btn-sm" ng-disabled="$first"
 | 
			
		||||
                       ng-click="moveUp(plays,$index,'savePlayLoading')"><span class="fa fa-arrow-up"></span></label>
 | 
			
		||||
                <label class="btn btn-primary btn-sm" ng-disabled="$last"
 | 
			
		||||
                       ng-click="moveDown(plays,$index,'savePlayLoading')"><span
 | 
			
		||||
                  class="fa fa-arrow-down"></span></label>
 | 
			
		||||
              </div>
 | 
			
		||||
            </td>
 | 
			
		||||
          </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <button class="btn btn-default" ng-click="showCreatePlayModal()">Create <span class="fa fa-plus"></span></button>
 | 
			
		||||
      <button class="btn btn-primary" ng-if="showSaveButton.savePlayLoading" ng-click="savePlaybook('savePlayLoading')">
 | 
			
		||||
        Save <span ng-if="loadingButtons.savePlayLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
      <button class="btn btn-warning" ng-if="showSaveButton.savePlayLoading" ng-click="cancelChange('savePlayLoading')">
 | 
			
		||||
        Cancel <span class="fa fa-times"></span></button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div uib-accordion-group class="panel-default" is-open="isopen.tasks" ng-show="selectedPlay.play">
 | 
			
		||||
      <uib-accordion-heading>
 | 
			
		||||
        Tasks <i class="pull-right glyphicon"
 | 
			
		||||
                 ng-class="{'glyphicon-chevron-down': isopen.tasks, 'glyphicon-chevron-right': !isopen.tasks}"></i>
 | 
			
		||||
      </uib-accordion-heading>
 | 
			
		||||
 | 
			
		||||
      <tasks tasks-list="selectedPlay.play.tasks" update-playbook-file-content="updatePlaybookFileContent" selected-play="selectedPlay" save-playbook="savePlaybook" execute-ansible-play-book="executeAnsiblePlayBook"></tasks>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div uib-accordion-group class="panel-default" is-open="isopen.roles" ng-show="selectedPlay.play.roles">
 | 
			
		||||
      <uib-accordion-heading>
 | 
			
		||||
        Roles <i class="pull-right glyphicon"
 | 
			
		||||
                 ng-class="{'glyphicon-chevron-down': isopen.tasks, 'glyphicon-chevron-right': !isopen.tasks}"></i>
 | 
			
		||||
      </uib-accordion-heading>
 | 
			
		||||
      <div class="table-responsive">
 | 
			
		||||
        <table class="table">
 | 
			
		||||
          <thead>
 | 
			
		||||
          <tr>
 | 
			
		||||
            <th>Select</th>
 | 
			
		||||
            <th>Name</th>
 | 
			
		||||
          </tr>
 | 
			
		||||
          </thead>
 | 
			
		||||
          <tbody>
 | 
			
		||||
          <tr ng-repeat="role in selectedPlay.play.roles">
 | 
			
		||||
            <td><input name="playGroup" type="radio" ng-model="selectedRole.role" ng-value="role">
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>{{role}}</td>
 | 
			
		||||
          </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="panel">
 | 
			
		||||
        <div class="panel panel-default">
 | 
			
		||||
          <div class="panel-heading">File Browser</div>
 | 
			
		||||
          <div class="panel-body">
 | 
			
		||||
 | 
			
		||||
            <treecontrol ng-if="roleData.children" class="tree-classic"
 | 
			
		||||
                         tree-model="roleData.children"
 | 
			
		||||
                         options="treeOptions"
 | 
			
		||||
                         on-selection="showSelected(node)"
 | 
			
		||||
                         selected-node="node1"
 | 
			
		||||
                         filter-expression="{name: '!.git'}">
 | 
			
		||||
              {{node.name}}
 | 
			
		||||
            </treecontrol>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  </uib-accordion>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="col-md-6">
 | 
			
		||||
  <div ng-readonly="!editPlaybook.value" ui-ace="{theme:'twilight',document:'YAML',mode:'yaml',onChange:codeChanged,onLoad:aceLoaded}"
 | 
			
		||||
       ng-model="selectedPlaybook.content">
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										10
									
								
								client/app/designer/playbook/playbook.routes.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/app/designer/playbook/playbook.routes.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default function($stateProvider) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
  $stateProvider
 | 
			
		||||
    .state('designer.playbook', {
 | 
			
		||||
      url: '/playbook',
 | 
			
		||||
      template: '<playbook></playbook>'
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								client/app/designer/roles/new_file/new_file.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								client/app/designer/roles/new_file/new_file.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function newFileController($scope,$uibModalInstance,ansible,selectedDirectory,copyFile,selectedFileName) {
 | 
			
		||||
  $scope.newFile = {name:null};
 | 
			
		||||
  $scope.createFileLoading = false;
 | 
			
		||||
  $scope.title = 'New File';
 | 
			
		||||
 | 
			
		||||
  var parentDirectory = selectedDirectory;
 | 
			
		||||
 | 
			
		||||
  // If copyFile use selectedFileName to create new role from
 | 
			
		||||
  // else nullify selectedFileName
 | 
			
		||||
  if(!copyFile){
 | 
			
		||||
    selectedFileName = null;
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    $scope.title = 'Copy File';
 | 
			
		||||
    $scope.newFile.name = 'Copy of ' + selectedFileName;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Create/Copy File - Either a new role or copy an existing role
 | 
			
		||||
   */
 | 
			
		||||
  $scope.createFile = function(){
 | 
			
		||||
 | 
			
		||||
    $scope.createFileLoading = true;
 | 
			
		||||
    ansible.createFile(parentDirectory + '/' + $scope.newFile.name,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createFileLoading = false;
 | 
			
		||||
        $scope.ok();
 | 
			
		||||
      },
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createFileLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data;
 | 
			
		||||
      },
 | 
			
		||||
      selectedFileName
 | 
			
		||||
    )
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Close create/copy modal
 | 
			
		||||
   */
 | 
			
		||||
  $scope.ok = function () {
 | 
			
		||||
    $uibModalInstance.close(null);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Cancel modal
 | 
			
		||||
   */
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.new_file', [])
 | 
			
		||||
  .controller('NewFileController', newFileController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: NewFileCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.new_file'));
 | 
			
		||||
 | 
			
		||||
  var NewFileCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    NewFileCtrl = $controller('NewFileCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										19
									
								
								client/app/designer/roles/new_file/new_file.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								client/app/designer/roles/new_file/new_file.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <h4 class="modal-title">{{title}}</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body">
 | 
			
		||||
 | 
			
		||||
    <p class="form-group">
 | 
			
		||||
      <label>File Name</label>
 | 
			
		||||
      <input type="text" ng-model="newFile.name" class="form-control">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <button class="btn btn-primary" ng-disabled="!newFile.name" ng-click="createFile()">Create <span ng-if="createFileLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
    <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										56
									
								
								client/app/designer/roles/new_role/new_role.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								client/app/designer/roles/new_role/new_role.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function newRoleController($scope,$uibModalInstance,ansible,selectedRoleName,copyRole) {
 | 
			
		||||
 | 
			
		||||
  $scope.newRole = {name:null};
 | 
			
		||||
  $scope.createRoleLoading = false;
 | 
			
		||||
  $scope.title = 'New Role';
 | 
			
		||||
 | 
			
		||||
  // If copyRole use selectedRoleName to create new role from
 | 
			
		||||
  // else nullify selectedRoleName
 | 
			
		||||
  if(!copyRole){
 | 
			
		||||
    selectedRoleName = null;
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    $scope.title = 'Copy Role';
 | 
			
		||||
    $scope.newRole.name = 'Copy of ' + selectedRoleName;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Create/Copy Role - Either a new role or copy an existing role
 | 
			
		||||
   */
 | 
			
		||||
  $scope.createRole = function(){
 | 
			
		||||
    $scope.createRoleLoading = true;
 | 
			
		||||
    ansible.createRole($scope.newRole.name,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createRoleLoading = false;
 | 
			
		||||
        $scope.ok();
 | 
			
		||||
      },
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.createRoleLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data;
 | 
			
		||||
      },
 | 
			
		||||
      selectedRoleName
 | 
			
		||||
    )
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Close create/copy modal
 | 
			
		||||
   */
 | 
			
		||||
  $scope.ok = function () {
 | 
			
		||||
    $uibModalInstance.close(null);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Cancel modal
 | 
			
		||||
   */
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.new_role', [])
 | 
			
		||||
  .controller('NewRoleController', newRoleController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: NewRoleCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.new_role'));
 | 
			
		||||
 | 
			
		||||
  var NewRoleCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    NewRoleCtrl = $controller('NewRoleCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										19
									
								
								client/app/designer/roles/new_role/new_role.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								client/app/designer/roles/new_role/new_role.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <h4 class="modal-title">{{title}}</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body">
 | 
			
		||||
 | 
			
		||||
    <p class="form-group">
 | 
			
		||||
      <label>Role Name</label>
 | 
			
		||||
      <input type="text" ng-model="newRole.name" class="form-control">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <button class="btn btn-primary" ng-disabled="!newRole.name" ng-click="createRole()">Create <span ng-if="createRoleLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
    <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										463
									
								
								client/app/designer/roles/roles.component.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										463
									
								
								client/app/designer/roles/roles.component.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,463 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
const uiRouter = require('angular-ui-router');
 | 
			
		||||
 | 
			
		||||
import routes from './roles.routes';
 | 
			
		||||
 | 
			
		||||
export class RolesComponent {
 | 
			
		||||
  /*@ngInject*/
 | 
			
		||||
  constructor($scope, ansible, $uibModal, yamlFile, Projects, editor) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
 | 
			
		||||
    $scope.treeOptions = {
 | 
			
		||||
      nodeChildren: "children",
 | 
			
		||||
      dirSelectable: true,
 | 
			
		||||
      isLeaf: function (node) {
 | 
			
		||||
        return !(node.type === 'directory');
 | 
			
		||||
      },
 | 
			
		||||
      injectClasses: {
 | 
			
		||||
        ul: "a1",
 | 
			
		||||
        li: "a2",
 | 
			
		||||
        liSelected: "a7",
 | 
			
		||||
        iExpanded: "a3",
 | 
			
		||||
        iCollapsed: "a4",
 | 
			
		||||
        iLeaf: "a5",
 | 
			
		||||
        label: "a6",
 | 
			
		||||
        labelSelected: "a8"
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.isopen = {roles: true, filebrowser: false, tasks: false};
 | 
			
		||||
 | 
			
		||||
    $scope.selectedRole = {role: "", tasks: null};
 | 
			
		||||
 | 
			
		||||
    $scope.selectedFile = {showSource: true, markdownContent: true, content: ""};
 | 
			
		||||
 | 
			
		||||
    $scope.editRole = {value: false};
 | 
			
		||||
    $scope.showSaveFileButton = false;
 | 
			
		||||
 | 
			
		||||
    $scope.$on('projectLoaded', function () {
 | 
			
		||||
      $scope.getRoles()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $scope.aceLoaded = function (_editor) {
 | 
			
		||||
      _editor.$blockScrolling = Infinity;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // --------------------------------------- PLAYBOOKS ----------------
 | 
			
		||||
 | 
			
		||||
    $scope.getRoles = function () {
 | 
			
		||||
      ansible.getRoleList(
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.roles = response.data;
 | 
			
		||||
          if(localStorage.selectedRoleName)
 | 
			
		||||
            $scope.selectedRole.role = localStorage.selectedRoleName
 | 
			
		||||
        },
 | 
			
		||||
        function (response) {
 | 
			
		||||
          console.log(response.data)
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var getRoleByName = function(roleName){
 | 
			
		||||
      var result = null;
 | 
			
		||||
      angular.forEach($scope.roles,function(role){
 | 
			
		||||
        if(role.name == roleName){
 | 
			
		||||
          result = role
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      return result;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if ($scope.$parent.selectedProject && $scope.$parent.selectedProject.ansibleEngine) {
 | 
			
		||||
      $scope.getRoles()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $scope.loadingModuleCode = false;
 | 
			
		||||
 | 
			
		||||
    $scope.markdownContent = "";
 | 
			
		||||
 | 
			
		||||
    $scope.showRoleCode = function (role_name) {
 | 
			
		||||
      $scope.loadingModuleCode = true;
 | 
			
		||||
      $scope.markdownContent = '';
 | 
			
		||||
      $scope.docType = 'text';
 | 
			
		||||
      $scope.selectedFile.content = 'Loading Role Files..';
 | 
			
		||||
      $scope.selectedRole.tasks = null;
 | 
			
		||||
      $scope.roleData = null;
 | 
			
		||||
 | 
			
		||||
      if (!role_name) {
 | 
			
		||||
        $scope.selectedFile.content = "Select a module";
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ansible.getRoleFiles(role_name, function (response) {
 | 
			
		||||
        $scope.loadingModuleCode = false;
 | 
			
		||||
        $scope.selectedFile.content = JSON.stringify(response.data, null, '\t');
 | 
			
		||||
        $scope.docType = 'json';
 | 
			
		||||
        $scope.roleData = response.data;
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.$watch('selectedRole.role', function (newValue, oldValue) {
 | 
			
		||||
      if (newValue && newValue !== oldValue) {
 | 
			
		||||
        $scope.currentRole = newValue;
 | 
			
		||||
        $scope.reloadRole();
 | 
			
		||||
        //$scope.isopen.roles = false;
 | 
			
		||||
        $scope.isopen.filebrowser = true;
 | 
			
		||||
        localStorage.selectedRoleName = $scope.selectedRole.role;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $scope.reloadRole = function () {
 | 
			
		||||
      $scope.selectedFile.content = "Loading Code...";
 | 
			
		||||
      $scope.docType = 'txt';
 | 
			
		||||
      $scope.showRoleCode($scope.currentRole);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*var setDocType = function (data, file) {
 | 
			
		||||
     if (typeof data == 'object') {
 | 
			
		||||
     $scope.selectedFile.content = JSON.stringify(data, null, '\t');
 | 
			
		||||
     } else {
 | 
			
		||||
     $scope.selectedFile.content = data;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     $scope.docType = editor.ui_ace_doctype_map[file.extension.replace('.', '')];
 | 
			
		||||
 | 
			
		||||
     if ($scope.docType == 'markdown') {
 | 
			
		||||
     $scope.markdownContent = $scope.selectedFile.content;
 | 
			
		||||
     $scope.selectedFile.showSource = false;
 | 
			
		||||
     }
 | 
			
		||||
     };*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show selected item in the tree
 | 
			
		||||
     * @param file
 | 
			
		||||
     * @param parent
 | 
			
		||||
     */
 | 
			
		||||
    $scope.showSelected = function (file, parent, decrypt) {
 | 
			
		||||
 | 
			
		||||
      if($scope.editRole.value){
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      if (file.children) {
 | 
			
		||||
        $scope.selectedFile.content = JSON.stringify(file, null, '\t');
 | 
			
		||||
        $scope.docType = 'json';
 | 
			
		||||
        $scope.selectedRole.tasks = null;
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $scope.selectedFile.content = 'Loading..';
 | 
			
		||||
 | 
			
		||||
      var command = 'cat "' + file.path + '"';
 | 
			
		||||
      $scope.encryptedFile = false;
 | 
			
		||||
      if(decrypt){
 | 
			
		||||
        command = 'ansible-vault view "' + file.path + '" --vault-password-file ~/.vault_pass.txt'
 | 
			
		||||
        $scope.encryptedFile = true;
 | 
			
		||||
        $scope.selectedFile.content = 'Loading Encrypted File..';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $scope.selectedFile.showSource = true;
 | 
			
		||||
      $scope.markdownContent = '';
 | 
			
		||||
      $scope.docType = 'text';
 | 
			
		||||
 | 
			
		||||
      $scope.selectedRole.tasks = null;
 | 
			
		||||
      $scope.selectedFileName = file.name;
 | 
			
		||||
      $scope.selectedFilePath = file.path;
 | 
			
		||||
      $scope.parentNode = parent;
 | 
			
		||||
 | 
			
		||||
      ansible.executeCommand(command,
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.preChangeData = null;
 | 
			
		||||
          editor.setContentAndType(response.data, file, $scope.selectedFile);
 | 
			
		||||
 | 
			
		||||
          var parentDirectory = file.path.replace(/^(.+)\/(.+)\/([^/]+)$/, "$2");
 | 
			
		||||
          if (parentDirectory == 'tasks') {
 | 
			
		||||
            $scope.selectedRole.tasks = YAML.parse(response.data) || [];
 | 
			
		||||
            $scope.isopen.tasks = true;
 | 
			
		||||
            $scope.isopen.roles = false;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if(response.data.indexOf('ANSIBLE_VAULT') > -1){
 | 
			
		||||
            editor.setContentAndType('Decrypting content...', file, $scope.selectedFile);
 | 
			
		||||
            $scope.showSelected(file, parent, true);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
        }, function (response) {
 | 
			
		||||
          $scope.selectedFile.content = response.data;
 | 
			
		||||
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.showCreateFileModal = function (selectedFile, copyFile) {
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/roles/new_file/new_file.html',
 | 
			
		||||
        controller: 'NewFileController',
 | 
			
		||||
        size: 'md',
 | 
			
		||||
        backdrop: 'static',
 | 
			
		||||
        keyboard: false,
 | 
			
		||||
        closeByEscape: false,
 | 
			
		||||
        closeByDocument: false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          copyFile: function () {
 | 
			
		||||
            return copyFile
 | 
			
		||||
          },
 | 
			
		||||
          selectedDirectory: function () {
 | 
			
		||||
            if (selectedFile.type == 'directory')
 | 
			
		||||
              return selectedFile.path;
 | 
			
		||||
            else return $scope.parentNode.path
 | 
			
		||||
          },
 | 
			
		||||
          selectedFileName: function () {
 | 
			
		||||
            return selectedFile
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      modalInstance.result.then(function () {
 | 
			
		||||
        //$scope.getRoles();
 | 
			
		||||
        $scope.reloadRole();
 | 
			
		||||
      }, function () {
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.showCreateRoleModal = function (copyRole) {
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/roles/new_role/new_role.html',
 | 
			
		||||
        controller: 'NewRoleController',
 | 
			
		||||
        size: 'md',
 | 
			
		||||
        backdrop: 'static',
 | 
			
		||||
        keyboard: false,
 | 
			
		||||
        closeByEscape: false,
 | 
			
		||||
        closeByDocument: false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          copyRole: function () {
 | 
			
		||||
            return copyRole
 | 
			
		||||
          },
 | 
			
		||||
          selectedRoleName: function () {
 | 
			
		||||
            return $scope.selectedRole.role
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      modalInstance.result.then(function () {
 | 
			
		||||
        $scope.getRoles();
 | 
			
		||||
      }, function () {
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.showSearchRoleModal = function () {
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/roles/search_role/search_role.html',
 | 
			
		||||
        controller: 'SearchRoleController',
 | 
			
		||||
        size: 'lg',
 | 
			
		||||
        backdrop: 'static',
 | 
			
		||||
        keyboard: false,
 | 
			
		||||
        closeByEscape: false,
 | 
			
		||||
        closeByDocument: false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          selectedProject: function () {
 | 
			
		||||
            return $scope.$parent.selectedProject
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      modalInstance.result.then(function () {
 | 
			
		||||
        $scope.getRoles();
 | 
			
		||||
      }, function () {
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $scope.saveRole = function () {
 | 
			
		||||
      $scope.saveRoleLoading = true;
 | 
			
		||||
      ansible.createRole($scope.selectedRole.role, $scope.selectedFile.content,
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.saveRoleLoading = false;
 | 
			
		||||
          $scope.editRole.value = false;
 | 
			
		||||
        },
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.saveRoleLoading = false;
 | 
			
		||||
          $scope.err_msg = response.data;
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.deleteRole = function () {
 | 
			
		||||
      $scope.deleteRoleLoading = true;
 | 
			
		||||
      ansible.deleteRole($scope.selectedRole.role,
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.deleteRoleLoading = false;
 | 
			
		||||
          $scope.selectedRole.role = "";
 | 
			
		||||
          $scope.selectedFile.content = "";
 | 
			
		||||
          $scope.roleData = null;
 | 
			
		||||
          $scope.getRoles();
 | 
			
		||||
        },
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.deleteRoleLoading = false;
 | 
			
		||||
          $scope.err_msg = response.data;
 | 
			
		||||
          $scope.selectedFile.content = "";
 | 
			
		||||
          $scope.roleData = null;
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.loadingButtons = {};
 | 
			
		||||
    $scope.showSaveButton = {};
 | 
			
		||||
 | 
			
		||||
    // ------------- PLAYBOOK ------------------
 | 
			
		||||
    $scope.saveTasksFile = function (buttonStates) {
 | 
			
		||||
 | 
			
		||||
      buttonStates = buttonStates || {};
 | 
			
		||||
 | 
			
		||||
      buttonStates.loading = true;
 | 
			
		||||
      var tasksFileContent = $scope.selectedFile.content;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      ansible.createPlaybook($scope.selectedFilePath, tasksFileContent,
 | 
			
		||||
        function (response) {
 | 
			
		||||
          buttonStates.loading = false;
 | 
			
		||||
          buttonStates.save = false;
 | 
			
		||||
        },
 | 
			
		||||
        function (response) {
 | 
			
		||||
          buttonStates.loading = false;
 | 
			
		||||
          buttonStates.save = false;
 | 
			
		||||
          buttonStates.err_msg = false;
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $scope.updatePlaybookFileContent = function (save, buttonStates, preChangedData) {
 | 
			
		||||
      $scope.selectedRole.tasks = preChangedData || $scope.selectedRole.tasks;
 | 
			
		||||
      $scope.selectedFile.content = yamlFile.jsonToYamlFile($scope.selectedRole.tasks, 'Tasks File: ' + $scope.selectedFileName);
 | 
			
		||||
      if (save)
 | 
			
		||||
        $scope.saveTasksFile(buttonStates);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $scope.editFile = function (selectedFile) {
 | 
			
		||||
      if (selectedFile.type == 'directory')return;
 | 
			
		||||
 | 
			
		||||
      if (!$scope.preChangeData){
 | 
			
		||||
        console.log("No prechanged data, setting pre change data");
 | 
			
		||||
        $scope.preChangeData = angular.copy($scope.selectedFile.content);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $scope.editRole.value = true;
 | 
			
		||||
      $scope.showSaveFileButton = true;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.cancelFileChanges = function (selectedFile) {
 | 
			
		||||
      if ($scope.preChangeData) {
 | 
			
		||||
        console.log("Replacing content with pre changed data");
 | 
			
		||||
        $scope.selectedFile.content = angular.copy($scope.preChangeData);
 | 
			
		||||
        $scope.preChangeData = null;
 | 
			
		||||
        console.log("Clearing pre changed data")
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $scope.editRole.value = false;
 | 
			
		||||
      $scope.showSaveFileButton = false;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.saveFile = function (selectedFile) {
 | 
			
		||||
      $scope.showSaveFileButtonLoading = true;
 | 
			
		||||
      $scope.preChangeData = null;
 | 
			
		||||
      ansible.updateFile(selectedFile.path, $scope.selectedFile.content,
 | 
			
		||||
        function (response) {
 | 
			
		||||
          $scope.showSaveFileButtonLoading = false;
 | 
			
		||||
          $scope.showSaveFileButton = false;
 | 
			
		||||
          $scope.editRole.value = false;
 | 
			
		||||
        }, function (error) {
 | 
			
		||||
          $scope.showSaveFileButtonLoading = false;
 | 
			
		||||
          $scope.err_msg = error.data;
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $scope.deleteFile = function (selectedFile) {
 | 
			
		||||
      ansible.deleteFile(selectedFile.path, function (response) {
 | 
			
		||||
        $scope.reloadRole();
 | 
			
		||||
      }, function (error) {
 | 
			
		||||
        $scope.showSaveFileButtonLoading = false;
 | 
			
		||||
        $scope.err_msg = error.data;
 | 
			
		||||
      })
 | 
			
		||||
    };
 | 
			
		||||
    // ------------------- EXECUTE PLAYBOOK MODAL -------------
 | 
			
		||||
 | 
			
		||||
    $scope.executeAnsiblePlayBook = function (tags, executionType, executionName, selectedPlay) {
 | 
			
		||||
      console.log("Tags type" + typeof tags);
 | 
			
		||||
 | 
			
		||||
      var projectRolesFolder = Projects.selectedProject.ansibleEngine.projectFolder + '/roles';
 | 
			
		||||
      var rolesFolder = projectRolesFolder + '/' + $scope.selectedRole.role;
 | 
			
		||||
      var roleName = $scope.selectedRole.role;
 | 
			
		||||
      console.log("Projects Roles Folder" + projectRolesFolder);
 | 
			
		||||
 | 
			
		||||
      var modalInstance = $uibModal.open({
 | 
			
		||||
        animation: true,
 | 
			
		||||
        /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
        templateUrl: 'app/designer/execution/executeModal.html',
 | 
			
		||||
        controller: 'ExecutionController',
 | 
			
		||||
        size: 'lg',
 | 
			
		||||
        backdrop: 'static',
 | 
			
		||||
        keyboard: false,
 | 
			
		||||
        closeByEscape: false,
 | 
			
		||||
        closeByDocument: false,
 | 
			
		||||
        resolve: {
 | 
			
		||||
          tags: function () {
 | 
			
		||||
            return tags
 | 
			
		||||
          },
 | 
			
		||||
          selectedProject: function () {
 | 
			
		||||
            return Projects.selectedProject
 | 
			
		||||
          },
 | 
			
		||||
          selectedPlaybook: function () {
 | 
			
		||||
            return {playbook: $scope.selectedRole.role + '/tests/test.yml'};
 | 
			
		||||
          },
 | 
			
		||||
          selectedPlay: function () {
 | 
			
		||||
            return selectedPlay
 | 
			
		||||
          },
 | 
			
		||||
          executionType: function () {
 | 
			
		||||
            return executionType
 | 
			
		||||
          },
 | 
			
		||||
          executionName: function () {
 | 
			
		||||
            return executionName
 | 
			
		||||
          },
 | 
			
		||||
          readOnly: function () {
 | 
			
		||||
            return false
 | 
			
		||||
          },
 | 
			
		||||
          runData: function () {
 | 
			
		||||
            return null
 | 
			
		||||
          },
 | 
			
		||||
          projectFolder: function () {
 | 
			
		||||
            return projectRolesFolder
 | 
			
		||||
          },
 | 
			
		||||
          roleName: function () {
 | 
			
		||||
            return roleName
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.roles', [uiRouter])
 | 
			
		||||
  .config(routes)
 | 
			
		||||
  .component('roles', {
 | 
			
		||||
    template: require('./roles.html'),
 | 
			
		||||
    controller: RolesComponent,
 | 
			
		||||
    controllerAs: 'rolesCtrl'
 | 
			
		||||
  })
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										17
									
								
								client/app/designer/roles/roles.component.spec.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								client/app/designer/roles/roles.component.spec.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Component: RolesComponent', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.roles'));
 | 
			
		||||
 | 
			
		||||
  var RolesComponent;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($componentController) {
 | 
			
		||||
    RolesComponent = $componentController('roles', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										0
									
								
								client/app/designer/roles/roles.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								client/app/designer/roles/roles.css
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										110
									
								
								client/app/designer/roles/roles.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								client/app/designer/roles/roles.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,110 @@
 | 
			
		|||
<div class="col-md-6">
 | 
			
		||||
  <uib-accordion close-others="false">
 | 
			
		||||
 | 
			
		||||
    <div uib-accordion-group class="panel-default" is-open="isopen.roles">
 | 
			
		||||
      <uib-accordion-heading>
 | 
			
		||||
        Roles <i class="pull-right glyphicon"
 | 
			
		||||
                 ng-class="{'glyphicon-chevron-down': isopen.roles, 'glyphicon-chevron-right': !isopen.roles}"></i>
 | 
			
		||||
      </uib-accordion-heading>
 | 
			
		||||
 | 
			
		||||
      <button class="btn btn-default" ng-click="showCreateRoleModal()">Create Role <span class="fa fa-plus"></span>
 | 
			
		||||
      </button>
 | 
			
		||||
      <button class="btn btn-default" ng-click="showSearchRoleModal()">Import Role <span
 | 
			
		||||
        class="fa fa-cloud-download"></span></button>
 | 
			
		||||
      <button class="btn btn-default" ng-disabled="!selectedRole.role" ng-click="showCreateRoleModal(true)">Copy Role <span class="fa fa-copy"></span>
 | 
			
		||||
      </button>
 | 
			
		||||
      <!--<button class="btn btn-default" ng-if="!editRole.value" ng-disabled="!selectedRole.role" ng-click="editRole.value = true">Edit <span
 | 
			
		||||
        ng-if="!saveRoleLoading" class="fa fa-edit"></span></button>
 | 
			
		||||
      <button class="btn btn-primary" ng-if="editRole.value" ng-disabled="!selectedRole.role" ng-click="saveRole()">Save
 | 
			
		||||
        <span ng-if="!saveRoleLoading" class="fa fa-save"></span><span ng-if="saveRoleLoading"
 | 
			
		||||
                                                                       class="fa fa-spinner fa-spin"></span></button>-->
 | 
			
		||||
      <button class="btn btn-danger" confirm="Are you sure you want to delete the role and all its contents? "
 | 
			
		||||
              ng-disabled="!selectedRole.role" ng-click="deleteRole()">Delete <span ng-if="!deleteRoleLoading"
 | 
			
		||||
                                                                                    class="fa fa-save"></span><span
 | 
			
		||||
        ng-if="deleteRoleLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
 | 
			
		||||
      <button class="btn btn-success"
 | 
			
		||||
              ng-disabled="!selectedRole.role" ng-click="executeAnsiblePlayBook(null,'Role',selectedRole.role,null)">Test <span
 | 
			
		||||
        class="fa fa-play"></span></button>
 | 
			
		||||
 | 
			
		||||
      <div class="table-responsive">
 | 
			
		||||
        <table class="table">
 | 
			
		||||
          <thead>
 | 
			
		||||
          <tr>
 | 
			
		||||
            <th>Select</th>
 | 
			
		||||
            <th>Name</th>
 | 
			
		||||
          </tr>
 | 
			
		||||
          </thead>
 | 
			
		||||
          <tbody>
 | 
			
		||||
          <tr ng-repeat="role in roles">
 | 
			
		||||
            <td><input name="playGroup" type="radio" ng-model="selectedRole.role" ng-value="role">
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>{{role}}</td>
 | 
			
		||||
          </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div uib-accordion-group class="panel-default" is-open="isopen.filebrowser" ng-show="roleData">
 | 
			
		||||
      <uib-accordion-heading>
 | 
			
		||||
        File Browser {{selectedRole.role ? '-' + selectedRole.role : ''}} <i class="pull-right glyphicon"
 | 
			
		||||
                                                                             ng-class="{'glyphicon-chevron-down': isopen.filebrowser, 'glyphicon-chevron-right': !isopen.filebrowser}"></i>
 | 
			
		||||
      </uib-accordion-heading>
 | 
			
		||||
 | 
			
		||||
      <treecontrol ng-show="roleData.children" class="tree-classic"
 | 
			
		||||
                   tree-model="roleData.children"
 | 
			
		||||
                   options="treeOptions"
 | 
			
		||||
                   on-selection="showSelected(node, $parentNode)"
 | 
			
		||||
                   selected-node="selectedFile"
 | 
			
		||||
                   filter-expression="{name: '!.git'}">
 | 
			
		||||
        {{node.name}}
 | 
			
		||||
      </treecontrol>
 | 
			
		||||
 | 
			
		||||
      <button class="btn btn-default" ng-click="showCreateFileModal(selectedFile)" ng-disabled="!(selectedFile.type === 'directory')">Create File <span class="fa fa-plus"></span>
 | 
			
		||||
      </button>
 | 
			
		||||
      <button class="btn btn-default" ng-show="!editRole.value" ng-disabled="!(selectedFile.type === 'file') || encryptedFile" ng-click="editFile(selectedFile)">Edit File <span class="fa fa-edit"></span>
 | 
			
		||||
      </button>
 | 
			
		||||
      <button class="btn btn-primary" ng-show="showSaveFileButton" ng-disabled="encryptedFile" ng-click="saveFile(selectedFile)">Save File <span ng-show="!showSaveFileButtonLoading" class="fa fa-save"></span><span ng-show="showSaveFileButtonLoading" class="fa fa-spin fa-spinner"></span>
 | 
			
		||||
      </button>
 | 
			
		||||
      <button class="btn btn-warning" ng-show="showSaveFileButton" confirm="Are you sure you want to cancel and discard the changes?" ng-click="cancelFileChanges(selectedFile)">Cancel <span class="fa fa-times"></span>
 | 
			
		||||
      </button>
 | 
			
		||||
      <button class="btn btn-danger" ng-disabled="!selectedFile.type" confirm="Are you sure you want to delete the selected {{selectedFile.type}}?" ng-click="deleteFile(selectedFile)">Delete <span class="fa fa-trash-o"></span>
 | 
			
		||||
      </button>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div uib-accordion-group class="panel-default" is-open="isopen.tasks" ng-show="selectedRole.tasks">
 | 
			
		||||
      <uib-accordion-heading>
 | 
			
		||||
        Tasks {{selectedRole.role ? '-' + selectedRole.role : ''}} <i class="pull-right glyphicon"
 | 
			
		||||
                                                                      ng-class="{'glyphicon-chevron-down': isopen.tasks, 'glyphicon-chevron-right': !isopen.tasks}"></i>
 | 
			
		||||
      </uib-accordion-heading>
 | 
			
		||||
 | 
			
		||||
      <tasks tasks-list="selectedRole.tasks" update-playbook-file-content="updatePlaybookFileContent" selected-play="selectedPlay" selected-role="selectedRole" move-up="moveUp" move-down="moveDown" save-playbook="saveTasksFile" files="parentNode.children" execute-ansible-play-book="executeAnsiblePlayBook"></tasks>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </uib-accordion>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="col-md-6">
 | 
			
		||||
 | 
			
		||||
  <button class="btn btn-default" ng-show="selectedFile.docType == 'markdown' && !selectedFile.showSource" ng-click="selectedFile.showSource = true"> Show
 | 
			
		||||
    Source
 | 
			
		||||
  </button>
 | 
			
		||||
  <button class="btn btn-default" ng-show="selectedFile.docType == 'markdown' && selectedFile.showSource" ng-click="selectedFile.showSource = false"> Hide
 | 
			
		||||
    Source
 | 
			
		||||
  </button>
 | 
			
		||||
  <!--{{docType}}-->
 | 
			
		||||
  <div ng-show="selectedFile.showSource" ng-readonly="!editRole.value"
 | 
			
		||||
       ui-ace="{theme:'twilight',document:selectedFile.docType,mode:selectedFile.docType,onChange:codeChanged,onLoad:aceLoaded}" ng-model="selectedFile.content">
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div ng-show="!selectedFile.showSource" btf-markdown="selectedFile.markdownContent">
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										10
									
								
								client/app/designer/roles/roles.routes.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/app/designer/roles/roles.routes.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
export default function($stateProvider) {
 | 
			
		||||
  'ngInject';
 | 
			
		||||
  $stateProvider
 | 
			
		||||
    .state('designer.roles', {
 | 
			
		||||
      url: '/roles',
 | 
			
		||||
      template: '<roles></roles>'
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function searchRoleController($scope,ansible,selectedProject,$uibModalInstance) {
 | 
			
		||||
 | 
			
		||||
  $scope.searchText = '';
 | 
			
		||||
  $scope.searchLoading = false;
 | 
			
		||||
 | 
			
		||||
  $scope.selectedRole = {role:{}};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  $scope.searchRoles = function(){
 | 
			
		||||
    $scope.searchResult = [];
 | 
			
		||||
    $scope.searchLoading = true;
 | 
			
		||||
    ansible.searchRolesGalaxy($scope.searchText,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.searchLoading = false;
 | 
			
		||||
        $scope.searchResult = $scope.searchResult.concat(response.data)
 | 
			
		||||
      },function(response){
 | 
			
		||||
        $scope.searchLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    ansible.searchRolesGithub($scope.searchText,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.searchLoading = false;
 | 
			
		||||
        $scope.searchResult = $scope.searchResult.concat(response.data)
 | 
			
		||||
      },function(response){
 | 
			
		||||
        $scope.searchLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  $scope.importRole = function(role){
 | 
			
		||||
    $scope.importLoading = true;
 | 
			
		||||
 | 
			
		||||
    if(role.type === 'galaxy')role.url = role.name;
 | 
			
		||||
 | 
			
		||||
    ansible.importRole(role.type,role.url,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.importLoading = false;
 | 
			
		||||
        $scope.ok();
 | 
			
		||||
      },function(response){
 | 
			
		||||
        $scope.importLoading = false;
 | 
			
		||||
        $scope.err_msg = response.data
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  $scope.ok = function () {
 | 
			
		||||
    $uibModalInstance.close(null);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.search_role', [])
 | 
			
		||||
  .controller('SearchRoleController', searchRoleController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: SearchRoleCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.search_role'));
 | 
			
		||||
 | 
			
		||||
  var SearchRoleCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    SearchRoleCtrl = $controller('SearchRoleCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										70
									
								
								client/app/designer/roles/search_role/search_role.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								client/app/designer/roles/search_role/search_role.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <h4 class="modal-title">Search Role</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body">
 | 
			
		||||
 | 
			
		||||
    <p class="form-group">
 | 
			
		||||
      <label>Search</label>
 | 
			
		||||
      <input type="text" ng-model="searchText" class="form-control">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div ng-repeat="role in searchResult">
 | 
			
		||||
        <div class="clearfix" ng-if="($index % 3) == 0"></div>
 | 
			
		||||
        <div class="col-md-4" >
 | 
			
		||||
 | 
			
		||||
          <div class="panel">
 | 
			
		||||
            <div class="panel panel-default">
 | 
			
		||||
              <!--<div class="panel-heading"></div>-->
 | 
			
		||||
              <div class="panel-body">
 | 
			
		||||
                <span class="fa fa-spin fa-spinner fa-4x" ng-show="searchLoading"></span>
 | 
			
		||||
                <h4>
 | 
			
		||||
                  <span class="fa fa-github fa-2x" ng-if="role.type == 'gitrepo'"></span>
 | 
			
		||||
                  <img src="assets/images/ansible_icon.png" ng-if="role.type == 'galaxy'">
 | 
			
		||||
                  {{role.name}}
 | 
			
		||||
                </h4>
 | 
			
		||||
                {{role.description}}
 | 
			
		||||
                <br>
 | 
			
		||||
                <button class="btn btn-success btn-sm" ng-click="importRole(role)"> <span class="fa fa-cloud-download"></span> Import</button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!--<div class="table-responsive">
 | 
			
		||||
      <table class="table">
 | 
			
		||||
        <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <th>Select</th>
 | 
			
		||||
          <th>Name</th>
 | 
			
		||||
          <th>Description</th>
 | 
			
		||||
          <th>Type</th>
 | 
			
		||||
        </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
        <tr ng-repeat="role in searchResult">
 | 
			
		||||
          <td><input name="roleGroup" type="radio" ng-model="selectedRole.role" ng-value="role">
 | 
			
		||||
          </td>
 | 
			
		||||
          <td>{{role.name}}</td>
 | 
			
		||||
          <td>{{role.description}}</td>
 | 
			
		||||
          <td>{{role.type}}</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
      </table>
 | 
			
		||||
    </div>
 | 
			
		||||
    {{selectedRole}}-->
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <button class="btn btn-primary" ng-disabled="!searchText" ng-click="searchRoles()">Search <span ng-if="!searchLoading" class="fa fa-search"></span> <span ng-if="searchLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
    <!--<button class="btn btn-primary" ng-disabled="!selectedRole.role.name" ng-click="importRole()">Import <span ng-if="importLoading" class="fa fa-spinner fa-spin"></span></button>-->
 | 
			
		||||
    <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										446
									
								
								client/app/designer/tasks/new_task/new_task.controller.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										446
									
								
								client/app/designer/tasks/new_task/new_task.controller.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,446 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export function newTaskController($window, $scope, $sce, $uibModal, ansi2html, ansible, $uibModalInstance, tasksList, selectedTaskIndex, copyTask, files, selectedPlay, selectedRole, $filter, Projects) {
 | 
			
		||||
  var selectedTask;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Edit task - in case of edit task , selectedTaskIndex is not null.
 | 
			
		||||
   * Set selectedTask to a copy of selected task to edit.
 | 
			
		||||
   */
 | 
			
		||||
  if(selectedTaskIndex > -1 && tasksList){
 | 
			
		||||
    if(copyTask){
 | 
			
		||||
      selectedTask = angular.copy(tasksList[selectedTaskIndex]);
 | 
			
		||||
      selectedTask.name = "Copy of " + selectedTask.name;
 | 
			
		||||
      selectedTaskIndex = null;
 | 
			
		||||
    }else{
 | 
			
		||||
      selectedTask = tasksList[selectedTaskIndex]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * List of files for include purpose
 | 
			
		||||
   */
 | 
			
		||||
  if(files){
 | 
			
		||||
    $scope.files = files;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $scope.getModuleDescriptionLoading = false;
 | 
			
		||||
  $scope.modulesLoading = false;
 | 
			
		||||
 | 
			
		||||
  $scope.modules = null;
 | 
			
		||||
  $scope.singeLineModules = ["shell"];
 | 
			
		||||
  $scope.showHelp = false;
 | 
			
		||||
 | 
			
		||||
  $scope.newTask = {};
 | 
			
		||||
  $scope.title = "New Task";
 | 
			
		||||
  $scope.createTaskLoading = false;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get Ansible Modules
 | 
			
		||||
   * If Edit Task, get module description for selected task
 | 
			
		||||
   */
 | 
			
		||||
  $scope.getAnsibleModules = function(){
 | 
			
		||||
    $scope.modulesLoading = true;
 | 
			
		||||
    ansible.getAnsibleModules(function(response){
 | 
			
		||||
      $scope.modules = response;
 | 
			
		||||
      $scope.modulesLoading = false;
 | 
			
		||||
 | 
			
		||||
      if(selectedTask){
 | 
			
		||||
        $scope.title = "Edit Task";
 | 
			
		||||
        selectedTask = angular.copy(selectedTask);
 | 
			
		||||
        $scope.newTask = selectedTask;
 | 
			
		||||
        if(selectedTask.tags)$scope.newTask.tags = $scope.newTask.tags.join(',');
 | 
			
		||||
        var module = $scope.getModuleFromTask(selectedTask);
 | 
			
		||||
        $scope.getModuleDescription(module,true)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    }, function(response){
 | 
			
		||||
      $scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>").replace(/ /g," "));
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get Module Description whenever a module is selected by the user
 | 
			
		||||
   * @param module - Module Object
 | 
			
		||||
   * @param override - Override variables in case of edit task
 | 
			
		||||
   * @param refresh - Refresh module description from server. Don't display from cache
 | 
			
		||||
   */
 | 
			
		||||
  $scope.getModuleDescription = function(module,override,refresh){
 | 
			
		||||
 | 
			
		||||
    if(!module)return;
 | 
			
		||||
 | 
			
		||||
    var module_copy = angular.copy(module);
 | 
			
		||||
 | 
			
		||||
    $scope.getModuleDescriptionLoading = true;
 | 
			
		||||
    var moduleName = module.name;
 | 
			
		||||
 | 
			
		||||
    if($scope.singeLineModules.indexOf(moduleName) > -1){
 | 
			
		||||
      module.singleLine = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $scope.detailHelp = "";
 | 
			
		||||
    $scope.examples = "";
 | 
			
		||||
    module.variables = [];
 | 
			
		||||
 | 
			
		||||
    ansible.getAnsibleModuleDescription(moduleName,
 | 
			
		||||
      function(response){
 | 
			
		||||
        $scope.showHelp = true;
 | 
			
		||||
        $scope.result = $sce.trustAsHtml(ansi2html.toHtml(response).replace(/\n/g, "<br>").replace(/ /g," "));
 | 
			
		||||
 | 
			
		||||
        $scope.detailHelp = response;
 | 
			
		||||
        $scope.examples = response.substr(response.indexOf("#"));
 | 
			
		||||
        //var re = /(^[-=] .*)/gm;
 | 
			
		||||
        //var re = /(^[-=] .*)[^]*?(?:(\(Choices[^]+?\))?\s*(\[.*\])|(?=^[-=]|^EXAMPLES))/gm;
 | 
			
		||||
        var re = /(^[-=] .*)([^]*?)(?:(\(Choices[^]+?\))?\s*(\[.*\])|(?=^[-=]|^EXAMPLES))/gm;
 | 
			
		||||
        var m;
 | 
			
		||||
 | 
			
		||||
        while ((m = re.exec($scope.detailHelp.split("EXAMPLES")[0]+"EXAMPLES")) !== null) {
 | 
			
		||||
          //while ((m = re.exec($scope.detailHelp.split("#")[0])) !== null) {
 | 
			
		||||
          //while ((m = re.exec($scope.detailHelp)) !== null) {
 | 
			
		||||
          if (m.index === re.lastIndex) {
 | 
			
		||||
            re.lastIndex++;
 | 
			
		||||
          }
 | 
			
		||||
          // View your result using the m-variable.
 | 
			
		||||
          // eg m[0] etc.
 | 
			
		||||
 | 
			
		||||
          var option_name = m[1];
 | 
			
		||||
          var description = m[2];
 | 
			
		||||
          var choices = m[3];
 | 
			
		||||
          var default_value = m[4];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
          var breakup = option_name.split(" ");
 | 
			
		||||
          var variable_name = breakup[1];
 | 
			
		||||
          var mandatory = breakup[0] == "=";
 | 
			
		||||
 | 
			
		||||
          var complex_value = {};
 | 
			
		||||
 | 
			
		||||
          if(default_value)
 | 
			
		||||
            default_value = default_value.replace(/\[Default: (.*)\]/,"$1");
 | 
			
		||||
 | 
			
		||||
          if(default_value == 'None')
 | 
			
		||||
            default_value = null
 | 
			
		||||
 | 
			
		||||
          var variable_value = default_value || '';
 | 
			
		||||
 | 
			
		||||
          if(choices)
 | 
			
		||||
            choices = choices.replace(/\s+/g,"").replace(/\n\s+/g,"").replace(/\(Choices:(.*)\)/,"$1").split(",");
 | 
			
		||||
 | 
			
		||||
          if(override && module_copy.variables){
 | 
			
		||||
            var matching_variable = module_copy.variables.filter(function(item){
 | 
			
		||||
              if(item.name == variable_name){
 | 
			
		||||
                return true
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
            if(matching_variable.length){
 | 
			
		||||
              variable_value = matching_variable[0].value;
 | 
			
		||||
              if(typeof variable_value=='object'){
 | 
			
		||||
                complex_value  = angular.copy(variable_value)
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          module.variables.push({name:variable_name,description:description,mandatory:mandatory,value:variable_value,complexValue:complex_value,choices:choices,default_value:default_value});
 | 
			
		||||
          $scope.getModuleDescriptionLoading = false;
 | 
			
		||||
        }
 | 
			
		||||
      }, function(response){
 | 
			
		||||
        $scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
 | 
			
		||||
        console.log(ansi2html.toHtml(response.data));
 | 
			
		||||
        $scope.detailHelp = response.data;
 | 
			
		||||
        $scope.getModuleDescriptionLoading = false;
 | 
			
		||||
      },refresh)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Reload Module Description and variables. Ignore displaying from cache.
 | 
			
		||||
   * Used when a custom module is updated and description and variables info need to be updated.
 | 
			
		||||
   * @param module - module object
 | 
			
		||||
   */
 | 
			
		||||
  $scope.reloadModuleDetails = function(module){
 | 
			
		||||
 | 
			
		||||
    if(selectedTask){
 | 
			
		||||
      $scope.getModuleDescription(module,true,true)
 | 
			
		||||
    }else{
 | 
			
		||||
      $scope.getModuleDescription(module,false,true)
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Identify module from a given task object.
 | 
			
		||||
   * @param task - Single task object containing task properties
 | 
			
		||||
   * @returns {{}}
 | 
			
		||||
   */
 | 
			
		||||
  $scope.getModuleFromTask = function(task){
 | 
			
		||||
    var moduleObject = {};
 | 
			
		||||
    $scope.local_action = false;
 | 
			
		||||
    var task_properties = null;
 | 
			
		||||
 | 
			
		||||
    var module =  ansible.getModuleFromTask(task);
 | 
			
		||||
 | 
			
		||||
    if(module === 'include'){
 | 
			
		||||
      module = null;
 | 
			
		||||
      task.tags = task.include.replace(/.*tags=(.*)/,"$1")
 | 
			
		||||
      return;
 | 
			
		||||
    }else if(module === 'local_action'){
 | 
			
		||||
      $scope.local_action = true;
 | 
			
		||||
      module = task.local_action.module;
 | 
			
		||||
      task_properties = task.local_action;
 | 
			
		||||
      delete task_properties.module;
 | 
			
		||||
    }else{
 | 
			
		||||
      task_properties = task[module];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    angular.forEach($scope.modules, function(item,index) {
 | 
			
		||||
      if(item.name == module){
 | 
			
		||||
        moduleObject = item;
 | 
			
		||||
        $scope.newTask.module = item;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if(!(moduleObject && moduleObject.name)){
 | 
			
		||||
      $scope.err_msg = "Unable to find module " + module + " in Ansible controller";
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //moduleObject.name = module;
 | 
			
		||||
    moduleObject.variables = [];
 | 
			
		||||
    if(typeof task_properties == "string"){
 | 
			
		||||
      moduleObject.variables.push({'name':'free_form','value':task_properties});
 | 
			
		||||
 | 
			
		||||
      var re = /\b(\w+)=\s*([^=]*\S)\b\s*(?=\w+=|$)/g;
 | 
			
		||||
      var m;
 | 
			
		||||
 | 
			
		||||
      while ((m = re.exec(task_properties)) !== null) {
 | 
			
		||||
        if (m.index === re.lastIndex) {
 | 
			
		||||
          re.lastIndex++;
 | 
			
		||||
        }
 | 
			
		||||
        // View your result using the m-variable.
 | 
			
		||||
        // eg m[0] etc.
 | 
			
		||||
        var k=m[1];
 | 
			
		||||
        var v=m[2];
 | 
			
		||||
        moduleObject.variables.push({'name':k,'value':v})
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    }else if(typeof task_properties == "object"){
 | 
			
		||||
 | 
			
		||||
      angular.forEach(task_properties,function(value,key){
 | 
			
		||||
        this.push({'name':key,'value':value,'complexValue':value})
 | 
			
		||||
      },moduleObject.variables)
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    return moduleObject
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Create Task - Creates new task object and set task variables.
 | 
			
		||||
   * Push new task object to tasksList
 | 
			
		||||
   * Close new task modal
 | 
			
		||||
   */
 | 
			
		||||
  $scope.createTask = function(){
 | 
			
		||||
 | 
			
		||||
    if(!$scope.newTask.module && !$scope.newTask.include){
 | 
			
		||||
      $scope.err_msg = "Must select atleast one module or include statement";
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $scope.createTaskLoading = true;
 | 
			
		||||
 | 
			
		||||
    if(!tasksList){
 | 
			
		||||
      tasksList = []
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var taskObject = {name:$scope.newTask.name};
 | 
			
		||||
 | 
			
		||||
    if($scope.newTask.include)
 | 
			
		||||
      taskObject['include'] = $scope.newTask.include;
 | 
			
		||||
 | 
			
		||||
    if($scope.newTask.tags)
 | 
			
		||||
      taskObject['tags'] = $scope.newTask.tags.split(',');
 | 
			
		||||
 | 
			
		||||
    if($scope.newTask.register)
 | 
			
		||||
      taskObject['register'] = $scope.newTask.register;
 | 
			
		||||
 | 
			
		||||
    if($scope.newTask.async){
 | 
			
		||||
      taskObject['async'] = $scope.newTask.async;
 | 
			
		||||
      if(!$scope.newTask.poll)
 | 
			
		||||
        $scope.newTask.poll = 10;
 | 
			
		||||
      taskObject['poll'] = $scope.newTask.poll;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var variablesObject = null;
 | 
			
		||||
    if($scope.newTask.module){
 | 
			
		||||
      if($scope.newTask.module.singleLine){
 | 
			
		||||
        variablesObject = "";
 | 
			
		||||
        //Add all mandatory variables first
 | 
			
		||||
        angular.forEach($scope.newTask.module.variables.filter(function(item){
 | 
			
		||||
          return item.mandatory
 | 
			
		||||
        }),function(item){
 | 
			
		||||
          if(item.name == 'free_form'){
 | 
			
		||||
            variablesObject += item.value;
 | 
			
		||||
          }else if(item.value){
 | 
			
		||||
            variablesObject += " " + item.name + "=" + item.value;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        //Add optional variables
 | 
			
		||||
        angular.forEach($scope.newTask.module.variables.filter(function(item){
 | 
			
		||||
          return !item.mandatory
 | 
			
		||||
        }),function(item){
 | 
			
		||||
          if(item.value != item.default_value){
 | 
			
		||||
            if(item.name == 'free_form'){
 | 
			
		||||
              variablesObject += item.value;
 | 
			
		||||
            }else if(item.value){
 | 
			
		||||
              variablesObject += " " + item.name + "=" + item.value;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
      }else{
 | 
			
		||||
        variablesObject = {};
 | 
			
		||||
        angular.forEach($scope.newTask.module.variables,function(item){
 | 
			
		||||
          if((item.value || (item.isComplexVariable && item.complexValue)) && item.value != item.default_value){
 | 
			
		||||
            if(item.isComplexVariable){
 | 
			
		||||
              variablesObject[item.name] = item.complexValue;
 | 
			
		||||
            }else{
 | 
			
		||||
              variablesObject[item.name] = item.value;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      taskObject[$scope.newTask.module.name] = variablesObject;
 | 
			
		||||
 | 
			
		||||
      if($scope.local_action){
 | 
			
		||||
        variablesObject.module = $scope.newTask.module.name;
 | 
			
		||||
        taskObject['local_action'] = variablesObject;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if(selectedTaskIndex != null){
 | 
			
		||||
      // If Edit Task
 | 
			
		||||
 | 
			
		||||
      tasksList[selectedTaskIndex] = taskObject
 | 
			
		||||
 | 
			
		||||
    }else{
 | 
			
		||||
      // If New Task
 | 
			
		||||
 | 
			
		||||
      tasksList.push(taskObject);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $uibModalInstance.close(taskObject);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Close modal
 | 
			
		||||
   */
 | 
			
		||||
  $scope.ok = function () {
 | 
			
		||||
    $uibModalInstance.close($scope.newTask);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Cancel modal
 | 
			
		||||
   */
 | 
			
		||||
  $scope.cancel = function () {
 | 
			
		||||
    $uibModalInstance.dismiss('cancel');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get host variables using Ansible Python API in the backend
 | 
			
		||||
   */
 | 
			
		||||
  $scope.getHostVars = function(){
 | 
			
		||||
 | 
			
		||||
    if(!(selectedPlay && selectedPlay.play && selectedPlay.play.hosts))return;
 | 
			
		||||
 | 
			
		||||
    ansible.getVars(Projects.selectedInventoryFileName,selectedPlay.play.hosts,function(response){
 | 
			
		||||
      console.log(response.data);
 | 
			
		||||
      if(response.data.length)
 | 
			
		||||
        $scope.hostvars = $filter('dictToKeyValueArray')(response.data[0]);
 | 
			
		||||
      else $scope.err_msg = "Getting host variables - No variables returned" ;
 | 
			
		||||
 | 
			
		||||
    },function(error){
 | 
			
		||||
      console.log(error.data);
 | 
			
		||||
      $scope.err_msg = "Getting host variables - " + error.data;
 | 
			
		||||
    })
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if(selectedPlay)
 | 
			
		||||
    $scope.getHostVars();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  $scope.getRoleVars = function(){
 | 
			
		||||
 | 
			
		||||
    if(!(selectedRole && selectedRole.role))return;
 | 
			
		||||
 | 
			
		||||
    ansible.getRoleVars(selectedRole.role,function(response){
 | 
			
		||||
      console.log(response.data);
 | 
			
		||||
      if(response.data)
 | 
			
		||||
        $scope.hostvars = $filter('dictToKeyValueArray')(response.data);
 | 
			
		||||
      else $scope.err_msg = "Getting host variables - No variables returned" ;
 | 
			
		||||
 | 
			
		||||
    },function(error){
 | 
			
		||||
      console.log(error.data);
 | 
			
		||||
      $scope.err_msg = "Getting host variables - " + error.data;
 | 
			
		||||
    })
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if(selectedRole)
 | 
			
		||||
    $scope.getRoleVars();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  if(!$scope.modules){
 | 
			
		||||
    $scope.getAnsibleModules();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $scope.showComplexVariable = function(variable){
 | 
			
		||||
    variable.isComplexVariable = true;
 | 
			
		||||
    var modalInstance = $uibModal.open({
 | 
			
		||||
      animation: true,
 | 
			
		||||
      /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
      templateUrl: 'app/modals/complex_var_modal/complexVariable.html',
 | 
			
		||||
      controller: 'ComplexVarModalController',
 | 
			
		||||
      size: 'sm',
 | 
			
		||||
      backdrop: 'static',
 | 
			
		||||
      keyboard: false,
 | 
			
		||||
      closeByEscape: false,
 | 
			
		||||
      closeByDocument: false,
 | 
			
		||||
      resolve: {
 | 
			
		||||
        path: function () {
 | 
			
		||||
          return variable.name
 | 
			
		||||
        },
 | 
			
		||||
        hostvars: function(){
 | 
			
		||||
          return $scope.hostvars
 | 
			
		||||
        },
 | 
			
		||||
        members: function(){
 | 
			
		||||
          return variable.complexValue
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    modalInstance.result.then(function (selectedItem) {
 | 
			
		||||
      variable.complexValue = selectedItem
 | 
			
		||||
    }, function () {
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.new_task', [])
 | 
			
		||||
  .controller('NewTaskController', newTaskController)
 | 
			
		||||
  .name;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Controller: NewTaskCtrl', function() {
 | 
			
		||||
  // load the controller's module
 | 
			
		||||
  beforeEach(module('webAppApp.new_task'));
 | 
			
		||||
 | 
			
		||||
  var NewTaskCtrl;
 | 
			
		||||
 | 
			
		||||
  // Initialize the controller and a mock scope
 | 
			
		||||
  beforeEach(inject(function($controller) {
 | 
			
		||||
    NewTaskCtrl = $controller('NewTaskCtrl', {});
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should ...', function() {
 | 
			
		||||
    expect(1).to.equal(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										134
									
								
								client/app/designer/tasks/new_task/new_task.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								client/app/designer/tasks/new_task/new_task.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,134 @@
 | 
			
		|||
<!--<script type="text/ng-template" id="createTaskContent.html">-->
 | 
			
		||||
<!-- Modal content-->
 | 
			
		||||
<div class="modal-content" >
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <button type="button" class="close" ng-click="cancel()" data-dismiss="modal">×</button>
 | 
			
		||||
    <h4 class="modal-title">{{title}}</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-body" id="TaskCreationModal">
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div class="col-md-6">
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
          <span class="input-group-addon" >Name</span>
 | 
			
		||||
          <input ng-model="newTask.name" type="text" class="form-control" placeholder="Task Name">
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
          <span class="input-group-addon" >Include</span>
 | 
			
		||||
          <input type="text" ng-model="newTask.include" uib-typeahead="file.name as file.name for file in files | filter: $viewValue"  class="form-control">
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
          <span class="input-group-addon" >Module</span>
 | 
			
		||||
          <input type="text" ng-disabled="!modules || newTask.include" ng-model="newTask.module" typeahead-on-select="getModuleDescription(newTask.module)" uib-typeahead="module as module.name for module in modules | filter: $viewValue"  class="form-control">
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <i ng-if="modulesLoading" class="fa fa-spinner fa-spin"></i>
 | 
			
		||||
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
          <span class="input-group-addon" >Tags</span>
 | 
			
		||||
          <input ng-model="newTask.tags" type="text" class="form-control" placeholder="Tags separated by comma">
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
          <span class="input-group-addon" >Async (s)</span>
 | 
			
		||||
          <input ng-model="newTask.async" type="text" class="form-control" placeholder="Max run time in seconds">
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div ng-if="newTask.async">
 | 
			
		||||
          <div class="hint">{{newTask.async / 60 | number}} minutes</div>
 | 
			
		||||
          <div class="input-group" >
 | 
			
		||||
            <span class="input-group-addon" >Poll (s)</span>
 | 
			
		||||
            <input ng-model="newTask.poll" type="text" class="form-control" placeholder="10" ng-value="10">
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="input-group">
 | 
			
		||||
          <span class="input-group-addon" >Register Result</span>
 | 
			
		||||
          <input ng-model="newTask.register" type="text" class="form-control" placeholder="Variable Name">
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="hint">Register results of to a variable</div>
 | 
			
		||||
 | 
			
		||||
        <label class="checkbox-inline"><input type="checkbox" ng-model="newTask.module.singleLine">Single Line</label>
 | 
			
		||||
        <span ng-if="getModuleDescriptionLoading" class="fa fa-spinner fa-spin"></span>
 | 
			
		||||
 | 
			
		||||
        <div ng-if="newTask.module.variables">
 | 
			
		||||
          <h4>Arguments</h4>
 | 
			
		||||
          <div ng-repeat="variable in newTask.module.variables | orderBy : '-mandatory'">
 | 
			
		||||
            <!--<div class="input-group" ng-show="!variable.isVariable && !variable.choices.length">
 | 
			
		||||
              <span class="input-group-addon" >{{ variable.mandatory ? "*" : "" }} {{variable.name}}</span>
 | 
			
		||||
              <input ng-model="variable.value" type="text" class="form-control" required="{{variable.mandatory}}">
 | 
			
		||||
            </div>-->
 | 
			
		||||
 | 
			
		||||
            <!--Input Type - Default Typeahead - variable or string-->
 | 
			
		||||
            <div class="input-group" ng-show="!variable.choices.length">
 | 
			
		||||
              <span class="input-group-addon" uib-tooltip="{{variable.description}}">{{ variable.mandatory ? "*" : "" }} {{variable.name}}</span>
 | 
			
		||||
              <!--<select class="form-control" ng-model="variable.value" ng-options="('"\{\{' + var.name + '\}\}"') as (var.name + '-' + var.value) disable when var.disabled for var in hostvars">
 | 
			
		||||
              </select>-->
 | 
			
		||||
 | 
			
		||||
              <input type="text" ng-model="variable.value" uib-typeahead="('\{\{' + var.key + '\}\}') as (var.key + ' = ' + var.value) for var in hostvars | filter: $viewValue"  class="form-control" placeholder="{{variable.description}}">
 | 
			
		||||
 | 
			
		||||
              <span class="input-group-btn">
 | 
			
		||||
                <button class="btn btn-secondary" ng-click="showComplexVariable(variable)" uib-tooltip="Click to configure Complex Variable">{ }</button>
 | 
			
		||||
              </span>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <!--Input Type - Choice-->
 | 
			
		||||
            <div class="input-group" ng-show="variable.choices.length">
 | 
			
		||||
              <span class="input-group-addon" uib-tooltip="{{variable.description}}">{{ variable.mandatory ? "*" : "" }} {{variable.name}}</span>
 | 
			
		||||
              <select class="form-control" ng-model="variable.value" ng-options="choice for choice in variable.choices">
 | 
			
		||||
              </select>
 | 
			
		||||
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            <!--<div class="input-group" ng-show="variable.isComplexVariable">
 | 
			
		||||
              <span class="input-group-addon" >{{ variable.mandatory ? "*" : "" }} {{variable.name}}</span>
 | 
			
		||||
              <!–<json-tree json="variable.complexValue"></json-tree>–>
 | 
			
		||||
              <button class="btn btn-default" ng-click="showComplexVariable(variable)">Configure</button>
 | 
			
		||||
            </div>-->
 | 
			
		||||
 | 
			
		||||
            <!--<label class="checkbox-inline"><input type="checkbox" ng-model="variable.isVariable">Variable</label>-->
 | 
			
		||||
            <label class="checkbox-inline" ng-show="variable.isVariable"><input type="checkbox" ng-model="variable.isComplexVariable">Complex Variable</label>
 | 
			
		||||
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-md-6">
 | 
			
		||||
        <div ng-if="showHelp">
 | 
			
		||||
          <h4>Help</h4>
 | 
			
		||||
          <textarea rows="20" cols="60" ng-model="detailHelp" style="font-size: 13px"></textarea>
 | 
			
		||||
 | 
			
		||||
          <h4>Examples</h4>
 | 
			
		||||
          <textarea rows="20" cols="60" ng-model="examples" style="font-size: 13px"></textarea>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
        <!--{{newTask.module.variables}}-->
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div class="col-md-3" style="text-align: left">
 | 
			
		||||
        <button class="btn btn-default" type="button" ng-click="reloadModuleDetails(newTask.module)">Reload Module <span class="fa fa-refresh"></span></button>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-md-9" style="text-align: right">
 | 
			
		||||
        <button class="btn btn-primary" ng-click="createTask()">Save <span ng-if="createTaskLoading" class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
        <button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
<!--
 | 
			
		||||
</script>
 | 
			
		||||
-->
 | 
			
		||||
							
								
								
									
										0
									
								
								client/app/designer/tasks/tasks.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								client/app/designer/tasks/tasks.css
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										229
									
								
								client/app/designer/tasks/tasks.directive.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								client/app/designer/tasks/tasks.directive.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,229 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
const angular = require('angular');
 | 
			
		||||
 | 
			
		||||
export default angular.module('webAppApp.tasks', [])
 | 
			
		||||
  .directive('tasks', function(ansible, $uibModal) {
 | 
			
		||||
    return {
 | 
			
		||||
      templateUrl: 'app/designer/tasks/tasks.html',
 | 
			
		||||
      restrict: 'EA',
 | 
			
		||||
      scope: {
 | 
			
		||||
        tasksList: '=',
 | 
			
		||||
        selectedPlay: '=',
 | 
			
		||||
        savePlaybook: '&',
 | 
			
		||||
        selectedRole: '=',
 | 
			
		||||
        updatePlaybookFileContent: '&',
 | 
			
		||||
        executeAnsiblePlayBook: '&',
 | 
			
		||||
        files: '=' //List of files for include purpose
 | 
			
		||||
      },
 | 
			
		||||
      link: function (scope, element, attrs) {
 | 
			
		||||
        scope.getModuleFromTask = ansible.getModuleFromTask;
 | 
			
		||||
 | 
			
		||||
        scope.buttonStates = {loading:false,save:false,err_msg:false};
 | 
			
		||||
 | 
			
		||||
        scope.tasksMetaData = [];
 | 
			
		||||
 | 
			
		||||
        scope.$watch('tasksList',function(){
 | 
			
		||||
          console.log('tasks list changed');
 | 
			
		||||
          scope.tasksMetaData = [];
 | 
			
		||||
 | 
			
		||||
          angular.forEach(scope.tasksList,function(task){
 | 
			
		||||
            var taskModule = ansible.getModuleFromTask(task);
 | 
			
		||||
            var taskName = task.name;
 | 
			
		||||
 | 
			
		||||
            if(taskModule === 'include'){
 | 
			
		||||
              taskName = task[taskModule].replace(/(.*yml) .*/,"$1")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            scope.tasksMetaData.push({taskModule:taskModule,taskName:taskName,selected:false})
 | 
			
		||||
          })
 | 
			
		||||
 | 
			
		||||
        },true);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Detect when the user selects tasks.
 | 
			
		||||
         * Enable play button if tasks are selected and has tags assigned
 | 
			
		||||
         * Enable delete button if tasks are selected
 | 
			
		||||
         */
 | 
			
		||||
        scope.$watch('tasksMetaData',function(newValue,oldValue){
 | 
			
		||||
          scope.selectedTasksPlayButton = false;
 | 
			
		||||
          scope.selectedTasksDeleteButton = false
 | 
			
		||||
 | 
			
		||||
          if(!(scope.tasksMetaData))return;
 | 
			
		||||
 | 
			
		||||
          var selectedTasks = scope.tasksMetaData.filter(item => item.selected);
 | 
			
		||||
          var includeTasks = scope.tasksMetaData.filter(item => item.taskModule === 'include');
 | 
			
		||||
          var selectedTasksWithoutTags = [];
 | 
			
		||||
 | 
			
		||||
          /**
 | 
			
		||||
           * Find selected tasks without any tags.
 | 
			
		||||
           * If there are any play button will not be enabled
 | 
			
		||||
           */
 | 
			
		||||
          angular.forEach(scope.tasksMetaData,function(item,index){
 | 
			
		||||
            scope.tasksListItem = scope.tasksList[index];
 | 
			
		||||
            if(!scope.tasksListItem.tags && item.selected){
 | 
			
		||||
              selectedTasksWithoutTags.push(scope.tasksListItem)
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          console.log("selectedTasksWithoutTags=")
 | 
			
		||||
          console.log(selectedTasksWithoutTags)
 | 
			
		||||
 | 
			
		||||
          if(selectedTasks.length){
 | 
			
		||||
            //if(!includeTasks.length && !selectedTasksWithoutTags.length){
 | 
			
		||||
            if(!selectedTasksWithoutTags.length){
 | 
			
		||||
              scope.selectedTasksPlayButton = true
 | 
			
		||||
            }
 | 
			
		||||
            scope.selectedTasksDeleteButton = true
 | 
			
		||||
 | 
			
		||||
          }else{
 | 
			
		||||
            scope.selectedTasksPlayButton = false;
 | 
			
		||||
            scope.selectedTasksDeleteButton = false
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
        },true);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //scope.moveUp = scope.moveUp();
 | 
			
		||||
        //scope.moveDown = scope.moveDown();
 | 
			
		||||
        scope.savePlaybook = scope.savePlaybook();
 | 
			
		||||
        scope.updatePlaybookFileContent = scope.updatePlaybookFileContent();
 | 
			
		||||
        scope.executeAnsiblePlayBook = scope.executeAnsiblePlayBook();
 | 
			
		||||
 | 
			
		||||
        scope.showTaskModal = function(selectedTaskIndex, copyTask){
 | 
			
		||||
          var modalInstance = $uibModal.open({
 | 
			
		||||
            animation: true,
 | 
			
		||||
            /*templateUrl: 'createTaskContent.html',*/
 | 
			
		||||
            templateUrl: 'app/designer/tasks/new_task/new_task.html',
 | 
			
		||||
            controller: 'NewTaskController',
 | 
			
		||||
            size: 'lg',
 | 
			
		||||
            backdrop  : 'static',
 | 
			
		||||
            keyboard  : false,
 | 
			
		||||
            closeByEscape : false,
 | 
			
		||||
            closeByDocument : false,
 | 
			
		||||
            resolve: {
 | 
			
		||||
              selectedProject: function () {
 | 
			
		||||
                return scope.$parent.selectedProject;
 | 
			
		||||
              },
 | 
			
		||||
              selectedPlay: function(){
 | 
			
		||||
                return scope.selectedPlay
 | 
			
		||||
              },
 | 
			
		||||
              selectedRole: function(){
 | 
			
		||||
                return scope.selectedRole
 | 
			
		||||
              },
 | 
			
		||||
              tasksList: function () {
 | 
			
		||||
                return scope.tasksList;
 | 
			
		||||
              },
 | 
			
		||||
              selectedTaskIndex: function(){
 | 
			
		||||
                return selectedTaskIndex
 | 
			
		||||
              },
 | 
			
		||||
              copyTask : function(){
 | 
			
		||||
                return copyTask
 | 
			
		||||
              },
 | 
			
		||||
              //List of files for include purpose
 | 
			
		||||
              files: function(){
 | 
			
		||||
                return scope.files
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          modalInstance.result.then(
 | 
			
		||||
            function (newTask) {
 | 
			
		||||
              // if(!selectedTaskIndex)
 | 
			
		||||
              //   scope.tasksList.push(newTask);
 | 
			
		||||
              scope.updatePlaybookFileContent(true);
 | 
			
		||||
              //$scope.selectedPlay = {play: ""};
 | 
			
		||||
            }, function () {
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        scope.deleteTask = function(index){
 | 
			
		||||
          scope.tasksList.splice(index,1);
 | 
			
		||||
          scope.updatePlaybookFileContent(true);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        scope.deleteTasks = function(){
 | 
			
		||||
 | 
			
		||||
          scope.tasksMetaData.filter(function(item, index){
 | 
			
		||||
            if(item.selected){
 | 
			
		||||
              scope.tasksList.splice(index,1);
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
          scope.updatePlaybookFileContent(true);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        scope.moveUp = function(list,index,buttonVariable){
 | 
			
		||||
          if(!scope.preChangeData) scope.preChangeData = angular.copy(list);
 | 
			
		||||
          var temp = angular.copy(list[index]);
 | 
			
		||||
          list[index] = list[index-1];
 | 
			
		||||
          list[index-1] = temp;
 | 
			
		||||
 | 
			
		||||
          scope.updatePlaybookFileContent(false);
 | 
			
		||||
 | 
			
		||||
          scope.buttonStates.save = true
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        scope.cancelChange = function(buttonVariable){
 | 
			
		||||
          if(scope.preChangeData){
 | 
			
		||||
            //scope.tasksList = angular.copy(scope.preChangeData);
 | 
			
		||||
            scope.selectedPlay.play.tasks = angular.copy(scope.preChangeData);
 | 
			
		||||
            scope.preChangeData = null
 | 
			
		||||
 | 
			
		||||
          }
 | 
			
		||||
          scope.updatePlaybookFileContent(false,null,scope.tasksList);
 | 
			
		||||
 | 
			
		||||
          scope.buttonStates.save = false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        scope.moveDown = function(list,index,buttonVariable){
 | 
			
		||||
          if(!scope.preChangeData) scope.preChangeData = angular.copy(list);
 | 
			
		||||
          var temp = angular.copy(list[index]);
 | 
			
		||||
          list[index] = list[index+1];
 | 
			
		||||
          list[index+1] = temp;
 | 
			
		||||
 | 
			
		||||
          scope.updatePlaybookFileContent(false);
 | 
			
		||||
          scope.buttonStates.save = true;
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        scope.executeSelectedTasks = function(){
 | 
			
		||||
 | 
			
		||||
          /*var selectedTasks = scope.tasksMetaData.map(function(item){return item.selected});*/
 | 
			
		||||
          var selectedTags = [];
 | 
			
		||||
          var selectedTaskNames = [];
 | 
			
		||||
          /*if(selectedTasks.length){
 | 
			
		||||
           selectedTags = selectedTasks.map(function(item){return item.tags});
 | 
			
		||||
           selectedTaskNames = selectedTasks.map(function(item){return item.name})
 | 
			
		||||
           }*/
 | 
			
		||||
 | 
			
		||||
          angular.forEach(scope.tasksMetaData, function(item,index){
 | 
			
		||||
            if(item.selected){
 | 
			
		||||
              if(scope.tasksList[index].tags){
 | 
			
		||||
                // As tags is an array and each task can have multiple tags
 | 
			
		||||
                var task_tags = scope.tasksList[index].tags
 | 
			
		||||
                if(typeof task_tags == 'object')
 | 
			
		||||
                  task_tags = task_tags[0]  //task_tags.join(',')
 | 
			
		||||
                selectedTags.push(task_tags);
 | 
			
		||||
                selectedTaskNames.push(scope.tasksList[index].name)
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          if(selectedTags.length){
 | 
			
		||||
            var play = scope.selectedPlay && scope.selectedPlay.play;
 | 
			
		||||
            scope.executeAnsiblePlayBook(selectedTags,'Tasks',selectedTaskNames.join(","),play)
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  })
 | 
			
		||||
  .name;
 | 
			
		||||
							
								
								
									
										20
									
								
								client/app/designer/tasks/tasks.directive.spec.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								client/app/designer/tasks/tasks.directive.spec.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
describe('Directive: tasks', function() {
 | 
			
		||||
  // load the directive's module and view
 | 
			
		||||
  beforeEach(module('webAppApp.tasks'));
 | 
			
		||||
  beforeEach(module('app/designer/tasks/tasks.html'));
 | 
			
		||||
 | 
			
		||||
  var element, scope;
 | 
			
		||||
 | 
			
		||||
  beforeEach(inject(function($rootScope) {
 | 
			
		||||
    scope = $rootScope.$new();
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  it('should make hidden element visible', inject(function($compile) {
 | 
			
		||||
    element = angular.element('<tasks></tasks>');
 | 
			
		||||
    element = $compile(element)(scope);
 | 
			
		||||
    scope.$apply();
 | 
			
		||||
    expect(element.text()).to.equal('this is the tasks directive');
 | 
			
		||||
  }));
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										53
									
								
								client/app/designer/tasks/tasks.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								client/app/designer/tasks/tasks.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
<div class="table-responsive">
 | 
			
		||||
  <table class="table">
 | 
			
		||||
    <thead>
 | 
			
		||||
    <tr>
 | 
			
		||||
      <th>Select</th>
 | 
			
		||||
      <th>Name</th>
 | 
			
		||||
      <th>Module</th>
 | 
			
		||||
      <th>Actions</th>
 | 
			
		||||
    </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody>
 | 
			
		||||
    <tr ng-repeat="task in tasksList">
 | 
			
		||||
      <td><input type="checkbox" ng-model="tasksMetaData[$index].selected">
 | 
			
		||||
      </td>
 | 
			
		||||
      <td>{{tasksMetaData[$index].taskName}}</td>
 | 
			
		||||
      <td>{{tasksMetaData[$index].taskModule}}</td>
 | 
			
		||||
      <td>
 | 
			
		||||
        <div class="btn-group">
 | 
			
		||||
          <label class="btn btn-default btn-sm" ng-click="showTaskModal($index)"><span
 | 
			
		||||
            class="fa fa-edit"></span></label>
 | 
			
		||||
          <!--<label class="btn btn-default btn-sm" ng-click="showTaskModal($index,true)"><span
 | 
			
		||||
            class="fa fa-copy"></span></label>-->
 | 
			
		||||
          <!--<label class="btn btn-danger btn-sm" ng-click="deleteTask($index)"
 | 
			
		||||
                 confirm="Are you sure you want to delete?"><span class="fa fa-trash"></span></label>-->
 | 
			
		||||
          <!--<div style="display: inline-block" tooltip-enable="!task.tags"
 | 
			
		||||
               uib-tooltip="Tag must be assigned to play individually"><label class="btn btn-success btn-sm"
 | 
			
		||||
                                                                              ng-disabled="!task.tags"
 | 
			
		||||
                                                                              ng-click="executeAnsiblePlayBook(task.tags,'Task',task.name, selectedPlay)"><span
 | 
			
		||||
            class="fa fa-play"></span></label></div>-->
 | 
			
		||||
          <label class="btn btn-primary btn-sm" ng-disabled="$first"
 | 
			
		||||
                 ng-click="moveUp(tasksList,$index,'saveTaskListLoading')"><span
 | 
			
		||||
            class="fa fa-arrow-up"></span></label>
 | 
			
		||||
          <label class="btn btn-primary btn-sm" ng-disabled="$last"
 | 
			
		||||
                 ng-click="moveDown(tasksList,$index,'saveTaskListLoading')"><span
 | 
			
		||||
            class="fa fa-arrow-down"></span></label>
 | 
			
		||||
        </div>
 | 
			
		||||
      </td>
 | 
			
		||||
    </tr>
 | 
			
		||||
    </tbody>
 | 
			
		||||
  </table>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<button class="btn btn-default" data-toggle="modal" data-target="#createTaskModal" ng-click="showTaskModal()">
 | 
			
		||||
  Create Task <span class="fa fa-plus"></span></button>
 | 
			
		||||
<button class="btn btn-success" data-toggle="modal" ng-disabled="!selectedTasksPlayButton" ng-if="!buttonStates.save"
 | 
			
		||||
        ng-click="executeSelectedTasks()"> Play <span class="fa fa-play"></span></button>
 | 
			
		||||
<button class="btn btn-danger" data-toggle="modal" ng-disabled="!selectedTasksDeleteButton" ng-if="!buttonStates.save" confirm="Are you sure you want to delete the selected tasks?"
 | 
			
		||||
        ng-click="deleteTasks()"> Delete <span class="fa fa-trash-o"></span></button>
 | 
			
		||||
<button class="btn btn-primary" ng-if="buttonStates.save"
 | 
			
		||||
        ng-click="savePlaybook(buttonStates)">Save <span ng-if="buttonStates.loading"
 | 
			
		||||
                                                         class="fa fa-spinner fa-spin"></span></button>
 | 
			
		||||
<button class="btn btn-warning" ng-if="buttonStates.save"
 | 
			
		||||
        ng-click="cancelChange(buttonStates)">Cancel <span class="fa fa-times"></span></button>
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue